diff --git a/Test.sln b/Test.sln index be21518af8..b47c833e4d 100644 --- a/Test.sln +++ b/Test.sln @@ -140,6 +140,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DapperExtensions.StrongName EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resources", "Resources", "{A45F0D24-1AF3-42BC-91A6-0262AFB1234D}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThreadingTest", "Tests\Kernels\ThreadingTest\ThreadingTest.csproj", "{9491798B-DE56-4300-9A3A-025A2F7B51C2}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AudioTests", "Tests\Kernels\AudioTests\AudioTests.csproj", "{8455DCAE-275E-47B3-B89B-2D9F3AB9977C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cosmos.Plugs", "source\Cosmos.Plugs\Cosmos.Plugs.csproj", "{0C65F6CA-C897-40A3-A36E-0CCCAD01D567}" @@ -791,6 +793,18 @@ Global {AC45D5B2-0D02-49B8-A88E-EABF34AE62B5}.TEST|Any CPU.Build.0 = Debug|Any CPU {AC45D5B2-0D02-49B8-A88E-EABF34AE62B5}.TEST|x86.ActiveCfg = Debug|Any CPU {AC45D5B2-0D02-49B8-A88E-EABF34AE62B5}.TEST|x86.Build.0 = Debug|Any CPU + {9491798B-DE56-4300-9A3A-025A2F7B51C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9491798B-DE56-4300-9A3A-025A2F7B51C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9491798B-DE56-4300-9A3A-025A2F7B51C2}.Debug|x86.ActiveCfg = Debug|Any CPU + {9491798B-DE56-4300-9A3A-025A2F7B51C2}.Debug|x86.Build.0 = Debug|Any CPU + {9491798B-DE56-4300-9A3A-025A2F7B51C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9491798B-DE56-4300-9A3A-025A2F7B51C2}.Release|Any CPU.Build.0 = Release|Any CPU + {9491798B-DE56-4300-9A3A-025A2F7B51C2}.Release|x86.ActiveCfg = Release|Any CPU + {9491798B-DE56-4300-9A3A-025A2F7B51C2}.Release|x86.Build.0 = Release|Any CPU + {9491798B-DE56-4300-9A3A-025A2F7B51C2}.TEST|Any CPU.ActiveCfg = TEST|Any CPU + {9491798B-DE56-4300-9A3A-025A2F7B51C2}.TEST|Any CPU.Build.0 = TEST|Any CPU + {9491798B-DE56-4300-9A3A-025A2F7B51C2}.TEST|x86.ActiveCfg = TEST|Any CPU + {9491798B-DE56-4300-9A3A-025A2F7B51C2}.TEST|x86.Build.0 = TEST|Any CPU {8455DCAE-275E-47B3-B89B-2D9F3AB9977C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8455DCAE-275E-47B3-B89B-2D9F3AB9977C}.Debug|Any CPU.Build.0 = Debug|Any CPU {8455DCAE-275E-47B3-B89B-2D9F3AB9977C}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -881,6 +895,7 @@ Global {30D9FA9C-0B4D-40FF-8903-6B9E9C825729} = {ECEA7778-E786-4317-90B9-A2D4427CB91C} {96855A39-A96B-4BDB-A6AE-29676DFEF637} = {29EEC029-6A2B-478A-B6E5-D63A91388ABA} {AC45D5B2-0D02-49B8-A88E-EABF34AE62B5} = {A45F0D24-1AF3-42BC-91A6-0262AFB1234D} + {9491798B-DE56-4300-9A3A-025A2F7B51C2} = {29EEC029-6A2B-478A-B6E5-D63A91388ABA} {8455DCAE-275E-47B3-B89B-2D9F3AB9977C} = {29EEC029-6A2B-478A-B6E5-D63A91388ABA} {0C65F6CA-C897-40A3-A36E-0CCCAD01D567} = {9A923E6F-FF63-4F02-A4EA-C2D44F9323FD} EndGlobalSection diff --git a/Tests/Cosmos.TestRunner.Full/Cosmos.TestRunner.Full.csproj b/Tests/Cosmos.TestRunner.Full/Cosmos.TestRunner.Full.csproj index 6246b420ed..1b9f177d91 100644 --- a/Tests/Cosmos.TestRunner.Full/Cosmos.TestRunner.Full.csproj +++ b/Tests/Cosmos.TestRunner.Full/Cosmos.TestRunner.Full.csproj @@ -34,6 +34,7 @@ + diff --git a/Tests/Cosmos.TestRunner.Full/TestKernelSets.cs b/Tests/Cosmos.TestRunner.Full/TestKernelSets.cs index 6e2f0e1c35..b91a29397c 100644 --- a/Tests/Cosmos.TestRunner.Full/TestKernelSets.cs +++ b/Tests/Cosmos.TestRunner.Full/TestKernelSets.cs @@ -38,6 +38,7 @@ public static IEnumerable GetStableKernelTypes() //yield return typeof(ConsoleTest.Kernel); yield return typeof(MemoryOperationsTest.Kernel); yield return typeof(ProcessorTests.Kernel); + yield return typeof(ThreadingTests.Kernel); } } } diff --git a/Tests/Kernels/ThreadingTest/Kernel.cs b/Tests/Kernels/ThreadingTest/Kernel.cs new file mode 100644 index 0000000000..cb86220ebb --- /dev/null +++ b/Tests/Kernels/ThreadingTest/Kernel.cs @@ -0,0 +1,80 @@ +using System; +using Sys = Cosmos.System; +using Cosmos.System; +using Cosmos.TestRunner; + +namespace ThreadingTests +{ + public class Kernel : Sys.Kernel + { + int variable = 0; + int variableTwo = 0; + int variableThree = 0; + + protected override void BeforeRun() + { + Global.Debugger.Send("Cosmos booted successfully. Let's Test Threading!"); + } + + protected override void Run() + { + try + { + Global.Debugger.Send("[MainThread] Inside Run method! Starting threads..."); + + var threadOne = new Thread(ThreadOne); + var threadTwo = new Thread(ThreadTwo); + + threadOne.Start(); + threadTwo.Start(); + + variableThree = 3; + + //Since Run is not a thread call PIT wait + System.Threading.Thread.Sleep(4000); + + //Cosmos.HAL.Global.PIT.Wait(3000); + + Global.Debugger.Send("[MainThread] Waited 4 sec."); + + Assert.AreEqual(variable, 1, "Changing global variable from thread works"); + + Assert.AreEqual(variableTwo, 2, "Changing global variable from second thread works"); + + Assert.AreEqual(variableThree, 3, "Changing global variable from main context works"); + + TestController.Completed(); + } + catch (Exception e) + { + mDebugger.Send("Exception occurred: " + e.Message); + mDebugger.Send(e.Message); + TestController.Failed(); + } + } + + public void ThreadOne() + { + //Make thread WAITING + Thread.Sleep(1000); + + Global.Debugger.Send("[FirstThread] Hello"); + + variable = 1; + + Global.Debugger.Send("[FirstThread] Bye"); + } + + public void ThreadTwo() + { + //Make thread WAITING + Thread.Sleep(2000); + + Global.Debugger.Send("[SecondThread] Hello"); + + variableTwo = 2; + + Global.Debugger.Send("[SecondThread] Bye"); + } + } +} diff --git a/Tests/Kernels/ThreadingTest/ThreadingTest.csproj b/Tests/Kernels/ThreadingTest/ThreadingTest.csproj new file mode 100644 index 0000000000..8ab8f818a1 --- /dev/null +++ b/Tests/Kernels/ThreadingTest/ThreadingTest.csproj @@ -0,0 +1,16 @@ + + + + net6.0 + False + Debug;Release;TEST + + + + + + + + + + diff --git a/source/Cosmos.Core/ACPI.cs b/source/Cosmos.Core/ACPI.cs index 20e4ef9fd8..8c910648cf 100644 --- a/source/Cosmos.Core/ACPI.cs +++ b/source/Cosmos.Core/ACPI.cs @@ -1,13 +1,49 @@ -using System; +using Cosmos.Debug.Kernel; +using System; +using System.Collections.Generic; +using System.IO; using System.Runtime.InteropServices; +using System.Text; namespace Cosmos.Core { + /// + /// PCI IRQ Routing information. + /// + public class IrqRouting + { + /// + /// The address of the PCI device. + /// + public uint Address; + + /// + /// The PCI pin number of the device. + /// + public byte Pin; + + /// + /// Source. + /// + public byte Source; + + /// + /// Source Index. + /// + public byte SourceIndex; + } + /// /// ACPI (Advanced Configuration and Power Interface) class. /// public unsafe class ACPI { + + /// + /// Debugger instance at the System ring, of the Global section. + /// + public static readonly Debugger debugger = new Debugger("Core"); + /// /// RSD table struct. /// @@ -18,29 +54,317 @@ public unsafe struct RSDPtr /// Signature. /// public fixed byte Signature[8]; + /// /// CheckSum /// public byte CheckSum; + /// /// OemID /// public fixed byte OemID[6]; + /// /// Revision /// public byte Revision; + /// /// RSDT Address /// public int RsdtAddress; }; - // New Port I/O + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct AcpiHeader + { + /// + /// Signature. + /// + public fixed byte Signature[4]; + + /// + /// Length. + /// + public uint Length; + + /// + /// Revision. + /// + public byte Revision; + + /// + /// Checksum. + /// + public byte Checksum; + + /// + /// OEM ID. + /// + public fixed byte OEMID[6]; + + /// + /// OEM Table ID. + /// + public fixed byte OEMTableID[8]; + + /// + /// OEM Revision. + /// + public uint OEMRevision; + + /// + /// CreatorID. + /// + public uint CreatorID; + + /// + /// Creator Revision. + /// + public uint CreatorRevision; + }; + + /// + /// FADT struct. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct FADTPtr + { + /// + /// ACPI Header. + /// + public AcpiHeader Header; + + /// + /// Firmware Control. + /// + public uint FirmwareCtrl; + + /// + /// DSDT Signature. + /// + public uint Dsdt; + + public byte Reserved; + public byte PreferredPowerManagementProfile; + public ushort SCI_Interrupt; + public uint SMI_CommandPort; + + /// + /// ACPI Enable. + /// + public byte AcpiEnable; + + /// + /// ACPI Disable. + /// + public byte AcpiDisable; + + public byte S4BIOS_REQ; + public byte PSTATE_Control; + public uint PM1aEventBlock; + public uint PM1bEventBlock; + public uint PM1aControlBlock; + public uint PM1bControlBlock; + public uint PM2ControlBlock; + public uint PMTimerBlock; + public uint GPE0Block; + public uint GPE1Block; + public byte PM1EventLength; + public byte PM1ControlLength; + public byte PM2ControlLength; + public byte PMTimerLength; + public byte GPE0Length; + public byte GPE1Length; + public byte GPE1Base; + public byte CStateControl; + public ushort WorstC2Latency; + public ushort WorstC3Latency; + public ushort FlushSize; + public ushort FlushStride; + public byte DutyOffset; + public byte DutyWidth; + public byte DayAlarm; + public byte MonthAlarm; + public byte Century; + + public ushort BootArchitectureFlags; + + public byte Reserved2; + public uint Flags; + + // 12 public byte structure; see below for details + public GenericAddressStructure ResetReg; + + public byte ResetValue; + public byte Reserved3; + public byte Reserved34; + public byte Reserved35; + + // 64bit pointers - Available on ACPI 2.0+ + public ulong X_FirmwareControl; + public ulong X_Dsdt; + + public GenericAddressStructure X_PM1aEventBlock; + public GenericAddressStructure X_PM1bEventBlock; + public GenericAddressStructure X_PM1aControlBlock; + public GenericAddressStructure X_PM1bControlBlock; + public GenericAddressStructure X_PM2ControlBlock; + public GenericAddressStructure X_PMTimerBlock; + public GenericAddressStructure X_GPE0Block; + public GenericAddressStructure X_GPE1Block; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct GenericAddressStructure + { + public byte AddressSpace; + public byte BitWidth; + public byte BitOffset; + public byte AccessSize; + public ulong Address; + }; + /// - /// IO port. + /// MADT struct. /// - private static ushort smiIO, pm1aIO, pm1bIO; + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct MADTPtr + { + /// + /// ACPI Header. + /// + public AcpiHeader Header; + + /// + /// Local APIC Address. + /// + public uint LocalAPICAddress; + + /// + /// Flags. + /// + public uint Flags; + } + + /// + /// APIC Header struct. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ApicHeader + { + /// + /// APIC Type. + /// + public ApicType Type; + + /// + /// Length. + /// + public byte Length; + } + + /// + /// APIC Type enum. + /// + public enum ApicType : byte + { + LocalAPIC, + IOAPIC, + InterruptOverride + } + + /// + /// ApicLocalApic struct. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct ApicLocalApic + { + /// + /// APIC Header. + /// + public ApicHeader Header; + + /// + /// ACPI Processor ID. + /// + public byte AcpiProcessorId; + + /// + /// APIC ID. + /// + public byte ApicId; + + /// + /// APIC Flags. + /// + public uint Flags; + } + + /// + /// ApicIOApic struct. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ApicIOApic + { + /// + /// APIC Header. + /// + public ApicHeader Header; + + /// + /// APIC ID. + /// + public byte IOApicId; + + /// + /// Reserved. + /// + public byte Reserved; + + /// + /// IO APIC Base Address. + /// + public uint IOApicAddress; + + /// + /// Global System Interrupt Base Address. + /// + public uint GlobalSystemInterruptBase; + } + + /// + /// ApicInterruptOverride struct. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct ApicInterruptOverride + { + /// + /// APIC Header. + /// + public ApicHeader Header; + + /// + /// Bus. + /// + public byte Bus; + + /// + /// Source. + /// + public byte Source; + + /// + /// Interrupt. + /// + public uint Interrupt; + + /// + /// Floags. + /// + public ushort Flags; + } // ACPI variables /// @@ -56,6 +380,10 @@ public unsafe struct RSDPtr /// private static byte ACPI_DISABLE; /// + /// Reset value to write into reset register when you need to reboot + /// + private static byte ResetValue; + /// /// PM1a CNT /// private static int* PM1a_CNT; @@ -79,6 +407,30 @@ public unsafe struct RSDPtr /// PM1 CNT LEN1 /// private static byte PM1_CNT_LEN; + /// + /// Global MADT. + /// + public static MADTPtr* MADT; + /// + /// Global IO APIC. + /// + public static ApicIOApic* IOAPIC; + /// + /// FADT table + /// + public static FADTPtr* FADT; + + public static uint DSDTLenght = 0; + + /// + /// PCI IRQ Routing Table. + /// + public static List IrqRoutingTable; + + /// + /// PCI IRQ Routing Table. + /// + public static List LocalApicCpus; /// /// Check ACPI header. @@ -91,71 +443,6 @@ static int acpiCheckHeader(byte* ptr, string sig) return Compare(sig, ptr); } - /// - /// FACP. - /// - private static byte* Facp = null; - /// - /// FACP struct. - /// - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct FACP - { - /// - /// Signature. - /// - public fixed byte Signature[4]; - /// - /// Length. - /// - public int Length; - - /// - /// Unused. - /// - public fixed byte unneded1[40 - 8]; - /// - /// DSDT. - /// - public int* DSDT; - /// - /// Unused. - /// - public fixed byte unneded2[48 - 44]; - /// - /// SMI CMD. - /// - public int* SMI_CMD; - /// - /// ACPI ENABLE. - /// - public byte ACPI_ENABLE; - /// - /// ACPI DISABLE. - /// - public byte ACPI_DISABLE; - /// - /// Unused. - /// - public fixed byte unneded3[64 - 54]; - /// - /// PM1a CNT BLK. - /// - public int* PM1a_CNT_BLK; - /// - /// PM1b CNT BLK. - /// - public int* PM1b_CNT_BLK; - /// - /// Unused. - /// - public fixed byte unneded4[89 - 72]; - /// - /// PM1 CNT LEN. - /// - public byte PM1_CNT_LEN; - }; - /// /// Compare string to byte array. /// @@ -164,7 +451,7 @@ struct FACP /// 0 - identical, -1 different. static int Compare(string c1, byte* c2) { - for (int i = 0; i < c1.Length; i++) + for (var i = 0; i < c1.Length; i++) { if (c1[i] != c2[i]) { return -1; } } @@ -179,9 +466,9 @@ static int Compare(string c1, byte* c2) static bool Check_RSD(uint address) { byte sum = 0; - byte* check = (byte*)address; + var check = (byte*)address; - for (int i = 0; i < 20; i++) + for (var i = 0; i < 20; i++) { sum += *check++; } @@ -213,17 +500,16 @@ public static void Start(bool initialize = true, bool enable = true) /// Thrown on IO error. public static void Shutdown() { - Console.Clear(); if (PM1a_CNT == null) { Init(); } - IOPort.Write16(pm1aIO, (ushort)(SLP_TYPa | SLP_EN)); + IOPort.Write16((ushort)PM1a_CNT, (ushort)(SLP_TYPa | SLP_EN)); if (PM1b_CNT != null) { - IOPort.Write16(pm1bIO, (ushort)(SLP_TYPb | SLP_EN)); + IOPort.Write16((ushort)PM1b_CNT, (ushort)(SLP_TYPb | SLP_EN)); } CPU.Halt(); @@ -236,7 +522,22 @@ public static void Shutdown() /// Thrown always. public static void Reboot() { - throw new NotImplementedException("ACPI Reset not implemented yet."); //TODO + if (PM1a_CNT == null) + { + Init(); + } + + var header = FADT->Header; + if (header.Revision >= 2 && (FADT->Flags & (1 << 10)) != 0) + { + IOPort.Write16((ushort)FADT->ResetReg.Address, ResetValue); + } + else + { + throw new Exception("Hardware does not support ACPI reboot."); + } + + throw new Exception("ACPI reboot failed."); } /// @@ -245,106 +546,283 @@ public static void Reboot() /// true on success, false on failure. private static bool Init() { - byte* ptr = (byte*)RSDPAddress(); - int addr = 0; + IOAPIC = null; + IrqRoutingTable = new List(); + LocalApicCpus = new List(); + + var rsdp = RSDPAddress(); + var ptr = (byte*)rsdp; + + Global.debugger.Send("ACPI v" + rsdp->Revision); - for (int i = 19; i >= 16; i--) + var rsdt = (AcpiHeader*)rsdp->RsdtAddress; + ptr = (byte*)rsdt; + + var p = (uint*)(rsdt + 1); + var end = (uint*)((byte*)rsdt + rsdt->Length); + + while (p < end) { - addr += *(ptr + i); - addr = i == 16 ? addr : addr << 8; - } + var address = *p++; - ptr = (byte*)addr; - ptr += 4; addr = 0; + ParseDT((AcpiHeader*)address); + } - for (int i = 3; i >= 0; i--) + if (LocalApicCpus.Count > 0) { - addr += *(ptr + i); - addr = i == 0 ? addr : addr << 8; + Global.debugger.Send("Found " + LocalApicCpus.Count + " CPUs via MADT."); } - int length = addr; - ptr -= 4; + return true; + } + + private static uint SdtLength = 0; + + private static void ReadHeader(BinaryReader _reader) + { + Global.debugger.Send("SDT header:"); + + //Signature + Global.debugger.Send("\tSignature: " + Encoding.ASCII.GetString(_reader.ReadBytes(4))); + + //Length + SdtLength = _reader.ReadUInt32(); + Global.debugger.Send("\tLendth: " + SdtLength.ToString()); + + //Revision + Global.debugger.Send("\tRevision: " + _reader.ReadByte().ToString()); - if (ptr != null && acpiCheckHeader(ptr, "RSDT") == 0) + //Checksum + Global.debugger.Send("\tChecksum: " + _reader.ReadByte().ToString()); + + //OEM ID + Global.debugger.Send("\tOEM ID: " + Encoding.ASCII.GetString(_reader.ReadBytes(6))); + + //OEMTableID + Global.debugger.Send("\tOEMTableID: " + Encoding.ASCII.GetString(_reader.ReadBytes(8))); + + //OEMRevision + Global.debugger.Send("\tOEMRevision: " + _reader.ReadUInt32().ToString()); + + //OEMRevision + Global.debugger.Send("\tCreatorID: " + _reader.ReadUInt32().ToString()); + + //OEMRevision + Global.debugger.Send("\tCreatorRevision: " + _reader.ReadUInt32().ToString()); + } + + private static void ParseS5() + { + byte* S5Addr = (byte*)FADT->Dsdt; + + while (0 < DSDTLenght--) { - addr = 0; - int entrys = length; - entrys = (entrys - 36) / 4; - ptr += 36; - byte* yeuse; + if (Compare("_S5_", S5Addr) == 0) + { + break; + } + S5Addr++; + } - while (0 < entrys--) + if (DSDTLenght > 0) + { + if ((*(S5Addr - 1) == 0x08 || (*(S5Addr - 2) == 0x08 && *(S5Addr - 1) == '\\')) && *(S5Addr + 4) == 0x12) { - for (int i = 3; i >= 0; i--) + S5Addr += 5; + S5Addr += ((*S5Addr & 0xC0) >> 6) + 2; + if (*S5Addr == 0x0A) + { + S5Addr++; + } + SLP_TYPa = (short)(*(S5Addr) << 10); + S5Addr++; + if (*S5Addr == 0x0A) { - addr += *(ptr + i); - addr = i == 0 ? addr : addr << 8; + S5Addr++; } + SLP_TYPb = (short)(*(S5Addr) << 10); + + Global.debugger.Send("SLP_TYPa=" + SLP_TYPa); + Global.debugger.Send("SLP_TYPb=" + SLP_TYPb); + } + } + } + + private static void ParsePRT() + { + /* + if (DSDTLenght > 0) + { + var dsdtBlock = new MemoryBlock08(FADT->Dsdt + (uint)sizeof(AcpiHeader), SdtLength - (uint)sizeof(AcpiHeader)); - yeuse = (byte*)addr; - Facp = yeuse; + Stream stream = new MemoryStream(dsdtBlock.ToArray()); - if (acpiCheckHeader((byte*)facpget(0), "DSDT") == 0) + Global.debugger.Send("Create parser..."); + + var root = new Parser(stream); + + Global.debugger.Send("Parse first node..."); + + var node = root.Parse(); + foreach (var item in node.Nodes) + { + Global.debugger.Send("Node: " + item.Name); + } + }*/ + } + + private static void ParseDT(AcpiHeader* hdr) + { + var signature = Encoding.ASCII.GetString(hdr->Signature, 4); + + Global.debugger.Send(signature + " detected"); + + if (signature == "FACP") + { + Global.debugger.Send("Parse FACP"); + + FADT = (FADTPtr*)hdr; + + SMI_CMD = (int*)FADT->SMI_CommandPort; + ACPI_ENABLE = FADT->AcpiEnable; + ACPI_DISABLE = FADT->AcpiDisable; + PM1a_CNT = (int*)FADT->PM1aControlBlock; + PM1b_CNT = (int*)FADT->PM1bControlBlock; + PM1_CNT_LEN = FADT->PM1ControlLength; + SLP_EN = 1 << 13; + + + if (acpiCheckHeader((byte*)FADT->Dsdt, "DSDT") == 0) + { + uint dsdtAddress = FADT->Dsdt; + uint dsdtLength = (uint)(*((int*)FADT->Dsdt + 1) - sizeof(AcpiHeader)); + + var dsdtHeader = new MemoryBlock08(dsdtAddress, 36); + var _reader = new BinaryReader(new MemoryStream(dsdtHeader.ToArray())); + + ReadHeader(_reader); + + Global.debugger.Send("Parsing _S5..."); + + ParseS5(); + + Global.debugger.Send("Parsing _PRT..."); + + ParsePRT(); + } + } + else if (signature == "APIC") + { + Global.debugger.Send("Parse APIC"); + + MADT = (MADTPtr*)hdr; + + var p = (byte*)(MADT + 1); + var end = (byte*)MADT + MADT->Header.Length; + while (p < end) + { + var header = (ApicHeader*)p; + var type = header->Type; + var length = header->Length; + + if (type == ApicType.LocalAPIC) { - byte* S5Addr = (byte*)facpget(0) + 36; - int dsdtLength = *(facpget(0) + 1) - 36; + var pic = (ApicLocalApic*)p; - while (0 < dsdtLength--) + if (((pic->Flags & 1) ^ ((pic->Flags >> 1) & 1)) != 0) { - if (Compare("_S5_", S5Addr) == 0) - { - break; - } - S5Addr++; - } + LocalApicCpus.Add(pic->ApicId); - if (dsdtLength > 0) + Global.debugger.Send("Found APIC " + (ulong)pic->ApicId + " (Address:0x" + ((ulong)MADT->LocalAPICAddress).ToString("X") + ", Processor ID:" + pic->AcpiProcessorId + ")"); + } + } + else if (type == ApicType.IOAPIC) + { + var ioapic = (ApicIOApic*)p; + if (IOAPIC == null) { - if ((*(S5Addr - 1) == 0x08 || (*(S5Addr - 2) == 0x08 && *(S5Addr - 1) == '\\')) && *(S5Addr + 4) == 0x12) - { - S5Addr += 5; - S5Addr += ((*S5Addr & 0xC0) >> 6) + 2; - if (*S5Addr == 0x0A) - { - S5Addr++; - } - SLP_TYPa = (short)(*S5Addr << 10); - S5Addr++; - if (*S5Addr == 0x0A) - { - S5Addr++; - } - SLP_TYPb = (short)(*S5Addr << 10); - SMI_CMD = facpget(1); - ACPI_ENABLE = facpbget(0); - ACPI_DISABLE = facpbget(1); - PM1a_CNT = facpget(2); - PM1b_CNT = facpget(3); - PM1_CNT_LEN = facpbget(3); - SLP_EN = 1 << 13; - - smiIO = (ushort)SMI_CMD; - pm1aIO = (ushort)PM1a_CNT; - pm1bIO = (ushort)PM1b_CNT; - - return true; - } + IOAPIC = ioapic; } + Global.debugger.Send("Found IO APIC " + (ulong)ioapic->IOApicId + " (Address:0x" + ((ulong)ioapic->IOApicAddress).ToString("X") + ", GSIB:" + (ulong)ioapic->GlobalSystemInterruptBase + ")"); } - ptr += 4; + else if (type == ApicType.InterruptOverride) + { + var ovr = (ApicInterruptOverride*)p; + + Global.debugger.Send("Found APIC Interrupt Override (Bus: " + ((ulong)ovr->Bus).ToString() + ", Source:" + ((ulong)ovr->Source).ToString() + ", Interrupt:0x" + ((ulong)ovr->Interrupt).ToString("X") + ", Flags:" + ((ulong)ovr->Flags).ToString() + ")"); + } + + p += length; } } + } + + /* + private static void PopulateNode(ParseNode op) + { + //Recursive function does a null reference exception trick the matrice with a Stack and iterative function + var sthack = new Stack(); + + sthack.Push(op); + + while (sthack.Count != 0) + { + ParseNode current = sthack.Pop(); + + if (current.Arguments.Count > 0) + { + SearchPackage(current); + } - return false; + if (current != null) + { + for (int i = current.Nodes.Count - 1; i >= 0; i--) + { + sthack.Push(current.Nodes[i]); + } + } + } } + + private static void SearchPackage(ParseNode op) + { + for (int x = 0; x < op.Op.ParseArgs.Length; x++) + { + if (op.Op.ParseArgs[x] == ParseArgFlags.DataObjectList || op.Op.ParseArgs[x] == ParseArgFlags.TermList || op.Op.ParseArgs[x] == ParseArgFlags.ObjectList) + continue; + + if (op.Arguments[x].ToString() == "Package") + { + Global.debugger.Send("Package found!"); + + //var arg = (ParseNode)op.Arguments[x]; + + /* + for (int y = 0; y < arg.Nodes.Count; y++) + { + List package = arg.Nodes[y].Nodes; + + var irqRouting = new IrqRouting() + { + Address = (uint)package[0].ConstantValue, + Pin = (byte)package[1].ConstantValue, + Source = (byte)package[2].ConstantValue, + SourceIndex = (byte)package[3].ConstantValue + }; + + IrqRoutingTable.Add(irqRouting); + } + + } + } + }*/ + /// /// Enable ACPI. /// public static void Enable() { - smiIO = ACPI_ENABLE; } /// @@ -352,14 +830,13 @@ public static void Enable() /// public static void Disable() { - smiIO = ACPI_DISABLE; } /// /// Get the RSDP address. /// /// uint value. - private static unsafe uint RSDPAddress() + private static unsafe RSDPtr* RSDPAddress() { for (uint addr = 0xE0000; addr < 0x100000; addr += 4) { @@ -367,118 +844,52 @@ private static unsafe uint RSDPAddress() { if (Check_RSD(addr)) { - return addr; + return (RSDPtr*)addr; } } } - uint ebda_address = *(uint*)0x040E; - ebda_address = (ebda_address * 0x10) & 0x000fffff; + var ebda_address = *(uint*)0x040E; + ebda_address = ebda_address * 0x10 & 0x000fffff; - for (uint addr = ebda_address; addr < ebda_address + 1024; addr += 4) + for (var addr = ebda_address; addr < ebda_address + 1024; addr += 4) { if (Compare("RSD PTR ", (byte*)addr) == 0) { - return addr; + return (RSDPtr*)addr; } } - return 0; + return null; } - /// - /// Check RSDT table - /// - /// A pointer to the RSDT - /// RSDT table address - private static uint* acpiCheckRSDPtr(uint* ptr) + public static uint RemapIRQ(uint irq) { - string sig = "RSD PTR "; - var rsdp = (RSDPtr*)ptr; + var p = (byte*)(MADT + 1); + var end = (byte*)MADT + MADT->Header.Length; - byte* bptr; - byte check = 0; - int i; - - if (Compare(sig, (byte*)rsdp) == 0) + while (p < end) { - bptr = (byte*)ptr; + var header = (ApicHeader*)p; + var type = header->Type; + var length = header->Length; - for (i = 0; i < 20; i++) + if (type == ApicType.InterruptOverride) { - check += *bptr; - bptr++; - } + var ovr = (ApicInterruptOverride*)p; - if (check == 0) - { - Compare("RSDT", (byte*)rsdp->RsdtAddress); - - if (rsdp->RsdtAddress != 0) + if (ovr->Source == irq) { - return (uint*)rsdp->RsdtAddress; + Global.debugger.Send("IRQ" + irq + " remapped to IRQ" + ovr->Interrupt); + + return ovr->Interrupt; } } - } - - return null; - } - /// - /// Get data from the FACP table. - /// - /// Index number of the data to get. - /// - /// 0 - ACPI ENABLE - /// 1 - ACPI DISABLE - /// 2 - PM1 CNT LEN - /// other - 0 - /// - /// - /// byte value. - private static byte facpbget(int number) - { - switch (number) - { - case 0: - return *(Facp + 52); - case 1: - return *(Facp + 53); - case 2: - return *(Facp + 89); - default: - return 0; + p += length; } - } - /// - /// Get pointer to the data on the FACP. - /// - /// Index number of the data to get. - /// - /// 0 - DSDT - /// 1 - SMI CMD - /// 2 - PM1a - /// 3 - PM1b - /// other - null - /// - /// - /// int pointer. - private static int* facpget(int number) - { - switch (number) - { - case 0: - return (int*)*(int*)(Facp + 40); - case 1: - return (int*)*(int*)(Facp + 48); - case 2: - return (int*)*(int*)(Facp + 64); - case 3: - return (int*)*(int*)(Facp + 68); - default: - return null; - } + return irq; } } } \ No newline at end of file diff --git a/source/Cosmos.Core/GCImplementation.cs b/source/Cosmos.Core/GCImplementation.cs index 66b4ef5c05..bedf95cc71 100644 --- a/source/Cosmos.Core/GCImplementation.cs +++ b/source/Cosmos.Core/GCImplementation.cs @@ -1,202 +1,202 @@ -#if DEBUG -//#define GC_DEBUG -#endif -//#define COSMOSDEBUG -using System; -using Cosmos.Core.Memory; - -namespace Cosmos.Core -{ - /// - /// GCImplementation class. Garbage collector. Mostly not implemented. - /// - /// Most of the class is yet to be implemented. - [System.Diagnostics.DebuggerStepThrough] - public unsafe static class GCImplementation - { - private unsafe static byte* memPtr = null; - private static ulong memLength = 0; - private static bool StartedMemoryManager = false; - /// - /// - /// Acquire lock. Not implemented. - /// - /// Thrown always. - private static void AcquireLock() - { - throw new NotImplementedException(); - } - - /// - /// Release lock. Not implemented. - /// - /// Thrown always. - private static void ReleaseLock() - { - throw new NotImplementedException(); - } - /// - /// Alloc new object. - /// - public unsafe static uint AllocNewObject(uint aSize) - { - return (uint)Heap.Alloc(aSize); - } - /// - /// Free Object from Memory - /// - /// Takes a memory allocated object - public unsafe static void Free(object aObj) - { - Heap.Free(GetPointer(aObj)); - } - - /// - /// Get amount of available Ram - /// - /// Returns amount of available memory to the System in MB - public static ulong GetAvailableRAM() - { - return memLength / 1024 / 1024; - } - /// - /// Get a rough estimate of used Memory by the System - /// - /// Returns the used PageSize by the MemoryManager in Bytes. - public static uint GetUsedRAM() - { - return (RAT.TotalPageCount - RAT.GetPageCount((byte)RAT.PageType.Empty)) * RAT.PageSize; - } - /// - /// Initialise the Memory Manager, this should not be called anymore since it is done very early during the boot process. - /// - public static unsafe void Init() - { - if (StartedMemoryManager) - { - return; - } - StartedMemoryManager = true; - - var largestBlock = CPU.GetLargestMemoryBlock(); - - if (largestBlock != null) - { - memPtr = (byte*)largestBlock->Address; - memLength = largestBlock->Length; - if ((uint)memPtr < CPU.GetEndOfKernel() + 1024) - { - memPtr = (byte*)CPU.GetEndOfKernel() + 1024; - memPtr += RAT.PageSize - ((uint)memPtr % RAT.PageSize); - memLength = largestBlock->Length - ((uint)memPtr - (uint)largestBlock->Address); - memLength -= memLength % RAT.PageSize; - } - } - else - { - memPtr = (byte*)CPU.GetEndOfKernel() + 1024; - memPtr += RAT.PageSize - (uint)memPtr % RAT.PageSize; - memLength = 128 * 1024 * 1024; - } - RAT.Init(memPtr, (uint)memLength); - } - /// - /// Get the Pointer of any object needed for Free() - /// - /// Takes any kind of object - /// Returns a pointer to the area in memory where the object is located - public static unsafe uint* GetPointer(object aObj) => throw null; // this is plugged - - /// - /// Get the pointer of any object as a uint - /// - /// - /// - public static unsafe uint GetSafePointer(object aObj) - { - return (uint)GetPointer(aObj); - } - - /// - /// Get cosmos internal type from object - /// - /// - /// - public static unsafe uint GetType(object aObj) - { - return *GetPointer(aObj); - } - - /// - /// Increments the root count of the object at the pointer by 1 - /// - /// - public static unsafe void IncRootCount(ushort* aPtr) - { - if (RAT.GetPageType(aPtr) != 0) - { - var rootCount = *(aPtr - 1) >> 1; // lowest bit is used to set if hit - *(aPtr - 1) = (ushort)((rootCount + 1) << 1); // loest bit can be zero since we shouldnt be doing this while gc is collecting - } - } - - /// - /// Decrements the root count of the object at the pointer by 1 - /// - /// - public static unsafe void DecRootCount(ushort* aPtr) - { - if (RAT.GetPageType(aPtr) != 0) - { - var rootCount = *(aPtr - 1) >> 1; // lowest bit is used to set if hit - *(aPtr - 1) = (ushort)((rootCount - 1) << 1); // lowest bit can be zero since we shouldnt be doing this while gc is collecting - } - } - - /// - /// Increments the root count of all object stored in this struct by 1 - /// - /// - /// Type of the struct - public static unsafe void IncRootCountsInStruct(ushort* aPtr, uint aType) - { - uint count = VTablesImpl.GetGCFieldCount(aType); - uint[] offset = VTablesImpl.GetGCFieldOffsets(aType); - uint[] types = VTablesImpl.GetGCFieldTypes(aType); - for (int i = 0; i < count; i++) - { - if (VTablesImpl.IsStruct(types[i])) - { - IncRootCountsInStruct(aPtr + offset[i] / 2, types[i]); - } - else - { - IncRootCount(*(ushort**)(aPtr + offset[i] / 2)); - } - } - } - - /// - /// Decrements the root count of all object stored in this struct by 1 - /// - /// - /// Type of the struct - public static unsafe void DecRootCountsInStruct(ushort* aPtr, uint aType) - { - uint count = VTablesImpl.GetGCFieldCount(aType); - uint[] offset = VTablesImpl.GetGCFieldOffsets(aType); - uint[] types = VTablesImpl.GetGCFieldTypes(aType); - for (int i = 0; i < count; i++) - { - if (VTablesImpl.IsStruct(types[i])) - { - DecRootCountsInStruct(aPtr + offset[i] / 2, types[i]); - } - else - { - DecRootCount(*(ushort**)(aPtr + offset[i] / 2)); - } - } - } - } -} +#if DEBUG +//#define GC_DEBUG +#endif +//#define COSMOSDEBUG +using System; +using Cosmos.Core.Memory; + +namespace Cosmos.Core +{ + /// + /// GCImplementation class. Garbage collector. Mostly not implemented. + /// + /// Most of the class is yet to be implemented. + [System.Diagnostics.DebuggerStepThrough] + public unsafe static class GCImplementation + { + private unsafe static byte* memPtr = null; + private static ulong memLength = 0; + private static bool StartedMemoryManager = false; + /// + /// + /// Acquire lock. Not implemented. + /// + /// Thrown always. + private static void AcquireLock() + { + throw new NotImplementedException(); + } + + /// + /// Release lock. Not implemented. + /// + /// Thrown always. + private static void ReleaseLock() + { + throw new NotImplementedException(); + } + /// + /// Alloc new object. + /// + public unsafe static uint AllocNewObject(uint aSize) + { + return (uint)Heap.Alloc(aSize); + } + /// + /// Free Object from Memory + /// + /// Takes a memory allocated object + public unsafe static void Free(object aObj) + { + Heap.Free(GetPointer(aObj)); + } + + /// + /// Get amount of available Ram + /// + /// Returns amount of available memory to the System in MB + public static ulong GetAvailableRAM() + { + return memLength / 1024 / 1024; + } + /// + /// Get a rough estimate of used Memory by the System + /// + /// Returns the used PageSize by the MemoryManager in Bytes. + public static uint GetUsedRAM() + { + return (RAT.TotalPageCount - RAT.GetPageCount((byte)RAT.PageType.Empty)) * RAT.PageSize; + } + /// + /// Initialise the Memory Manager, this should not be called anymore since it is done very early during the boot process. + /// + public static unsafe void Init() + { + if (StartedMemoryManager) + { + return; + } + StartedMemoryManager = true; + + var largestBlock = CPU.GetLargestMemoryBlock(); + + if (largestBlock != null) + { + memPtr = (byte*)largestBlock->Address; + memLength = largestBlock->Length; + if ((uint)memPtr < CPU.GetEndOfKernel() + 1024) + { + memPtr = (byte*)CPU.GetEndOfKernel() + 1024; + memPtr += RAT.PageSize - ((uint)memPtr % RAT.PageSize); + memLength = largestBlock->Length - ((uint)memPtr - (uint)largestBlock->Address); + memLength -= memLength % RAT.PageSize; + } + } + else + { + memPtr = (byte*)CPU.GetEndOfKernel() + 1024; + memPtr += RAT.PageSize - (uint)memPtr % RAT.PageSize; + memLength = 128 * 1024 * 1024; + } + RAT.Init(memPtr, (uint)memLength); + } + /// + /// Get the Pointer of any object needed for Free() + /// + /// Takes any kind of object + /// Returns a pointer to the area in memory where the object is located + public static unsafe uint* GetPointer(object aObj) => throw null; // this is plugged + + /// + /// Get the pointer of any object as a uint + /// + /// + /// + public static unsafe uint GetSafePointer(object aObj) + { + return (uint)GetPointer(aObj); + } + + /// + /// Get cosmos internal type from object + /// + /// + /// + public static unsafe uint GetType(object aObj) + { + return *GetPointer(aObj); + } + + /// + /// Increments the root count of the object at the pointer by 1 + /// + /// + public static unsafe void IncRootCount(ushort* aPtr) + { + if (RAT.GetPageType(aPtr) != 0) + { + var rootCount = *(aPtr - 1) >> 1; // lowest bit is used to set if hit + *(aPtr - 1) = (ushort)((rootCount + 1) << 1); // loest bit can be zero since we shouldnt be doing this while gc is collecting + } + } + + /// + /// Decrements the root count of the object at the pointer by 1 + /// + /// + public static unsafe void DecRootCount(ushort* aPtr) + { + if (RAT.GetPageType(aPtr) != 0) + { + var rootCount = *(aPtr - 1) >> 1; // lowest bit is used to set if hit + *(aPtr - 1) = (ushort)((rootCount - 1) << 1); // lowest bit can be zero since we shouldnt be doing this while gc is collecting + } + } + + /// + /// Increments the root count of all object stored in this struct by 1 + /// + /// + /// Type of the struct + public static unsafe void IncRootCountsInStruct(ushort* aPtr, uint aType) + { + uint count = VTablesImpl.GetGCFieldCount(aType); + uint[] offset = VTablesImpl.GetGCFieldOffsets(aType); + uint[] types = VTablesImpl.GetGCFieldTypes(aType); + for (int i = 0; i < count; i++) + { + if (VTablesImpl.IsStruct(types[i])) + { + IncRootCountsInStruct(aPtr + offset[i] / 2, types[i]); + } + else + { + IncRootCount(*(ushort**)(aPtr + offset[i] / 2)); + } + } + } + + /// + /// Decrements the root count of all object stored in this struct by 1 + /// + /// + /// Type of the struct + public static unsafe void DecRootCountsInStruct(ushort* aPtr, uint aType) + { + uint count = VTablesImpl.GetGCFieldCount(aType); + uint[] offset = VTablesImpl.GetGCFieldOffsets(aType); + uint[] types = VTablesImpl.GetGCFieldTypes(aType); + for (int i = 0; i < count; i++) + { + if (VTablesImpl.IsStruct(types[i])) + { + DecRootCountsInStruct(aPtr + offset[i] / 2, types[i]); + } + else + { + DecRootCount(*(ushort**)(aPtr + offset[i] / 2)); + } + } + } + } +} \ No newline at end of file diff --git a/source/Cosmos.Core/INTs.cs b/source/Cosmos.Core/INTs.cs index 662e970ff3..20b47bd007 100644 --- a/source/Cosmos.Core/INTs.cs +++ b/source/Cosmos.Core/INTs.cs @@ -234,6 +234,8 @@ public struct IRQContext { [AsmMarker(AsmMarker.Type.Int_LastKnownAddress)] private static uint mLastKnownAddress = 0; + public static uint mStackContext; + /// /// IRQ handlers. /// @@ -852,6 +854,8 @@ public static void Dummy() { HandleInterrupt_47(ref xCtx); HandleInterrupt_48(ref xCtx); HandleInterrupt_49(ref xCtx); + Processing.ProcessorScheduler.SwitchTask(); + Processing.ProcessorScheduler.EntryPoint(); } } } diff --git a/source/Cosmos.Core/LocalApic.cs b/source/Cosmos.Core/LocalApic.cs new file mode 100644 index 0000000000..1ac1c45710 --- /dev/null +++ b/source/Cosmos.Core/LocalApic.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Cosmos.Core.MemoryGroup; + +namespace Cosmos.Core +{ + /// + /// Local APIC class. + /// + public static unsafe class LocalAPIC + { + public const ushort LAPIC_ID = 0x0020; + public const ushort LAPIC_VER = 0x0030; + public const ushort LAPIC_TPR = 0x0080; + public const ushort LAPIC_APR = 0x0090; + public const ushort LAPIC_PPR = 0x00a0; + public const ushort LAPIC_EOI = 0x00b0; + public const ushort LAPIC_RRD = 0x00c0; + public const ushort LAPIC_LDR = 0x00d0; + public const ushort LAPIC_DFR = 0x00e0; + public const ushort LAPIC_SVR = 0x00f0; + public const ushort LAPIC_ISR = 0x0100; + public const ushort LAPIC_TMR = 0x0180; + public const ushort LAPIC_IRR = 0x0200; + public const ushort LAPIC_ESR = 0x0280; + public const ushort LAPIC_ICRLO = 0x0300; + public const ushort LAPIC_ICRHI = 0x0310; + public const ushort LAPIC_TIMER = 0x0320; + public const ushort LAPIC_THERMAL = 0x0330; + public const ushort LAPIC_PERF = 0x0340; + public const ushort LAPIC_LINT0 = 0x0350; + public const ushort LAPIC_LINT1 = 0x0360; + public const ushort LAPIC_ERROR = 0x0370; + public const ushort LAPIC_TICR = 0x0380; + public const ushort LAPIC_TCCR = 0x0390; + public const ushort LAPIC_TDCR = 0x03e0; + + public const ushort ICR_FIXED = 0x00000000; + public const ushort ICR_LOWEST = 0x00000100; + public const ushort ICR_SMI = 0x00000200; + public const ushort ICR_NMI = 0x00000400; + public const ushort ICR_INIT = 0x00000500; + public const ushort ICR_STARTUP = 0x00000600; + + public const ushort ICR_PHYSICAL = 0x00000000; + public const ushort ICR_LOGICAL = 0x00000800; + + public const ushort ICR_IDLE = 0x00000000; + public const ushort ICR_SEND_PENDING = 0x00001000; + + public const ushort ICR_DEASSERT = 0x00000000; + public const ushort ICR_ASSERT = 0x00004000; + + public const ushort ICR_EDGE = 0x00000000; + public const ushort ICR_LEVEL = 0x00008000; + + public const int ICR_NO_SHORTHAND = 0x00000000; + public const int ICR_SELF = 0x00040000; + public const int ICR_ALL_INCLUDING_SELF = 0x00080000; + public const int ICR_ALL_EXCLUDING_SELF = 0x000c0000; + + public const int ICR_DESTINATION_SHIFT = 24; + + /// + /// Local APIC Base Address. + /// + private static uint Address = 0; + + /// + /// Initialize local APIC. + /// + public static void Initialize() + { + //TODO: Fix ACPI tables on Bochs + if (ACPI.LocalApicCpus.Count == 0) + { + //No APIC detected, hardcode APIC address + Address = 0xFEE00000; + } + else + { + Address = ACPI.MADT->LocalAPICAddress; + } + + Out(LAPIC_TPR, 0); + Out(LAPIC_SVR, 0x100 | 0xFF); + + Global.debugger.Send("Local APIC " + GetId() + " initialized"); + } + + /// + /// IO APIC MMIO Out. + /// + /// IO APIC Register. + /// Data. + public static void Out(uint reg, uint val) + { + MMIOBase.Write32(Address + reg, val); + } + + /// + /// IO APIC MMIO In. + /// + /// IO APIC Register. + public static uint In(uint reg) + { + return MMIOBase.Read32(Address + reg); + } + + /// + /// End of Interrupt. + /// + public static void EndOfInterrupt() + { + Out(LAPIC_EOI, 0); + } + + /// + /// Get Local APIC ID. + /// + /// integer value. + public static uint GetId() + { + return In(LAPIC_ID) >> 24; + } + } +} \ No newline at end of file diff --git a/source/Cosmos.Core/MMIO.cs b/source/Cosmos.Core/MMIO.cs new file mode 100644 index 0000000000..ed943a5e0f --- /dev/null +++ b/source/Cosmos.Core/MMIO.cs @@ -0,0 +1,149 @@ +using IL2CPU.API.Attribs; + +namespace Cosmos.Core +{ + /// + /// MMIOBase abstract class. + /// + public unsafe abstract class MMIOBase + { + /// + /// Address. + /// + protected readonly uint Address; + + // all ctors are internal - Only Core ring can create it.. but hardware ring can use it. + /// + /// Create new instance of the class. + /// + /// An address. + protected MMIOBase(uint address) + { + Address = address; + } + + /// + /// Create new instance of the class. + /// + /// A base address. + /// An offset from the base address. + protected MMIOBase(uint address, uint aOffset) + { + // C# math promotes things to integers, so we have this constructor + // to relieve the use from having to do so many casts + Address = address + aOffset; + } + + /// + /// Write byte to adress. + /// + /// An address to write to. + /// A data. + public static void Write8(uint address, byte aData) + { + *(uint*)address = aData; + } + + /// + /// Write Word to address. + /// + /// A address to write to. + /// A data. + public static void Write16(uint address, ushort aData) + { + *(uint*)address = aData; + } + + /// + /// Write DWord to address. + /// + /// An address to write to. + /// A data. + public static void Write32(uint address, uint aData) + { + *(uint*)address = aData; + } + + /// + /// Read byte from address. + /// + /// An address to read from. + /// byte value. + public static byte Read8(uint address) + { + return (byte)*(uint*)address; + } + + /// + /// Read Word from address. + /// + /// An address to read from. + /// ushort value. + public static ushort Read16(uint address) + { + return (ushort)*(uint*)address; + } + + /// + /// Read DWord from address. + /// + /// An address to read from. + /// uint value. + public static uint Read32(uint address) + { + return *(uint*)address; + } + } + + /// + /// MMIO class. Used to read and write to address. + /// + public class MMIO : MMIOBase + { + /// + /// Create new instance of the class. + /// + /// An address. + public MMIO(uint address) + : base(address) + { + } + + /// + /// Create new instance of the class. + /// + /// A base address. + /// Offset from the base address. + public MMIO(uint address, uint aOffset) + : base(address, aOffset) + { + } + + /// + /// Get and set Byte value in address. + /// + public byte Byte + { + get => Read8(Address); + set => Write8(Address, value); + } + + /// + /// Get and set Word value in address. + /// + public ushort Word + { + get => Read16(Address); + set => Write16(Address, value); + } + + /// + /// Get and set DWord value in address. + /// + public uint DWord + { + get => Read32(Address); + set => Write32(Address, value); + } + } +} \ No newline at end of file diff --git a/source/Cosmos.Core/Memory/Heap.cs b/source/Cosmos.Core/Memory/Heap.cs index 5d86e4412b..862c10c7c3 100644 --- a/source/Cosmos.Core/Memory/Heap.cs +++ b/source/Cosmos.Core/Memory/Heap.cs @@ -1,4 +1,5 @@ using System; +using Cosmos.Core.Processing; using Cosmos.Debug.Kernel; using IL2CPU.API; @@ -20,6 +21,8 @@ public enum ObjectGCStatus : ushort public static unsafe class Heap { private static uint* StackStart; + private static Mutex mMemeoryGate = new Mutex(); + /// /// Init heap. /// @@ -44,6 +47,11 @@ public static unsafe void Init() /// New pointer with specified size while maintaining old data. public static byte* Realloc(byte* aPtr, uint newSize) { + if (mMemeoryGate != null) + { + mMemeoryGate.Lock(); + } + // TODO: don't move memory position if there is enough space in the current one. // Get existing size @@ -72,6 +80,12 @@ public static unsafe void Init() // Free the old data and return Free(aPtr); + + if (mMemeoryGate != null) + { + mMemeoryGate.Unlock(); + } + return ToReturn; } @@ -82,24 +96,47 @@ public static unsafe void Init() /// Byte pointer to the start of the block. public static byte* Alloc(uint aSize) { + if (mMemeoryGate != null) + { + mMemeoryGate.Lock(); + } + CPU.DisableInterrupts(); if (aSize <= HeapSmall.mMaxItemSize) { byte* ptr = HeapSmall.Alloc((ushort)aSize); CPU.EnableInterrupts(); + + if (mMemeoryGate != null) + { + mMemeoryGate.Unlock(); + } + return ptr; } else if (aSize <= HeapMedium.MaxItemSize) { byte* ptr = HeapMedium.Alloc(aSize); CPU.EnableInterrupts(); + + if (mMemeoryGate != null) + { + mMemeoryGate.Unlock(); + } + return ptr; } else { byte* ptr = HeapLarge.Alloc(aSize); CPU.EnableInterrupts(); + + if (mMemeoryGate != null) + { + mMemeoryGate.Unlock(); + } + return ptr; } } @@ -128,6 +165,11 @@ public static uint SafeAlloc(uint aSize) /// public static void Free(void* aPtr) { + if (mMemeoryGate != null) + { + mMemeoryGate.Lock(); + } + //TODO find a better way to remove the double look up here for GetPageType and then again in the // .Free methods which actually free the entries in the RAT. //Debugger.DoSendNumber(0x77); @@ -146,6 +188,11 @@ public static void Free(void* aPtr) default: throw new Exception("Heap item not found in RAT."); } + + if (mMemeoryGate != null) + { + mMemeoryGate.Unlock(); + } } /// @@ -154,6 +201,11 @@ public static void Free(void* aPtr) /// Number of objects freed public static int Collect() { + if (mMemeoryGate != null) + { + mMemeoryGate.Lock(); + } + //Disable interrupts: Prevent CPU exception when allocation is called from interrupt code CPU.DisableInterrupts(); @@ -291,6 +343,11 @@ public static int Collect() //Enable interrupts back CPU.EnableInterrupts(); + if (mMemeoryGate != null) + { + mMemeoryGate.Unlock(); + } + return freed; } diff --git a/source/Cosmos.Core/Memory/HeapLarge.cs b/source/Cosmos.Core/Memory/HeapLarge.cs index 7436f0e86d..eae50f0514 100644 --- a/source/Cosmos.Core/Memory/HeapLarge.cs +++ b/source/Cosmos.Core/Memory/HeapLarge.cs @@ -1,59 +1,59 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Cosmos.Debug.Kernel; -using Native = System.UInt32; - -namespace Cosmos.Core.Memory -{ - /// - /// HeapLarge class. Used to alloc and free large memory blocks on the heap. - /// - public static unsafe class HeapLarge - { - /// - /// Prefix block. Used to store meta information. - /// - public const uint PrefixBytes = 4 * sizeof(uint); - - /// - /// Init HeapLarge instance. - /// - /// Empty function - public static void Init() - { - } - - /// - /// Alloc memory block, of a given size. - /// - /// A size of block to alloc, in bytes. - /// Byte pointer to the start of the block. - public static byte* Alloc(uint aSize, RAT.PageType aType = RAT.PageType.HeapLarge) - { - uint xPages = (aSize + PrefixBytes) / RAT.PageSize + 1; - var xPtr = (uint*)RAT.AllocPages(aType, xPages); - if (xPtr == null) - { - Debugger.SendKernelPanic(0x67); // out of pages - while (true) { } - } - xPtr[0] = xPages * RAT.PageSize - PrefixBytes; // Allocated data size - xPtr[1] = aSize; // Actual data size - xPtr[3] = 0; // padding for now?, - xPtr[2] = 0; // padding + GC object status - return (byte*)xPtr + PrefixBytes; - } - - /// - /// Free block. - /// - /// A pointer to the block. - /// Thrown if page type is not found. - public static void Free(void* aPtr) - { - var xPageIdx = RAT.GetFirstRATIndex(aPtr); - RAT.Free(xPageIdx); - } - } -} +using System; +using System.Linq; +using System.Threading.Tasks; +using Cosmos.Debug.Kernel; +using Native = System.UInt32; + +namespace Cosmos.Core.Memory +{ + /// + /// HeapLarge class. Used to alloc and free large memory blocks on the heap. + /// + public static unsafe class HeapLarge + { + /// + /// Prefix block. Used to store meta information. + /// + public const uint PrefixBytes = 4 * sizeof(uint); + + /// + /// Init HeapLarge instance. + /// + /// Empty function + public static void Init() + { + } + + /// + /// Alloc memory block, of a given size. + /// + /// A size of block to alloc, in bytes. + /// Byte pointer to the start of the block. + public static byte* Alloc(uint aSize, RAT.PageType aType = RAT.PageType.HeapLarge) + { + uint xPages = (aSize + PrefixBytes) / RAT.PageSize + 1; + var xPtr = (uint*)RAT.AllocPages(aType, xPages); + if (xPtr == null) + { + Debugger.SendKernelPanic(0x67); // out of pages + while (true) { } + } + xPtr[0] = xPages * RAT.PageSize - PrefixBytes; // Allocated data size + xPtr[1] = aSize; // Actual data size + xPtr[3] = 0; // padding for now?, + xPtr[2] = 0; // padding + GC object status + return (byte*)xPtr + PrefixBytes; + } + + /// + /// Free block. + /// + /// A pointer to the block. + /// Thrown if page type is not found. + public static void Free(void* aPtr) + { + var xPageIdx = RAT.GetFirstRATIndex(aPtr); + RAT.Free(xPageIdx); + } + } +} diff --git a/source/Cosmos.Core/Memory/HeapSmall.cs b/source/Cosmos.Core/Memory/HeapSmall.cs index 26b5f56ba4..9d0c18b361 100644 --- a/source/Cosmos.Core/Memory/HeapSmall.cs +++ b/source/Cosmos.Core/Memory/HeapSmall.cs @@ -1,626 +1,626 @@ -using System; -using Cosmos.Debug.Kernel; -using IL2CPU.API; -using IL2CPU.API.Attribs; - -namespace Cosmos.Core.Memory -{ - /// - /// Page containing Size Map Table - /// - public unsafe struct SMTPage - { - /// - /// Pointer to the next page - /// - public SMTPage* Next; - /// - /// Pointer to the root of the smallest block stored on this page - /// - public RootSMTBlock* First; - } - - public unsafe struct RootSMTBlock - { - /// - /// Elements stored in the page have a size less or equal to this - /// Does not include the prefix bytes - /// - public uint Size; - /// - /// Pointer to the first block for this size - /// - public SMTBlock* First; - /// - /// Next larger size root block - /// - public RootSMTBlock* LargerSize; - } - // Changing the ordering will break SMTBlock* NextFreeBlock(SMTPage* aPage) - public unsafe struct SMTBlock - { - /// - /// Pointer to the actual page, where the elements are stored - /// - public byte* PagePtr; - /// - /// How much space is left on the page, makes it easier to skip full pages - /// - public uint SpacesLeft; - /// - /// Pointer to the next block for this size - /// - public SMTBlock* NextBlock; - } - - //TODO Remove empty pages as necessary - /// - /// HeapSmall class. Used to alloc and free small memory blocks on the heap. - /// - unsafe static public class HeapSmall - { - /// - /// Number of prefix bytes for each item. - /// We technically only need 2 but to keep it aligned we have two padding - /// - public const uint PrefixItemBytes = 2 * sizeof(ushort); - /// - /// Max item size in the heap. - /// - public static uint mMaxItemSize; - - /// - /// Size map table - /// - public static SMTPage* SMT; - - #region SMT - - /// - /// Find the next free block in a page - /// - /// Pointer to the start of block in the SMT. null if all SMT pages are full - private static SMTBlock* NextFreeBlock(SMTPage* aPage) - { - SMTBlock* ptr = (SMTBlock*)aPage->First; // since both RootSMTBlock and SMTBlock have the same size (20) it doesnt matter if cast is wrong - while (ptr->PagePtr != null) // this would check Size if its actually a RootSMTBlock, which is always non-zero - { - ptr += 1; - if (ptr >= (byte*)aPage + RAT.PageSize - 8) - { - return null; - } - } - return ptr; - - } - - /// - /// Gets the root block in the SMT for objects of this size - /// - /// Size of allocated block - /// Page to seach in - /// Pointer of block in SMT. - private static RootSMTBlock* GetFirstBlock(SMTPage* aPage, uint aSize) - { - RootSMTBlock* ptr = aPage->First; - uint curSize = ptr->Size; - while (curSize < aSize) - { - ptr = ptr->LargerSize; - if (ptr == null) - { - return null; - } - curSize = ptr->Size; - } - return ptr; - } - - /// - /// Gets the last block on a certain page for objects of this size - /// - /// Page to search - /// - /// - private static SMTBlock* GetLastBlock(SMTPage* page, uint aSize) - { - SMTBlock* ptr = GetFirstBlock(page, aSize)->First; - if (ptr == null) - { - return null; - } - - while (ptr->NextBlock != null) - { - ptr = ptr->NextBlock; - } - return ptr; - } - - /// - /// Get the first block for this size on any SMT page, which has space left to allocate to - /// - /// - /// Null if no more space on any block of this size - private static SMTBlock* GetFirstWithSpace(uint aSize) - { - SMTPage* page = SMT; - SMTBlock* block = null; - do - { - block = GetFirstWithSpace(page, aSize); - page = page->Next; - } while (block == null && page != null); - return block; - } - - /// - /// Get the first block for this size on this SMT page, which has space left to allocate to - /// - /// - /// Null if no more space on this page - private static SMTBlock* GetFirstWithSpace(SMTPage* aPage, uint aSize) - { - return GetFirstWithSpace(GetFirstBlock(aPage, aSize), aSize); - } - - /// - /// Get the first block for this size in this SMT block chain, which has space left to allocate to - /// - /// The root node to start the search at - /// - /// - private static SMTBlock* GetFirstWithSpace(RootSMTBlock* aRoot, uint aSize) - { - SMTBlock* ptr = aRoot->First; - if (ptr == null) // Can this ever happen? - { - return null; - } - while (ptr->SpacesLeft == 0) - { - ptr = ptr->NextBlock; - if (ptr == null) - { - return null; - } - } - return ptr; - } - - /// - /// Add a new root block for a certain size to a certain SMT page - /// - /// Size must be divisible by 2 otherwise Alloc breaks - private static void AddRootSMTBlock(SMTPage* aPage, uint aSize) - { - RootSMTBlock* ptr = aPage->First; - while (ptr->LargerSize != null) - { - ptr = ptr->LargerSize; - } - - if (aSize < ptr->Size) - { - // we cant later add a block with a size smaller than an earlier block. That would break the algorithm - Debugger.DoSendNumber(aSize); - Debugger.DoSendNumber(ptr->Size); - Debugger.SendKernelPanic(0x83); - while (true) { } - } - - if (ptr->Size == 0) // This is the first block to be allocated on the page - { - ptr->Size = aSize; - } - else - { - RootSMTBlock* block = (RootSMTBlock*)NextFreeBlock(aPage); // we should actually check that this is not null - //but we should also only call this code right at the beginning so it should be fine - block->Size = aSize; - ptr->LargerSize = block; - } - CreatePage(aPage, aSize); - } - - /// - /// Get the Last Page of the SMT - /// - /// - private static SMTPage* GetSMTLastPage() - { - var page = SMT; - while (page->Next != null) - { - page = page->Next; - } - return page; - } - - /// - /// Return the size a certain element will be allocated as - /// - /// - public static uint GetRoundedSize(uint aSize) - { - return GetFirstBlock(SMT, aSize)->Size; - } - - #endregion - - /// - /// Init small heap. - /// - /// Thrown on fatal error, contact support. - static public void Init() - { - //TODO Adjust for new page and header sizes - // 4 slots, ~1k ea - uint xMaxItemSize = RAT.PageSize / 4 - PrefixItemBytes; - // Word align it - mMaxItemSize = xMaxItemSize / sizeof(uint) * sizeof(uint); - - SMT = InitSMTPage(); - } - - /// - /// Allocates and initialise a page for the SMT table - /// - /// - private static SMTPage* InitSMTPage() - { - SMTPage* page = (SMTPage*)RAT.AllocPages(RAT.PageType.SMT, 1); - page->First = (RootSMTBlock*)page + 1; - - // TODO Change these sizes after further study and also when page size changes. - // SMT can be grown as needed. Also can adjust and create new ones dynamicaly as it runs. - // The current algorithm only works if we create the inital pages in increasing order - AddRootSMTBlock(page, 16); - AddRootSMTBlock(page, 24); - AddRootSMTBlock(page, 48); - AddRootSMTBlock(page, 64); - AddRootSMTBlock(page, 128); - AddRootSMTBlock(page, 256); - AddRootSMTBlock(page, 512); - AddRootSMTBlock(page, mMaxItemSize); - return page; - } - - /// - /// Create a page with the size of an item and try add it to the SMT at a certain page - /// If the SMT page is full, it will be added to the first SMT page with space or a new SMT page is allocated - /// - /// Object size in bytes - /// Thrown if: - /// - /// aItemSize is 0. - /// aItemSize is not word aligned. - /// SMT is not initialized. - /// The item size is bigger then a small heap size. - /// - /// - static void CreatePage(SMTPage* aPage, uint aItemSize) - { - byte* xPtr = (byte*)RAT.AllocPages(RAT.PageType.HeapSmall, 1); - if (xPtr == null) - { - return; // we failed to create the page, Alloc should still handle this case - } - - uint xSlotSize = aItemSize + PrefixItemBytes; - uint xItemCount = RAT.PageSize / xSlotSize; - for (uint i = 0; i < xItemCount; i++) - { - byte* xSlotPtr = xPtr + i * xSlotSize; - ushort* xMetaDataPtr = (ushort*)xSlotPtr; - xMetaDataPtr[0] = 0; // Actual data size. 0 is empty. - xMetaDataPtr[1] = 0; // Ref count - } - - //now add it to the smt - SMTBlock* parent = GetLastBlock(aPage, aItemSize); - SMTBlock* smtBlock = NextFreeBlock(aPage); //get the next free block in the smt - - if (smtBlock == null) // we could not allocate a new block since the SMT table is all full on this page - { - // we now have two options: - // 1. there exists a later page in the chain, which has space - // 2. all SMT Pages are full and we need to allocate a new one - - // first, check if we find a later page with space - SMTPage* currentSMTPage = aPage->Next; - while (currentSMTPage != null) - { - smtBlock = NextFreeBlock(currentSMTPage); - if(smtBlock != null) - { - break; - } - currentSMTPage = currentSMTPage->Next; - } - - if (smtBlock == null) - { - // we need to expand the SMT table by a page - SMTPage* last = GetSMTLastPage(); - last->Next = InitSMTPage(); - aPage = last->Next; - parent = GetLastBlock(aPage, aItemSize); - smtBlock = NextFreeBlock(aPage); - - if (smtBlock == null) - { - Debugger.SendKernelPanic(0x93); - while (true) { }; - } - } - else - { - aPage = currentSMTPage; - parent = GetLastBlock(aPage, aItemSize); - // we have already found the smt block above - } - } - - if (parent != null) - { - // there is already a block for the same size on the same page - parent->NextBlock = smtBlock; - } - else - { - // in this case this is the first block of the size, so we can link it to root - RootSMTBlock* root = GetFirstBlock(aPage, aItemSize); - root->First = smtBlock; - } - - smtBlock->SpacesLeft = xItemCount; - smtBlock->PagePtr = xPtr; - } - - /// - /// Alloc memory block, of a given size. - /// - /// A size of block to alloc, in bytes. - /// Byte pointer to the start of the block. - public static byte* Alloc(ushort aSize) - { - SMTBlock* pageBlock = GetFirstWithSpace(aSize); - if (pageBlock == null) // This happens when the page is full and we need to allocate a new page for this size - { - CreatePage(SMT, GetRoundedSize(aSize)); // CreatePage will try add this page to any page of the SMT until it finds one with space - pageBlock = GetFirstWithSpace(aSize); - if (pageBlock == null) - { - //this means that we cant allocate another page - Debugger.SendKernelPanic(0x121); - } - } - - //now find position in the block - ushort* page = (ushort*)pageBlock->PagePtr; - uint elementSize = GetRoundedSize(aSize) + PrefixItemBytes; - uint positions = RAT.PageSize / elementSize; - for (int i = 0; i < positions; i++) - { - if (page[i * elementSize / 2] == 0) - { - // we have found an empty slot - - // update SMT block info - pageBlock->SpacesLeft--; - - // set info in page - ushort* heapObject = &page[i * elementSize / 2]; - heapObject[0] = aSize; // size of actual object being allocated - heapObject[1] = 0; // gc status starts as 0 - - return (byte*)&heapObject[2]; - - } - } - - // if we get here, RAM is corrupted, since we know we had a space but it turns out we didnt - Debugger.DoSendNumber((uint)pageBlock); - Debugger.DoSendNumber(aSize); - Debugger.SendKernelPanic(0x122); - while (true) { } - } - - /// - /// Free a object - /// - /// A pointer to the start object. - public static void Free(void* aPtr) - { - ushort* heapObject = (ushort*)aPtr; - ushort size = heapObject[-2]; - if (size == 0) - { - // double free, this object has already been freed - Debugger.DoBochsBreak(); - Debugger.DoSendNumber((uint)heapObject); - Debugger.SendKernelPanic(0x99); - } - - uint* allocated = (uint*)aPtr; - allocated[-1] = 0; // zero both size and gc status at once - - // now zero the object so its ready for next allocation - if (size < 4) // so we dont actually forget to clean up too small items - { - size = 4; - } - int bytes = size / 4; - if (size % 4 != 0) - { - bytes += 1; - } - for (int i = 0; i < bytes; i++) - { - allocated[i] = 0; - } - - // need to increase count in SMT again - // todo: store this info somewhere so this can be done in constant time - byte* allocatedOnPage = RAT.GetPagePtr(aPtr); - SMTPage* smtPage = SMT; - SMTBlock* blockPtr = null; - while (smtPage != null) - { - blockPtr = GetFirstBlock(smtPage, size)->First; - while (blockPtr != null) - { - if(blockPtr->PagePtr == allocatedOnPage) - { - blockPtr->SpacesLeft++; - return; - } - blockPtr = blockPtr->NextBlock; - } - smtPage = smtPage->Next; - } - - // this shouldnt happen - Debugger.DoSendNumber((uint)aPtr); - Debugger.DoSendNumber((uint)SMT); - Debugger.SendKernelPanic(0x98); - while (true) { } - } - - #region Statistics - - /// - /// Counts how many elements are currently allocated - /// - public static int GetAllocatedObjectCount() - { - var ptr = SMT; - int count = 0; - do - { - count += GetAllocatedObjectCount(ptr); - ptr = ptr->Next; - } while (ptr != null); - return count; - } - - /// - /// Counts how many elements are currently allocated on a certain page - /// - /// - /// - private static int GetAllocatedObjectCount(SMTPage* aPage) - { - var ptr = aPage->First; - int count = 0; - while (ptr != null) - { - count += GetAllocatedObjectCount(aPage, ptr->Size); - ptr = ptr->LargerSize; - } - return count; - } - - /// - /// Counts how many elements are currently allocated in this category on a certain page - /// - /// - /// - private static int GetAllocatedObjectCount(SMTPage* aPage, uint aSize) - { - RootSMTBlock* root = GetFirstBlock(aPage, aSize); - SMTBlock* ptr = root->First; - - uint size = root->Size; - int count = 0; - - while (ptr != null) - { - count += (int)(RAT.PageSize / (size + PrefixItemBytes)) - (int)ptr->SpacesLeft; - ptr = ptr->NextBlock; - } - - return count; - } - - #endregion - - #region Cleanup - - /// - /// This function will free all pages allocated for small objects which are emnpty - /// - /// Number of pages freed - public static int PruneSMT() - { - int freed = 0; - SMTPage* page = SMT; - while (page != null) - { - freed += PruneSMT(page); - page = page->Next; - } - return freed; - } - - /// - /// Prune all empty pages allocated on a certain page - /// - /// - /// - private static int PruneSMT(SMTPage* aPage) - { - int freed = 0; - RootSMTBlock* ptr = (RootSMTBlock*)aPage->First; // since both RootSMTBlock and SMTBlock have the same size (20) it doesnt matter if cast is wrong - while(ptr != null) - { - freed += PruneSMT(ptr, ptr->Size); - ptr = ptr->LargerSize; - } - return freed; - } - - /// - /// Prune all empty pages which are linked to root block for a certain size - /// The root block or first one following it will not be removed! - /// - /// - /// - /// - private static int PruneSMT(RootSMTBlock* aBlock, uint aSize) - { - int freed = 0; - int maxElements = (int)(RAT.PageSize / (aSize + PrefixItemBytes)); - SMTBlock* prev = aBlock->First; - SMTBlock* block = prev->NextBlock; - while(block != null) - { - if (block->SpacesLeft == maxElements) - { - // This block is currently empty so free it - prev->NextBlock = block->NextBlock; - RAT.Free(block->PagePtr); - - uint* toCleanUp = (uint*) block; - block = prev->NextBlock; - - toCleanUp[0] = 0; - toCleanUp[1] = 0; - toCleanUp[2] = 0; - - freed++; - } - else - { - prev = block; - block = block->NextBlock; - } - } - return freed; - } - - #endregion - } -} +using System; +using Cosmos.Debug.Kernel; +using IL2CPU.API; +using IL2CPU.API.Attribs; + +namespace Cosmos.Core.Memory +{ + /// + /// Page containing Size Map Table + /// + public unsafe struct SMTPage + { + /// + /// Pointer to the next page + /// + public SMTPage* Next; + /// + /// Pointer to the root of the smallest block stored on this page + /// + public RootSMTBlock* First; + } + + public unsafe struct RootSMTBlock + { + /// + /// Elements stored in the page have a size less or equal to this + /// Does not include the prefix bytes + /// + public uint Size; + /// + /// Pointer to the first block for this size + /// + public SMTBlock* First; + /// + /// Next larger size root block + /// + public RootSMTBlock* LargerSize; + } + // Changing the ordering will break SMTBlock* NextFreeBlock(SMTPage* aPage) + public unsafe struct SMTBlock + { + /// + /// Pointer to the actual page, where the elements are stored + /// + public byte* PagePtr; + /// + /// How much space is left on the page, makes it easier to skip full pages + /// + public uint SpacesLeft; + /// + /// Pointer to the next block for this size + /// + public SMTBlock* NextBlock; + } + + //TODO Remove empty pages as necessary + /// + /// HeapSmall class. Used to alloc and free small memory blocks on the heap. + /// + unsafe static public class HeapSmall + { + /// + /// Number of prefix bytes for each item. + /// We technically only need 2 but to keep it aligned we have two padding + /// + public const uint PrefixItemBytes = 2 * sizeof(ushort); + /// + /// Max item size in the heap. + /// + public static uint mMaxItemSize; + + /// + /// Size map table + /// + public static SMTPage* SMT; + + #region SMT + + /// + /// Find the next free block in a page + /// + /// Pointer to the start of block in the SMT. null if all SMT pages are full + private static SMTBlock* NextFreeBlock(SMTPage* aPage) + { + SMTBlock* ptr = (SMTBlock*)aPage->First; // since both RootSMTBlock and SMTBlock have the same size (20) it doesnt matter if cast is wrong + while (ptr->PagePtr != null) // this would check Size if its actually a RootSMTBlock, which is always non-zero + { + ptr += 1; + if (ptr >= (byte*)aPage + RAT.PageSize - 8) + { + return null; + } + } + return ptr; + + } + + /// + /// Gets the root block in the SMT for objects of this size + /// + /// Size of allocated block + /// Page to seach in + /// Pointer of block in SMT. + private static RootSMTBlock* GetFirstBlock(SMTPage* aPage, uint aSize) + { + RootSMTBlock* ptr = aPage->First; + uint curSize = ptr->Size; + while (curSize < aSize) + { + ptr = ptr->LargerSize; + if (ptr == null) + { + return null; + } + curSize = ptr->Size; + } + return ptr; + } + + /// + /// Gets the last block on a certain page for objects of this size + /// + /// Page to search + /// + /// + private static SMTBlock* GetLastBlock(SMTPage* page, uint aSize) + { + SMTBlock* ptr = GetFirstBlock(page, aSize)->First; + if (ptr == null) + { + return null; + } + + while (ptr->NextBlock != null) + { + ptr = ptr->NextBlock; + } + return ptr; + } + + /// + /// Get the first block for this size on any SMT page, which has space left to allocate to + /// + /// + /// Null if no more space on any block of this size + private static SMTBlock* GetFirstWithSpace(uint aSize) + { + SMTPage* page = SMT; + SMTBlock* block = null; + do + { + block = GetFirstWithSpace(page, aSize); + page = page->Next; + } while (block == null && page != null); + return block; + } + + /// + /// Get the first block for this size on this SMT page, which has space left to allocate to + /// + /// + /// Null if no more space on this page + private static SMTBlock* GetFirstWithSpace(SMTPage* aPage, uint aSize) + { + return GetFirstWithSpace(GetFirstBlock(aPage, aSize), aSize); + } + + /// + /// Get the first block for this size in this SMT block chain, which has space left to allocate to + /// + /// The root node to start the search at + /// + /// + private static SMTBlock* GetFirstWithSpace(RootSMTBlock* aRoot, uint aSize) + { + SMTBlock* ptr = aRoot->First; + if (ptr == null) // Can this ever happen? + { + return null; + } + while (ptr->SpacesLeft == 0) + { + ptr = ptr->NextBlock; + if (ptr == null) + { + return null; + } + } + return ptr; + } + + /// + /// Add a new root block for a certain size to a certain SMT page + /// + /// Size must be divisible by 2 otherwise Alloc breaks + private static void AddRootSMTBlock(SMTPage* aPage, uint aSize) + { + RootSMTBlock* ptr = aPage->First; + while (ptr->LargerSize != null) + { + ptr = ptr->LargerSize; + } + + if (aSize < ptr->Size) + { + // we cant later add a block with a size smaller than an earlier block. That would break the algorithm + Debugger.DoSendNumber(aSize); + Debugger.DoSendNumber(ptr->Size); + Debugger.SendKernelPanic(0x83); + while (true) { } + } + + if (ptr->Size == 0) // This is the first block to be allocated on the page + { + ptr->Size = aSize; + } + else + { + RootSMTBlock* block = (RootSMTBlock*)NextFreeBlock(aPage); // we should actually check that this is not null + //but we should also only call this code right at the beginning so it should be fine + block->Size = aSize; + ptr->LargerSize = block; + } + CreatePage(aPage, aSize); + } + + /// + /// Get the Last Page of the SMT + /// + /// + private static SMTPage* GetSMTLastPage() + { + var page = SMT; + while (page->Next != null) + { + page = page->Next; + } + return page; + } + + /// + /// Return the size a certain element will be allocated as + /// + /// + public static uint GetRoundedSize(uint aSize) + { + return GetFirstBlock(SMT, aSize)->Size; + } + + #endregion + + /// + /// Init small heap. + /// + /// Thrown on fatal error, contact support. + static public void Init() + { + //TODO Adjust for new page and header sizes + // 4 slots, ~1k ea + uint xMaxItemSize = RAT.PageSize / 4 - PrefixItemBytes; + // Word align it + mMaxItemSize = xMaxItemSize / sizeof(uint) * sizeof(uint); + + SMT = InitSMTPage(); + } + + /// + /// Allocates and initialise a page for the SMT table + /// + /// + private static SMTPage* InitSMTPage() + { + SMTPage* page = (SMTPage*)RAT.AllocPages(RAT.PageType.SMT, 1); + page->First = (RootSMTBlock*)page + 1; + + // TODO Change these sizes after further study and also when page size changes. + // SMT can be grown as needed. Also can adjust and create new ones dynamicaly as it runs. + // The current algorithm only works if we create the inital pages in increasing order + AddRootSMTBlock(page, 16); + AddRootSMTBlock(page, 24); + AddRootSMTBlock(page, 48); + AddRootSMTBlock(page, 64); + AddRootSMTBlock(page, 128); + AddRootSMTBlock(page, 256); + AddRootSMTBlock(page, 512); + AddRootSMTBlock(page, mMaxItemSize); + return page; + } + + /// + /// Create a page with the size of an item and try add it to the SMT at a certain page + /// If the SMT page is full, it will be added to the first SMT page with space or a new SMT page is allocated + /// + /// Object size in bytes + /// Thrown if: + /// + /// aItemSize is 0. + /// aItemSize is not word aligned. + /// SMT is not initialized. + /// The item size is bigger then a small heap size. + /// + /// + static void CreatePage(SMTPage* aPage, uint aItemSize) + { + byte* xPtr = (byte*)RAT.AllocPages(RAT.PageType.HeapSmall, 1); + if (xPtr == null) + { + return; // we failed to create the page, Alloc should still handle this case + } + + uint xSlotSize = aItemSize + PrefixItemBytes; + uint xItemCount = RAT.PageSize / xSlotSize; + for (uint i = 0; i < xItemCount; i++) + { + byte* xSlotPtr = xPtr + i * xSlotSize; + ushort* xMetaDataPtr = (ushort*)xSlotPtr; + xMetaDataPtr[0] = 0; // Actual data size. 0 is empty. + xMetaDataPtr[1] = 0; // Ref count + } + + //now add it to the smt + SMTBlock* parent = GetLastBlock(aPage, aItemSize); + SMTBlock* smtBlock = NextFreeBlock(aPage); //get the next free block in the smt + + if (smtBlock == null) // we could not allocate a new block since the SMT table is all full on this page + { + // we now have two options: + // 1. there exists a later page in the chain, which has space + // 2. all SMT Pages are full and we need to allocate a new one + + // first, check if we find a later page with space + SMTPage* currentSMTPage = aPage->Next; + while (currentSMTPage != null) + { + smtBlock = NextFreeBlock(currentSMTPage); + if(smtBlock != null) + { + break; + } + currentSMTPage = currentSMTPage->Next; + } + + if (smtBlock == null) + { + // we need to expand the SMT table by a page + SMTPage* last = GetSMTLastPage(); + last->Next = InitSMTPage(); + aPage = last->Next; + parent = GetLastBlock(aPage, aItemSize); + smtBlock = NextFreeBlock(aPage); + + if (smtBlock == null) + { + Debugger.SendKernelPanic(0x93); + while (true) { }; + } + } + else + { + aPage = currentSMTPage; + parent = GetLastBlock(aPage, aItemSize); + // we have already found the smt block above + } + } + + if (parent != null) + { + // there is already a block for the same size on the same page + parent->NextBlock = smtBlock; + } + else + { + // in this case this is the first block of the size, so we can link it to root + RootSMTBlock* root = GetFirstBlock(aPage, aItemSize); + root->First = smtBlock; + } + + smtBlock->SpacesLeft = xItemCount; + smtBlock->PagePtr = xPtr; + } + + /// + /// Alloc memory block, of a given size. + /// + /// A size of block to alloc, in bytes. + /// Byte pointer to the start of the block. + public static byte* Alloc(ushort aSize) + { + SMTBlock* pageBlock = GetFirstWithSpace(aSize); + if (pageBlock == null) // This happens when the page is full and we need to allocate a new page for this size + { + CreatePage(SMT, GetRoundedSize(aSize)); // CreatePage will try add this page to any page of the SMT until it finds one with space + pageBlock = GetFirstWithSpace(aSize); + if (pageBlock == null) + { + //this means that we cant allocate another page + Debugger.SendKernelPanic(0x121); + } + } + + //now find position in the block + ushort* page = (ushort*)pageBlock->PagePtr; + uint elementSize = GetRoundedSize(aSize) + PrefixItemBytes; + uint positions = RAT.PageSize / elementSize; + for (int i = 0; i < positions; i++) + { + if (page[i * elementSize / 2] == 0) + { + // we have found an empty slot + + // update SMT block info + pageBlock->SpacesLeft--; + + // set info in page + ushort* heapObject = &page[i * elementSize / 2]; + heapObject[0] = aSize; // size of actual object being allocated + heapObject[1] = 0; // gc status starts as 0 + + return (byte*)&heapObject[2]; + + } + } + + // if we get here, RAM is corrupted, since we know we had a space but it turns out we didnt + Debugger.DoSendNumber((uint)pageBlock); + Debugger.DoSendNumber(aSize); + Debugger.SendKernelPanic(0x122); + while (true) { } + } + + /// + /// Free a object + /// + /// A pointer to the start object. + public static void Free(void* aPtr) + { + ushort* heapObject = (ushort*)aPtr; + ushort size = heapObject[-2]; + if (size == 0) + { + // double free, this object has already been freed + Debugger.DoBochsBreak(); + Debugger.DoSendNumber((uint)heapObject); + Debugger.SendKernelPanic(0x99); + } + + uint* allocated = (uint*)aPtr; + allocated[-1] = 0; // zero both size and gc status at once + + // now zero the object so its ready for next allocation + if (size < 4) // so we dont actually forget to clean up too small items + { + size = 4; + } + int bytes = size / 4; + if (size % 4 != 0) + { + bytes += 1; + } + for (int i = 0; i < bytes; i++) + { + allocated[i] = 0; + } + + // need to increase count in SMT again + // todo: store this info somewhere so this can be done in constant time + byte* allocatedOnPage = RAT.GetPagePtr(aPtr); + SMTPage* smtPage = SMT; + SMTBlock* blockPtr = null; + while (smtPage != null) + { + blockPtr = GetFirstBlock(smtPage, size)->First; + while (blockPtr != null) + { + if(blockPtr->PagePtr == allocatedOnPage) + { + blockPtr->SpacesLeft++; + return; + } + blockPtr = blockPtr->NextBlock; + } + smtPage = smtPage->Next; + } + + // this shouldnt happen + Debugger.DoSendNumber((uint)aPtr); + Debugger.DoSendNumber((uint)SMT); + Debugger.SendKernelPanic(0x98); + while (true) { } + } + + #region Statistics + + /// + /// Counts how many elements are currently allocated + /// + public static int GetAllocatedObjectCount() + { + var ptr = SMT; + int count = 0; + do + { + count += GetAllocatedObjectCount(ptr); + ptr = ptr->Next; + } while (ptr != null); + return count; + } + + /// + /// Counts how many elements are currently allocated on a certain page + /// + /// + /// + private static int GetAllocatedObjectCount(SMTPage* aPage) + { + var ptr = aPage->First; + int count = 0; + while (ptr != null) + { + count += GetAllocatedObjectCount(aPage, ptr->Size); + ptr = ptr->LargerSize; + } + return count; + } + + /// + /// Counts how many elements are currently allocated in this category on a certain page + /// + /// + /// + private static int GetAllocatedObjectCount(SMTPage* aPage, uint aSize) + { + RootSMTBlock* root = GetFirstBlock(aPage, aSize); + SMTBlock* ptr = root->First; + + uint size = root->Size; + int count = 0; + + while (ptr != null) + { + count += (int)(RAT.PageSize / (size + PrefixItemBytes)) - (int)ptr->SpacesLeft; + ptr = ptr->NextBlock; + } + + return count; + } + + #endregion + + #region Cleanup + + /// + /// This function will free all pages allocated for small objects which are emnpty + /// + /// Number of pages freed + public static int PruneSMT() + { + int freed = 0; + SMTPage* page = SMT; + while (page != null) + { + freed += PruneSMT(page); + page = page->Next; + } + return freed; + } + + /// + /// Prune all empty pages allocated on a certain page + /// + /// + /// + private static int PruneSMT(SMTPage* aPage) + { + int freed = 0; + RootSMTBlock* ptr = (RootSMTBlock*)aPage->First; // since both RootSMTBlock and SMTBlock have the same size (20) it doesnt matter if cast is wrong + while(ptr != null) + { + freed += PruneSMT(ptr, ptr->Size); + ptr = ptr->LargerSize; + } + return freed; + } + + /// + /// Prune all empty pages which are linked to root block for a certain size + /// The root block or first one following it will not be removed! + /// + /// + /// + /// + private static int PruneSMT(RootSMTBlock* aBlock, uint aSize) + { + int freed = 0; + int maxElements = (int)(RAT.PageSize / (aSize + PrefixItemBytes)); + SMTBlock* prev = aBlock->First; + SMTBlock* block = prev->NextBlock; + while(block != null) + { + if (block->SpacesLeft == maxElements) + { + // This block is currently empty so free it + prev->NextBlock = block->NextBlock; + RAT.Free(block->PagePtr); + + uint* toCleanUp = (uint*) block; + block = prev->NextBlock; + + toCleanUp[0] = 0; + toCleanUp[1] = 0; + toCleanUp[2] = 0; + + freed++; + } + else + { + prev = block; + block = block->NextBlock; + } + } + return freed; + } + + #endregion + } +} diff --git a/source/Cosmos.Core/Memory/RAT.cs b/source/Cosmos.Core/Memory/RAT.cs index 3413e3e687..aa590927ab 100644 --- a/source/Cosmos.Core/Memory/RAT.cs +++ b/source/Cosmos.Core/Memory/RAT.cs @@ -1,359 +1,359 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Cosmos.Debug.Kernel; -using Native = System.UInt32; - -namespace Cosmos.Core.Memory -{ - /// - /// RAT (RAM Allocation Table) class. - /// - unsafe static public class RAT - { - // RAT: RAM Allocation Table - // - // A byte table which defines the code which owns the page. - // Owners can further subdivide table types on their own and RAT - // code must not assume anything about contents of pages other - // than who owns them. - - /// - /// PageType enum. Used to define the type of the page. - /// - public enum PageType : byte - { - /// - /// Empty page. - /// Can also indicate invalid page. - /// - Empty = 0, - - // Data Types from 1, special meanings from 255 down. - /// - /// Indicates that the page contains objects managed by the GC - /// - GCManaged = 1, - /// - /// Small heap page. - /// - HeapSmall = 3, - /// - /// Medium heap page. - /// - HeapMedium = 5, - /// - /// Large heap page. - /// - HeapLarge = 7, - - /// - /// RAT type page. - /// - RAT = 32, - /// - /// Page which is part of the SMT - /// - SMT = 64, - // Extension of previous page. - /// - /// Extension of pre-existing page. - /// - Extension = 128, - } - - /// - /// Debug flag. - /// - /// Used to bypass certain checks that will fail during tests and debugging. - static internal bool Debug = false; - - /// - /// Native Intel page size. - /// - /// - /// x86 Page Size: 4k, 2m (PAE only), 4m. - /// x64 Page Size: 4k, 2m - /// - public const uint PageSize = 4096; - - /// - /// Start of area usable for heap, and also start of heap. - /// - public static byte* RamStart; - /// - /// Pointer to end of the heap - /// - public static byte* HeapEnd; - /// - /// Size of heap. - /// - public static uint RamSize; - /// - /// Number of pages in the heap. - /// - /// Calculated from mSize. - public static uint TotalPageCount; - - /// - /// Number of pages which are currently not in use - /// - public static uint FreePageCount; - - /// - /// If number of free pages drops below this number, we trigger the GC.Collect automatically - /// If set to -1 it is disabled - /// - public static int MinFreePages = -1; - - /// - /// Number of the times the GC has been triggered automatically - /// - public static uint GCTriggered = 0; - - /// - /// Pointer to the RAT. - /// - /// Covers Data area only. - // We need a pointer as the RAT can move around in future with dynamic RAM etc. - public static byte* mRAT; - - /// - /// Init RAT. - /// - /// A pointer to the start of the heap. - /// A heap size, in bytes. - /// Thrown if: - /// - /// RAM start or size is not page aligned. - /// - /// - public static void Init(byte* aStartPtr, uint aSize) - { - if ((uint)aStartPtr % PageSize != 0 && !Debug) - { - Debugger.DoSendNumber((uint)aStartPtr % PageSize); - Debugger.DoBochsBreak(); - throw new Exception("RAM start must be page aligned."); - } - - if (aSize % PageSize != 0) - { - Debugger.DoSendNumber(aSize % PageSize); - Debugger.SendKernelPanic(11); - throw new Exception("RAM size must be page aligned."); - } - - RamStart = aStartPtr; - RamSize = aSize; - HeapEnd = aStartPtr + aSize; - TotalPageCount = aSize / PageSize; - FreePageCount = TotalPageCount; - - // We need one status byte for each block. - // Intel blocks are 4k (10 bits). So for 4GB, this means - // 32 - 12 = 20 bits, 1 MB for a RAT for 4GB. 0.025% - uint xRatPageCount = (TotalPageCount - 1) / PageSize + 1; - uint xRatTotalSize = xRatPageCount * PageSize; - mRAT = RamStart + RamSize - xRatTotalSize; - - if (mRAT > HeapEnd) - { - throw new Exception("mRAT is greater than heap. rattotalsize is "+xRatTotalSize); - } - - // Mark empty pages as such in the RAT Table - for (byte* p = mRAT; p < mRAT + TotalPageCount - xRatPageCount; p++) - { - *p = (byte)PageType.Empty; - } - // Mark the rat pages as such - for (byte* p = mRAT + TotalPageCount - xRatPageCount; p < mRAT + xRatTotalSize; p++) - { - *p = (byte)PageType.RAT; - } - // Remove pages needed for RAT table from count - FreePageCount -= xRatPageCount; - - Heap.Init(); - } - - /// - /// Get page count. - /// - /// A page type to count. - /// Number of pages of this type including extension pages - public static uint GetPageCount(byte aType = 0) - { - uint xResult = 0; - bool xCounting = false; - for (byte* p = mRAT; p < mRAT + TotalPageCount; p++) - { - if (*p == aType) - { - xResult++; - xCounting = true; - } - else if (xCounting) - { - if (*p == (byte)PageType.Extension || *p == aType) - { - xResult++; - } - else - { - xCounting = false; - } - } - } - return xResult; - } - - /// - /// Alloc a given number of pages, all of the same type. - /// - /// A type of pages to alloc. - /// Number of pages to alloc. (default = 1) - /// A pointer to the first page on success, null on failure. - public static void* AllocPages(PageType aType, uint aPageCount = 1) - { - if (MinFreePages > FreePageCount) - { - Heap.Collect(); - GCTriggered++; - } - - byte* xPos = null; - - // Could combine with an external method or delegate, but will slow things down - // unless we can force it to be inlined. - // Alloc single blocks at bottom, larger blocks at top to help reduce fragmentation. - uint xCount = 0; - if (aPageCount == 1) - { - for (byte* p = mRAT; p < mRAT + TotalPageCount; p++) - { - if (*p == (byte)PageType.Empty) - { - xPos = p; - break; - } - } - } - else - { - // This loop will FAIL if mRAT is ever 0. This should be impossible though - // so we don't bother to account for such a case. xPos would also have issues. - for (byte* p = mRAT + TotalPageCount - 1; p >= mRAT; p--) - { - if (*p == (byte)PageType.Empty) - { - if (++xCount == aPageCount) - { - xPos = p; - break; - } - } - else - { - xCount = 0; - } - } - } - - // If we found enough space, mark it as used. - if (xPos != null) - { - var diff = xPos - mRAT; - byte* xResult = RamStart + diff * PageSize; - *xPos = (byte)aType; - for (byte* p = xPos + 1; p < xPos + xCount; p++) - { - *p = (byte)PageType.Extension; - } - CPU.ZeroFill((uint)xResult, PageSize * aPageCount); - - // Decrement free page count - FreePageCount -= aPageCount; - - return xResult; - } - return null; - - } - - /// - /// Get the first RAT address. - /// - /// A pointer to the block. - /// The index in RAT to which this pointer belongs - /// Thrown if page type is not found. - public static uint GetFirstRATIndex(void* aPtr) - { - var xPos = (uint)((byte*)aPtr - RamStart) / PageSize; - // See note about when mRAT = 0 in Alloc. - for (byte* p = mRAT + xPos; p >= mRAT; p--) - { - if (*p != (byte)PageType.Extension) - { - return (uint)(p - mRAT); - } - } - throw new Exception("Page type not found. Likely RAT is rotten."); - } - - /// - /// Get the pointer to the start of the page containing the pointer's address - /// - /// - /// - public static byte* GetPagePtr(void* aPtr) - { - return (byte*)aPtr - ((byte*)aPtr - RamStart) % PageSize; - } - - /// - /// Get the page type pointed by a pointer to the RAT entry. - /// - /// A pointer to the page to get the type of. - /// byte value. - /// Thrown if page type is not found. - public static PageType GetPageType(void* aPtr) - { - if(aPtr < RamStart || aPtr > HeapEnd) - { - return PageType.Empty; - } - return (PageType)mRAT[GetFirstRATIndex(aPtr)]; - } - - /// - /// Free page. - /// - /// A index to the page to be freed. - public static void Free(uint aPageIdx) - { - byte* p = mRAT + aPageIdx; - *p = (byte)PageType.Empty; - FreePageCount++; - for (; p < mRAT + TotalPageCount;) - { - if (*++p != (byte)PageType.Extension) - { - break; - } - *p = (byte)PageType.Empty; - FreePageCount++; - } - } - - /// - /// Free the page this pointer points to - /// - /// - public static void Free(void* aPtr) - { - Free(GetFirstRATIndex(aPtr)); - } - } -} +using System; +using System.Linq; +using System.Threading.Tasks; +using Cosmos.Debug.Kernel; +using Native = System.UInt32; + +namespace Cosmos.Core.Memory +{ + /// + /// RAT (RAM Allocation Table) class. + /// + unsafe static public class RAT + { + // RAT: RAM Allocation Table + // + // A byte table which defines the code which owns the page. + // Owners can further subdivide table types on their own and RAT + // code must not assume anything about contents of pages other + // than who owns them. + + /// + /// PageType enum. Used to define the type of the page. + /// + public enum PageType : byte + { + /// + /// Empty page. + /// Can also indicate invalid page. + /// + Empty = 0, + + // Data Types from 1, special meanings from 255 down. + /// + /// Indicates that the page contains objects managed by the GC + /// + GCManaged = 1, + /// + /// Small heap page. + /// + HeapSmall = 3, + /// + /// Medium heap page. + /// + HeapMedium = 5, + /// + /// Large heap page. + /// + HeapLarge = 7, + + /// + /// RAT type page. + /// + RAT = 32, + /// + /// Page which is part of the SMT + /// + SMT = 64, + // Extension of previous page. + /// + /// Extension of pre-existing page. + /// + Extension = 128, + } + + /// + /// Debug flag. + /// + /// Used to bypass certain checks that will fail during tests and debugging. + static internal bool Debug = false; + + /// + /// Native Intel page size. + /// + /// + /// x86 Page Size: 4k, 2m (PAE only), 4m. + /// x64 Page Size: 4k, 2m + /// + public const uint PageSize = 4096; + + /// + /// Start of area usable for heap, and also start of heap. + /// + public static byte* RamStart; + /// + /// Pointer to end of the heap + /// + public static byte* HeapEnd; + /// + /// Size of heap. + /// + public static uint RamSize; + /// + /// Number of pages in the heap. + /// + /// Calculated from mSize. + public static uint TotalPageCount; + + /// + /// Number of pages which are currently not in use + /// + public static uint FreePageCount; + + /// + /// If number of free pages drops below this number, we trigger the GC.Collect automatically + /// If set to -1 it is disabled + /// + public static int MinFreePages = -1; + + /// + /// Number of the times the GC has been triggered automatically + /// + public static uint GCTriggered = 0; + + /// + /// Pointer to the RAT. + /// + /// Covers Data area only. + // We need a pointer as the RAT can move around in future with dynamic RAM etc. + public static byte* mRAT; + + /// + /// Init RAT. + /// + /// A pointer to the start of the heap. + /// A heap size, in bytes. + /// Thrown if: + /// + /// RAM start or size is not page aligned. + /// + /// + public static void Init(byte* aStartPtr, uint aSize) + { + if ((uint)aStartPtr % PageSize != 0 && !Debug) + { + Debugger.DoSendNumber((uint)aStartPtr % PageSize); + Debugger.DoBochsBreak(); + throw new Exception("RAM start must be page aligned."); + } + + if (aSize % PageSize != 0) + { + Debugger.DoSendNumber(aSize % PageSize); + Debugger.SendKernelPanic(11); + throw new Exception("RAM size must be page aligned."); + } + + RamStart = aStartPtr; + RamSize = aSize; + HeapEnd = aStartPtr + aSize; + TotalPageCount = aSize / PageSize; + FreePageCount = TotalPageCount; + + // We need one status byte for each block. + // Intel blocks are 4k (10 bits). So for 4GB, this means + // 32 - 12 = 20 bits, 1 MB for a RAT for 4GB. 0.025% + uint xRatPageCount = (TotalPageCount - 1) / PageSize + 1; + uint xRatTotalSize = xRatPageCount * PageSize; + mRAT = RamStart + RamSize - xRatTotalSize; + + if (mRAT > HeapEnd) + { + throw new Exception("mRAT is greater than heap. rattotalsize is "+xRatTotalSize); + } + + // Mark empty pages as such in the RAT Table + for (byte* p = mRAT; p < mRAT + TotalPageCount - xRatPageCount; p++) + { + *p = (byte)PageType.Empty; + } + // Mark the rat pages as such + for (byte* p = mRAT + TotalPageCount - xRatPageCount; p < mRAT + xRatTotalSize; p++) + { + *p = (byte)PageType.RAT; + } + // Remove pages needed for RAT table from count + FreePageCount -= xRatPageCount; + + Heap.Init(); + } + + /// + /// Get page count. + /// + /// A page type to count. + /// Number of pages of this type including extension pages + public static uint GetPageCount(byte aType = 0) + { + uint xResult = 0; + bool xCounting = false; + for (byte* p = mRAT; p < mRAT + TotalPageCount; p++) + { + if (*p == aType) + { + xResult++; + xCounting = true; + } + else if (xCounting) + { + if (*p == (byte)PageType.Extension || *p == aType) + { + xResult++; + } + else + { + xCounting = false; + } + } + } + return xResult; + } + + /// + /// Alloc a given number of pages, all of the same type. + /// + /// A type of pages to alloc. + /// Number of pages to alloc. (default = 1) + /// A pointer to the first page on success, null on failure. + public static void* AllocPages(PageType aType, uint aPageCount = 1) + { + if (MinFreePages > FreePageCount) + { + Heap.Collect(); + GCTriggered++; + } + + byte* xPos = null; + + // Could combine with an external method or delegate, but will slow things down + // unless we can force it to be inlined. + // Alloc single blocks at bottom, larger blocks at top to help reduce fragmentation. + uint xCount = 0; + if (aPageCount == 1) + { + for (byte* p = mRAT; p < mRAT + TotalPageCount; p++) + { + if (*p == (byte)PageType.Empty) + { + xPos = p; + break; + } + } + } + else + { + // This loop will FAIL if mRAT is ever 0. This should be impossible though + // so we don't bother to account for such a case. xPos would also have issues. + for (byte* p = mRAT + TotalPageCount - 1; p >= mRAT; p--) + { + if (*p == (byte)PageType.Empty) + { + if (++xCount == aPageCount) + { + xPos = p; + break; + } + } + else + { + xCount = 0; + } + } + } + + // If we found enough space, mark it as used. + if (xPos != null) + { + var diff = xPos - mRAT; + byte* xResult = RamStart + diff * PageSize; + *xPos = (byte)aType; + for (byte* p = xPos + 1; p < xPos + xCount; p++) + { + *p = (byte)PageType.Extension; + } + CPU.ZeroFill((uint)xResult, PageSize * aPageCount); + + // Decrement free page count + FreePageCount -= aPageCount; + + return xResult; + } + return null; + + } + + /// + /// Get the first RAT address. + /// + /// A pointer to the block. + /// The index in RAT to which this pointer belongs + /// Thrown if page type is not found. + public static uint GetFirstRATIndex(void* aPtr) + { + var xPos = (uint)((byte*)aPtr - RamStart) / PageSize; + // See note about when mRAT = 0 in Alloc. + for (byte* p = mRAT + xPos; p >= mRAT; p--) + { + if (*p != (byte)PageType.Extension) + { + return (uint)(p - mRAT); + } + } + throw new Exception("Page type not found. Likely RAT is rotten."); + } + + /// + /// Get the pointer to the start of the page containing the pointer's address + /// + /// + /// + public static byte* GetPagePtr(void* aPtr) + { + return (byte*)aPtr - ((byte*)aPtr - RamStart) % PageSize; + } + + /// + /// Get the page type pointed by a pointer to the RAT entry. + /// + /// A pointer to the page to get the type of. + /// byte value. + /// Thrown if page type is not found. + public static PageType GetPageType(void* aPtr) + { + if(aPtr < RamStart || aPtr > HeapEnd) + { + return PageType.Empty; + } + return (PageType)mRAT[GetFirstRATIndex(aPtr)]; + } + + /// + /// Free page. + /// + /// A index to the page to be freed. + public static void Free(uint aPageIdx) + { + byte* p = mRAT + aPageIdx; + *p = (byte)PageType.Empty; + FreePageCount++; + for (; p < mRAT + TotalPageCount;) + { + if (*++p != (byte)PageType.Extension) + { + break; + } + *p = (byte)PageType.Empty; + FreePageCount++; + } + } + + /// + /// Free the page this pointer points to + /// + /// + public static void Free(void* aPtr) + { + Free(GetFirstRATIndex(aPtr)); + } + } +} diff --git a/source/Cosmos.Core/MemoryBlock.cs b/source/Cosmos.Core/MemoryBlock.cs index bed3816176..31d94fa613 100644 --- a/source/Cosmos.Core/MemoryBlock.cs +++ b/source/Cosmos.Core/MemoryBlock.cs @@ -486,6 +486,33 @@ public unsafe byte this[uint aByteOffset] (*(byte*)(Base + aByteOffset)) = value; } } + + /// + /// Convert part for the memory block to array. + /// + /// A starting position of the data at the source memory block. + /// A index to be the staring index at the destination array. + /// Number of bytes to get. + /// uint array. + public unsafe byte[] ToArray(int aStart, int aIndex, int aCount) + { + byte* xDest = (byte*)(Base + aStart); + byte[] array = new byte[aCount]; + fixed (byte* aArrayPtr = array) + { + MemoryOperations.Copy(aArrayPtr + aIndex, xDest, aCount); + } + return array; + } + + /// + /// Convert the memory block to array. + /// + /// uint array. + public byte[] ToArray() + { + return ToArray(0, 0, (int)Size); + } } /// diff --git a/source/Cosmos.Core/ObjUtilities.cs b/source/Cosmos.Core/ObjUtilities.cs new file mode 100644 index 0000000000..0de530713f --- /dev/null +++ b/source/Cosmos.Core/ObjUtilities.cs @@ -0,0 +1,19 @@ +using IL2CPU.API.Attribs; +using System; + +namespace Cosmos.Core +{ + public static unsafe class ObjUtilities + { + public static uint GetPointer(Delegate aVal) + { + return (uint)aVal.GetHashCode(); + } + + [PlugMethod(PlugRequired = true)] + public static uint GetPointer(Object aVal) { return 0; } + + [PlugMethod(PlugRequired = true)] + public static uint GetEntryPoint() { return 0; } + } +} diff --git a/source/Cosmos.Core/Processing/Mutex.cs b/source/Cosmos.Core/Processing/Mutex.cs new file mode 100644 index 0000000000..6cf4a51783 --- /dev/null +++ b/source/Cosmos.Core/Processing/Mutex.cs @@ -0,0 +1,27 @@ +using IL2CPU.API.Attribs; + +namespace Cosmos.Core.Processing +{ + public unsafe class Mutex + { + public int gate; + + [PlugMethod(PlugRequired = true)] + public static void MutexLock(int* mtx) { } + + public void Lock() + { + while (gate != 0) { } + gate = 1; + /*fixed (int* p = &gate) + { + MutexLock(p); + }*/ + } + + public void Unlock() + { + gate = 0; + } + } +} diff --git a/source/Cosmos.Core/Processing/ProcessContext.cs b/source/Cosmos.Core/Processing/ProcessContext.cs new file mode 100644 index 0000000000..4259f3701a --- /dev/null +++ b/source/Cosmos.Core/Processing/ProcessContext.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Cosmos.Core.Processing +{ + public static unsafe class ProcessContext + { + public enum Thread_State + { + ALIVE = 0, + DEAD = 1, + WAITING_SLEEP = 2, + PAUSED = 3 + } + + public enum Context_Type + { + THREAD = 0, + PROCESS = 1 + } + + public class Context + { + public Context next; + public Context_Type type; + public uint tid; + public string name; + public uint esp; + public uint stacktop; + public System.Threading.ThreadStart entry; + public System.Threading.ParameterizedThreadStart paramentry; + public Thread_State state; + public object param; + public int arg; + public uint priority; + public uint age; + public uint parent; + } + + public const uint STACK_SIZE = 4096; + public static uint m_NextCID; + public static Context m_CurrentContext; + public static Context m_ContextList; + + public static Context GetContext(uint tid) + { + /*for(int i = 0; i < m_ContextList.Count; i++) + { + if(m_ContextList[i].tid == tid) + { + return m_ContextList[i]; + } + }*/ + Context ctx = m_ContextList; + while (ctx.next != null) + { + if (ctx.tid == tid) + { + return ctx; + } + ctx = ctx.next; + } + if (ctx.tid == tid) + { + return ctx; + } + return null; + } + + public static uint* SetupStack(uint* stack) + { + uint origin = (uint)stack; + *--stack = 0xFFFFFFFF; // trash + *--stack = 0xFFFFFFFF; // trash + *--stack = 0xFFFFFFFF; // trash + *--stack = 0xFFFFFFFF; // trash + *--stack = 0x10; // ss ? + *--stack = 0x00000202; // eflags + *--stack = 0x8; // cs + *--stack = ObjUtilities.GetEntryPoint(); // eip + *--stack = 0; // error + *--stack = 0; // int + *--stack = 0; // eax + *--stack = 0; // ebx + *--stack = 0; // ecx + *--stack = 0; // offset + *--stack = 0; // edx + *--stack = 0; // esi + *--stack = 0; // edi + *--stack = origin; //ebp + *--stack = 0x10; // ds + *--stack = 0x10; // fs + *--stack = 0x10; // es + *--stack = 0x10; // gs + return stack; + } + + public static uint StartContext(string name, System.Threading.ThreadStart entry, Context_Type type) + { + Context context = new Context(); + context.type = type; + context.tid = m_NextCID++; + context.name = name; + context.stacktop = GCImplementation.AllocNewObject(4096); + context.esp = (uint)SetupStack((uint*)(context.stacktop + 4000)); + context.state = Thread_State.PAUSED; + context.entry = entry; + if (type == Context_Type.PROCESS) + { + context.parent = 0; + } + else + { + context.parent = m_CurrentContext.tid; + } + Context ctx = m_ContextList; + while (ctx.next != null) + { + ctx = ctx.next; + } + ctx.next = context; + return context.tid; + } + + public static uint StartContext(string name, System.Threading.ParameterizedThreadStart entry, Context_Type type, object param) + { + Context context = new Context(); + context.type = type; + context.tid = m_NextCID++; + context.name = name; + context.stacktop = GCImplementation.AllocNewObject(4096); + context.esp = (uint)SetupStack((uint*)(context.stacktop + 4000)); + context.state = Thread_State.ALIVE; + context.paramentry = entry; + context.param = param; + if (type == Context_Type.PROCESS) + { + context.parent = 0; + } + else + { + context.parent = m_CurrentContext.tid; + } + Context ctx = m_ContextList; + while (ctx.next != null) + { + ctx = ctx.next; + } + ctx.next = context; + return context.tid; + } + } +} diff --git a/source/Cosmos.Core/Processing/ProcessorScheduler.cs b/source/Cosmos.Core/Processing/ProcessorScheduler.cs new file mode 100644 index 0000000000..733971de69 --- /dev/null +++ b/source/Cosmos.Core/Processing/ProcessorScheduler.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Text; +using IL2CPU.API.Attribs; + +namespace Cosmos.Core.Processing +{ + public static unsafe class ProcessorScheduler + { + public static void Initialize() + { + var context = new ProcessContext.Context(); + context.type = ProcessContext.Context_Type.PROCESS; + context.tid = ProcessContext.m_NextCID++; + context.name = "Boot"; + context.esp = 0; + context.stacktop = 0; + context.state = ProcessContext.Thread_State.ALIVE; + context.arg = 0; + context.priority = 0; + context.age = 0; + context.parent = 0; + ProcessContext.m_ContextList = context; + ProcessContext.m_CurrentContext = context; + } + + public static void EntryPoint() + { + ProcessContext.m_CurrentContext.entry?.Invoke(); + ProcessContext.m_CurrentContext.paramentry?.Invoke(ProcessContext.m_CurrentContext.param); + ProcessContext.m_CurrentContext.state = ProcessContext.Thread_State.DEAD; + while (true) { } // remove from thread pool later + } + + public static void SwitchTask() + { + if (ProcessContext.m_CurrentContext != null) + { + CPU.DisableInterrupts(); + + ProcessContext.Context ctx = ProcessContext.m_ContextList; + ProcessContext.Context last = ctx; + while (ctx != null) + { + if (ctx.state == ProcessContext.Thread_State.DEAD) + { + last.next = ctx.next; + break; + } + last = ctx; + ctx = ctx.next; + } + ctx = ProcessContext.m_ContextList; + while (ctx != null) + { + if (ctx.state == ProcessContext.Thread_State.WAITING_SLEEP) + { + ctx.arg -= 10; //Since Local APIC Frequency = 100Hz remove 10ms per interrupt + if (ctx.arg <= 0) + { + ctx.state = ProcessContext.Thread_State.ALIVE; + } + } + ctx.age++; + ctx = ctx.next; + } + ProcessContext.m_CurrentContext.esp = INTs.mStackContext; + tryagain: + if (ProcessContext.m_CurrentContext.next != null) + { + ProcessContext.m_CurrentContext = ProcessContext.m_CurrentContext.next; + } + else + { + ProcessContext.m_CurrentContext = ProcessContext.m_ContextList; + } + if (ProcessContext.m_CurrentContext.state != ProcessContext.Thread_State.ALIVE) + { + goto tryagain; + } + ProcessContext.m_CurrentContext.age = ProcessContext.m_CurrentContext.priority; + INTs.mStackContext = ProcessContext.m_CurrentContext.esp; + + CPU.EnableInterrupts(); + } + + LocalAPIC.EndOfInterrupt(); + } + } +} \ No newline at end of file diff --git a/source/Cosmos.Core_Asm/CPU/CPUUpdateIDTAsm.cs b/source/Cosmos.Core_Asm/CPU/CPUUpdateIDTAsm.cs index f5f5acde80..f48e403335 100644 --- a/source/Cosmos.Core_Asm/CPU/CPUUpdateIDTAsm.cs +++ b/source/Cosmos.Core_Asm/CPU/CPUUpdateIDTAsm.cs @@ -13,7 +13,7 @@ namespace Cosmos.Core_Asm { public class CPUUpdateIDTAsm : AssemblerMethod { - private static MethodBase GetMethodDef(Assembly aAssembly, string aType, string aMethodName, bool aErrorWhenNotFound) + public static MethodBase GetMethodDef(Assembly aAssembly, string aType, string aMethodName, bool aErrorWhenNotFound) { Type xType = aAssembly.GetType(aType, false); if (xType != null) @@ -79,36 +79,71 @@ public override void AssembleNew(Assembler aAssembler, object aMethodInfo) XS.Push(0); } XS.Push((uint)j); - XS.PushAllRegisters(); - XS.Sub(ESP, 4); - XS.Set(EAX, ESP); // preserve old stack address for passing to interrupt handler - - // store floating point data - XS.And(ESP, 0xfffffff0); // fxsave needs to be 16-byte alligned - XS.Sub(ESP, 512); // fxsave needs 512 bytes - XS.SSE.FXSave(ESP, isIndirect: true); // save the registers - XS.Set(EAX, ESP, destinationIsIndirect: true); - - XS.Push(EAX); // - XS.Push(EAX); // pass old stack address (pointer to InterruptContext struct) to the interrupt handler - - XS.JumpToSegment(8, "__ISR_Handler_" + j.ToString("X2") + "_SetCS"); - XS.Label("__ISR_Handler_" + j.ToString("X2") + "_SetCS"); - MethodBase xHandler = GetInterruptHandler((byte)j); - if (xHandler == null) + if (j != 0x28) { - xHandler = GetMethodDef(typeof(Cosmos.Core.INTs).Assembly, typeof(Cosmos.Core.INTs).FullName, "HandleInterrupt_Default", true); + XS.PushAllRegisters(); + + XS.Sub(ESP, 4); + XS.Set(EAX, ESP); // preserve old stack address for passing to interrupt handler + + // store floating point data + XS.And(ESP, 0xfffffff0); // fxsave needs to be 16-byte alligned + XS.Sub(ESP, 512); // fxsave needs 512 bytes + XS.SSE.FXSave(ESP, isIndirect: true); // save the registers + XS.Set(EAX, ESP, destinationIsIndirect: true); + + XS.Push(EAX); // + XS.Push(EAX); // pass old stack address (pointer to InterruptContext struct) to the interrupt handler + + XS.JumpToSegment(8, "__ISR_Handler_" + j.ToString("X2") + "_SetCS"); + XS.Label("__ISR_Handler_" + j.ToString("X2") + "_SetCS"); + MethodBase xHandler = GetInterruptHandler((byte)j); + if (xHandler == null) + { + xHandler = GetMethodDef(typeof(Cosmos.Core.INTs).Assembly, typeof(Cosmos.Core.INTs).FullName, "HandleInterrupt_Default", true); + } + XS.Call(LabelName.Get(xHandler)); + XS.Pop(EAX); + XS.SSE.FXRestore(ESP, isIndirect: true); + + XS.Set(ESP, EAX); // this restores the stack for the FX stuff, except the pointer to the FX data + XS.Add(ESP, 4); // "pop" the pointer + + XS.PopAllRegisters(); } - XS.Call(LabelName.Get(xHandler)); - XS.Pop(EAX); - XS.SSE.FXRestore(ESP, isIndirect: true); - - XS.Set(ESP, EAX); // this restores the stack for the FX stuff, except the pointer to the FX data - XS.Add(ESP, 4); // "pop" the pointer - - XS.PopAllRegisters(); - + else + { + new LiteralAssemblerCode("pushad"); + new LiteralAssemblerCode("mov eax, ds"); + new LiteralAssemblerCode("push eax"); + new LiteralAssemblerCode("mov eax, es"); + new LiteralAssemblerCode("push eax"); + new LiteralAssemblerCode("mov eax, fs"); + new LiteralAssemblerCode("push eax"); + new LiteralAssemblerCode("mov eax, gs"); + new LiteralAssemblerCode("push eax"); + new LiteralAssemblerCode("mov ax, 0x10"); + new LiteralAssemblerCode("mov ds, ax"); + new LiteralAssemblerCode("mov es, ax"); + new LiteralAssemblerCode("mov fs, ax"); + new LiteralAssemblerCode("mov gs, ax"); + new LiteralAssemblerCode("mov eax, esp"); + XS.Set("static_field__A1Cosmos_Core_INTs_mStackContext", EAX, destinationIsIndirect: true); + XS.Call(LabelName.Get(GetMethodDef(typeof(Cosmos.Core.Processing.ProcessorScheduler).Assembly, typeof(Cosmos.Core.Processing.ProcessorScheduler).FullName, "SwitchTask", true))); + XS.Set(EAX, "static_field__A1Cosmos_Core_INTs_mStackContext", sourceIsIndirect: true); + new LiteralAssemblerCode("mov esp, eax"); + new LiteralAssemblerCode("pop eax"); + new LiteralAssemblerCode("mov gs, eax"); + new LiteralAssemblerCode("pop eax"); + new LiteralAssemblerCode("mov fs, eax"); + new LiteralAssemblerCode("pop eax"); + new LiteralAssemblerCode("mov es, eax"); + new LiteralAssemblerCode("pop eax"); + new LiteralAssemblerCode("mov ds, eax"); + new LiteralAssemblerCode("popad"); + } + XS.Add(ESP, 8); XS.Label("__ISR_Handler_" + j.ToString("X2") + "_END"); XS.InterruptReturn(); diff --git a/source/Cosmos.Core_Asm/DelegateImpl.cs b/source/Cosmos.Core_Asm/DelegateImpl.cs index 627cb0b531..2db3205252 100644 --- a/source/Cosmos.Core_Asm/DelegateImpl.cs +++ b/source/Cosmos.Core_Asm/DelegateImpl.cs @@ -4,7 +4,7 @@ namespace Cosmos.Core_Asm { [Plug(Target = typeof(Delegate), Inheritable = true)] - public static class DelegateImpl + public static unsafe class DelegateImpl { [PlugMethod(Assembler = typeof(DelegateCtorAsm), IsWildcard = true, WildcardMatchParameters = true)] public static void Ctor(Delegate aThis, object aTarget, IntPtr aMethod) @@ -28,5 +28,10 @@ public static bool Equals(Delegate aThis, object aThat) { throw new NotImplementedException(); } + + public static int GetHashCode(Delegate aThis, [FieldAccess(Name = "System.IntPtr System.Delegate._methodPtr")] ref IntPtr aAddress) + { + return (int)aAddress.ToPointer(); + } } } diff --git a/source/Cosmos.Core_Asm/MutexImpl.cs b/source/Cosmos.Core_Asm/MutexImpl.cs new file mode 100644 index 0000000000..1fb7a23fe4 --- /dev/null +++ b/source/Cosmos.Core_Asm/MutexImpl.cs @@ -0,0 +1,36 @@ +using Cosmos.Core.Processing; +using IL2CPU.API.Attribs; +using XSharp; +using XSharp.Assembler; + +namespace Cosmos.Core_Asm +{ + [Plug(Target = typeof(Mutex))] + public static unsafe class MutexImpl + { + [PlugMethod(Assembler = typeof(MutexLockASM))] + public static void MutexLock(int* mtx) { } + } + + public class MutexLockASM : AssemblerMethod + { + public override void AssembleNew(Assembler aAssembler, object aMethodInfo) + { + new LiteralAssemblerCode("lock_asm:"); + new LiteralAssemblerCode("mov eax, [esp + 8]"); + new LiteralAssemblerCode("mov ebx, 0"); + new LiteralAssemblerCode("lock bts[eax], ebx"); + new LiteralAssemblerCode("jc.spin_wait"); + new LiteralAssemblerCode("mov ebx, 1"); + new LiteralAssemblerCode("mov dword[eax], ebx"); + new LiteralAssemblerCode("jmp .finished"); + new LiteralAssemblerCode(".spin_wait:"); + new LiteralAssemblerCode("mov ebx, 1"); + new LiteralAssemblerCode("test dword[eax], ebx"); + new LiteralAssemblerCode("pause"); + new LiteralAssemblerCode("jnz.spin_wait"); + new LiteralAssemblerCode("jmp lock_asm"); + new LiteralAssemblerCode(".finished"); + } + } +} diff --git a/source/Cosmos.Core_Asm/ObjUtilitiesImpl.cs b/source/Cosmos.Core_Asm/ObjUtilitiesImpl.cs new file mode 100644 index 0000000000..f757352cd1 --- /dev/null +++ b/source/Cosmos.Core_Asm/ObjUtilitiesImpl.cs @@ -0,0 +1,40 @@ +using Cosmos.Core; +using IL2CPU.API; +using IL2CPU.API.Attribs; +using System; +using XSharp; +using XSharp.Assembler; + +namespace Cosmos.Core_Asm +{ + [Plug(Target = typeof(ObjUtilities))] + public static unsafe class ObjUtilitiesImpl + { + [PlugMethod(Assembler = typeof(ObjUtilitiesGetPointer))] + public static uint GetPointer(Delegate aVal) { return 0; } + + [PlugMethod(Assembler = typeof(ObjUtilitiesGetPointer))] + public static uint GetPointer(Object aVal) { return 0; } + + [PlugMethod(Assembler = typeof(ObjUtilitiesGetEntry))] + public static uint GetEntryPoint() { return 0; } + } + + public class ObjUtilitiesGetPointer : AssemblerMethod + { + public override void AssembleNew(Assembler aAssembler, object aMethodInfo) + { + XS.Set(XSRegisters.EAX, XSRegisters.EBP, sourceDisplacement: 0x8); + XS.Push(XSRegisters.EAX); + } + } + + public class ObjUtilitiesGetEntry : AssemblerMethod + { + public override void AssembleNew(Assembler aAssembler, object aMethodInfo) + { + XS.Set(XSRegisters.EAX, LabelName.Get(CPUUpdateIDTAsm.GetMethodDef(typeof(Cosmos.Core.Processing.ProcessorScheduler).Assembly, typeof(Cosmos.Core.Processing.ProcessorScheduler).FullName, "EntryPoint", true))); + XS.Push(XSRegisters.EAX); + } + } +} diff --git a/source/Cosmos.Core_Plugs/System/DelegateImpl.cs b/source/Cosmos.Core_Plugs/System/DelegateImpl.cs index bb4e48e2af..2009571699 100644 --- a/source/Cosmos.Core_Plugs/System/DelegateImpl.cs +++ b/source/Cosmos.Core_Plugs/System/DelegateImpl.cs @@ -6,7 +6,7 @@ namespace Cosmos.Core_Plugs.System { [Plug(Target = typeof(Delegate))] - public static class DelegateImpl + public static unsafe class DelegateImpl { [PlugMethod(Signature = "System_Boolean__System_Delegate_Equals_System_Object_")] public static bool Equals(Delegate aThis, object aThat) @@ -25,6 +25,11 @@ public static unsafe bool InternalEqualTypes([ObjectPointerAccess] uint** a, [Ob return xTypeA == xTypeB; } + public static int GetHashCode(Delegate aThis, [FieldAccess(Name = "System.IntPtr System.Delegate._methodPtr")] ref IntPtr aAddress) + { + return (int)aAddress.ToPointer(); + } + [PlugMethod(Signature = "System_IRuntimeMethodInfo__System_Delegate_FindMethodHandle__")] public static object FindMethodHandle(Delegate aThis) { diff --git a/source/Cosmos.Core_Plugs/System/Threading/ThreadImpl.cs b/source/Cosmos.Core_Plugs/System/Threading/ThreadImpl.cs new file mode 100644 index 0000000000..2c62d7a323 --- /dev/null +++ b/source/Cosmos.Core_Plugs/System/Threading/ThreadImpl.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading; +using IL2CPU.API.Attribs; + +namespace Cosmos.Core_Plugs.System.Threading +{ + [Plug("System.Threading.Thread, System.Private.CoreLib")] + public static class ThreadImpl + { + public static Thread GetCurrentThreadNative() + { + return null; + } + + public static void MemoryBarrier() + { + + } + + public static void Ctor(ThreadStart aThis, ThreadStart aEntry) + { + Console.WriteLine("Thread started"); + } + + // public static void SleepInternal(int ms) + // { + // // Implementation of http://referencesource.microsoft.com/#mscorlib/system/threading/thread.cs,6a577476abf2f437,references + // // see https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx for more details + + // if ((ms > 0) && (ms != Timeout.Infinite)) + // { + // double fac = CPU.GetCycleRate() / 1000d; + // double ticks = ms / 1000d * Stopwatch.Frequency + CPU.GetCycleCount() * fac; + + // while (ticks < CPU.GetCycleCount() * fac) + // new Action(() => { }).Invoke(); // execute an empty operation + // } + // else if (ms < 0) + // throw new ThreadInterruptedException(); + // } + } +} diff --git a/source/Cosmos.HAL2/ApicTimer.cs b/source/Cosmos.HAL2/ApicTimer.cs new file mode 100644 index 0000000000..e2bdcba413 --- /dev/null +++ b/source/Cosmos.HAL2/ApicTimer.cs @@ -0,0 +1,71 @@ +using Cosmos.Core; + +namespace Cosmos.HAL +{ + /// + /// Local APIC Timer class. + /// + public static class ApicTimer + { + /// + /// APIC base frequency + /// + public static ulong BaseFrequency; + + /// + /// APIC frequency + /// + public static ulong Frequency; + + private static ulong Ticks => LocalAPIC.In(LocalAPIC.LAPIC_TCCR); + + /// + /// Initialize local APIC timer. + /// + public static void Initialize() + { + Frequency = 100; + BaseFrequency = EstimateBusSpeed(); + + Global.debugger.Send("APIC timer frequency: " + Frequency + "Hz, Divisor: " + 16 + ", IRQ: 8"); + Global.debugger.Send("Base frequency is " + BaseFrequency / 1048576 + "mhz"); + Global.debugger.Send("Local APIC Timer Initialized"); + } + + /// + /// Start local APIC timer. + /// + public static void Start() + { + LocalAPIC.Out(LocalAPIC.LAPIC_TIMER, 0x00020000 | 0x28); + LocalAPIC.Out(LocalAPIC.LAPIC_TDCR, 0x3); //Divide 16 + LocalAPIC.Out(LocalAPIC.LAPIC_TICR, (uint)((BaseFrequency / 16) / Frequency)); + } + + /// + /// Stop local APIC timer. + /// + public static void Stop() + { + LocalAPIC.Out(LocalAPIC.LAPIC_TIMER, 0x20000 | 0x10000); + } + + /// + /// Calculate BUS Speed using PIT + /// + private static uint EstimateBusSpeed() + { + LocalAPIC.Out(LocalAPIC.LAPIC_TIMER, 0x10000); + LocalAPIC.Out(LocalAPIC.LAPIC_TDCR, 0x3); + LocalAPIC.Out(LocalAPIC.LAPIC_TIMER, 0); + + uint T0 = 0xFFFFFFFF; // -1 + LocalAPIC.Out(0x380, T0); + Global.PIT.Wait(100); //100ms + LocalAPIC.Out(LocalAPIC.LAPIC_TIMER, 0x10000); + + ulong Freq = (T0 - Ticks) * 16; + return (uint)(Freq * 1000000 / 100000); + } + } +} \ No newline at end of file diff --git a/source/Cosmos.HAL2/Global.cs b/source/Cosmos.HAL2/Global.cs index c088298b5b..5aa42c7739 100644 --- a/source/Cosmos.HAL2/Global.cs +++ b/source/Cosmos.HAL2/Global.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; - +using System.Threading; using Cosmos.Core; using Cosmos.Debug.Kernel; using Cosmos.HAL.BlockDevice; using Cosmos.HAL.Network; +using static Cosmos.HAL.PIT; namespace Cosmos.HAL { @@ -12,13 +13,13 @@ public static class Global { public static readonly Debugger debugger = new("Global"); - public static PIT PIT = new(); + public static PIT PIT; // Must be static init, other static inits rely on it not being null public static TextScreenBase TextScreen = new TextScreen(); public static PCI Pci; - public static readonly PS2Controller PS2Controller = new(); + public static PS2Controller PS2Controller; // TODO: continue adding exceptions to the list, as HAL and Core would be documented. /// @@ -46,14 +47,35 @@ static public void Init(TextScreenBase textScreen, bool InitScrollWheel, bool In // system level and not accessible from Core. Need to think about this // for the future. Console.Clear(); - Console.WriteLine("Finding PCI Devices"); - debugger.Send("PCI Devices"); - PCI.Setup(); Console.WriteLine("Starting ACPI"); debugger.Send("ACPI Init"); ACPI.Start(); + Console.WriteLine("Finding PCI Devices"); + debugger.Send("PCI Devices"); + PCI.Setup(); + + Console.WriteLine("Starting APIC"); + debugger.Send("Local APIC Init"); + LocalAPIC.Initialize(); + + Console.WriteLine("Starting PIT"); + debugger.Send("PIT init"); + PIT = new(); + + Console.WriteLine("Starting Processor Scheduler"); + debugger.Send("Processor Scheduler"); + Core.Processing.ProcessorScheduler.Initialize(); + + debugger.Send("Local APIC Timer Init"); + ApicTimer.Initialize(); + ApicTimer.Start(); + + Console.WriteLine("Starting PS/2"); + debugger.Send("PS/2 init"); + PS2Controller = new(); + // http://wiki.osdev.org/%228042%22_PS/2_Controller#Initialising_the_PS.2F2_Controller // TODO: USB should be initialized before the PS/2 controller // TODO: ACPI should be used to check if a PS/2 controller exists @@ -76,6 +98,7 @@ static public void Init(TextScreenBase textScreen, bool InitScrollWheel, bool In } AHCI.InitDriver(); //EHCI.InitDriver(); + if (InitNetwork) { debugger.Send("Network Devices Init"); @@ -104,6 +127,16 @@ public static void EnableInterrupts() /// public static bool InterruptsEnabled => CPU.mInterruptsEnabled; + public static uint SpawnThread(ThreadStart aStart) + { + return Core.Processing.ProcessContext.StartContext("", aStart, Core.Processing.ProcessContext.Context_Type.THREAD); + } + + public static uint SpawnThread(ParameterizedThreadStart aStart, object param) + { + return Core.Processing.ProcessContext.StartContext("", aStart, Core.Processing.ProcessContext.Context_Type.THREAD, param); + } + /// /// Get keyboard devices. /// diff --git a/source/Cosmos.System2/Thread.cs b/source/Cosmos.System2/Thread.cs new file mode 100644 index 0000000000..16d9607ec9 --- /dev/null +++ b/source/Cosmos.System2/Thread.cs @@ -0,0 +1,45 @@ +using st = System.Threading; + +namespace Cosmos.System +{ + public class Thread + { + public uint ThreadID; + private Cosmos.Core.Processing.ProcessContext.Context Data; + + public Thread(st.ThreadStart start) + { + ThreadID = Cosmos.HAL.Global.SpawnThread(start); + ThreadFinalSetup(); + } + + public Thread(st.ParameterizedThreadStart start, object param) + { + ThreadID = Cosmos.HAL.Global.SpawnThread(start, param); + ThreadFinalSetup(); + } + + private void ThreadFinalSetup() + { + Data = Cosmos.Core.Processing.ProcessContext.GetContext(ThreadID); + Data.state = Core.Processing.ProcessContext.Thread_State.PAUSED; + } + + public void Start() + { + Data.state = Core.Processing.ProcessContext.Thread_State.ALIVE; + } + + public void Stop() + { + Data.state = Core.Processing.ProcessContext.Thread_State.PAUSED; + } + + public static void Sleep(int ms) + { + Cosmos.Core.Processing.ProcessContext.m_CurrentContext.arg = ms; + Cosmos.Core.Processing.ProcessContext.m_CurrentContext.state = Core.Processing.ProcessContext.Thread_State.WAITING_SLEEP; + while (Cosmos.Core.Processing.ProcessContext.m_CurrentContext.state == Core.Processing.ProcessContext.Thread_State.WAITING_SLEEP) { } + } + } +}