From e6f2b1940ae6a7538c2b16030dec557bbd40147a Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 7 Nov 2018 01:14:20 -0300 Subject: [PATCH] Initial implementation of KProcess --- ChocolArm64/CpuThread.cs | 4 +- ChocolArm64/Memory/MemoryManager.cs | 28 +- Ryujinx.Common/BitUtils.cs | 84 ++ Ryujinx.HLE/FileSystem/SaveHelper.cs | 5 +- Ryujinx.HLE/HOS/GlobalStateTable.cs | 17 +- Ryujinx.HLE/HOS/Horizon.cs | 204 ++- Ryujinx.HLE/HOS/Ipc/IpcHandler.cs | 6 +- Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs | 2 +- Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs | 2 +- Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs | 14 + Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs | 51 +- Ryujinx.HLE/HOS/Kernel/HleScheduler.cs | 14 +- Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs | 236 ++-- Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs | 83 ++ Ryujinx.HLE/HOS/Kernel/KCoreContext.cs | 5 +- ...{KRecursiveLock.cs => KCriticalSection.cs} | 8 +- Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs | 22 + .../HOS/Kernel/KMemoryArrangeRegion.cs | 16 + Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs | 1180 +++++++++++++---- Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs | 43 + .../HOS/Kernel/KMemoryRegionManager.cs | 404 ++++++ Ryujinx.HLE/HOS/Kernel/KPageList.cs | 60 + Ryujinx.HLE/HOS/Kernel/KPageNode.cs | 14 + Ryujinx.HLE/HOS/Kernel/KProcess.cs | 885 +++++++++++++ .../HOS/Kernel/KProcessCapabilities.cs | 311 +++++ Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs | 55 +- Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs | 8 +- Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs | 16 + Ryujinx.HLE/HOS/Kernel/KScheduler.cs | 9 +- Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs | 50 + Ryujinx.HLE/HOS/Kernel/KSynchronization.cs | 18 +- Ryujinx.HLE/HOS/Kernel/KThread.cs | 311 +++-- Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs | 73 + Ryujinx.HLE/HOS/Kernel/KernelInit.cs | 107 ++ Ryujinx.HLE/HOS/Kernel/KernelResult.cs | 24 +- Ryujinx.HLE/HOS/Kernel/LimitableResource.cs | 11 + Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs | 12 + Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs | 10 + Ryujinx.HLE/HOS/Kernel/MemoryState.cs | 2 +- Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs | 128 ++ Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs | 37 + Ryujinx.HLE/HOS/Kernel/ProcessState.cs | 14 + Ryujinx.HLE/HOS/Kernel/SvcHandler.cs | 16 +- Ryujinx.HLE/HOS/Kernel/SvcMemory.cs | 14 +- Ryujinx.HLE/HOS/Kernel/SvcSystem.cs | 44 +- Ryujinx.HLE/HOS/Kernel/SvcThread.cs | 149 ++- Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs | 38 +- Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs | 14 +- Ryujinx.HLE/HOS/Kernel/ThreadType.cs | 10 + Ryujinx.HLE/HOS/Process.cs | 528 -------- Ryujinx.HLE/HOS/ProgramLoader.cs | 129 ++ Ryujinx.HLE/HOS/ServiceCtx.cs | 4 +- .../HOS/Services/Am/ICommonStateGetter.cs | 6 +- Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs | 8 +- Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs | 2 +- .../HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs | 7 +- .../Nv/NvHostChannel/NvHostChannelIoctl.cs | 7 +- .../Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs | 7 +- .../HOS/Services/Nv/NvMap/NvMapIoctl.cs | 7 +- Ryujinx.HLE/Homebrew.npdm | Bin 0 -> 972 bytes .../Loaders/Compression/BackwardsLz.cs | 99 ++ Ryujinx.HLE/Loaders/ElfRel.cs | 19 - Ryujinx.HLE/Loaders/ElfRelType.cs | 128 -- Ryujinx.HLE/Loaders/Executable.cs | 95 -- .../Loaders/Executables/IExecutable.cs | 5 +- .../Executables/KernelInitialProcess.cs | 128 ++ Ryujinx.HLE/Loaders/Executables/Nso.cs | 6 - Ryujinx.HLE/Loaders/Npdm/ApplicationType.cs | 9 - Ryujinx.HLE/Loaders/Npdm/FsPermissionBool.cs | 33 - Ryujinx.HLE/Loaders/Npdm/FsPermissionRw.cs | 45 - .../Loaders/Npdm/KernelAccessControl.cs | 162 +-- .../Loaders/Npdm/KernelAccessControlIrq.cs | 14 - .../Loaders/Npdm/KernelAccessControlMmio.cs | 22 - Ryujinx.HLE/Loaders/Npdm/KernelAccessItem.cs | 33 - Ryujinx.HLE/Loaders/Npdm/Npdm.cs | 36 +- .../Loaders/Npdm/ServiceAccessControl.cs | 4 +- Ryujinx.HLE/Loaders/Npdm/SvcName.cs | 134 -- Ryujinx.HLE/Ryujinx.HLE.csproj | 2 + Ryujinx.HLE/Utilities/WSAError.cs | 10 +- 79 files changed, 4551 insertions(+), 2006 deletions(-) create mode 100644 Ryujinx.Common/BitUtils.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs rename Ryujinx.HLE/HOS/Kernel/{KRecursiveLock.cs => KCriticalSection.cs} (95%) create mode 100644 Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KPageList.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KPageNode.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KProcess.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KernelInit.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/LimitableResource.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/ProcessState.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/ThreadType.cs delete mode 100644 Ryujinx.HLE/HOS/Process.cs create mode 100644 Ryujinx.HLE/HOS/ProgramLoader.cs create mode 100644 Ryujinx.HLE/Homebrew.npdm create mode 100644 Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs delete mode 100644 Ryujinx.HLE/Loaders/ElfRel.cs delete mode 100644 Ryujinx.HLE/Loaders/ElfRelType.cs create mode 100644 Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs delete mode 100644 Ryujinx.HLE/Loaders/Npdm/ApplicationType.cs delete mode 100644 Ryujinx.HLE/Loaders/Npdm/FsPermissionBool.cs delete mode 100644 Ryujinx.HLE/Loaders/Npdm/FsPermissionRw.cs delete mode 100644 Ryujinx.HLE/Loaders/Npdm/KernelAccessControlIrq.cs delete mode 100644 Ryujinx.HLE/Loaders/Npdm/KernelAccessControlMmio.cs delete mode 100644 Ryujinx.HLE/Loaders/Npdm/KernelAccessItem.cs delete mode 100644 Ryujinx.HLE/Loaders/Npdm/SvcName.cs diff --git a/ChocolArm64/CpuThread.cs b/ChocolArm64/CpuThread.cs index 11f41236e3..dac376a1db 100644 --- a/ChocolArm64/CpuThread.cs +++ b/ChocolArm64/CpuThread.cs @@ -18,7 +18,7 @@ namespace ChocolArm64 private int _isExecuting; - public CpuThread(Translator translator, MemoryManager memory, long entryPoint) + public CpuThread(Translator translator, MemoryManager memory, long entrypoint) { _translator = translator; Memory = memory; @@ -31,7 +31,7 @@ namespace ChocolArm64 Work = new Thread(delegate() { - translator.ExecuteSubroutine(this, entryPoint); + translator.ExecuteSubroutine(this, entrypoint); memory.RemoveMonitor(ThreadState.Core); diff --git a/ChocolArm64/Memory/MemoryManager.cs b/ChocolArm64/Memory/MemoryManager.cs index ef3fb00646..21c0cb91fd 100644 --- a/ChocolArm64/Memory/MemoryManager.cs +++ b/ChocolArm64/Memory/MemoryManager.cs @@ -276,16 +276,38 @@ namespace ChocolArm64.Memory public byte[] ReadBytes(long position, long size) { - if ((uint)size > int.MaxValue) + long endAddr = position + size; + + if ((ulong)size > int.MaxValue) { throw new ArgumentOutOfRangeException(nameof(size)); } - EnsureRangeIsValid(position, size); + if ((ulong)endAddr < (ulong)position) + { + throw new ArgumentOutOfRangeException(nameof(position)); + } byte[] data = new byte[size]; - Marshal.Copy((IntPtr)Translate(position), data, 0, (int)size); + int offset = 0; + + while ((ulong)position < (ulong)endAddr) + { + long pageLimit = (position + PageSize) & ~(long)PageMask; + + if ((ulong)pageLimit > (ulong)endAddr) + { + pageLimit = endAddr; + } + + int copySize = (int)(pageLimit - position); + + Marshal.Copy((IntPtr)Translate(position), data, offset, copySize); + + position += copySize; + offset += copySize; + } return data; } diff --git a/Ryujinx.Common/BitUtils.cs b/Ryujinx.Common/BitUtils.cs new file mode 100644 index 0000000000..84d2072052 --- /dev/null +++ b/Ryujinx.Common/BitUtils.cs @@ -0,0 +1,84 @@ +namespace Ryujinx.Common +{ + public static class BitUtils + { + public static int AlignUp(int Value, int Size) + { + return (Value + (Size - 1)) & -Size; + } + + public static long AlignUp(long Value, int Size) + { + return (Value + (Size - 1)) & -(long)Size; + } + + public static int AlignDown(int Value, int Size) + { + return Value & -Size; + } + + public static long AlignDown(long Value, int Size) + { + return Value & -(long)Size; + } + + public static bool IsPowerOfTwo32(int Value) + { + return Value != 0 && (Value & (Value - 1)) == 0; + } + + public static bool IsPowerOfTwo64(long Value) + { + return Value != 0 && (Value & (Value - 1)) == 0; + } + + public static int CountLeadingZeros32(int Value) + { + return (int)CountLeadingZeros((ulong)Value, 32); + } + + public static int CountLeadingZeros64(long Value) + { + return (int)CountLeadingZeros((ulong)Value, 64); + } + + private static readonly byte[] ClzNibbleTbl = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; + + private static ulong CountLeadingZeros(ulong Value, int Size) // Size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). + { + if (Value == 0ul) + { + return (ulong)Size; + } + + int NibbleIdx = Size; + int PreCount, Count = 0; + + do + { + NibbleIdx -= 4; + PreCount = ClzNibbleTbl[(Value >> NibbleIdx) & 0b1111]; + Count += PreCount; + } + while (PreCount == 4); + + return (ulong)Count; + } + + public static long ReverseBits64(long Value) + { + return (long)ReverseBits64((ulong)Value); + } + + private static ulong ReverseBits64(ulong Value) + { + Value = ((Value & 0xaaaaaaaaaaaaaaaa) >> 1 ) | ((Value & 0x5555555555555555) << 1 ); + Value = ((Value & 0xcccccccccccccccc) >> 2 ) | ((Value & 0x3333333333333333) << 2 ); + Value = ((Value & 0xf0f0f0f0f0f0f0f0) >> 4 ) | ((Value & 0x0f0f0f0f0f0f0f0f) << 4 ); + Value = ((Value & 0xff00ff00ff00ff00) >> 8 ) | ((Value & 0x00ff00ff00ff00ff) << 8 ); + Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16); + + return (Value >> 32) | (Value << 32); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/FileSystem/SaveHelper.cs b/Ryujinx.HLE/FileSystem/SaveHelper.cs index b74d853c79..20138c8cd6 100644 --- a/Ryujinx.HLE/FileSystem/SaveHelper.cs +++ b/Ryujinx.HLE/FileSystem/SaveHelper.cs @@ -29,10 +29,7 @@ namespace Ryujinx.HLE.FileSystem if (SaveMetaData.TitleId == 0 && SaveMetaData.SaveDataType == SaveDataType.SaveData) { - if (Context.Process.MetaData != null) - { - CurrentTitleId = Context.Process.MetaData.ACI0.TitleId; - } + CurrentTitleId = Context.Process.TitleId; } string SaveAccount = SaveMetaData.UserId.IsZero() ? "savecommon" : SaveMetaData.UserId.ToString(); diff --git a/Ryujinx.HLE/HOS/GlobalStateTable.cs b/Ryujinx.HLE/HOS/GlobalStateTable.cs index faf47b2ee5..d7d834533a 100644 --- a/Ryujinx.HLE/HOS/GlobalStateTable.cs +++ b/Ryujinx.HLE/HOS/GlobalStateTable.cs @@ -1,3 +1,4 @@ +using Ryujinx.HLE.HOS.Kernel; using System.Collections.Concurrent; using System.Collections.Generic; @@ -5,28 +6,28 @@ namespace Ryujinx.HLE.HOS { class GlobalStateTable { - private ConcurrentDictionary DictByProcess; + private ConcurrentDictionary DictByProcess; public GlobalStateTable() { - DictByProcess = new ConcurrentDictionary(); + DictByProcess = new ConcurrentDictionary(); } - public bool Add(Process Process, int Id, object Data) + public bool Add(KProcess Process, int Id, object Data) { IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary()); return Dict.Add(Id, Data); } - public int Add(Process Process, object Data) + public int Add(KProcess Process, object Data) { IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary()); return Dict.Add(Data); } - public object GetData(Process Process, int Id) + public object GetData(KProcess Process, int Id) { if (DictByProcess.TryGetValue(Process, out IdDictionary Dict)) { @@ -36,7 +37,7 @@ namespace Ryujinx.HLE.HOS return null; } - public T GetData(Process Process, int Id) + public T GetData(KProcess Process, int Id) { if (DictByProcess.TryGetValue(Process, out IdDictionary Dict)) { @@ -46,7 +47,7 @@ namespace Ryujinx.HLE.HOS return default(T); } - public object Delete(Process Process, int Id) + public object Delete(KProcess Process, int Id) { if (DictByProcess.TryGetValue(Process, out IdDictionary Dict)) { @@ -56,7 +57,7 @@ namespace Ryujinx.HLE.HOS return null; } - public ICollection DeleteProcess(Process Process) + public ICollection DeleteProcess(KProcess Process) { if (DictByProcess.TryRemove(Process, out IdDictionary Dict)) { diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index d967c8969b..8741f99ac7 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -7,37 +7,58 @@ using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Npdm; using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; +using System.Threading; + using Nso = Ryujinx.HLE.Loaders.Executables.Nso; namespace Ryujinx.HLE.HOS { public class Horizon : IDisposable { + internal const int InitialKipId = 1; + internal const int InitialProcessId = 0x51; + internal const int HidSize = 0x40000; internal const int FontSize = 0x1100000; - private Switch Device; + private const long UserSlabHeapBase = DramMemoryMap.SlabHeapBase; + private const long UserSlabHeapItemSize = KMemoryManager.PageSize; + private const long UserSlabHeapSize = 0x3de000; - private ConcurrentDictionary Processes; + internal Switch Device { get; private set; } public SystemStateMgr State { get; private set; } - internal KRecursiveLock CriticalSectionLock { get; private set; } + internal bool KernelInitialized { get; private set; } + + internal KResourceLimit ResourceLimit { get; private set; } + + internal KMemoryRegionManager[] MemoryRegions { get; private set; } + + internal KSlabHeap UserSlabHeapPages { get; private set; } + + internal KCriticalSection CriticalSection { get; private set; } internal KScheduler Scheduler { get; private set; } internal KTimeManager TimeManager { get; private set; } - internal KAddressArbiter AddressArbiter { get; private set; } - internal KSynchronization Synchronization { get; private set; } internal LinkedList Withholders { get; private set; } + internal KContextIdManager ContextIdManager { get; private set; } + + private long KipId; + private long ProcessId; + private long ThreadUid; + + internal AppletStateMgr AppletState { get; private set; } + internal KSharedMemory HidSharedMem { get; private set; } internal KSharedMemory FontSharedMem { get; private set; } @@ -61,24 +82,36 @@ namespace Ryujinx.HLE.HOS { this.Device = Device; - Processes = new ConcurrentDictionary(); - State = new SystemStateMgr(); - CriticalSectionLock = new KRecursiveLock(this); + ResourceLimit = new KResourceLimit(); + + MemoryRegions = KernelInit.GetMemoryRegions(); + + UserSlabHeapPages = new KSlabHeap( + UserSlabHeapBase, + UserSlabHeapItemSize, + UserSlabHeapSize); + + CriticalSection = new KCriticalSection(this); Scheduler = new KScheduler(this); TimeManager = new KTimeManager(); - AddressArbiter = new KAddressArbiter(this); - Synchronization = new KSynchronization(this); Withholders = new LinkedList(); + ContextIdManager = new KContextIdManager(); + + KipId = InitialKipId; + ProcessId = InitialProcessId; + Scheduler.StartAutoPreemptionThread(); + KernelInitialized = true; + if (!Device.Memory.Allocator.TryAllocate(HidSize, out long HidPA) || !Device.Memory.Allocator.TryAllocate(FontSize, out long FontPA)) { @@ -88,6 +121,10 @@ namespace Ryujinx.HLE.HOS HidSharedMem = new KSharedMemory(HidPA, HidSize); FontSharedMem = new KSharedMemory(FontPA, FontSize); + AppletState = new AppletStateMgr(this); + + AppletState.SetFocus(true); + Font = new SharedFontManager(Device, FontSharedMem.PA); VsyncEvent = new KEvent(this); @@ -120,13 +157,15 @@ namespace Ryujinx.HLE.HOS else { Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); + + MetaData = GetDefaultNpdm(); } - Process MainProcess = MakeProcess(MetaData); + List StaticObjects = new List(); - void LoadNso(string FileName) + void LoadNso(string SearchPattern) { - foreach (string File in Directory.GetFiles(ExeFsDir, FileName)) + foreach (string File in Directory.GetFiles(ExeFsDir, SearchPattern)) { if (Path.GetExtension(File) != string.Empty) { @@ -139,31 +178,28 @@ namespace Ryujinx.HLE.HOS { string Name = Path.GetFileNameWithoutExtension(File); - Nso Program = new Nso(Input, Name); + Nso StaticObject = new Nso(Input, Name); - MainProcess.LoadProgram(Program); + StaticObjects.Add(StaticObject); } } } - if (!(MainProcess.MetaData?.Is64Bits ?? true)) + if (!MetaData.Is64Bits) { throw new NotImplementedException("32-bit titles are unsupported!"); } - CurrentTitle = MainProcess.MetaData.ACI0.TitleId.ToString("x16"); + CurrentTitle = MetaData.ACI0.TitleId.ToString("x16"); LoadNso("rtld"); - - MainProcess.SetEmptyArgs(); - LoadNso("main"); LoadNso("subsdk*"); LoadNso("sdk"); ContentManager.LoadEntries(); - MainProcess.Run(); + ProgramLoader.LoadStaticObjects(this, MetaData, StaticObjects.ToArray()); } public void LoadXci(string XciFile) @@ -356,9 +392,11 @@ namespace Ryujinx.HLE.HOS else { Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); + + MetaData = GetDefaultNpdm(); } - Process MainProcess = MakeProcess(MetaData); + List StaticObjects = new List(); void LoadNso(string Filename) { @@ -373,9 +411,9 @@ namespace Ryujinx.HLE.HOS string Name = Path.GetFileNameWithoutExtension(File.Name); - Nso Program = new Nso(Exefs.OpenFile(File), Name); + Nso StaticObject = new Nso(Exefs.OpenFile(File), Name); - MainProcess.LoadProgram(Program); + StaticObjects.Add(StaticObject); } } @@ -399,71 +437,50 @@ namespace Ryujinx.HLE.HOS return ControlData; } - if (ControlNca != null) + if (!MetaData.Is64Bits) { - MainProcess.ControlData = ReadControlData(); - } - else - { - CurrentTitle = MainProcess.MetaData.ACI0.TitleId.ToString("x16"); - } - - if (!MainProcess.MetaData.Is64Bits) - { - throw new NotImplementedException("32-bit titles are unsupported!"); + throw new NotImplementedException("32-bit titles are not supported!"); } LoadNso("rtld"); - - MainProcess.SetEmptyArgs(); - LoadNso("main"); LoadNso("subsdk"); LoadNso("sdk"); ContentManager.LoadEntries(); - MainProcess.Run(); + ProgramLoader.LoadStaticObjects(this, MetaData, StaticObjects.ToArray()); } public void LoadProgram(string FilePath) { + Npdm MetaData = GetDefaultNpdm(); + bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro"; - string Name = Path.GetFileNameWithoutExtension(FilePath); - string SwitchFilePath = Device.FileSystem.SystemPathToSwitchPath(FilePath); - - if (IsNro && (SwitchFilePath == null || !SwitchFilePath.StartsWith("sdmc:/"))) - { - string SwitchPath = $"sdmc:/switch/{Name}{Homebrew.TemporaryNroSuffix}"; - string TempPath = Device.FileSystem.SwitchPathToSystemPath(SwitchPath); - - string SwitchDir = Path.GetDirectoryName(TempPath); - - if (!Directory.Exists(SwitchDir)) - { - Directory.CreateDirectory(SwitchDir); - } - - File.Copy(FilePath, TempPath, true); - - FilePath = TempPath; - } - - Process MainProcess = MakeProcess(); - using (FileStream Input = new FileStream(FilePath, FileMode.Open)) { - MainProcess.LoadProgram(IsNro + IExecutable StaticObject = IsNro ? (IExecutable)new Nro(Input, FilePath) - : (IExecutable)new Nso(Input, FilePath)); + : (IExecutable)new Nso(Input, FilePath); + + ProgramLoader.LoadStaticObjects(this, MetaData, new IExecutable[] { StaticObject }); } + } - MainProcess.SetEmptyArgs(); + private Npdm GetDefaultNpdm() + { + Assembly Asm = Assembly.GetCallingAssembly(); - ContentManager.LoadEntries(); + using (Stream NpdmStream = Asm.GetManifestResourceStream("Ryujinx.HLE.Homebrew.npdm")) + { + return new Npdm(NpdmStream); + } + } - MainProcess.Run(IsNro); + private Stream GetHomebrewNpdmStream() + { + return Assembly.GetCallingAssembly().GetManifestResourceStream("Ryujinx.HLE.Homebrew.npdm"); } public void LoadKeySet() @@ -507,51 +524,19 @@ namespace Ryujinx.HLE.HOS VsyncEvent.ReadableEvent.Signal(); } - private Process MakeProcess(Npdm MetaData = null) + internal long GetThreadUid() { - HasStarted = true; - - Process Process; - - lock (Processes) - { - int ProcessId = 0; - - while (Processes.ContainsKey(ProcessId)) - { - ProcessId++; - } - - Process = new Process(Device, ProcessId, MetaData); - - Processes.TryAdd(ProcessId, Process); - } - - InitializeProcess(Process); - - return Process; + return Interlocked.Increment(ref ThreadUid) - 1; } - private void InitializeProcess(Process Process) + internal long GetKipId() { - Process.AppletState.SetFocus(true); + return Interlocked.Increment(ref KipId) - 1; } - internal void ExitProcess(int ProcessId) + internal long GetProcessId() { - if (Processes.TryRemove(ProcessId, out Process Process)) - { - Process.Dispose(); - - if (Processes.Count == 0) - { - Scheduler.Dispose(); - - TimeManager.Dispose(); - - Device.Unload(); - } - } + return Interlocked.Increment(ref ProcessId) - 1; } public void EnableMultiCoreScheduling() @@ -579,10 +564,11 @@ namespace Ryujinx.HLE.HOS { if (Disposing) { - foreach (Process Process in Processes.Values) - { - Process.Dispose(); - } + Scheduler.Dispose(); + + TimeManager.Dispose(); + + Device.Unload(); } } } diff --git a/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs b/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs index ec27a6eaf9..860c8242e7 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs @@ -8,8 +8,8 @@ namespace Ryujinx.HLE.HOS.Ipc static class IpcHandler { public static long IpcCall( - Switch Ns, - Process Process, + Switch Device, + KProcess Process, MemoryManager Memory, KSession Session, IpcMessage Request, @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Ipc BinaryWriter ResWriter = new BinaryWriter(ResMS); ServiceCtx Context = new ServiceCtx( - Ns, + Device, Process, Memory, Session, diff --git a/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs b/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs index c97caf42a8..6f7b230e70 100644 --- a/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs +++ b/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Kernel { Addr32Bits = 0, Addr36Bits = 1, - Addr36BitsNoMap = 2, + Addr32BitsNoMap = 2, Addr39Bits = 3 } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs b/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs index 8a2d47f7b0..b584d7192b 100644 --- a/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs +++ b/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs @@ -6,4 +6,4 @@ namespace Ryujinx.HLE.HOS.Kernel DecrementAndWaitIfLessThan = 1, WaitIfEqual = 2 } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs b/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs new file mode 100644 index 0000000000..f580d0a76e --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + static class DramMemoryMap + { + public const long DramBase = 0x80000000; + public const long DramSize = 0x100000000; + public const long DramEnd = DramBase + DramSize; + + public const long KernelReserveBase = DramBase + 0x60000; + + public const long SlabHeapBase = KernelReserveBase + 0x85000; + public const long SlapHeapSize = 0xa21000; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs b/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs index 0bfa2710cd..6a424cf23d 100644 --- a/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs @@ -5,24 +5,61 @@ namespace Ryujinx.HLE.HOS.Kernel { class HleCoreManager { - private ConcurrentDictionary Threads; + private class PausableThread + { + public ManualResetEvent Event { get; private set; } + + public bool IsExiting { get; set; } + + public PausableThread() + { + Event = new ManualResetEvent(false); + } + } + + private ConcurrentDictionary Threads; public HleCoreManager() { - Threads = new ConcurrentDictionary(); + Threads = new ConcurrentDictionary(); } - public ManualResetEvent GetThread(Thread Thread) + public void Set(Thread Thread) { - return Threads.GetOrAdd(Thread, (Key) => new ManualResetEvent(false)); + GetThread(Thread).Event.Set(); + } + + public void Reset(Thread Thread) + { + GetThread(Thread).Event.Reset(); + } + + public void Wait(Thread Thread) + { + PausableThread PausableThread = GetThread(Thread); + + if (!PausableThread.IsExiting) + { + PausableThread.Event.WaitOne(); + } + } + + public void Exit(Thread Thread) + { + GetThread(Thread).IsExiting = true; + } + + private PausableThread GetThread(Thread Thread) + { + return Threads.GetOrAdd(Thread, (Key) => new PausableThread()); } public void RemoveThread(Thread Thread) { - if (Threads.TryRemove(Thread, out ManualResetEvent Event)) + if (Threads.TryRemove(Thread, out PausableThread PausableThread)) { - Event.Set(); - Event.Dispose(); + PausableThread.Event.Set(); + PausableThread.Event.Dispose(); } } } diff --git a/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs b/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs index e0cb158c98..d7cec8b462 100644 --- a/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs +++ b/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs @@ -49,11 +49,11 @@ namespace Ryujinx.HLE.HOS.Kernel if (SelectedCount == 0) { - CoreManager.GetThread(Thread.CurrentThread).Reset(); + CoreManager.Reset(Thread.CurrentThread); } else if (SelectedCount == 1) { - CoreManager.GetThread(Thread.CurrentThread).Set(); + CoreManager.Set(Thread.CurrentThread); } else { @@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Kernel return; } - CoreManager.GetThread(CurrentThread.Context.Work).Reset(); + CoreManager.Reset(CurrentThread.Context.Work); } //Advance current core and try picking a thread, @@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Kernel { CoreContext.CurrentThread.ClearExclusive(); - CoreManager.GetThread(CoreContext.CurrentThread.Context.Work).Set(); + CoreManager.Set(CoreContext.CurrentThread.Context.Work); CoreContext.CurrentThread.Context.Execute(); @@ -111,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Kernel } } - CoreManager.GetThread(Thread.CurrentThread).WaitOne(); + CoreManager.Wait(Thread.CurrentThread); } private void PreemptCurrentThread() @@ -134,11 +134,11 @@ namespace Ryujinx.HLE.HOS.Kernel } } - public void StopThread(KThread Thread) + public void ExitThread(KThread Thread) { Thread.Context.StopExecution(); - CoreManager.GetThread(Thread.Context.Work).Set(); + CoreManager.Exit(Thread.Context.Work); } public void RemoveThread(KThread Thread) diff --git a/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs b/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs index 4a0f955fe3..3f1c3f424c 100644 --- a/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs +++ b/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs @@ -1,4 +1,3 @@ -using ChocolArm64.Memory; using System.Collections.Generic; using System.Linq; @@ -23,39 +22,36 @@ namespace Ryujinx.HLE.HOS.Kernel ArbiterThreads = new List(); } - public long ArbitrateLock( - Process Process, - MemoryManager Memory, - int OwnerHandle, - long MutexAddress, - int RequesterHandle) + public long ArbitrateLock(int OwnerHandle, long MutexAddress, int RequesterHandle) { - System.CriticalSectionLock.Lock(); - KThread CurrentThread = System.Scheduler.GetCurrentThread(); + System.CriticalSection.Enter(); + CurrentThread.SignaledObj = null; CurrentThread.ObjSyncResult = 0; - if (!UserToKernelInt32(Memory, MutexAddress, out int MutexValue)) + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + + if (!UserToKernelInt32(MutexAddress, out int MutexValue)) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);; } if (MutexValue != (OwnerHandle | HasListenersMask)) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return 0; } - KThread MutexOwner = Process.HandleTable.GetObject(OwnerHandle); + KThread MutexOwner = CurrentProcess.HandleTable.GetObject(OwnerHandle); if (MutexOwner == null) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); } @@ -67,26 +63,26 @@ namespace Ryujinx.HLE.HOS.Kernel CurrentThread.Reschedule(ThreadSchedState.Paused); - System.CriticalSectionLock.Unlock(); - System.CriticalSectionLock.Lock(); + System.CriticalSection.Leave(); + System.CriticalSection.Enter(); if (CurrentThread.MutexOwner != null) { CurrentThread.MutexOwner.RemoveMutexWaiter(CurrentThread); } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return (uint)CurrentThread.ObjSyncResult; } - public long ArbitrateUnlock(MemoryManager Memory, long MutexAddress) + public long ArbitrateUnlock(long MutexAddress) { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); KThread CurrentThread = System.Scheduler.GetCurrentThread(); - (long Result, KThread NewOwnerThread) = MutexUnlock(Memory, CurrentThread, MutexAddress); + (long Result, KThread NewOwnerThread) = MutexUnlock(CurrentThread, MutexAddress); if (Result != 0 && NewOwnerThread != null) { @@ -94,19 +90,18 @@ namespace Ryujinx.HLE.HOS.Kernel NewOwnerThread.ObjSyncResult = (int)Result; } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return Result; } public long WaitProcessWideKeyAtomic( - MemoryManager Memory, - long MutexAddress, - long CondVarAddress, - int ThreadHandle, - long Timeout) + long MutexAddress, + long CondVarAddress, + int ThreadHandle, + long Timeout) { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); KThread CurrentThread = System.Scheduler.GetCurrentThread(); @@ -116,16 +111,16 @@ namespace Ryujinx.HLE.HOS.Kernel if (CurrentThread.ShallBeTerminated || CurrentThread.SchedFlags == ThreadSchedState.TerminationPending) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating); } - (long Result, _) = MutexUnlock(Memory, CurrentThread, MutexAddress); + (long Result, _) = MutexUnlock(CurrentThread, MutexAddress); if (Result != 0) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return Result; } @@ -146,14 +141,14 @@ namespace Ryujinx.HLE.HOS.Kernel } } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); if (Timeout > 0) { System.TimeManager.UnscheduleFutureInvocation(CurrentThread); } - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); if (CurrentThread.MutexOwner != null) { @@ -162,12 +157,12 @@ namespace Ryujinx.HLE.HOS.Kernel CondVarThreads.Remove(CurrentThread); - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return (uint)CurrentThread.ObjSyncResult; } - private (long, KThread) MutexUnlock(MemoryManager Memory, KThread CurrentThread, long MutexAddress) + private (long, KThread) MutexUnlock(KThread CurrentThread, long MutexAddress) { KThread NewOwnerThread = CurrentThread.RelinquishMutex(MutexAddress, out int Count); @@ -190,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Kernel long Result = 0; - if (!KernelToUserInt32(Memory, MutexAddress, MutexValue)) + if (!KernelToUserInt32(MutexAddress, MutexValue)) { Result = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); } @@ -198,17 +193,17 @@ namespace Ryujinx.HLE.HOS.Kernel return (Result, NewOwnerThread); } - public void SignalProcessWideKey(Process Process, MemoryManager Memory, long Address, int Count) + public void SignalProcessWideKey(long Address, int Count) { Queue SignaledThreads = new Queue(); - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); IOrderedEnumerable SortedThreads = CondVarThreads.OrderBy(x => x.DynamicPriority); foreach (KThread Thread in SortedThreads.Where(x => x.CondVarAddress == Address)) { - TryAcquireMutex(Process, Memory, Thread); + TryAcquireMutex(Thread); SignaledThreads.Enqueue(Thread); @@ -224,19 +219,21 @@ namespace Ryujinx.HLE.HOS.Kernel CondVarThreads.Remove(Thread); } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); } - private KThread TryAcquireMutex(Process Process, MemoryManager Memory, KThread Requester) + private KThread TryAcquireMutex(KThread Requester) { long Address = Requester.MutexAddress; - Memory.SetExclusive(0, Address); + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); - if (!UserToKernelInt32(Memory, Address, out int MutexValue)) + CurrentProcess.CpuMemory.SetExclusive(0, Address); + + if (!UserToKernelInt32(Address, out int MutexValue)) { //Invalid address. - Memory.ClearExclusive(0); + CurrentProcess.CpuMemory.ClearExclusive(0); Requester.SignaledObj = null; Requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -246,27 +243,27 @@ namespace Ryujinx.HLE.HOS.Kernel while (true) { - if (Memory.TestExclusive(0, Address)) + if (CurrentProcess.CpuMemory.TestExclusive(0, Address)) { if (MutexValue != 0) { //Update value to indicate there is a mutex waiter now. - Memory.WriteInt32(Address, MutexValue | HasListenersMask); + CurrentProcess.CpuMemory.WriteInt32(Address, MutexValue | HasListenersMask); } else { //No thread owning the mutex, assign to requesting thread. - Memory.WriteInt32(Address, Requester.ThreadHandleForUserMutex); + CurrentProcess.CpuMemory.WriteInt32(Address, Requester.ThreadHandleForUserMutex); } - Memory.ClearExclusiveForStore(0); + CurrentProcess.CpuMemory.ClearExclusiveForStore(0); break; } - Memory.SetExclusive(0, Address); + CurrentProcess.CpuMemory.SetExclusive(0, Address); - MutexValue = Memory.ReadInt32(Address); + MutexValue = CurrentProcess.CpuMemory.ReadInt32(Address); } if (MutexValue == 0) @@ -282,7 +279,7 @@ namespace Ryujinx.HLE.HOS.Kernel MutexValue &= ~HasListenersMask; - KThread MutexOwner = Process.HandleTable.GetObject(MutexValue); + KThread MutexOwner = CurrentProcess.HandleTable.GetObject(MutexValue); if (MutexOwner != null) { @@ -301,16 +298,16 @@ namespace Ryujinx.HLE.HOS.Kernel return MutexOwner; } - public long WaitForAddressIfEqual(MemoryManager Memory, long Address, int Value, long Timeout) + public long WaitForAddressIfEqual(long Address, int Value, long Timeout) { KThread CurrentThread = System.Scheduler.GetCurrentThread(); - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); if (CurrentThread.ShallBeTerminated || CurrentThread.SchedFlags == ThreadSchedState.TerminationPending) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating); } @@ -318,9 +315,9 @@ namespace Ryujinx.HLE.HOS.Kernel CurrentThread.SignaledObj = null; CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout); - if (!UserToKernelInt32(Memory, Address, out int CurrentValue)) + if (!UserToKernelInt32(Address, out int CurrentValue)) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); } @@ -329,7 +326,7 @@ namespace Ryujinx.HLE.HOS.Kernel { if (Timeout == 0) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.Timeout); } @@ -346,14 +343,14 @@ namespace Ryujinx.HLE.HOS.Kernel System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout); } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); if (Timeout > 0) { System.TimeManager.UnscheduleFutureInvocation(CurrentThread); } - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); if (CurrentThread.WaitingInArbitration) { @@ -362,31 +359,26 @@ namespace Ryujinx.HLE.HOS.Kernel CurrentThread.WaitingInArbitration = false; } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return CurrentThread.ObjSyncResult; } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); } - public long WaitForAddressIfLessThan( - MemoryManager Memory, - long Address, - int Value, - bool ShouldDecrement, - long Timeout) + public long WaitForAddressIfLessThan(long Address, int Value, bool ShouldDecrement, long Timeout) { KThread CurrentThread = System.Scheduler.GetCurrentThread(); - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); if (CurrentThread.ShallBeTerminated || CurrentThread.SchedFlags == ThreadSchedState.TerminationPending) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating); } @@ -394,12 +386,14 @@ namespace Ryujinx.HLE.HOS.Kernel CurrentThread.SignaledObj = null; CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout); - //If ShouldDecrement is true, do atomic decrement of the value at Address. - Memory.SetExclusive(0, Address); + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); - if (!UserToKernelInt32(Memory, Address, out int CurrentValue)) + //If ShouldDecrement is true, do atomic decrement of the value at Address. + CurrentProcess.CpuMemory.SetExclusive(0, Address); + + if (!UserToKernelInt32(Address, out int CurrentValue)) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); } @@ -408,28 +402,28 @@ namespace Ryujinx.HLE.HOS.Kernel { while (CurrentValue < Value) { - if (Memory.TestExclusive(0, Address)) + if (CurrentProcess.CpuMemory.TestExclusive(0, Address)) { - Memory.WriteInt32(Address, CurrentValue - 1); + CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue - 1); - Memory.ClearExclusiveForStore(0); + CurrentProcess.CpuMemory.ClearExclusiveForStore(0); break; } - Memory.SetExclusive(0, Address); + CurrentProcess.CpuMemory.SetExclusive(0, Address); - CurrentValue = Memory.ReadInt32(Address); + CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address); } } - Memory.ClearExclusive(0); + CurrentProcess.CpuMemory.ClearExclusive(0); if (CurrentValue < Value) { if (Timeout == 0) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.Timeout); } @@ -446,14 +440,14 @@ namespace Ryujinx.HLE.HOS.Kernel System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout); } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); if (Timeout > 0) { System.TimeManager.UnscheduleFutureInvocation(CurrentThread); } - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); if (CurrentThread.WaitingInArbitration) { @@ -462,12 +456,12 @@ namespace Ryujinx.HLE.HOS.Kernel CurrentThread.WaitingInArbitration = false; } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return CurrentThread.ObjSyncResult; } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); } @@ -498,63 +492,65 @@ namespace Ryujinx.HLE.HOS.Kernel public long Signal(long Address, int Count) { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); WakeArbiterThreads(Address, Count); - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return 0; } - public long SignalAndIncrementIfEqual(MemoryManager Memory, long Address, int Value, int Count) + public long SignalAndIncrementIfEqual(long Address, int Value, int Count) { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); - Memory.SetExclusive(0, Address); + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); - if (!UserToKernelInt32(Memory, Address, out int CurrentValue)) + CurrentProcess.CpuMemory.SetExclusive(0, Address); + + if (!UserToKernelInt32(Address, out int CurrentValue)) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); } while (CurrentValue == Value) { - if (Memory.TestExclusive(0, Address)) + if (CurrentProcess.CpuMemory.TestExclusive(0, Address)) { - Memory.WriteInt32(Address, CurrentValue + 1); + CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue + 1); - Memory.ClearExclusiveForStore(0); + CurrentProcess.CpuMemory.ClearExclusiveForStore(0); break; } - Memory.SetExclusive(0, Address); + CurrentProcess.CpuMemory.SetExclusive(0, Address); - CurrentValue = Memory.ReadInt32(Address); + CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address); } - Memory.ClearExclusive(0); + CurrentProcess.CpuMemory.ClearExclusive(0); if (CurrentValue != Value) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); } WakeArbiterThreads(Address, Count); - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return 0; } - public long SignalAndModifyIfEqual(MemoryManager Memory, long Address, int Value, int Count) + public long SignalAndModifyIfEqual(long Address, int Value, int Count) { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); int Offset; @@ -580,43 +576,45 @@ namespace Ryujinx.HLE.HOS.Kernel Offset = 1; } - Memory.SetExclusive(0, Address); + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); - if (!UserToKernelInt32(Memory, Address, out int CurrentValue)) + CurrentProcess.CpuMemory.SetExclusive(0, Address); + + if (!UserToKernelInt32(Address, out int CurrentValue)) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); } while (CurrentValue == Value) { - if (Memory.TestExclusive(0, Address)) + if (CurrentProcess.CpuMemory.TestExclusive(0, Address)) { - Memory.WriteInt32(Address, CurrentValue + Offset); + CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue + Offset); - Memory.ClearExclusiveForStore(0); + CurrentProcess.CpuMemory.ClearExclusiveForStore(0); break; } - Memory.SetExclusive(0, Address); + CurrentProcess.CpuMemory.SetExclusive(0, Address); - CurrentValue = Memory.ReadInt32(Address); + CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address); } - Memory.ClearExclusive(0); + CurrentProcess.CpuMemory.ClearExclusive(0); if (CurrentValue != Value) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); } WakeArbiterThreads(Address, Count); - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return 0; } @@ -649,11 +647,13 @@ namespace Ryujinx.HLE.HOS.Kernel } } - private bool UserToKernelInt32(MemoryManager Memory, long Address, out int Value) + private bool UserToKernelInt32(long Address, out int Value) { - if (Memory.IsMapped(Address)) + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + + if (CurrentProcess.CpuMemory.IsMapped(Address)) { - Value = Memory.ReadInt32(Address); + Value = CurrentProcess.CpuMemory.ReadInt32(Address); return true; } @@ -663,11 +663,13 @@ namespace Ryujinx.HLE.HOS.Kernel return false; } - private bool KernelToUserInt32(MemoryManager Memory, long Address, int Value) + private bool KernelToUserInt32(long Address, int Value) { - if (Memory.IsMapped(Address)) + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + + if (CurrentProcess.CpuMemory.IsMapped(Address)) { - Memory.WriteInt32ToSharedAddr(Address, Value); + CurrentProcess.CpuMemory.WriteInt32ToSharedAddr(Address, Value); return true; } diff --git a/Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs b/Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs new file mode 100644 index 0000000000..03e7dddf7d --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs @@ -0,0 +1,83 @@ +using Ryujinx.Common; +using System; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class KContextIdManager + { + private const int IdMasksCount = 8; + + private int[] IdMasks; + + private int NextFreeBitHint; + + public KContextIdManager() + { + IdMasks = new int[IdMasksCount]; + } + + public int GetId() + { + lock (IdMasks) + { + int Id = 0; + + if (!TestBit(NextFreeBitHint)) + { + Id = NextFreeBitHint; + } + else + { + for (int Index = 0; Index < IdMasksCount; Index++) + { + int Mask = IdMasks[Index]; + + int FirstFreeBit = BitUtils.CountLeadingZeros32((Mask + 1) & ~Mask); + + if (FirstFreeBit < 32) + { + int BaseBit = Index * 32 + 31; + + Id = BaseBit - FirstFreeBit; + + break; + } + else if (Index == IdMasksCount - 1) + { + throw new InvalidOperationException("Maximum number of Ids reached!"); + } + } + } + + NextFreeBitHint = Id + 1; + + SetBit(Id); + + return Id; + } + } + + public void PutId(int Id) + { + lock (IdMasks) + { + ClearBit(Id); + } + } + + private bool TestBit(int Bit) + { + return (IdMasks[NextFreeBitHint / 32] & (1 << (NextFreeBitHint & 31))) != 0; + } + + private void SetBit(int Bit) + { + IdMasks[NextFreeBitHint / 32] |= (1 << (NextFreeBitHint & 31)); + } + + private void ClearBit(int Bit) + { + IdMasks[NextFreeBitHint / 32] &= ~(1 << (NextFreeBitHint & 31)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs b/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs index 02354e16ad..39f9625b28 100644 --- a/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs +++ b/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs @@ -1,5 +1,4 @@ using Ryujinx.Common; -using System; namespace Ryujinx.HLE.HOS.Kernel { @@ -48,7 +47,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (CurrentThread != null) { - CoreManager.GetThread(CurrentThread.Context.Work).Reset(); + CoreManager.Reset(CurrentThread.Context.Work); } CurrentThread = SelectedThread; @@ -57,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Kernel { CurrentThread.ClearExclusive(); - CoreManager.GetThread(CurrentThread.Context.Work).Set(); + CoreManager.Set(CurrentThread.Context.Work); CurrentThread.Context.Execute(); } diff --git a/Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs b/Ryujinx.HLE/HOS/Kernel/KCriticalSection.cs similarity index 95% rename from Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs rename to Ryujinx.HLE/HOS/Kernel/KCriticalSection.cs index 30c1a88051..b02a11954d 100644 --- a/Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs +++ b/Ryujinx.HLE/HOS/Kernel/KCriticalSection.cs @@ -3,7 +3,7 @@ using System.Threading; namespace Ryujinx.HLE.HOS.Kernel { - class KRecursiveLock + class KCriticalSection { private Horizon System; @@ -11,21 +11,21 @@ namespace Ryujinx.HLE.HOS.Kernel private int RecursionCount; - public KRecursiveLock(Horizon System) + public KCriticalSection(Horizon System) { this.System = System; LockObj = new object(); } - public void Lock() + public void Enter() { Monitor.Enter(LockObj); RecursionCount++; } - public void Unlock() + public void Leave() { if (RecursionCount == 0) { diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs new file mode 100644 index 0000000000..af393b68e9 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + class KMemoryArrange + { + public KMemoryArrangeRegion Service { get; private set; } + public KMemoryArrangeRegion NvServices { get; private set; } + public KMemoryArrangeRegion Applet { get; private set; } + public KMemoryArrangeRegion Application { get; private set; } + + public KMemoryArrange( + KMemoryArrangeRegion Service, + KMemoryArrangeRegion NvServices, + KMemoryArrangeRegion Applet, + KMemoryArrangeRegion Application) + { + this.Service = Service; + this.NvServices = NvServices; + this.Applet = Applet; + this.Application = Application; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs new file mode 100644 index 0000000000..4e8853ce76 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs @@ -0,0 +1,16 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + struct KMemoryArrangeRegion + { + public long Address { get; private set; } + public long Size { get; private set; } + + public long EndAddr => Address + Size; + + public KMemoryArrangeRegion(long Address, long Size) + { + this.Address = Address; + this.Size = Size; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs index 4cfb2aa20a..388002a44c 100644 --- a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs @@ -1,4 +1,5 @@ using ChocolArm64.Memory; +using Ryujinx.Common; using Ryujinx.HLE.Memory; using System; using System.Collections.Generic; @@ -17,173 +18,492 @@ namespace Ryujinx.HLE.HOS.Kernel private ArenaAllocator Allocator; + private Horizon System; + public long AddrSpaceStart { get; private set; } public long AddrSpaceEnd { get; private set; } public long CodeRegionStart { get; private set; } public long CodeRegionEnd { get; private set; } - public long MapRegionStart { get; private set; } - public long MapRegionEnd { get; private set; } - public long HeapRegionStart { get; private set; } public long HeapRegionEnd { get; private set; } - public long NewMapRegionStart { get; private set; } - public long NewMapRegionEnd { get; private set; } + private long CurrentHeapAddr; + + public long AliasRegionStart { get; private set; } + public long AliasRegionEnd { get; private set; } + + public long StackRegionStart { get; private set; } + public long StackRegionEnd { get; private set; } public long TlsIoRegionStart { get; private set; } public long TlsIoRegionEnd { get; private set; } + private long HeapCapacity; + public long PersonalMmHeapUsage { get; private set; } - private long CurrentHeapAddr; + private MemoryRegion MemRegion; - public KMemoryManager(Process Process) + private bool AslrDisabled; + + public int AddrSpaceWidth { get; private set; } + + private bool IsKernel; + private bool AslrEnabled; + + private int ContextId; + + private MersenneTwister RandomNumberGenerator; + + public KMemoryManager(Horizon System, MemoryManager CpuMemory) { - CpuMemory = Process.Memory; - Allocator = Process.Device.Memory.Allocator; + this.System = System; + this.CpuMemory = CpuMemory; - long CodeRegionSize; - long MapRegionSize; - long HeapRegionSize; - long NewMapRegionSize; - long TlsIoRegionSize; - int AddrSpaceWidth; + Blocks = new LinkedList(); + } - AddressSpaceType AddrType = AddressSpaceType.Addr39Bits; + private static readonly int[] AddrSpaceSizes = new int[] { 32, 36, 32, 39 }; - if (Process.MetaData != null) + public KernelResult InitializeForProcess( + AddressSpaceType AddrSpaceType, + bool AslrEnabled, + bool AslrDisabled, + MemoryRegion MemRegion, + long Address, + long Size) + { + if ((uint)AddrSpaceType > (uint)AddressSpaceType.Addr39Bits) { - AddrType = (AddressSpaceType)Process.MetaData.AddressSpaceWidth; + throw new ArgumentException(nameof(AddrSpaceType)); } - switch (AddrType) + ContextId = System.ContextIdManager.GetId(); + + long AddrSpaceBase = 0; + long AddrSpaceSize = 1L << AddrSpaceSizes[(int)AddrSpaceType]; + + KernelResult Result = CreateUserAddressSpace( + AddrSpaceType, + AslrEnabled, + AslrDisabled, + AddrSpaceBase, + AddrSpaceSize, + MemRegion, + Address, + Size); + + if (Result != KernelResult.Success) + { + System.ContextIdManager.PutId(ContextId); + } + + return Result; + } + + private class Region + { + public long Start; + public long End; + public long Size; + public long AslrOffset; + } + + private KernelResult CreateUserAddressSpace( + AddressSpaceType AddrSpaceType, + bool AslrEnabled, + bool AslrDisabled, + long AddrSpaceStart, + long AddrSpaceEnd, + MemoryRegion MemRegion, + long Address, + long Size) + { + long EndAddr = Address + Size; + + Region AliasRegion = new Region(); + Region HeapRegion = new Region(); + Region StackRegion = new Region(); + Region TlsIoRegion = new Region(); + + long CodeRegionSize; + long StackAndTlsIoStart; + long StackAndTlsIoEnd; + long BaseAddress; + + switch (AddrSpaceType) { case AddressSpaceType.Addr32Bits: - CodeRegionStart = 0x200000; - CodeRegionSize = 0x3fe00000; - MapRegionSize = 0x40000000; - HeapRegionSize = 0x40000000; - NewMapRegionSize = 0; - TlsIoRegionSize = 0; - AddrSpaceWidth = 32; + AliasRegion.Size = 0x40000000; + HeapRegion.Size = 0x40000000; + StackRegion.Size = 0; + TlsIoRegion.Size = 0; + CodeRegionStart = 0x200000; + CodeRegionSize = 0x3fe00000; + StackAndTlsIoStart = 0x200000; + StackAndTlsIoEnd = 0x40000000; + BaseAddress = 0x200000; + AddrSpaceWidth = 32; break; case AddressSpaceType.Addr36Bits: - CodeRegionStart = 0x8000000; - CodeRegionSize = 0x78000000; - MapRegionSize = 0x180000000; - HeapRegionSize = 0x180000000; - NewMapRegionSize = 0; - TlsIoRegionSize = 0; - AddrSpaceWidth = 36; + AliasRegion.Size = 0x180000000; + HeapRegion.Size = 0x180000000; + StackRegion.Size = 0; + TlsIoRegion.Size = 0; + CodeRegionStart = 0x8000000; + CodeRegionSize = 0x78000000; + StackAndTlsIoStart = 0x8000000; + StackAndTlsIoEnd = 0x80000000; + BaseAddress = 0x8000000; + AddrSpaceWidth = 36; break; - case AddressSpaceType.Addr36BitsNoMap: - CodeRegionStart = 0x200000; - CodeRegionSize = 0x3fe00000; - MapRegionSize = 0; - HeapRegionSize = 0x80000000; - NewMapRegionSize = 0; - TlsIoRegionSize = 0; - AddrSpaceWidth = 36; + case AddressSpaceType.Addr32BitsNoMap: + AliasRegion.Size = 0; + HeapRegion.Size = 0x80000000; + StackRegion.Size = 0; + TlsIoRegion.Size = 0; + CodeRegionStart = 0x200000; + CodeRegionSize = 0x3fe00000; + StackAndTlsIoStart = 0x200000; + StackAndTlsIoEnd = 0x40000000; + BaseAddress = 0x200000; + AddrSpaceWidth = 32; break; case AddressSpaceType.Addr39Bits: - CodeRegionStart = 0x8000000; - CodeRegionSize = 0x80000000; - MapRegionSize = 0x1000000000; - HeapRegionSize = 0x180000000; - NewMapRegionSize = 0x80000000; - TlsIoRegionSize = 0x1000000000; - AddrSpaceWidth = 39; + AliasRegion.Size = 0x1000000000; + HeapRegion.Size = 0x180000000; + StackRegion.Size = 0x80000000; + TlsIoRegion.Size = 0x1000000000; + CodeRegionStart = BitUtils.AlignDown(Address, 0x200000); + CodeRegionSize = BitUtils.AlignUp (EndAddr, 0x200000) - CodeRegionStart; + StackAndTlsIoStart = 0; + StackAndTlsIoEnd = 0; + BaseAddress = 0x8000000; + AddrSpaceWidth = 39; break; - default: throw new InvalidOperationException(); + default: throw new ArgumentException(nameof(AddrSpaceType)); } - AddrSpaceStart = 0; - AddrSpaceEnd = 1L << AddrSpaceWidth; + CodeRegionEnd = CodeRegionStart + CodeRegionSize; - CodeRegionEnd = CodeRegionStart + CodeRegionSize; - MapRegionStart = CodeRegionEnd; - MapRegionEnd = CodeRegionEnd + MapRegionSize; - HeapRegionStart = MapRegionEnd; - HeapRegionEnd = MapRegionEnd + HeapRegionSize; - NewMapRegionStart = HeapRegionEnd; - NewMapRegionEnd = HeapRegionEnd + NewMapRegionSize; - TlsIoRegionStart = NewMapRegionEnd; - TlsIoRegionEnd = NewMapRegionEnd + TlsIoRegionSize; + long MapBaseAddress; + long MapAvailableSize; - CurrentHeapAddr = HeapRegionStart; - - if (NewMapRegionSize == 0) + if (CodeRegionStart - BaseAddress >= AddrSpaceEnd - CodeRegionEnd) { - NewMapRegionStart = AddrSpaceStart; - NewMapRegionEnd = AddrSpaceEnd; + //Has more space before the start of the code region. + MapBaseAddress = BaseAddress; + MapAvailableSize = CodeRegionStart - BaseAddress; + } + else + { + //Has more space after the end of the code region. + MapBaseAddress = CodeRegionEnd; + MapAvailableSize = AddrSpaceEnd - CodeRegionEnd; } - Blocks = new LinkedList(); + long MapTotalSize = AliasRegion.Size + HeapRegion.Size + StackRegion.Size + TlsIoRegion.Size; + long AslrMaxOffset = MapAvailableSize - MapTotalSize; + + this.AddrSpaceStart = AddrSpaceStart; + this.AddrSpaceEnd = AddrSpaceEnd; + + if (MapAvailableSize < MapTotalSize) + { + return KernelResult.OutOfMemory; + } + + if (AslrEnabled) + { + AliasRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21; + HeapRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21; + StackRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21; + TlsIoRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21; + } + + //Regions are sorted based on ASLR offset. + //When ASLR is disabled, the order is Map, Heap, NewMap and TlsIo. + AliasRegion.Start = MapBaseAddress + AliasRegion.AslrOffset; + AliasRegion.End = AliasRegion.Start + AliasRegion.Size; + HeapRegion.Start = MapBaseAddress + HeapRegion.AslrOffset; + HeapRegion.End = HeapRegion.Start + HeapRegion.Size; + StackRegion.Start = MapBaseAddress + StackRegion.AslrOffset; + StackRegion.End = StackRegion.Start + StackRegion.Size; + TlsIoRegion.Start = MapBaseAddress + TlsIoRegion.AslrOffset; + TlsIoRegion.End = TlsIoRegion.Start + TlsIoRegion.Size; + + SortRegion(HeapRegion, AliasRegion); + + if (StackRegion.Size != 0) + { + SortRegion(StackRegion, AliasRegion); + SortRegion(StackRegion, HeapRegion); + } + else + { + StackRegion.Start = StackAndTlsIoStart; + StackRegion.End = StackAndTlsIoEnd; + } + + if (TlsIoRegion.Size != 0) + { + SortRegion(TlsIoRegion, AliasRegion); + SortRegion(TlsIoRegion, HeapRegion); + SortRegion(TlsIoRegion, StackRegion); + } + else + { + TlsIoRegion.Start = StackAndTlsIoStart; + TlsIoRegion.End = StackAndTlsIoEnd; + } + + AliasRegionStart = AliasRegion.Start; + AliasRegionEnd = AliasRegion.End; + HeapRegionStart = HeapRegion.Start; + HeapRegionEnd = HeapRegion.End; + StackRegionStart = StackRegion.Start; + StackRegionEnd = StackRegion.End; + TlsIoRegionStart = TlsIoRegion.Start; + TlsIoRegionEnd = TlsIoRegion.End; + + CurrentHeapAddr = HeapRegionStart; + HeapCapacity = 0; + PersonalMmHeapUsage = 0; + + this.MemRegion = MemRegion; + this.AslrDisabled = AslrDisabled; + + InitializeBlocks(AddrSpaceStart, AddrSpaceEnd); + + return KernelResult.Success; + } + + private long GetRandomValue(long Min, long Max) + { + if (RandomNumberGenerator == null) + { + RandomNumberGenerator = new MersenneTwister(0); + } + + return RandomNumberGenerator.GenRandomNumber(Min, Max); + } + + private static void SortRegion(Region Lhs, Region Rhs) + { + if (Lhs.AslrOffset < Rhs.AslrOffset) + { + Rhs.Start += Lhs.Size; + Rhs.End += Lhs.Size; + } + else + { + Lhs.Start += Rhs.Size; + Lhs.End += Rhs.Size; + } + } + + private void InitializeBlocks(long AddrSpaceStart, long AddrSpaceEnd) + { long AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize; InsertBlock(AddrSpaceStart, AddrSpacePagesCount, MemoryState.Unmapped); } - public void HleMapProcessCode(long Position, long Size) + public KernelResult MapPages( + long Address, + KPageList PageList, + MemoryState State, + MemoryPermission Permission) { - long PagesCount = Size / PageSize; + long PagesCount = PageList.GetPagesCount(); - if (!Allocator.TryAllocate(Size, out long PA)) + if (!IsUnmapped(Address, PagesCount * PageSize)) { - throw new InvalidOperationException(); + return KernelResult.InvalidMemState; } - lock (Blocks) - { - InsertBlock(Position, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute); + KernelResult Result = MmuMapPages(Address, PageList); - CpuMemory.Map(Position, PA, Size); + if (Result == KernelResult.Success) + { + InsertBlock(Address, PagesCount, State, Permission); } + + return Result; } - public long MapProcessCodeMemory(long Dst, long Src, long Size) + public KernelResult MapNormalMemory(long Address, long Size, MemoryPermission Permission) { - lock (Blocks) + //TODO. + return KernelResult.Success; + } + + public KernelResult MapIoMemory(long Address, long Size, MemoryPermission Permission) + { + //TODO. + return KernelResult.Success; + } + + public KernelResult AllocateOrMapPa( + long NeededPagesCount, + int Alignment, + long SrcPa, + bool Map, + long RegionStart, + long RegionPagesCount, + MemoryState State, + MemoryPermission Permission, + out long Address) + { + Address = 0; + + long RegionSize = RegionPagesCount * PageSize; + + long RegionEndAddr = RegionStart + RegionSize; + + if (!ValidateRegionForState(RegionStart, RegionSize, State)) { - long PagesCount = Size / PageSize; - - bool Success = IsUnmapped(Dst, Size); - - Success &= CheckRange( - Src, - Size, - MemoryState.Mask, - MemoryState.Heap, - MemoryPermission.Mask, - MemoryPermission.ReadAndWrite, - MemoryAttribute.Mask, - MemoryAttribute.None, - MemoryAttribute.IpcAndDeviceMapped, - out _, - out _, - out _); - - if (Success) - { - long PA = CpuMemory.GetPhysicalAddress(Src); - - InsertBlock(Dst, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute); - InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.None); - - CpuMemory.Map(Dst, PA, Size); - - return 0; - } + return KernelResult.InvalidMemState; } - return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + if ((ulong)RegionPagesCount <= (ulong)NeededPagesCount) + { + return KernelResult.OutOfMemory; + } + + long ReservedPagesCount = IsKernel ? 1 : 4; + + lock (Blocks) + { + if (AslrEnabled) + { + long TotalNeededSize = (ReservedPagesCount + NeededPagesCount) * PageSize; + + long RemainingPages = RegionPagesCount - NeededPagesCount; + + long AslrMaxOffset = ((RemainingPages + ReservedPagesCount) * PageSize) / Alignment; + + for (int Attempt = 0; Attempt < 8; Attempt++) + { + Address = BitUtils.AlignDown(RegionStart + GetRandomValue(0, AslrMaxOffset) * Alignment, Alignment); + + long EndAddr = Address + TotalNeededSize; + + KMemoryInfo Info = FindBlock(Address).GetInfo(); + + if (Info.State != MemoryState.Unmapped) + { + continue; + } + + long BlkStartAddr = Info.Position + ReservedPagesCount * PageSize; + + long BlkEndAddr = Info.Position + Info.Size; + + if ((ulong)Address >= (ulong)RegionStart && + (ulong)Address >= (ulong)BlkStartAddr && + (ulong)EndAddr - 1 <= (ulong)RegionEndAddr - 1 && + (ulong)EndAddr - 1 <= (ulong)BlkEndAddr - 1) + { + break; + } + } + + if (Address == 0) + { + long AslrPage = GetRandomValue(0, AslrMaxOffset); + + Address = FindFirstFit( + RegionStart + AslrPage * PageSize, + RegionPagesCount - AslrPage, + NeededPagesCount, + Alignment, + 0, + ReservedPagesCount); + } + } + + if (Address == 0) + { + Address = FindFirstFit( + RegionStart, + RegionPagesCount, + NeededPagesCount, + Alignment, + 0, + ReservedPagesCount); + } + + if (Address == 0) + { + return KernelResult.OutOfMemory; + } + + MemoryOperation Operation = Map + ? MemoryOperation.MapPa + : MemoryOperation.Allocate; + + KernelResult Result = DoMmuOperation( + Address, + NeededPagesCount, + SrcPa, + Map, + Permission, + Operation); + + if (Result != KernelResult.Success) + { + return Result; + } + + InsertBlock(Address, NeededPagesCount, State, Permission); + } + + return KernelResult.Success; + } + + public KernelResult MapNewProcessCode( + long Address, + long PagesCount, + MemoryState State, + MemoryPermission Permission) + { + long Size = PagesCount * PageSize; + + if (!ValidateRegionForState(Address, Size, State)) + { + return KernelResult.InvalidMemState; + } + + lock (Blocks) + { + if (!IsUnmapped(Address, Size)) + { + return KernelResult.InvalidMemState; + } + + KernelResult Result = DoMmuOperation( + Address, + PagesCount, + 0, + false, + Permission, + MemoryOperation.Allocate); + + if (Result == KernelResult.Success) + { + InsertBlock(Address, PagesCount, State, Permission); + } + + return Result; + } } public long UnmapProcessCodeMemory(long Dst, long Src, long Size) @@ -193,32 +513,32 @@ namespace Ryujinx.HLE.HOS.Kernel long PagesCount = Size / PageSize; bool Success = CheckRange( - Dst, - Size, - MemoryState.Mask, - MemoryState.CodeStatic, - MemoryPermission.None, - MemoryPermission.None, - MemoryAttribute.Mask, - MemoryAttribute.None, - MemoryAttribute.IpcAndDeviceMapped, - out _, - out _, - out _); + Dst, + Size, + MemoryState.Mask, + MemoryState.CodeStatic, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); Success &= CheckRange( - Src, - Size, - MemoryState.Mask, - MemoryState.Heap, - MemoryPermission.Mask, - MemoryPermission.None, - MemoryAttribute.Mask, - MemoryAttribute.None, - MemoryAttribute.IpcAndDeviceMapped, - out _, - out _, - out _); + Src, + Size, + MemoryState.Mask, + MemoryState.Heap, + MemoryPermission.Mask, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); if (Success) { @@ -234,65 +554,15 @@ namespace Ryujinx.HLE.HOS.Kernel return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); } - public void HleMapCustom(long Position, long Size, MemoryState State, MemoryPermission Permission) - { - long PagesCount = Size / PageSize; - - if (!Allocator.TryAllocate(Size, out long PA)) - { - throw new InvalidOperationException(); - } - - lock (Blocks) - { - InsertBlock(Position, PagesCount, State, Permission); - - CpuMemory.Map(Position, PA, Size); - } - } - - public long HleMapTlsPage() - { - bool HasTlsIoRegion = TlsIoRegionStart != TlsIoRegionEnd; - - long Position = HasTlsIoRegion ? TlsIoRegionStart : CodeRegionStart; - - lock (Blocks) - { - while (Position < (HasTlsIoRegion ? TlsIoRegionEnd : CodeRegionEnd)) - { - if (FindBlock(Position).State == MemoryState.Unmapped) - { - InsertBlock(Position, 1, MemoryState.ThreadLocal, MemoryPermission.ReadAndWrite); - - if (!Allocator.TryAllocate(PageSize, out long PA)) - { - throw new InvalidOperationException(); - } - - CpuMemory.Map(Position, PA, PageSize); - - return Position; - } - - Position += PageSize; - } - - throw new InvalidOperationException(); - } - } - - public long TrySetHeapSize(long Size, out long Position) + public KernelResult SetHeapSize(long Size, out long Position) { Position = 0; if ((ulong)Size > (ulong)(HeapRegionEnd - HeapRegionStart)) { - return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory); + return KernelResult.OutOfMemory; } - bool Success = false; - long CurrentHeapSize = GetHeapSize(); if ((ulong)CurrentHeapSize <= (ulong)Size) @@ -302,19 +572,35 @@ namespace Ryujinx.HLE.HOS.Kernel lock (Blocks) { - if (Success = IsUnmapped(CurrentHeapAddr, DiffSize)) + long PagesCount = DiffSize / PageSize; + + KMemoryRegionManager Region = GetMemoryRegionManager(); + + KernelResult Result = Region.AllocatePages(PagesCount, AslrDisabled, out KPageList PageList); + + if (Result != KernelResult.Success) { - if (!Allocator.TryAllocate(DiffSize, out long PA)) - { - return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory); - } - - long PagesCount = DiffSize / PageSize; - - InsertBlock(CurrentHeapAddr, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite); - - CpuMemory.Map(CurrentHeapAddr, PA, DiffSize); + return Result; } + + if (!IsUnmapped(CurrentHeapAddr, DiffSize)) + { + return KernelResult.InvalidMemState; + } + + Result = DoMmuOperation( + CurrentHeapAddr, + PagesCount, + PageList, + MemoryPermission.ReadAndWrite, + MemoryOperation.MapVa); + + if (Result != KernelResult.Success) + { + return Result; + } + + InsertBlock(CurrentHeapAddr, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite); } } else @@ -325,7 +611,7 @@ namespace Ryujinx.HLE.HOS.Kernel lock (Blocks) { - Success = CheckRange( + if (!CheckRange( FreeAddr, DiffSize, MemoryState.Mask, @@ -337,38 +623,60 @@ namespace Ryujinx.HLE.HOS.Kernel MemoryAttribute.IpcAndDeviceMapped, out _, out _, - out _); - - if (Success) + out _)) { - long PagesCount = DiffSize / PageSize; - - InsertBlock(FreeAddr, PagesCount, MemoryState.Unmapped); - - FreePages(FreeAddr, PagesCount); - - CpuMemory.Unmap(FreeAddr, DiffSize); + return KernelResult.InvalidMemState; } + + long PagesCount = DiffSize / PageSize; + + KernelResult Result = DoMmuOperation( + FreeAddr, + PagesCount, + 0, + false, + MemoryPermission.None, + MemoryOperation.Unmap); + + if (Result != KernelResult.Success) + { + return Result; + } + + InsertBlock(FreeAddr, PagesCount, MemoryState.Unmapped); } } CurrentHeapAddr = HeapRegionStart + Size; - if (Success) - { - Position = HeapRegionStart; + Position = HeapRegionStart; - return 0; - } - - return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + return KernelResult.Success; } - public long GetHeapSize() + public long GetTotalHeapSize() + { + lock (Blocks) + { + return GetHeapSize() + PersonalMmHeapUsage; + } + } + + private long GetHeapSize() { return CurrentHeapAddr - HeapRegionStart; } + public KernelResult SetHeapCapacity(long Capacity) + { + lock (Blocks) + { + HeapCapacity = Capacity; + } + + return KernelResult.Success; + } + public long SetMemoryAttribute( long Position, long Size, @@ -455,8 +763,7 @@ namespace Ryujinx.HLE.HOS.Kernel long PagesCount = Size / PageSize; InsertBlock(Src, PagesCount, SrcState, MemoryPermission.None, MemoryAttribute.Borrowed); - - InsertBlock(Dst, PagesCount, MemoryState.MappedMemory, MemoryPermission.ReadAndWrite); + InsertBlock(Dst, PagesCount, MemoryState.Stack, MemoryPermission.ReadAndWrite); long PA = CpuMemory.GetPhysicalAddress(Src); @@ -467,6 +774,37 @@ namespace Ryujinx.HLE.HOS.Kernel return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); } + public KernelResult UnmapForKernel(long Address, long PagesCount, MemoryState StateExpected) + { + long Size = PagesCount * PageSize; + + lock (Blocks) + { + if (CheckRange( + Address, + Size, + MemoryState.Mask, + StateExpected, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _)) + { + CpuMemory.Unmap(Address, Size); + + InsertBlock(Address, PagesCount, MemoryState.Unmapped); + + return KernelResult.Success; + } + } + + return KernelResult.InvalidMemState; + } + public long Unmap(long Src, long Dst, long Size) { bool Success; @@ -491,7 +829,7 @@ namespace Ryujinx.HLE.HOS.Kernel Dst, Size, MemoryState.Mask, - MemoryState.MappedMemory, + MemoryState.Stack, MemoryPermission.None, MemoryPermission.None, MemoryAttribute.Mask, @@ -506,7 +844,6 @@ namespace Ryujinx.HLE.HOS.Kernel long PagesCount = Size / PageSize; InsertBlock(Src, PagesCount, SrcState, MemoryPermission.ReadAndWrite); - InsertBlock(Dst, PagesCount, MemoryState.Unmapped); CpuMemory.Unmap(Dst, Size); @@ -640,26 +977,36 @@ namespace Ryujinx.HLE.HOS.Kernel MemoryAttribute.Mask, MemoryAttribute.None, MemoryAttribute.IpcAndDeviceMapped, - out MemoryState State, - out _, + out MemoryState OldState, + out MemoryPermission OldPermission, out _)) { - if (State == MemoryState.CodeStatic) + MemoryState NewState = OldState; + + //If writing into the code region is allowed, then we need + //to change it to mutable. + if ((Permission & MemoryPermission.Write) != 0) { - State = MemoryState.CodeMutable; - } - else if (State == MemoryState.ModCodeStatic) - { - State = MemoryState.ModCodeMutable; - } - else - { - throw new InvalidOperationException(); + if (OldState == MemoryState.CodeStatic) + { + NewState = MemoryState.CodeMutable; + } + else if (OldState == MemoryState.ModCodeStatic) + { + NewState = MemoryState.ModCodeMutable; + } + else + { + throw new InvalidOperationException($"Memory state \"{OldState}\" not valid for this operation."); + } } - long PagesCount = Size / PageSize; + if (NewState != OldState || Permission != OldPermission) + { + long PagesCount = Size / PageSize; - InsertBlock(Position, PagesCount, State, Permission); + InsertBlock(Position, PagesCount, NewState, Permission); + } return 0; } @@ -1147,13 +1494,69 @@ namespace Ryujinx.HLE.HOS.Kernel Block.PagesCount = (long)((End - Start) / PageSize); } - private static bool BlockStateEquals(KMemoryBlock LHS, KMemoryBlock RHS) + private static bool BlockStateEquals(KMemoryBlock Lhs, KMemoryBlock Rhs) { - return LHS.State == RHS.State && - LHS.Permission == RHS.Permission && - LHS.Attribute == RHS.Attribute && - LHS.DeviceRefCount == RHS.DeviceRefCount && - LHS.IpcRefCount == RHS.IpcRefCount; + return Lhs.State == Rhs.State && + Lhs.Permission == Rhs.Permission && + Lhs.Attribute == Rhs.Attribute && + Lhs.DeviceRefCount == Rhs.DeviceRefCount && + Lhs.IpcRefCount == Rhs.IpcRefCount; + } + + private long FindFirstFit( + long RegionStart, + long RegionPagesCount, + long NeededPagesCount, + int Alignment, + long ReservedStart, + long ReservedPagesCount) + { + long ReservedSize = ReservedPagesCount * PageSize; + + long TotalNeededSize = ReservedSize + NeededPagesCount * PageSize; + + long RegionEndAddr = RegionStart + RegionPagesCount * PageSize; + + LinkedListNode Node = FindBlockNode(RegionStart); + + KMemoryInfo Info = Node.Value.GetInfo(); + + while ((ulong)RegionEndAddr >= (ulong)Info.Position) + { + if (Info.State == MemoryState.Unmapped) + { + long BlkStartAddr = Info.Position + ReservedSize; + + long BlkEndAddr = Info.Position + Info.Size - 1; + + long Address = BitUtils.AlignDown(BlkStartAddr, Alignment) + ReservedStart; + + if ((ulong)BlkStartAddr > (ulong)Address) + { + Address += Alignment; + } + + long AllocationEndAddr = Address + TotalNeededSize - 1; + + if ((ulong)AllocationEndAddr <= (ulong)RegionEndAddr && + (ulong)AllocationEndAddr <= (ulong)BlkEndAddr && + (ulong)Address < (ulong)AllocationEndAddr) + { + return Address; + } + } + + Node = Node.Next; + + if (Node == null) + { + break; + } + + Info = Node.Value.GetInfo(); + } + + return 0; } private KMemoryBlock FindBlock(long Position) @@ -1187,5 +1590,310 @@ namespace Ryujinx.HLE.HOS.Kernel return null; } + + private bool ValidateRegionForState(long Address, long Size, MemoryState State) + { + long EndAddr = Address + Size; + + long RegionBaseAddr = GetBaseAddrForState(State); + + long RegionEndAddr = RegionBaseAddr + GetSizeForState(State); + + bool InsideRegion() + { + return (ulong)RegionBaseAddr <= (ulong)Address && + (ulong)EndAddr > (ulong)Address && + (ulong)EndAddr - 1 <= (ulong)RegionEndAddr - 1; + } + + bool OutsideHeapRegion() + { + return (ulong)EndAddr <= (ulong)HeapRegionStart || + (ulong)Address >= (ulong)HeapRegionEnd; + } + + bool OutsideMapRegion() + { + return (ulong)EndAddr <= (ulong)AliasRegionStart || + (ulong)Address >= (ulong)AliasRegionEnd; + } + + switch (State) + { + case MemoryState.Io: + case MemoryState.Normal: + case MemoryState.CodeStatic: + case MemoryState.CodeMutable: + case MemoryState.SharedMemory: + case MemoryState.ModCodeStatic: + case MemoryState.ModCodeMutable: + case MemoryState.Stack: + case MemoryState.ThreadLocal: + case MemoryState.TransferMemoryIsolated: + case MemoryState.TransferMemory: + case MemoryState.ProcessMemory: + case MemoryState.CodeReadOnly: + case MemoryState.CodeWritable: + return InsideRegion() && OutsideHeapRegion() && OutsideMapRegion(); + + case MemoryState.Heap: + return InsideRegion() && OutsideMapRegion(); + + case MemoryState.IpcBuffer0: + case MemoryState.IpcBuffer1: + case MemoryState.IpcBuffer3: + return InsideRegion() && OutsideHeapRegion(); + + case MemoryState.KernelStack: + return InsideRegion(); + } + + throw new ArgumentException($"Invalid state value \"{State}\"."); + } + + private long GetBaseAddrForState(MemoryState State) + { + switch (State) + { + case MemoryState.Io: + case MemoryState.Normal: + case MemoryState.ThreadLocal: + return TlsIoRegionStart; + + case MemoryState.CodeStatic: + case MemoryState.CodeMutable: + case MemoryState.SharedMemory: + case MemoryState.ModCodeStatic: + case MemoryState.ModCodeMutable: + case MemoryState.TransferMemoryIsolated: + case MemoryState.TransferMemory: + case MemoryState.ProcessMemory: + case MemoryState.CodeReadOnly: + case MemoryState.CodeWritable: + return GetAddrSpaceBaseAddr(); + + case MemoryState.Heap: + return HeapRegionStart; + + case MemoryState.IpcBuffer0: + case MemoryState.IpcBuffer1: + case MemoryState.IpcBuffer3: + return AliasRegionStart; + + case MemoryState.Stack: + return StackRegionStart; + + case MemoryState.KernelStack: + return AddrSpaceStart; + } + + throw new ArgumentException($"Invalid state value \"{State}\"."); + } + + private long GetSizeForState(MemoryState State) + { + switch (State) + { + case MemoryState.Io: + case MemoryState.Normal: + case MemoryState.ThreadLocal: + return TlsIoRegionEnd - TlsIoRegionStart; + + case MemoryState.CodeStatic: + case MemoryState.CodeMutable: + case MemoryState.SharedMemory: + case MemoryState.ModCodeStatic: + case MemoryState.ModCodeMutable: + case MemoryState.TransferMemoryIsolated: + case MemoryState.TransferMemory: + case MemoryState.ProcessMemory: + case MemoryState.CodeReadOnly: + case MemoryState.CodeWritable: + return GetAddrSpaceSize(); + + case MemoryState.Heap: + return HeapRegionEnd - HeapRegionStart; + + case MemoryState.IpcBuffer0: + case MemoryState.IpcBuffer1: + case MemoryState.IpcBuffer3: + return AliasRegionEnd - AliasRegionStart; + + case MemoryState.Stack: + return StackRegionEnd - StackRegionStart; + + case MemoryState.KernelStack: + return AddrSpaceEnd - AddrSpaceStart; + } + + throw new ArgumentException($"Invalid state value \"{State}\"."); + } + + private long GetAddrSpaceBaseAddr() + { + if (AddrSpaceWidth == 36 || AddrSpaceWidth == 39) + { + return 0x8000000; + } + else if (AddrSpaceWidth == 32) + { + return 0x200000; + } + else + { + throw new InvalidOperationException("Invalid address space width!"); + } + } + + private long GetAddrSpaceSize() + { + if (AddrSpaceWidth == 36) + { + return 0xff8000000; + } + else if (AddrSpaceWidth == 39) + { + return 0x7ff8000000; + } + else if (AddrSpaceWidth == 32) + { + return 0xffe00000; + } + else + { + throw new InvalidOperationException("Invalid address space width!"); + } + } + + private KernelResult DoMmuOperation( + long DstVa, + long PagesCount, + long SrcPa, + bool Map, + MemoryPermission Permission, + MemoryOperation Operation) + { + KernelResult Result; + + switch (Operation) + { + case MemoryOperation.MapPa: + { + long Size = PagesCount * PageSize; + + CpuMemory.Map(DstVa, SrcPa, Size); + + Result = KernelResult.Success; + + break; + } + + case MemoryOperation.Allocate: + { + KMemoryRegionManager Region = GetMemoryRegionManager(); + + Result = Region.AllocatePages(PagesCount, AslrDisabled, out KPageList PageList); + + if (Result == KernelResult.Success) + { + Result = MmuMapPages(DstVa, PageList); + } + + break; + } + + case MemoryOperation.Unmap: + { + long Size = PagesCount * PageSize; + + CpuMemory.Unmap(DstVa, Size); + + Result = KernelResult.Success; + + break; + } + + default: throw new NotImplementedException($"Unsupported memory operation \"{Operation}\"."); + } + + return Result; + } + + private KernelResult DoMmuOperation( + long Address, + long PagesCount, + KPageList PageList, + MemoryPermission Permission, + MemoryOperation Operation) + { + if (Operation != MemoryOperation.MapVa) + { + throw new ArgumentException($"Invalid memory operation \"{Operation}\" specified."); + } + + return MmuMapPages(Address, PageList); + } + + private KMemoryRegionManager GetMemoryRegionManager() + { + return System.MemoryRegions[(int)MemRegion]; + } + + private KernelResult MmuMapPages(long Address, KPageList PageList) + { + foreach (KPageNode PageNode in PageList) + { + long Size = PageNode.PagesCount * PageSize; + + CpuMemory.Map(Address, PageNode.Address - DramMemoryMap.DramBase, Size); + + Address += Size; + } + + return KernelResult.Success; + } + + public KernelResult ConvertVaToPa(long Va, out long Pa) + { + Pa = DramMemoryMap.DramBase + CpuMemory.GetPhysicalAddress(Va); + + return KernelResult.Success; + } + + public bool InsideAddrSpace(long Position, long Size) + { + ulong Start = (ulong)Position; + ulong End = (ulong)Size + Start; + + return Start >= (ulong)AddrSpaceStart && + End < (ulong)AddrSpaceEnd; + } + + public bool InsideMapRegion(long Position, long Size) + { + ulong Start = (ulong)Position; + ulong End = (ulong)Size + Start; + + return Start >= (ulong)AliasRegionStart && + End < (ulong)AliasRegionEnd; + } + + public bool InsideHeapRegion(long Position, long Size) + { + ulong Start = (ulong)Position; + ulong End = (ulong)Size + Start; + + return Start >= (ulong)HeapRegionStart && + End < (ulong)HeapRegionEnd; + } + + public bool InsideNewMapRegion(long Position, long Size) + { + ulong Start = (ulong)Position; + ulong End = (ulong)Size + Start; + + return Start >= (ulong)StackRegionStart && + End < (ulong)StackRegionEnd; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs new file mode 100644 index 0000000000..b5bd73c3d6 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs @@ -0,0 +1,43 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + class KMemoryRegionBlock + { + public long[][] Masks; + + public long FreeCount; + public int MaxLevel; + public long StartAligned; + public long SizeInBlocksTruncated; + public long SizeInBlocksRounded; + public int Order; + public int NextOrder; + + public bool TryCoalesce(int Index, int Size) + { + long Mask = ((1L << Size) - 1) << (Index & 63); + + Index /= 64; + + if ((Mask & ~Masks[MaxLevel - 1][Index]) != 0) + { + return false; + } + + Masks[MaxLevel - 1][Index] &= ~Mask; + + for (int Level = MaxLevel - 2; Level >= 0; Level--, Index /= 64) + { + Masks[Level][Index / 64] &= ~(1L << (Index & 63)); + + if (Masks[Level][Index / 64] != 0) + { + break; + } + } + + FreeCount -= Size; + + return true; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs new file mode 100644 index 0000000000..14f8f6f443 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs @@ -0,0 +1,404 @@ +using Ryujinx.Common; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class KMemoryRegionManager + { + private static readonly int[] BlockOrders = new int[] { 12, 16, 21, 22, 25, 29, 30 }; + + private long Address; + private long EndAddr; + private long Size; + private int BlockOrdersCount; + + private KMemoryRegionBlock[] Blocks; + + public KMemoryRegionManager(long Address, long Size, long EndAddr) + { + Blocks = new KMemoryRegionBlock[BlockOrders.Length]; + + this.Address = Address; + this.Size = Size; + this.EndAddr = EndAddr; + + BlockOrdersCount = BlockOrders.Length; + + for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++) + { + Blocks[BlockIndex] = new KMemoryRegionBlock(); + + Blocks[BlockIndex].Order = BlockOrders[BlockIndex]; + + int NextOrder = BlockIndex == BlockOrdersCount - 1 ? 0 : BlockOrders[BlockIndex + 1]; + + Blocks[BlockIndex].NextOrder = NextOrder; + + int CurrBlockSize = 1 << BlockOrders[BlockIndex]; + int NextBlockSize = CurrBlockSize; + + if (NextOrder != 0) + { + NextBlockSize = 1 << NextOrder; + } + + long StartAligned = BitUtils.AlignDown(Address, NextBlockSize); + long EndAddrAligned = BitUtils.AlignDown(EndAddr, CurrBlockSize); + + ulong SizeInBlocksTruncated = (ulong)(EndAddrAligned - StartAligned) >> BlockOrders[BlockIndex]; + + long EndAddrRounded = BitUtils.AlignUp(Address + Size, NextBlockSize); + + ulong SizeInBlocksRounded = (ulong)(EndAddrRounded - StartAligned) >> BlockOrders[BlockIndex]; + + Blocks[BlockIndex].StartAligned = StartAligned; + Blocks[BlockIndex].SizeInBlocksTruncated = (long)SizeInBlocksTruncated; + Blocks[BlockIndex].SizeInBlocksRounded = (long)SizeInBlocksRounded; + + ulong CurrSizeInBlocks = SizeInBlocksRounded; + + int MaxLevel = 0; + + do + { + MaxLevel++; + } + while ((CurrSizeInBlocks /= 64) != 0); + + Blocks[BlockIndex].MaxLevel = MaxLevel; + + Blocks[BlockIndex].Masks = new long[MaxLevel][]; + + CurrSizeInBlocks = SizeInBlocksRounded; + + for (int Level = MaxLevel - 1; Level >= 0; Level--) + { + CurrSizeInBlocks = (CurrSizeInBlocks + 63) / 64; + + Blocks[BlockIndex].Masks[Level] = new long[CurrSizeInBlocks]; + } + } + + if (Size != 0) + { + FreePages(Address, Size / KMemoryManager.PageSize); + } + } + + public KernelResult AllocatePages(long PagesCount, bool Backwards, out KPageList PageList) + { + lock (Blocks) + { + return AllocatePagesImpl(PagesCount, Backwards, out PageList); + } + } + + private KernelResult AllocatePagesImpl(long PagesCount, bool Backwards, out KPageList PageList) + { + Backwards = false; + + PageList = new KPageList(); + + if (BlockOrdersCount > 0) + { + ulong AvailablePages = 0; + + for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++) + { + KMemoryRegionBlock Block = Blocks[BlockIndex]; + + ulong BlockPagesCount = (1UL << Block.Order) / KMemoryManager.PageSize; + + AvailablePages += BlockPagesCount * (ulong)Block.FreeCount; + } + + if (AvailablePages < (ulong)PagesCount) + { + return KernelResult.OutOfMemory; + } + } + else if (PagesCount != 0) + { + return KernelResult.OutOfMemory; + } + + for (int BlockIndex = BlockOrdersCount - 1; BlockIndex >= 0; BlockIndex--) + { + KMemoryRegionBlock Block = Blocks[BlockIndex]; + + int BestFitBlockSize = 1 << Block.Order; + + int BlockPagesCount = BestFitBlockSize / KMemoryManager.PageSize; + + //Check if this is the best fit for this page size. + //If so, try allocating as much requested pages as possible. + while ((ulong)BlockPagesCount <= (ulong)PagesCount) + { + long Address = 0; + + for (int CurrBlockIndex = BlockIndex; + CurrBlockIndex < BlockOrdersCount && Address == 0; + CurrBlockIndex++) + { + Block = Blocks[CurrBlockIndex]; + + int Index = 0; + + bool ZeroMask = false; + + for (int Level = 0; Level < Block.MaxLevel; Level++) + { + long Mask = Block.Masks[Level][Index]; + + if (Mask == 0) + { + ZeroMask = true; + + break; + } + + if (Backwards) + { + Index = (Index * 64 + 63) - BitUtils.CountLeadingZeros64(Mask); + } + else + { + Index = Index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(Mask)); + } + } + + if ((ulong)Block.SizeInBlocksTruncated <= (ulong)Index || ZeroMask) + { + continue; + } + + Block.FreeCount--; + + int TempIdx = Index; + + for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, TempIdx /= 64) + { + Block.Masks[Level][TempIdx / 64] &= ~(1L << (TempIdx & 63)); + + if (Block.Masks[Level][TempIdx / 64] != 0) + { + break; + } + } + + Address = Block.StartAligned + ((long)Index << Block.Order); + } + + for (int CurrBlockIndex = BlockIndex; + CurrBlockIndex < BlockOrdersCount && Address == 0; + CurrBlockIndex++) + { + Block = Blocks[CurrBlockIndex]; + + int Index = 0; + + bool ZeroMask = false; + + for (int Level = 0; Level < Block.MaxLevel; Level++) + { + long Mask = Block.Masks[Level][Index]; + + if (Mask == 0) + { + ZeroMask = true; + + break; + } + + if (Backwards) + { + Index = Index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(Mask)); + } + else + { + Index = (Index * 64 + 63) - BitUtils.CountLeadingZeros64(Mask); + } + } + + if ((ulong)Block.SizeInBlocksTruncated <= (ulong)Index || ZeroMask) + { + continue; + } + + Block.FreeCount--; + + int TempIdx = Index; + + for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, TempIdx /= 64) + { + Block.Masks[Level][TempIdx / 64] &= ~(1L << (TempIdx & 63)); + + if (Block.Masks[Level][TempIdx / 64] != 0) + { + break; + } + } + + Address = Block.StartAligned + ((long)Index << Block.Order); + } + + //The address being zero means that no free space was found on that order, + //just give up and try with the next one. + if (Address == 0) + { + break; + } + + //If we are using a larger order than best fit, then we should + //split it into smaller blocks. + int FirstFreeBlockSize = 1 << Block.Order; + + if (FirstFreeBlockSize > BestFitBlockSize) + { + FreePages(Address + BestFitBlockSize, (FirstFreeBlockSize - BestFitBlockSize) / KMemoryManager.PageSize); + } + + //Add new allocated page(s) to the pages list. + //If an error occurs, then free all allocated pages and fail. + KernelResult Result = PageList.AddRange(Address, BlockPagesCount); + + if (Result != KernelResult.Success) + { + FreePages(Address, BlockPagesCount); + + foreach (KPageNode PageNode in PageList) + { + FreePages(PageNode.Address, PageNode.PagesCount); + } + + return Result; + } + + PagesCount -= BlockPagesCount; + } + } + + //Success case, all requested pages were allocated successfully. + if (PagesCount == 0) + { + return KernelResult.Success; + } + + //Error case, free allocated pages and return out of memory. + foreach (KPageNode PageNode in PageList) + { + FreePages(PageNode.Address, PageNode.PagesCount); + } + + return KernelResult.OutOfMemory; + } + + public void FreePages(long Address, long PagesCount) + { + long EndAddr = Address + PagesCount * KMemoryManager.PageSize; + + int BlockIndex = BlockOrdersCount - 1; + + long AddressRounded = 0; + long EndAddrTruncated = 0; + + for (; BlockIndex >= 0; BlockIndex--) + { + KMemoryRegionBlock AllocInfo = Blocks[BlockIndex]; + + long BlockSize = 1L << AllocInfo.Order; + + AddressRounded = (Address + BlockSize - 1) & -BlockSize; + + EndAddrTruncated = EndAddr & -BlockSize; + + if ((ulong)AddressRounded < (ulong)EndAddrTruncated) + { + break; + } + } + + void FreeRegion(long CurrAddress) + { + for (int CurrBlockIndex = BlockIndex; + CurrBlockIndex < BlockOrdersCount && CurrAddress != 0; + CurrBlockIndex++) + { + KMemoryRegionBlock Block = Blocks[CurrBlockIndex]; + + Block.FreeCount++; + + ulong FreedBlocks = (ulong)(CurrAddress - Block.StartAligned) >> Block.Order; + + int Index = (int)FreedBlocks; + + for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, Index /= 64) + { + long Mask = Block.Masks[Level][Index / 64]; + + Block.Masks[Level][Index / 64] = Mask | (1L << (Index & 63)); + + if (Mask != 0) + { + break; + } + } + + int BlockSizeDelta = 1 << (Block.NextOrder - Block.Order); + + int FreedBlocksTruncated = (int)FreedBlocks & -BlockSizeDelta; + + if (!Block.TryCoalesce(FreedBlocksTruncated, BlockSizeDelta)) + { + break; + } + + CurrAddress = Block.StartAligned + ((long)FreedBlocksTruncated << Block.Order); + } + } + + //Free inside aligned region. + long BaseAddress = AddressRounded; + + while ((ulong)BaseAddress < (ulong)EndAddrTruncated) + { + long BlockSize = 1L << Blocks[BlockIndex].Order; + + FreeRegion(BaseAddress); + + BaseAddress += BlockSize; + } + + int NextBlockIndex = BlockIndex - 1; + + //Free region between Address and aligned region start. + BaseAddress = AddressRounded; + + for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--) + { + long BlockSize = 1L << Blocks[BlockIndex].Order; + + while ((ulong)(BaseAddress - BlockSize) >= (ulong)Address) + { + BaseAddress -= BlockSize; + + FreeRegion(BaseAddress); + } + } + + //Free region between aligned region end and End Address. + BaseAddress = EndAddrTruncated; + + for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--) + { + long BlockSize = 1L << Blocks[BlockIndex].Order; + + while ((ulong)(BaseAddress + BlockSize) <= (ulong)EndAddr) + { + FreeRegion(BaseAddress); + + BaseAddress += BlockSize; + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KPageList.cs b/Ryujinx.HLE/HOS/Kernel/KPageList.cs new file mode 100644 index 0000000000..49f5d2cfb1 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KPageList.cs @@ -0,0 +1,60 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class KPageList : IEnumerable + { + private LinkedList Nodes; + + public KPageList() + { + Nodes = new LinkedList(); + } + + public KernelResult AddRange(long Address, long PagesCount) + { + if (PagesCount != 0) + { + if (Nodes.Last != null) + { + KPageNode LastNode = Nodes.Last.Value; + + if (LastNode.Address + LastNode.PagesCount * 4096 == Address) + { + Address = LastNode.Address; + PagesCount += LastNode.PagesCount; + + Nodes.RemoveLast(); + } + } + + Nodes.AddLast(new KPageNode(Address, PagesCount)); + } + + return KernelResult.Success; + } + + public long GetPagesCount() + { + long Sum = 0; + + foreach (KPageNode Node in Nodes) + { + Sum += Node.PagesCount; + } + + return Sum; + } + + public IEnumerator GetEnumerator() + { + return Nodes.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KPageNode.cs b/Ryujinx.HLE/HOS/Kernel/KPageNode.cs new file mode 100644 index 0000000000..cc11e53129 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KPageNode.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + struct KPageNode + { + public long Address; + public long PagesCount; + + public KPageNode(long Address, long PagesCount) + { + this.Address = Address; + this.PagesCount = PagesCount; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/KProcess.cs new file mode 100644 index 0000000000..3c96d4fc1e --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KProcess.cs @@ -0,0 +1,885 @@ +using ChocolArm64; +using ChocolArm64.Memory; +using Ryujinx.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class KProcess : KSynchronizationObject + { + public const int KernelVersionMajor = 10; + public const int KernelVersionMinor = 4; + public const int KernelVersionRevision = 0; + + public const int KernelVersionPacked = + (KernelVersionMajor << 19) | + (KernelVersionMinor << 15) | + (KernelVersionRevision << 0); + + public KMemoryManager MemoryManager { get; private set; } + + private long TotalMemoryUsage; + + private SortedDictionary FullTlsPages; + private SortedDictionary FreeTlsPages; + + public int DefaultCpuCore { get; private set; } + + public KResourceLimit ResourceLimit { get; private set; } + + private long PersonalMmHeapBase; + private long PersonalMmHeapPagesCount; + + private ProcessState State; + + private object ProcessLock; + private object ThreadingLock; + + public KAddressArbiter AddressArbiter { get; private set; } + + private long[] RandomEntropy; + + private bool Signaled; + private bool UseSystemMemBlocks; + + private string Name; + + private int ThreadCount; + + public int MmuFlags { get; private set; } + + private MemoryRegion MemRegion; + + public KProcessCapabilities Capabilities { get; private set; } + + public long TitleId { get; private set; } + public long Pid { get; private set; } + + private long CreationTimestamp; + private long Entrypoint; + private long ImageSize; + private long MainThreadStackSize; + private long MemoryUsageCapacity; + private int Category; + + public KProcessHandleTable HandleTable { get; private set; } + + private long TlsAddress; + + private LinkedList Threads; + + public bool IsPaused { get; private set; } + + public Translator Translator { get; private set; } + + public MemoryManager CpuMemory { get; private set; } + + private SvcHandler SvcHandler; + + public KProcess(Horizon System) : base(System) + { + ProcessLock = new object(); + ThreadingLock = new object(); + + CpuMemory = new MemoryManager(System.Device.Memory.RamPointer); + + AddressArbiter = new KAddressArbiter(System); + + MemoryManager = new KMemoryManager(System, CpuMemory); + + FullTlsPages = new SortedDictionary(); + FreeTlsPages = new SortedDictionary(); + + ResourceLimit = new KResourceLimit(); + + Capabilities = new KProcessCapabilities(); + + RandomEntropy = new long[KScheduler.CpuCoresCount]; + + Threads = new LinkedList(); + + Translator = new Translator(); + + SvcHandler = new SvcHandler(System.Device, this); + } + + public KernelResult InitializeKip( + ProcessCreationInfo CreationInfo, + int[] Caps, + KPageList PageList, + KResourceLimit ResourceLimit, + MemoryRegion MemRegion) + { + AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7); + + bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0; + + long CodeAddress = CreationInfo.CodeAddress; + + long CodeSize = (long)(uint)CreationInfo.CodePagesCount * KMemoryManager.PageSize; + + KernelResult Result = MemoryManager.InitializeForProcess( + AddrSpaceType, + AslrEnabled, + !AslrEnabled, + MemRegion, + CodeAddress, + CodeSize); + + if (Result != KernelResult.Success) + { + return Result; + } + + if (!ValidateCodeAddressAndSize(CodeAddress, CodeSize)) + { + return KernelResult.InvalidMemRange; + } + + Result = MemoryManager.MapPages( + CodeAddress, + PageList, + MemoryState.CodeStatic, + MemoryPermission.None); + + if (Result != KernelResult.Success) + { + return Result; + } + + Result = Capabilities.InitializeForKernel(Caps, MemoryManager); + + if (Result != KernelResult.Success) + { + return Result; + } + + Pid = System.GetKipId(); + + if (Pid == 0 || (ulong)Pid >= Horizon.InitialProcessId) + { + throw new InvalidOperationException($"Invalid KIP Id {Pid}."); + } + + Result = ParseProcessInfo(CreationInfo); + + return Result; + } + + public KernelResult Initialize( + ProcessCreationInfo CreationInfo, + int[] Caps, + KResourceLimit ResourceLimit, + MemoryRegion MemRegion) + { + long PersonalMmHeapSize = CreationInfo.PersonalMmHeapPagesCount * KMemoryManager.PageSize; + + long CodePagesCount = (long)(uint)CreationInfo.CodePagesCount; + + long NeededSizeForProcess = PersonalMmHeapSize + CodePagesCount * KMemoryManager.PageSize; + + if (NeededSizeForProcess != 0 && ResourceLimit != null) + { + if (!ResourceLimit.Reserve(LimitableResource.Memory, NeededSizeForProcess)) + { + return KernelResult.ResLimitExceeded; + } + } + + void CleanUpForError() + { + if (NeededSizeForProcess != 0 && ResourceLimit != null) + { + ResourceLimit.Release(LimitableResource.Memory, NeededSizeForProcess); + } + } + + PersonalMmHeapPagesCount = CreationInfo.PersonalMmHeapPagesCount; + + if (PersonalMmHeapPagesCount != 0) + { + + } + + AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7); + + bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0; + + long CodeAddress = CreationInfo.CodeAddress; + + long CodeSize = CodePagesCount * KMemoryManager.PageSize; + + KernelResult Result = MemoryManager.InitializeForProcess( + AddrSpaceType, + AslrEnabled, + !AslrEnabled, + MemRegion, + CodeAddress, + CodeSize); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + if (!ValidateCodeAddressAndSize(CodeAddress, CodeSize)) + { + CleanUpForError(); + + return KernelResult.InvalidMemRange; + } + + Result = MemoryManager.MapNewProcessCode( + CodeAddress, + CodePagesCount, + MemoryState.CodeStatic, + MemoryPermission.None); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + Result = Capabilities.InitializeForUser(Caps, MemoryManager); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + Pid = System.GetProcessId(); + + if (Pid == -1 || (ulong)Pid < Horizon.InitialProcessId) + { + throw new InvalidOperationException($"Invalid Process Id {Pid}."); + } + + Result = ParseProcessInfo(CreationInfo); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + } + + return Result; + } + + private bool ValidateCodeAddressAndSize(long CodeAddress, long CodeSize) + { + long CodeRegionStart; + long CodeRegionSize; + + switch (MemoryManager.AddrSpaceWidth) + { + case 32: + CodeRegionStart = 0x200000; + CodeRegionSize = 0x3fe00000; + break; + + case 36: + CodeRegionStart = 0x8000000; + CodeRegionSize = 0x78000000; + break; + + case 39: + CodeRegionStart = 0x8000000; + CodeRegionSize = 0x7ff8000000; + break; + + default: throw new InvalidOperationException("Invalid address space width on memory manager."); + } + + long CodeEndAddr = CodeAddress + CodeSize; + + long CodeRegionEnd = CodeRegionStart + CodeRegionSize; + + if ((ulong)CodeEndAddr <= (ulong)CodeAddress || + (ulong)CodeEndAddr - 1 > (ulong)CodeRegionEnd - 1) + { + return false; + } + + if (MemoryManager.InsideHeapRegion(CodeAddress, CodeSize) || + MemoryManager.InsideMapRegion (CodeAddress, CodeSize)) + { + return false; + } + + return true; + } + + private KernelResult ParseProcessInfo(ProcessCreationInfo CreationInfo) + { + //Ensure that the current kernel version is equal or above to the minimum required. + uint RequiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19; + uint RequiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf; + + if (RequiredKernelVersionMajor > KernelVersionMajor) + { + return KernelResult.InvalidCombination; + } + + if (RequiredKernelVersionMajor != KernelVersionMajor && RequiredKernelVersionMajor < 3) + { + return KernelResult.InvalidCombination; + } + + if (RequiredKernelVersionMinor > KernelVersionMinor) + { + return KernelResult.InvalidCombination; + } + + KernelResult Result = AllocateThreadLocalStorage(out TlsAddress); + + if (Result != KernelResult.Success) + { + return Result; + } + + MemoryHelper.FillWithZeros(CpuMemory, TlsAddress, KTlsPageInfo.TlsEntrySize); + + Name = CreationInfo.Name; + + State = ProcessState.Created; + + CreationTimestamp = PerformanceCounter.ElapsedMilliseconds; + + MmuFlags = CreationInfo.MmuFlags; + Category = CreationInfo.Category; + TitleId = CreationInfo.TitleId; + Entrypoint = CreationInfo.CodeAddress; + ImageSize = CreationInfo.CodePagesCount * KMemoryManager.PageSize; + + UseSystemMemBlocks = ((MmuFlags >> 6) & 1) != 0; + + switch ((AddressSpaceType)((MmuFlags >> 1) & 7)) + { + case AddressSpaceType.Addr32Bits: + case AddressSpaceType.Addr36Bits: + case AddressSpaceType.Addr39Bits: + MemoryUsageCapacity = MemoryManager.HeapRegionEnd - + MemoryManager.HeapRegionStart; + break; + + case AddressSpaceType.Addr32BitsNoMap: + MemoryUsageCapacity = MemoryManager.HeapRegionEnd - + MemoryManager.HeapRegionStart + + MemoryManager.AliasRegionEnd - + MemoryManager.AliasRegionStart; + break; + + default: throw new InvalidOperationException($"Invalid MMU flags value 0x{MmuFlags:x2}."); + } + + GenerateRandomEntropy(); + + return KernelResult.Success; + } + + public KernelResult AllocateThreadLocalStorage(out long Address) + { + System.CriticalSection.Enter(); + + KernelResult Result; + + if (FreeTlsPages.Count > 0) + { + //If we have free TLS pages available, just use the first one. + KTlsPageInfo PageInfo = FreeTlsPages.Values.First(); + + if (!PageInfo.TryGetFreePage(out Address)) + { + throw new InvalidOperationException("Unexpected failure getting free TLS page!"); + } + + if (PageInfo.IsFull()) + { + FreeTlsPages.Remove(PageInfo.PageAddr); + + FullTlsPages.Add(PageInfo.PageAddr, PageInfo); + } + + Result = KernelResult.Success; + } + else + { + //Otherwise, we need to create a new one. + Result = AllocateTlsPage(out KTlsPageInfo PageInfo); + + if (Result == KernelResult.Success) + { + if (!PageInfo.TryGetFreePage(out Address)) + { + throw new InvalidOperationException("Unexpected failure getting free TLS page!"); + } + + FreeTlsPages.Add(PageInfo.PageAddr, PageInfo); + } + else + { + Address = 0; + } + } + + System.CriticalSection.Leave(); + + return Result; + } + + private KernelResult AllocateTlsPage(out KTlsPageInfo PageInfo) + { + PageInfo = default(KTlsPageInfo); + + if (!System.UserSlabHeapPages.TryGetItem(out long TlsPagePa)) + { + return KernelResult.OutOfMemory; + } + + long RegionStart = MemoryManager.TlsIoRegionStart; + + long RegionPagesCount = (MemoryManager.TlsIoRegionEnd - RegionStart) / KMemoryManager.PageSize; + + KernelResult Result = MemoryManager.AllocateOrMapPa( + 1, + KMemoryManager.PageSize, + TlsPagePa, + true, + RegionStart, + RegionPagesCount, + MemoryState.ThreadLocal, + MemoryPermission.ReadAndWrite, + out long TlsPageVa); + + if (Result != KernelResult.Success) + { + System.UserSlabHeapPages.Free(TlsPagePa); + } + else + { + PageInfo = new KTlsPageInfo(TlsPageVa); + + MemoryHelper.FillWithZeros(CpuMemory, TlsPageVa, KMemoryManager.PageSize); + } + + return Result; + } + + public KernelResult FreeThreadLocalStorage(long TlsSlotAddr) + { + long TlsPageAddr = BitUtils.AlignDown(TlsSlotAddr, KMemoryManager.PageSize); + + System.CriticalSection.Enter(); + + KernelResult Result = KernelResult.Success; + + KTlsPageInfo PageInfo = null; + + if (FullTlsPages.TryGetValue(TlsPageAddr, out PageInfo)) + { + //TLS page was full, free slot and move to free pages tree. + FullTlsPages.Remove(TlsPageAddr); + + FreeTlsPages.Add(TlsPageAddr, PageInfo); + } + else if (!FreeTlsPages.TryGetValue(TlsPageAddr, out PageInfo)) + { + Result = KernelResult.InvalidAddress; + } + + if (PageInfo != null) + { + PageInfo.FreeTlsSlot(TlsSlotAddr); + + if (PageInfo.IsEmpty()) + { + //TLS page is now empty, we should ensure it is removed + //from all trees, and free the memory it was using. + FreeTlsPages.Remove(TlsPageAddr); + + System.CriticalSection.Leave(); + + FreeTlsPage(PageInfo); + + return KernelResult.Success; + } + } + + System.CriticalSection.Leave(); + + return Result; + } + + private KernelResult FreeTlsPage(KTlsPageInfo PageInfo) + { + KernelResult Result = MemoryManager.ConvertVaToPa(PageInfo.PageAddr, out long TlsPagePa); + + if (Result != KernelResult.Success) + { + throw new InvalidOperationException("Unexpected failure translating virtual address to physical."); + } + + Result = MemoryManager.UnmapForKernel(PageInfo.PageAddr, 1, MemoryState.ThreadLocal); + + if (Result == KernelResult.Success) + { + System.UserSlabHeapPages.Free(TlsPagePa); + } + + return Result; + } + + private void GenerateRandomEntropy() + { + //TODO. + } + + public KernelResult Start(int MainThreadPriority, long StackSize) + { + lock (ProcessLock) + { + if (State > ProcessState.CreatedAttached) + { + return KernelResult.InvalidState; + } + + if (ResourceLimit != null && !ResourceLimit.Reserve(LimitableResource.Thread, 1)) + { + return KernelResult.ResLimitExceeded; + } + + KResourceLimit ThreadResourceLimit = ResourceLimit; + KResourceLimit MemoryResourceLimit = null; + + if (MainThreadStackSize != 0) + { + throw new InvalidOperationException("Trying to start a process with a invalid state!"); + } + + long StackSizeRounded = BitUtils.AlignUp(StackSize, KMemoryManager.PageSize); + + long NeededSize = StackSizeRounded + ImageSize; + + //Check if the needed size for the code and the stack will fit on the + //memory usage capacity of this Process. Also check for possible overflow + //on the above addition. + if ((ulong)NeededSize > (ulong)MemoryUsageCapacity || + (ulong)NeededSize < (ulong)StackSizeRounded) + { + ThreadResourceLimit?.Release(LimitableResource.Thread, 1); + + return KernelResult.OutOfMemory; + } + + if (StackSizeRounded != 0 && ResourceLimit != null) + { + MemoryResourceLimit = ResourceLimit; + + if (!MemoryResourceLimit.Reserve(LimitableResource.Memory, StackSizeRounded)) + { + ThreadResourceLimit?.Release(LimitableResource.Thread, 1); + + return KernelResult.ResLimitExceeded; + } + } + + KernelResult Result; + + KThread MainThread = null; + + long StackTop = 0; + + void CleanUpForError() + { + MainThread?.Terminate(); + HandleTable.Destroy(); + + if (MainThreadStackSize != 0) + { + long StackBottom = StackTop - MainThreadStackSize; + + long StackPagesCount = MainThreadStackSize / KMemoryManager.PageSize; + + MemoryManager.UnmapForKernel(StackBottom, StackPagesCount, MemoryState.Stack); + } + + MemoryResourceLimit?.Release(LimitableResource.Memory, StackSizeRounded); + ThreadResourceLimit?.Release(LimitableResource.Thread, 1); + } + + if (StackSizeRounded != 0) + { + long StackPagesCount = StackSizeRounded / KMemoryManager.PageSize; + + long RegionStart = MemoryManager.StackRegionStart; + long RegionPagesCount = (MemoryManager.StackRegionEnd - RegionStart) / KMemoryManager.PageSize; + + Result = MemoryManager.AllocateOrMapPa( + StackPagesCount, + KMemoryManager.PageSize, + 0, + false, + RegionStart, + RegionPagesCount, + MemoryState.Stack, + MemoryPermission.ReadAndWrite, + out long StackBottom); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + MainThreadStackSize += StackSizeRounded; + + StackTop = StackBottom + StackSizeRounded; + } + + long HeapCapacity = MemoryUsageCapacity - MainThreadStackSize - ImageSize; + + Result = MemoryManager.SetHeapCapacity(HeapCapacity); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + HandleTable = new KProcessHandleTable(System); + + Result = HandleTable.Initialize(Capabilities.HandleTableSize); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + MainThread = new KThread(System); + + Result = MainThread.Initialize( + Entrypoint, + 0, + StackTop, + MainThreadPriority, + DefaultCpuCore, + this); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + Result = HandleTable.GenerateHandle(MainThread, out int MainThreadHandle); + + if (Result != KernelResult.Success) + { + CleanUpForError(); + + return Result; + } + + MainThread.SetEntryArguments(0, MainThreadHandle); + + ProcessState OldState = State; + ProcessState NewState = State != ProcessState.Created + ? ProcessState.Attached + : ProcessState.Started; + + SetState(NewState); + + //TODO: We can't call KThread.Start from a non-guest thread. + //We will need to make some changes to allow the creation of + //dummy threads that will be used to initialize the current + //thread on KCoreContext so that GetCurrentThread doesn't fail. + /* Result = MainThread.Start(); + + if (Result != KernelResult.Success) + { + SetState(OldState); + + CleanUpForError(); + } */ + + MainThread.TimeUp(); + + return Result; + } + } + + private void SetState(ProcessState NewState) + { + if (State != NewState) + { + State = NewState; + Signaled = true; + + Signal(); + } + } + + public KernelResult InitializeThread( + KThread Thread, + long Entrypoint, + long ArgsPtr, + long StackTop, + int Priority, + int CpuCore) + { + lock (ProcessLock) + { + return Thread.Initialize(Entrypoint, ArgsPtr, StackTop, Priority, CpuCore, this); + } + } + + public void SubscribeThreadEventHandlers(CpuThread Context) + { + Context.ThreadState.Interrupt += InterruptHandler; + Context.ThreadState.SvcCall += SvcHandler.SvcCall; + } + + private void InterruptHandler(object sender, EventArgs e) + { + System.Scheduler.ContextSwitch(); + } + + public void IncrementThreadCount() + { + Interlocked.Increment(ref ThreadCount); + } + + public void DecrementThreadCountAndTerminateIfZero() + { + if (Interlocked.Decrement(ref ThreadCount) == 0) + { + Terminate(); + } + } + + public long GetMemoryCapacity() + { + //TODO: Personal Mm Heap. + return 0xcd500000; + } + + public long GetMemoryUsage() + { + //TODO: Personal Mm Heap. + return ImageSize + MainThreadStackSize + MemoryManager.GetTotalHeapSize(); + } + + public void AddThread(KThread Thread) + { + lock (ThreadingLock) + { + Thread.ProcessListNode = Threads.AddLast(Thread); + } + } + + public void RemoveThread(KThread Thread) + { + lock (ThreadingLock) + { + Threads.Remove(Thread.ProcessListNode); + } + } + + public bool IsAllowedCpuCore(int Core) + { + return (Capabilities.AllowedCpuCoresMask & (1L << Core)) != 0; + } + + public bool IsAllowedPriority(int Priority) + { + return (Capabilities.AllowedThreadPriosMask & (1L << Priority)) != 0; + } + + public override bool IsSignaled() + { + return Signaled; + } + + public KernelResult Terminate() + { + KernelResult Result; + + bool ShallTerminate = false; + + lock (ProcessLock) + { + if (State >= ProcessState.Started) + { + System.CriticalSection.Enter(); + + if (State == ProcessState.Started || + State == ProcessState.Crashed || + State == ProcessState.Attached || + State == ProcessState.DebugSuspended) + { + SetState(ProcessState.Exiting); + + ShallTerminate = true; + } + + System.CriticalSection.Leave(); + + Result = KernelResult.Success; + } + else + { + Result = KernelResult.InvalidState; + } + } + + if (ShallTerminate) + { + UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread()); + + HandleTable.Destroy(); + + SignalExitForDebugEvent(); + SignalExit(); + } + + return Result; + } + + private void UnpauseAndTerminateAllThreadsExcept(KThread Thread) + { + //TODO. + } + + private void SignalExitForDebugEvent() + { + //TODO: Debug events. + } + + private void SignalExit() + { + if (ResourceLimit != null) + { + ResourceLimit.Release(LimitableResource.Memory, GetMemoryUsage()); + } + + System.CriticalSection.Enter(); + + SetState(ProcessState.Exited); + + System.CriticalSection.Leave(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs b/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs new file mode 100644 index 0000000000..1a06077436 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs @@ -0,0 +1,311 @@ +using Ryujinx.Common; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class KProcessCapabilities + { + public byte[] SvcAccessMask { get; private set; } + public byte[] IrqAccessMask { get; private set; } + + public long AllowedCpuCoresMask { get; private set; } + public long AllowedThreadPriosMask { get; private set; } + + public int DebuggingFlags { get; private set; } + public int HandleTableSize { get; private set; } + public int KernelReleaseVersion { get; private set; } + public int ApplicationType { get; private set; } + + public KProcessCapabilities() + { + SvcAccessMask = new byte[0x10]; + IrqAccessMask = new byte[0x80]; + } + + public KernelResult InitializeForKernel(int[] Caps, KMemoryManager MemoryManager) + { + AllowedCpuCoresMask = 0xf; + AllowedThreadPriosMask = -1; + DebuggingFlags &= ~3; + KernelReleaseVersion = KProcess.KernelVersionPacked; + + return Parse(Caps, MemoryManager); + } + + public KernelResult InitializeForUser(int[] Caps, KMemoryManager MemoryManager) + { + return Parse(Caps, MemoryManager); + } + + private KernelResult Parse(int[] Caps, KMemoryManager MemoryManager) + { + int Mask0 = 0; + int Mask1 = 0; + + for (int Index = 0; Index < Caps.Length; Index++) + { + int Cap = Caps[Index]; + + if (((Cap + 1) & ~Cap) != 0x40) + { + KernelResult Result = ParseCapability(Cap, ref Mask0, ref Mask1, MemoryManager); + + if (Result != KernelResult.Success) + { + return Result; + } + } + else + { + if ((uint)Index + 1 >= Caps.Length) + { + return KernelResult.InvalidCombination; + } + + int PrevCap = Cap; + + Cap = Caps[Index + 1]; + + if (((Cap + 1) & ~Cap) != 0x40) + { + return KernelResult.InvalidCombination; + } + + if ((Cap & 0x78000000) != 0) + { + return KernelResult.MaximumExceeded; + } + + if ((Cap & 0x7ffff80) == 0) + { + return KernelResult.InvalidSize; + } + + long Address = ((long)(uint)PrevCap << 5) & 0xffffff000; + long Size = ((long)(uint)Cap << 5) & 0xfffff000; + + if (((ulong)(Address + Size - 1) >> 36) != 0) + { + return KernelResult.InvalidAddress; + } + + MemoryPermission Perm = (PrevCap >> 31) != 0 + ? MemoryPermission.Read + : MemoryPermission.ReadAndWrite; + + KernelResult Result; + + if ((Cap >> 31) != 0) + { + Result = MemoryManager.MapNormalMemory(Address, Size, Perm); + } + else + { + Result = MemoryManager.MapIoMemory(Address, Size, Perm); + } + + if (Result != KernelResult.Success) + { + return Result; + } + } + } + + return KernelResult.Success; + } + + private KernelResult ParseCapability(int Cap, ref int Mask0, ref int Mask1, KMemoryManager MemoryManager) + { + int Code = (Cap + 1) & ~Cap; + + if (Code == 1) + { + return KernelResult.InvalidCapability; + } + else if (Code == 0) + { + return KernelResult.Success; + } + + int CodeMask = 1 << (32 - BitUtils.CountLeadingZeros32(Code + 1)); + + //Check if the property was already set. + if (((Mask0 & CodeMask) & 0x1e008) != 0) + { + return KernelResult.InvalidCombination; + } + + Mask0 |= CodeMask; + + switch (Code) + { + case 8: + { + if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0) + { + return KernelResult.InvalidCapability; + } + + int LowestCpuCore = (Cap >> 16) & 0xff; + int HighestCpuCore = (Cap >> 24) & 0xff; + + if (LowestCpuCore > HighestCpuCore) + { + return KernelResult.InvalidCombination; + } + + int HighestThreadPrio = (Cap >> 4) & 0x3f; + int LowestThreadPrio = (Cap >> 10) & 0x3f; + + if (LowestThreadPrio > HighestThreadPrio) + { + return KernelResult.InvalidCombination; + } + + if (HighestCpuCore >= KScheduler.CpuCoresCount) + { + return KernelResult.InvalidCpuCore; + } + + AllowedCpuCoresMask = GetMaskFromMinMax(LowestCpuCore, HighestCpuCore); + AllowedThreadPriosMask = GetMaskFromMinMax(LowestThreadPrio, HighestThreadPrio); + + break; + } + + case 0x10: + { + int Slot = (Cap >> 29) & 7; + + int SvcSlotMask = 1 << Slot; + + if ((Mask1 & SvcSlotMask) != 0) + { + return KernelResult.InvalidCombination; + } + + Mask1 |= SvcSlotMask; + + int SvcMask = (Cap >> 5) & 0xffffff; + + int BaseSvc = Slot * 24; + + for (int Index = 0; Index < 24; Index++) + { + if (((SvcMask >> Index) & 1) == 0) + { + continue; + } + + int SvcId = BaseSvc + Index; + + if (SvcId > 0x7f) + { + return KernelResult.MaximumExceeded; + } + + SvcAccessMask[SvcId / 8] |= (byte)(1 << (SvcId & 7)); + } + + break; + } + + case 0x80: + { + long Address = ((long)(uint)Cap << 4) & 0xffffff000; + + MemoryManager.MapIoMemory(Address, KMemoryManager.PageSize, MemoryPermission.ReadAndWrite); + + break; + } + + case 0x800: + { + //TODO: GIC distributor check. + int Irq0 = (Cap >> 12) & 0x3ff; + int Irq1 = (Cap >> 22) & 0x3ff; + + if (Irq0 != 0x3ff) + { + IrqAccessMask[Irq0 / 8] |= (byte)(1 << (Irq0 & 7)); + } + + if (Irq1 != 0x3ff) + { + IrqAccessMask[Irq1 / 8] |= (byte)(1 << (Irq1 & 7)); + } + + break; + } + + case 0x2000: + { + int ApplicationType = Cap >> 14; + + if ((uint)ApplicationType > 7) + { + return KernelResult.ReservedValue; + } + + this.ApplicationType = ApplicationType; + + break; + } + + case 0x4000: + { + //Note: This check is bugged on kernel too, we are just replicating the bug here. + if ((KernelReleaseVersion >> 17) != 0 || Cap < 0x80000) + { + return KernelResult.ReservedValue; + } + + KernelReleaseVersion = Cap; + + break; + } + + case 0x8000: + { + int HandleTableSize = Cap >> 26; + + if ((uint)HandleTableSize > 0x3ff) + { + return KernelResult.ReservedValue; + } + + this.HandleTableSize = HandleTableSize; + + break; + } + + case 0x10000: + { + int DebuggingFlags = Cap >> 19; + + if ((uint)DebuggingFlags > 3) + { + return KernelResult.ReservedValue; + } + + this.DebuggingFlags &= ~3; + this.DebuggingFlags |= DebuggingFlags; + + break; + } + + default: return KernelResult.InvalidCapability; + } + + return KernelResult.Success; + } + + private static long GetMaskFromMinMax(int Min, int Max) + { + int Range = Max - Min + 1; + + long Mask = (1L << Range) - 1; + + return Mask << Min; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs b/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs index 682f08d4ff..c88b9c9230 100644 --- a/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs @@ -20,7 +20,10 @@ namespace Ryujinx.HLE.HOS.Kernel private ushort IdCounter; - private object LockObj; + public KProcessHandleTable(Horizon System) + { + this.System = System; + } public KProcessHandleTable(Horizon System, int Size = 1024) { @@ -47,15 +50,51 @@ namespace Ryujinx.HLE.HOS.Kernel Table[Size - 1].Next = null; NextFreeEntry = TableHead; + } - LockObj = new object(); + public KernelResult Initialize(int Size) + { + if ((uint)Size > 1024) + { + return KernelResult.OutOfMemory; + } + + if (Size < 1) + { + Size = 1024; + } + + this.Size = Size; + + IdCounter = 1; + + Table = new KHandleEntry[Size]; + + TableHead = new KHandleEntry(0); + + KHandleEntry Entry = TableHead; + + for (int Index = 0; Index < Size; Index++) + { + Table[Index] = Entry; + + Entry.Next = new KHandleEntry(Index + 1); + + Entry = Entry.Next; + } + + Table[Size - 1].Next = null; + + NextFreeEntry = TableHead; + + return KernelResult.Success; } public KernelResult GenerateHandle(object Obj, out int Handle) { Handle = 0; - lock (LockObj) + lock (Table) { if (ActiveSlotsCount >= Size) { @@ -95,12 +134,12 @@ namespace Ryujinx.HLE.HOS.Kernel return false; } - int Index = (Handle >> 0) & 0x7fff; + int Index = (Handle >> 0) & 0x7fff; int HandleId = (Handle >> 15); bool Result = false; - lock (LockObj) + lock (Table) { if (HandleId != 0 && Index < Size) { @@ -125,10 +164,10 @@ namespace Ryujinx.HLE.HOS.Kernel public T GetObject(int Handle) { - int Index = (Handle >> 0) & 0x7fff; + int Index = (Handle >> 0) & 0x7fff; int HandleId = (Handle >> 15); - lock (LockObj) + lock (Table) { if ((Handle >> 30) == 0 && HandleId != 0) { @@ -158,7 +197,7 @@ namespace Ryujinx.HLE.HOS.Kernel public void Destroy() { - lock (LockObj) + lock (Table) { for (int Index = 0; Index < Size; Index++) { diff --git a/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs b/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs index d43fe8249e..bfb8e7e2db 100644 --- a/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs +++ b/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Kernel public override void Signal() { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); if (!Signaled) { @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Kernel base.Signal(); } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); } public KernelResult Clear() @@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Kernel { KernelResult Result; - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); if (Signaled) { @@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Kernel Result = KernelResult.InvalidState; } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return Result; } diff --git a/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs b/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs new file mode 100644 index 0000000000..0912acf608 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs @@ -0,0 +1,16 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + class KResourceLimit + { + public bool Reserve(LimitableResource Resource, long Amount) + { + //TODO. + return true; + } + + public void Release(LimitableResource Resource, long Amount) + { + //TODO. + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/KScheduler.cs index 3cfda4197f..fb83867958 100644 --- a/Ryujinx.HLE/HOS/Kernel/KScheduler.cs +++ b/Ryujinx.HLE/HOS/Kernel/KScheduler.cs @@ -38,14 +38,14 @@ namespace Ryujinx.HLE.HOS.Kernel private void PreemptThreads() { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); PreemptThread(PreemptionPriorityCores012, 0); PreemptThread(PreemptionPriorityCores012, 1); PreemptThread(PreemptionPriorityCores012, 2); PreemptThread(PreemptionPriorityCore3, 3); - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); } private void PreemptThread(int Prio, int Core) @@ -212,6 +212,11 @@ namespace Ryujinx.HLE.HOS.Kernel throw new InvalidOperationException("Current thread is not scheduled!"); } + public KProcess GetCurrentProcess() + { + return GetCurrentThread().Owner; + } + public void Dispose() { Dispose(true); diff --git a/Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs b/Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs new file mode 100644 index 0000000000..a728542960 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class KSlabHeap + { + private LinkedList Items; + + public KSlabHeap(long Pa, long ItemSize, long Size) + { + Items = new LinkedList(); + + int ItemsCount = (int)(Size / ItemSize); + + for (int Index = 0; Index < ItemsCount; Index++) + { + Items.AddLast(Pa); + + Pa += ItemSize; + } + } + + public bool TryGetItem(out long Pa) + { + lock (Items) + { + if (Items.First != null) + { + Pa = Items.First.Value; + + Items.RemoveFirst(); + + return true; + } + } + + Pa = 0; + + return false; + } + + public void Free(long Pa) + { + lock (Items) + { + Items.AddFirst(Pa); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs index 57a6296c2b..19e700f4c2 100644 --- a/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs +++ b/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel { long Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout); - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); //Check if objects are already signaled before waiting. for (int Index = 0; Index < SyncObjs.Length; Index++) @@ -29,14 +29,14 @@ namespace Ryujinx.HLE.HOS.Kernel HndIndex = Index; - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return 0; } if (Timeout == 0) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return Result; } @@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Kernel System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout); } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); CurrentThread.WaitingSync = false; @@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Kernel System.TimeManager.UnscheduleFutureInvocation(CurrentThread); } - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); Result = (uint)CurrentThread.ObjSyncResult; @@ -100,14 +100,14 @@ namespace Ryujinx.HLE.HOS.Kernel } } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return Result; } public void SignalObject(KSynchronizationObject SyncObj) { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); if (SyncObj.IsSignaled()) { @@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Kernel { KThread Thread = Node.Value; - if ((Thread.SchedFlags & ThreadSchedState.LowNibbleMask) == ThreadSchedState.Paused) + if ((Thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused) { Thread.SignaledObj = SyncObj; Thread.ObjSyncResult = 0; @@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Kernel } } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KThread.cs b/Ryujinx.HLE/HOS/Kernel/KThread.cs index 73ee2322d0..b55230ff28 100644 --- a/Ryujinx.HLE/HOS/Kernel/KThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/KThread.cs @@ -1,4 +1,5 @@ using ChocolArm64; +using ChocolArm64.Memory; using System; using System.Collections.Generic; using System.Linq; @@ -13,14 +14,19 @@ namespace Ryujinx.HLE.HOS.Kernel public long AffinityMask { get; set; } - public int ThreadId { get; private set; } + public long ThreadUid { get; private set; } public KSynchronizationObject SignaledObj; public long CondVarAddress { get; set; } - public long MutexAddress { get; set; } - public Process Owner { get; private set; } + private long Entrypoint; + + public long MutexAddress { get; set; } + + public KProcess Owner { get; private set; } + + private long TlsAddress; public long LastScheduledTicks { get; set; } @@ -28,6 +34,8 @@ namespace Ryujinx.HLE.HOS.Kernel private LinkedListNode WithholderNode; + public LinkedListNode ProcessListNode { get; set; } + private LinkedList MutexWaiters; private LinkedListNode MutexWaiterNode; @@ -65,38 +73,131 @@ namespace Ryujinx.HLE.HOS.Kernel public long LastPc { get; set; } - public KThread( - CpuThread Thread, - Process Process, - Horizon System, - int ProcessorId, - int Priority, - int ThreadId) : base(System) + public KThread(Horizon System) : base(System) { - this.ThreadId = ThreadId; - - Context = Thread; - Owner = Process; - PreferredCore = ProcessorId; Scheduler = System.Scheduler; SchedulingData = System.Scheduler.SchedulingData; SiblingsPerCore = new LinkedListNode[KScheduler.CpuCoresCount]; MutexWaiters = new LinkedList(); - - AffinityMask = 1 << ProcessorId; - - DynamicPriority = BasePriority = Priority; - - CurrentCore = PreferredCore; } - public long Start() + public KernelResult Initialize( + long Entrypoint, + long ArgsPtr, + long StackTop, + int Priority, + int DefaultCpuCore, + KProcess Owner, + ThreadType Type = ThreadType.User) { - long Result = MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating); + if ((uint)Type > 3) + { + throw new ArgumentException($"Invalid thread type \"{Type}\"."); + } - System.CriticalSectionLock.Lock(); + PreferredCore = DefaultCpuCore; + + AffinityMask |= 1L << DefaultCpuCore; + + SchedFlags = Type == ThreadType.Dummy + ? ThreadSchedState.Running + : ThreadSchedState.None; + + CurrentCore = PreferredCore; + + DynamicPriority = Priority; + BasePriority = Priority; + + ObjSyncResult = 0x7201; + + this.Entrypoint = Entrypoint; + + if (Type == ThreadType.User) + { + if (Owner.AllocateThreadLocalStorage(out TlsAddress) != KernelResult.Success) + { + return KernelResult.OutOfMemory; + } + + MemoryHelper.FillWithZeros(Owner.CpuMemory, TlsAddress, KTlsPageInfo.TlsEntrySize); + } + + bool Is64Bits; + + if (Owner != null) + { + this.Owner = Owner; + + Owner.IncrementThreadCount(); + + Is64Bits = (Owner.MmuFlags & 1) != 0; + } + else + { + Is64Bits = true; + } + + Context = new CpuThread(Owner.Translator, Owner.CpuMemory, Entrypoint); + + Context.ThreadState.X0 = (ulong)ArgsPtr; + Context.ThreadState.X31 = (ulong)StackTop; + + Context.ThreadState.CntfrqEl0 = 19200000; + Context.ThreadState.Tpidr = TlsAddress; + + Owner.SubscribeThreadEventHandlers(Context); + + Context.WorkFinished += ThreadFinishedHandler; + + ThreadUid = System.GetThreadUid(); + + if (Owner != null) + { + Owner.AddThread(this); + + if (Owner.IsPaused) + { + System.CriticalSection.Enter(); + + if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending) + { + System.CriticalSection.Leave(); + + return KernelResult.Success; + } + + ForcePauseFlags |= ThreadSchedState.ProcessPauseFlag; + + CombineForcePauseFlags(); + + System.CriticalSection.Leave(); + } + } + + return KernelResult.Success; + } + + public KernelResult Start() + { + if (!System.KernelInitialized) + { + System.CriticalSection.Enter(); + + if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending) + { + ForcePauseFlags |= ThreadSchedState.KernelInitPauseFlag; + + CombineForcePauseFlags(); + } + + System.CriticalSection.Leave(); + } + + KernelResult Result = KernelResult.ThreadTerminating; + + System.CriticalSection.Enter(); if (!ShallBeTerminated) { @@ -106,9 +207,9 @@ namespace Ryujinx.HLE.HOS.Kernel CurrentThread.SchedFlags != ThreadSchedState.TerminationPending && !CurrentThread.ShallBeTerminated) { - if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.None) + if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.None) { - Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidState); + Result = KernelResult.InvalidState; break; } @@ -130,8 +231,8 @@ namespace Ryujinx.HLE.HOS.Kernel { CurrentThread.CombineForcePauseFlags(); - System.CriticalSectionLock.Unlock(); - System.CriticalSectionLock.Lock(); + System.CriticalSection.Leave(); + System.CriticalSection.Enter(); if (CurrentThread.ShallBeTerminated) { @@ -141,25 +242,25 @@ namespace Ryujinx.HLE.HOS.Kernel } } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return Result; } public void Exit() { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); - ForcePauseFlags &= ~ThreadSchedState.ExceptionalMask; + ForcePauseFlags &= ~ThreadSchedState.ForcePauseMask; ExitImpl(); - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); } private void ExitImpl() { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); SetNewSchedFlags(ThreadSchedState.TerminationPending); @@ -167,16 +268,16 @@ namespace Ryujinx.HLE.HOS.Kernel Signal(); - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); } public long Sleep(long Timeout) { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating); } @@ -188,7 +289,7 @@ namespace Ryujinx.HLE.HOS.Kernel System.TimeManager.ScheduleFutureInvocation(this, Timeout); } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); if (Timeout > 0) { @@ -200,11 +301,11 @@ namespace Ryujinx.HLE.HOS.Kernel public void Yield() { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); if (SchedFlags != ThreadSchedState.Running) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); System.Scheduler.ContextSwitch(); @@ -219,27 +320,27 @@ namespace Ryujinx.HLE.HOS.Kernel Scheduler.ThreadReselectionRequested = true; - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); System.Scheduler.ContextSwitch(); } public void YieldWithLoadBalancing() { - System.CriticalSectionLock.Lock(); - - int Prio = DynamicPriority; - int Core = CurrentCore; + System.CriticalSection.Enter(); if (SchedFlags != ThreadSchedState.Running) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); System.Scheduler.ContextSwitch(); return; } + int Prio = DynamicPriority; + int Core = CurrentCore; + KThread NextThreadOnCurrentQueue = null; if (DynamicPriority < KScheduler.PrioritiesCount) @@ -292,18 +393,18 @@ namespace Ryujinx.HLE.HOS.Kernel Scheduler.ThreadReselectionRequested = true; } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); System.Scheduler.ContextSwitch(); } public void YieldAndWaitForLoadBalancing() { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); if (SchedFlags != ThreadSchedState.Running) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); System.Scheduler.ContextSwitch(); @@ -348,47 +449,47 @@ namespace Ryujinx.HLE.HOS.Kernel Scheduler.ThreadReselectionRequested = true; } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); System.Scheduler.ContextSwitch(); } public void SetPriority(int Priority) { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); BasePriority = Priority; UpdatePriorityInheritance(); - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); } public long SetActivity(bool Pause) { long Result = 0; - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); - ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask; + ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowMask; if (LowNibble != ThreadSchedState.Paused && LowNibble != ThreadSchedState.Running) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); } - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending) { if (Pause) { //Pause, the force pause flag should be clear (thread is NOT paused). - if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) == 0) + if ((ForcePauseFlags & ThreadSchedState.ThreadPauseFlag) == 0) { - ForcePauseFlags |= ThreadSchedState.ForcePauseFlag; + ForcePauseFlags |= ThreadSchedState.ThreadPauseFlag; CombineForcePauseFlags(); } @@ -400,17 +501,17 @@ namespace Ryujinx.HLE.HOS.Kernel else { //Unpause, the force pause flag should be set (thread is paused). - if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) != 0) + if ((ForcePauseFlags & ThreadSchedState.ThreadPauseFlag) != 0) { ThreadSchedState OldForcePauseFlags = ForcePauseFlags; - ForcePauseFlags &= ~ThreadSchedState.ForcePauseFlag; + ForcePauseFlags &= ~ThreadSchedState.ThreadPauseFlag; - if ((OldForcePauseFlags & ~ThreadSchedState.ForcePauseFlag) == ThreadSchedState.None) + if ((OldForcePauseFlags & ~ThreadSchedState.ThreadPauseFlag) == ThreadSchedState.None) { ThreadSchedState OldSchedFlags = SchedFlags; - SchedFlags &= ThreadSchedState.LowNibbleMask; + SchedFlags &= ThreadSchedState.LowMask; AdjustScheduling(OldSchedFlags); } @@ -422,17 +523,17 @@ namespace Ryujinx.HLE.HOS.Kernel } } - System.CriticalSectionLock.Unlock(); - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); + System.CriticalSection.Leave(); return Result; } public void CancelSynchronization() { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); - if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.Paused || !WaitingSync) + if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.Paused || !WaitingSync) { SyncCancelled = true; } @@ -456,12 +557,12 @@ namespace Ryujinx.HLE.HOS.Kernel SyncCancelled = false; } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); } public long SetCoreAndAffinityMask(int NewCore, long NewAffinityMask) { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); bool UseOverride = AffinityOverrideCount != 0; @@ -472,7 +573,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((NewAffinityMask & (1 << NewCore)) == 0) { - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue); } @@ -510,7 +611,7 @@ namespace Ryujinx.HLE.HOS.Kernel } } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); return 0; } @@ -531,7 +632,7 @@ namespace Ryujinx.HLE.HOS.Kernel private void CombineForcePauseFlags() { ThreadSchedState OldFlags = SchedFlags; - ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask; + ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowMask; SchedFlags = LowNibble | ForcePauseFlags; @@ -540,25 +641,25 @@ namespace Ryujinx.HLE.HOS.Kernel private void SetNewSchedFlags(ThreadSchedState NewFlags) { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); ThreadSchedState OldFlags = SchedFlags; - SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) | NewFlags; + SchedFlags = (OldFlags & ThreadSchedState.HighMask) | NewFlags; - if ((OldFlags & ThreadSchedState.LowNibbleMask) != NewFlags) + if ((OldFlags & ThreadSchedState.LowMask) != NewFlags) { AdjustScheduling(OldFlags); } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); } public void ReleaseAndResume() { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); - if ((SchedFlags & ThreadSchedState.LowNibbleMask) == ThreadSchedState.Paused) + if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused) { if (WithholderNode != null) { @@ -574,21 +675,21 @@ namespace Ryujinx.HLE.HOS.Kernel } } - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); } public void Reschedule(ThreadSchedState NewFlags) { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); ThreadSchedState OldFlags = SchedFlags; - SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) | - (NewFlags & ThreadSchedState.LowNibbleMask); + SchedFlags = (OldFlags & ThreadSchedState.HighMask) | + (NewFlags & ThreadSchedState.LowMask); AdjustScheduling(OldFlags); - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); } public void AddMutexWaiter(KThread Requester) @@ -866,18 +967,60 @@ namespace Ryujinx.HLE.HOS.Kernel return HasExited; } + public void SetEntryArguments(long ArgsPtr, int ThreadHandle) + { + Context.ThreadState.X0 = (ulong)ArgsPtr; + Context.ThreadState.X1 = (ulong)ThreadHandle; + } + public void ClearExclusive() { - Owner.Memory.ClearExclusive(CurrentCore); + Owner.CpuMemory.ClearExclusive(CurrentCore); } public void TimeUp() { - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); SetNewSchedFlags(ThreadSchedState.Running); - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); + } + + private void ThreadFinishedHandler(object sender, EventArgs e) + { + System.Scheduler.ExitThread(this); + + Terminate(); + + System.Scheduler.RemoveThread(this); + } + + public void Terminate() + { + Owner?.RemoveThread(this); + + if (TlsAddress != 0 && Owner.FreeThreadLocalStorage(TlsAddress) != KernelResult.Success) + { + throw new InvalidOperationException("Unexpected failure freeing thread local storage."); + } + + System.CriticalSection.Enter(); + + //Wake up all threads that may be waiting for a mutex being held + //by this thread. + foreach (KThread Thread in MutexWaiters) + { + Thread.MutexOwner = null; + Thread.PreferredCoreOverride = 0; + Thread.ObjSyncResult = 0xfa01; + + Thread.ReleaseAndResume(); + } + + System.CriticalSection.Leave(); + + Owner?.DecrementThreadCountAndTerminateIfZero(); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs b/Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs new file mode 100644 index 0000000000..7f88c757c7 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs @@ -0,0 +1,73 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + class KTlsPageInfo + { + public const int TlsEntrySize = 0x200; + + public long PageAddr { get; private set; } + + private bool[] IsSlotFree; + + public KTlsPageInfo(long PageAddress) + { + this.PageAddr = PageAddress; + + IsSlotFree = new bool[KMemoryManager.PageSize / TlsEntrySize]; + + for (int Index = 0; Index < IsSlotFree.Length; Index++) + { + IsSlotFree[Index] = true; + } + } + + public bool TryGetFreePage(out long Address) + { + Address = PageAddr; + + for (int Index = 0; Index < IsSlotFree.Length; Index++) + { + if (IsSlotFree[Index]) + { + IsSlotFree[Index] = false; + + return true; + } + + Address += TlsEntrySize; + } + + Address = 0; + + return false; + } + + public bool IsFull() + { + bool HasFree = false; + + for (int Index = 0; Index < IsSlotFree.Length; Index++) + { + HasFree |= IsSlotFree[Index]; + } + + return !HasFree; + } + + public bool IsEmpty() + { + bool AllFree = true; + + for (int Index = 0; Index < IsSlotFree.Length; Index++) + { + AllFree &= IsSlotFree[Index]; + } + + return AllFree; + } + + public void FreeTlsSlot(long Address) + { + IsSlotFree[(Address - PageAddr) / TlsEntrySize] = true; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KernelInit.cs b/Ryujinx.HLE/HOS/Kernel/KernelInit.cs new file mode 100644 index 0000000000..4e933f50b1 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KernelInit.cs @@ -0,0 +1,107 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + static class KernelInit + { + public static KMemoryRegionManager[] GetMemoryRegions() + { + KMemoryArrange Arrange = GetMemoryArrange(); + + return new KMemoryRegionManager[] + { + GetMemoryRegion(Arrange.Application), + GetMemoryRegion(Arrange.Applet), + GetMemoryRegion(Arrange.Service), + GetMemoryRegion(Arrange.NvServices) + }; + } + + private static KMemoryRegionManager GetMemoryRegion(KMemoryArrangeRegion Region) + { + return new KMemoryRegionManager( + Region.Address, + Region.Size, + Region.EndAddr); + } + + private static KMemoryArrange GetMemoryArrange() + { + int McEmemCfg = 0x1000; + + ulong EmemApertureSize = (ulong)(McEmemCfg & 0x3fff) << 20; + + int KernelMemoryCfg = 0; + + ulong RamSize; + + switch ((KernelMemoryCfg >> 16) & 3) + { + case 1: RamSize = 0x180000000; break; + case 2: RamSize = 0x200000000; break; + default: RamSize = 0x100000000; break; + } + + long RamPart0; + long RamPart1; + + if (RamSize * 2 > EmemApertureSize) + { + RamPart0 = (long)(EmemApertureSize / 2); + RamPart1 = (long)(EmemApertureSize / 2); + } + else + { + RamPart0 = (long)EmemApertureSize; + RamPart1 = 0; + } + + int MemoryArrange = 1; + + long ApplicationRgSize; + + switch (MemoryArrange) + { + case 2: ApplicationRgSize = 0x80000000; break; + case 0x11: + case 0x21: ApplicationRgSize = 0x133400000; break; + default: ApplicationRgSize = 0xcd500000; break; + } + + long AppletRgSize; + + switch (MemoryArrange) + { + case 2: AppletRgSize = 0x61200000; break; + case 3: AppletRgSize = 0x1c000000; break; + case 0x11: AppletRgSize = 0x23200000; break; + case 0x12: + case 0x21: AppletRgSize = 0x89100000; break; + default: AppletRgSize = 0x1fb00000; break; + } + + KMemoryArrangeRegion ServiceRg; + KMemoryArrangeRegion NvServicesRg; + KMemoryArrangeRegion AppletRg; + KMemoryArrangeRegion ApplicationRg; + + const long NvServicesRgSize = 0x29ba000; + + long ApplicationRgEnd = DramMemoryMap.DramEnd; //- RamPart0; + + ApplicationRg = new KMemoryArrangeRegion(ApplicationRgEnd - ApplicationRgSize, ApplicationRgSize); + + long NvServicesRgEnd = ApplicationRg.Address - AppletRgSize; + + NvServicesRg = new KMemoryArrangeRegion(NvServicesRgEnd - NvServicesRgSize, NvServicesRgSize); + AppletRg = new KMemoryArrangeRegion(NvServicesRgEnd, AppletRgSize); + + //Note: There is an extra region used by the kernel, however + //since we are doing HLE we are not going to use that memory, so give all + //the remaining memory space to services. + long ServiceRgSize = NvServicesRg.Address - DramMemoryMap.KernelReserveBase; + + ServiceRg = new KMemoryArrangeRegion(DramMemoryMap.KernelReserveBase, ServiceRgSize); + + return new KMemoryArrange(ServiceRg, NvServicesRg, AppletRg, ApplicationRg); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KernelResult.cs b/Ryujinx.HLE/HOS/Kernel/KernelResult.cs index d9cbfc6735..f33bf646a6 100644 --- a/Ryujinx.HLE/HOS/Kernel/KernelResult.cs +++ b/Ryujinx.HLE/HOS/Kernel/KernelResult.cs @@ -2,9 +2,25 @@ namespace Ryujinx.HLE.HOS.Kernel { enum KernelResult { - Success = 0, - HandleTableFull = 0xd201, - InvalidHandle = 0xe401, - InvalidState = 0xfa01 + Success = 0, + InvalidCapability = 0x1c01, + ThreadTerminating = 0x7601, + InvalidSize = 0xca01, + InvalidAddress = 0xcc01, + OutOfMemory = 0xd001, + HandleTableFull = 0xd201, + InvalidMemState = 0xd401, + InvalidMemRange = 0xdc01, + InvalidPriority = 0xe001, + InvalidCpuCore = 0xe201, + InvalidHandle = 0xe401, + InvalidCombination = 0xe801, + TimedOut = 0xea01, + Cancelled = 0xec01, + MaximumExceeded = 0xee01, + InvalidThread = 0xf401, + InvalidState = 0xfa01, + ReservedValue = 0xfc01, + ResLimitExceeded = 0x10801 } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs b/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs new file mode 100644 index 0000000000..4055148361 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + enum LimitableResource : byte + { + Memory, + Thread, + Event, + TransferMemory, + Session + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs b/Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs new file mode 100644 index 0000000000..b93501210d --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + enum MemoryOperation + { + MapPa, + MapVa, + Allocate, + Unmap, + ChangePermRw, + ChangePermsAndAttributes + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs b/Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs new file mode 100644 index 0000000000..ea4f33c9ce --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + enum MemoryRegion + { + Application = 0, + Applet = 1, + Service = 2, + NvServices = 3 + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/MemoryState.cs b/Ryujinx.HLE/HOS/Kernel/MemoryState.cs index 2c37723c18..e2ce27ef2f 100644 --- a/Ryujinx.HLE/HOS/Kernel/MemoryState.cs +++ b/Ryujinx.HLE/HOS/Kernel/MemoryState.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel ModCodeStatic = 0x00DD7E08, ModCodeMutable = 0x03FFBD09, IpcBuffer0 = 0x005C3C0A, - MappedMemory = 0x005C3C0B, + Stack = 0x005C3C0B, ThreadLocal = 0x0040200C, TransferMemoryIsolated = 0x015C3C0D, TransferMemory = 0x005C380E, diff --git a/Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs b/Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs new file mode 100644 index 0000000000..b90d54d264 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs @@ -0,0 +1,128 @@ +using Ryujinx.Common; + +namespace Ryujinx.HLE.HOS.Kernel +{ + class MersenneTwister + { + private int Index; + private uint[] Mt; + + public MersenneTwister(uint Seed) + { + Mt = new uint[624]; + + Mt[0] = Seed; + + for (int MtIdx = 1; MtIdx < Mt.Length; MtIdx++) + { + uint Prev = Mt[MtIdx - 1]; + + Mt[MtIdx] = (uint)(0x6c078965 * (Prev ^ (Prev >> 30)) + MtIdx); + } + + Index = Mt.Length; + } + + public long GenRandomNumber(long Min, long Max) + { + long Range = Max - Min; + + if (Min == Max) + { + return Min; + } + + if (Range == -1) + { + //Increment would cause a overflow, special case. + return GenRandomNumber(2, 2, 32, 0xffffffffu, 0xffffffffu); + } + + Range++; + + //This is log2(Range) plus one. + int NextRangeLog2 = 64 - BitUtils.CountLeadingZeros64(Range); + + //If Range is already power of 2, subtract one to use log2(Range) directly. + int RangeLog2 = NextRangeLog2 - (BitUtils.IsPowerOfTwo64(Range) ? 1 : 0); + + int Parts = RangeLog2 > 32 ? 2 : 1; + int BitsPerPart = RangeLog2 / Parts; + + int FullParts = Parts - (RangeLog2 - Parts * BitsPerPart); + + uint Mask = 0xffffffffu >> (32 - BitsPerPart); + uint MaskPlus1 = 0xffffffffu >> (31 - BitsPerPart); + + long RandomNumber; + + do + { + RandomNumber = GenRandomNumber(Parts, FullParts, BitsPerPart, Mask, MaskPlus1); + } + while ((ulong)RandomNumber >= (ulong)Range); + + return Min + RandomNumber; + } + + private long GenRandomNumber( + int Parts, + int FullParts, + int BitsPerPart, + uint Mask, + uint MaskPlus1) + { + long RandomNumber = 0; + + int Part = 0; + + for (; Part < FullParts; Part++) + { + RandomNumber <<= BitsPerPart; + RandomNumber |= GenRandomNumber() & Mask; + } + + for (; Part < Parts; Part++) + { + RandomNumber <<= BitsPerPart + 1; + RandomNumber |= GenRandomNumber() & MaskPlus1; + } + + return RandomNumber; + } + + private uint GenRandomNumber() + { + if (Index >= Mt.Length) + { + Twist(); + } + + uint Value = Mt[Index++]; + + Value ^= Value >> 11; + Value ^= (Value << 7) & 0x9d2c5680; + Value ^= (Value << 15) & 0xefc60000; + Value ^= Value >> 18; + + return Value; + } + + private void Twist() + { + for (int MtIdx = 0; MtIdx < Mt.Length; MtIdx++) + { + uint Value = (Mt[MtIdx] & 0x80000000) + (Mt[(MtIdx + 1) % Mt.Length] & 0x7fffffff); + + Mt[MtIdx] = Mt[(MtIdx + 397) % Mt.Length] ^ (Value >> 1); + + if ((Value & 1) != 0) + { + Mt[MtIdx] ^= 0x9908b0df; + } + } + + Index = 0; + } + } +} diff --git a/Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs b/Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs new file mode 100644 index 0000000000..d3511b8202 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs @@ -0,0 +1,37 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + struct ProcessCreationInfo + { + public string Name { get; private set; } + + public int Category { get; private set; } + public long TitleId { get; private set; } + + public long CodeAddress { get; private set; } + public int CodePagesCount { get; private set; } + + public int MmuFlags { get; private set; } + public int ResourceLimitHandle { get; private set; } + public int PersonalMmHeapPagesCount { get; private set; } + + public ProcessCreationInfo( + string Name, + int Category, + long TitleId, + long CodeAddress, + int CodePagesCount, + int MmuFlags, + int ResourceLimitHandle, + int PersonalMmHeapPagesCount) + { + this.Name = Name; + this.Category = Category; + this.TitleId = TitleId; + this.CodeAddress = CodeAddress; + this.CodePagesCount = CodePagesCount; + this.MmuFlags = MmuFlags; + this.ResourceLimitHandle = ResourceLimitHandle; + this.PersonalMmHeapPagesCount = PersonalMmHeapPagesCount; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/ProcessState.cs b/Ryujinx.HLE/HOS/Kernel/ProcessState.cs new file mode 100644 index 0000000000..98ff4207e7 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/ProcessState.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + enum ProcessState : byte + { + Created = 0, + CreatedAttached = 1, + Started = 2, + Crashed = 3, + Attached = 4, + Exiting = 5, + Exited = 6, + DebugSuspended = 7 + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs index 9b475d4ebf..c9c0ded90f 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs @@ -1,8 +1,8 @@ using ChocolArm64.Events; using ChocolArm64.Memory; using ChocolArm64.State; -using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.Common.Logging; using System; using System.Collections.Generic; @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel private Dictionary SvcFuncs; private Switch Device; - private Process Process; + private KProcess Process; private Horizon System; private MemoryManager Memory; @@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Kernel private static Random Rng; - public SvcHandler(Switch Device, Process Process) + public SvcHandler(Switch Device, KProcess Process) { SvcFuncs = new Dictionary() { @@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Kernel { 0x05, SvcUnmapMemory }, { 0x06, SvcQueryMemory }, { 0x07, SvcExitProcess }, - { 0x08, SvcCreateThread }, + { 0x08, CreateThread64 }, { 0x09, SvcStartThread }, { 0x0a, SvcExitThread }, { 0x0b, SvcSleepThread }, @@ -92,8 +92,8 @@ namespace Ryujinx.HLE.HOS.Kernel this.Device = Device; this.Process = Process; - this.System = Process.Device.System; - this.Memory = Process.Memory; + this.System = Device.System; + this.Memory = Process.CpuMemory; } static SvcHandler() @@ -105,7 +105,7 @@ namespace Ryujinx.HLE.HOS.Kernel { CpuThreadState ThreadState = (CpuThreadState)sender; - Process.GetThread(ThreadState.Tpidr).LastPc = e.Position; + //Process.GetThread(ThreadState.Tpidr).LastPc = e.Position; if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func)) { @@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Kernel } else { - Process.PrintStackTrace(ThreadState); + //Process.PrintStackTrace(ThreadState); throw new NotImplementedException($"0x{e.Id:x4}"); } diff --git a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs index 560ad4b3f7..5b5f3b8ed5 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs @@ -20,17 +20,17 @@ namespace Ryujinx.HLE.HOS.Kernel return; } - long Result = Process.MemoryManager.TrySetHeapSize((long)Size, out long Position); + KernelResult Result = Process.MemoryManager.SetHeapSize((long)Size, out long Position); ThreadState.X0 = (ulong)Result; - if (Result == 0) + if (Result == KernelResult.Success) { ThreadState.X1 = (ulong)Position; } else { - Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\"."); } } @@ -552,8 +552,8 @@ namespace Ryujinx.HLE.HOS.Kernel ulong Start = (ulong)Position; ulong End = (ulong)Size + Start; - return Start >= (ulong)Process.MemoryManager.MapRegionStart && - End < (ulong)Process.MemoryManager.MapRegionEnd; + return Start >= (ulong)Process.MemoryManager.AliasRegionStart && + End < (ulong)Process.MemoryManager.AliasRegionEnd; } private bool InsideHeapRegion(long Position, long Size) @@ -570,8 +570,8 @@ namespace Ryujinx.HLE.HOS.Kernel ulong Start = (ulong)Position; ulong End = (ulong)Size + Start; - return Start >= (ulong)Process.MemoryManager.NewMapRegionStart && - End < (ulong)Process.MemoryManager.NewMapRegionEnd; + return Start >= (ulong)Process.MemoryManager.StackRegionStart && + End < (ulong)Process.MemoryManager.StackRegionEnd; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs index 54aef5d708..5964fd28d7 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs @@ -1,9 +1,9 @@ using ChocolArm64.Memory; using ChocolArm64.State; -using Ryujinx.Common.Logging; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Services; +using Ryujinx.Common.Logging; using System; using System.Threading; @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Kernel private void SvcExitProcess(CpuThreadState ThreadState) { - Device.System.ExitProcess(Process.ProcessId); + System.Scheduler.GetCurrentProcess().Terminate(); } private void SignalEvent64(CpuThreadState ThreadState) @@ -187,17 +187,13 @@ namespace Ryujinx.HLE.HOS.Kernel private void SendSyncRequest(CpuThreadState ThreadState, long MessagePtr, long Size, int Handle) { - KThread CurrThread = Process.GetThread(ThreadState.Tpidr); - byte[] MessageData = Memory.ReadBytes(MessagePtr, Size); KSession Session = Process.HandleTable.GetObject(Handle); if (Session != null) { - //Process.Scheduler.Suspend(CurrThread); - - System.CriticalSectionLock.Lock(); + System.CriticalSection.Enter(); KThread CurrentThread = System.Scheduler.GetCurrentThread(); @@ -214,7 +210,7 @@ namespace Ryujinx.HLE.HOS.Kernel Message, MessagePtr)); - System.CriticalSectionLock.Unlock(); + System.CriticalSection.Leave(); ThreadState.X0 = (ulong)CurrentThread.ObjSyncResult; } @@ -247,17 +243,9 @@ namespace Ryujinx.HLE.HOS.Kernel long Unknown = (long)ThreadState.X1; long Info = (long)ThreadState.X2; - if ((Reason & (1 << 31)) == 0) - { - Process.PrintStackTrace(ThreadState); + //Process.PrintStackTrace(ThreadState); - throw new GuestBrokeExecutionException(); - } - else - { - Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered"); - Process.PrintStackTrace(ThreadState); - } + throw new GuestBrokeExecutionException(); } private void SvcOutputDebugString(CpuThreadState ThreadState) @@ -298,12 +286,12 @@ namespace Ryujinx.HLE.HOS.Kernel break; case 2: - ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionStart; + ThreadState.X1 = (ulong)Process.MemoryManager.AliasRegionStart; break; case 3: - ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionEnd - - (ulong)Process.MemoryManager.MapRegionStart; + ThreadState.X1 = (ulong)Process.MemoryManager.AliasRegionEnd - + (ulong)Process.MemoryManager.AliasRegionStart; break; case 4: @@ -316,11 +304,11 @@ namespace Ryujinx.HLE.HOS.Kernel break; case 6: - ThreadState.X1 = (ulong)Process.Device.Memory.Allocator.TotalAvailableSize; + ThreadState.X1 = (ulong)Process.GetMemoryCapacity(); break; case 7: - ThreadState.X1 = (ulong)Process.Device.Memory.Allocator.TotalUsedSize; + ThreadState.X1 = (ulong)Process.GetMemoryUsage(); break; case 8: @@ -341,16 +329,16 @@ namespace Ryujinx.HLE.HOS.Kernel break; case 14: - ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionStart; + ThreadState.X1 = (ulong)Process.MemoryManager.StackRegionStart; break; case 15: - ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionEnd - - (ulong)Process.MemoryManager.NewMapRegionStart; + ThreadState.X1 = (ulong)Process.MemoryManager.StackRegionEnd - + (ulong)Process.MemoryManager.StackRegionStart; break; case 16: - ThreadState.X1 = (ulong)(Process.MetaData?.SystemResourceSize ?? 0); + //ThreadState.X1 = (ulong)(Process.MetaData?.SystemResourceSize ?? 0); break; case 17: @@ -358,7 +346,7 @@ namespace Ryujinx.HLE.HOS.Kernel break; default: - Process.PrintStackTrace(ThreadState); + //Process.PrintStackTrace(ThreadState); throw new NotImplementedException($"SvcGetInfo: {InfoType} 0x{Handle:x8} {InfoId}"); } diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs index 53a557de5e..b40d252a0d 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs @@ -7,48 +7,82 @@ namespace Ryujinx.HLE.HOS.Kernel { partial class SvcHandler { - private void SvcCreateThread(CpuThreadState ThreadState) + private void CreateThread64(CpuThreadState ThreadState) { - long EntryPoint = (long)ThreadState.X1; - long ArgsPtr = (long)ThreadState.X2; - long StackTop = (long)ThreadState.X3; - int Priority = (int)ThreadState.X4; - int ProcessorId = (int)ThreadState.X5; + long Entrypoint = (long)ThreadState.X1; + long ArgsPtr = (long)ThreadState.X2; + long StackTop = (long)ThreadState.X3; + int Priority = (int)ThreadState.X4; + int CpuCore = (int)ThreadState.X5; - if ((uint)Priority > 0x3f) - { - Logger.PrintWarning(LogClass.KernelSvc, $"Invalid priority 0x{Priority:x8}!"); + KernelResult Result = CreateThread(Entrypoint, ArgsPtr, StackTop, Priority, CpuCore, out int Handle); - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPriority); - - return; - } - - if (ProcessorId == -2) - { - //TODO: Get this value from the NPDM file. - ProcessorId = 0; - } - else if ((uint)ProcessorId > 3) - { - Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{ProcessorId:x8}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId); - - return; - } - - int Handle = Process.MakeThread( - EntryPoint, - StackTop, - ArgsPtr, - Priority, - ProcessorId); - - ThreadState.X0 = 0; + ThreadState.X0 = (ulong)Result; ThreadState.X1 = (ulong)Handle; } + private KernelResult CreateThread( + long Entrypoint, + long ArgsPtr, + long StackTop, + int Priority, + int CpuCore, + out int Handle) + { + Handle = 0; + + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + + if (CpuCore == -2) + { + CpuCore = CurrentProcess.DefaultCpuCore; + } + + if ((uint)CpuCore >= KScheduler.CpuCoresCount || !CurrentProcess.IsAllowedCpuCore(CpuCore)) + { + return KernelResult.InvalidCpuCore; + } + + if ((uint)Priority >= KScheduler.PrioritiesCount || !CurrentProcess.IsAllowedPriority(Priority)) + { + return KernelResult.InvalidPriority; + } + + if (CurrentProcess.ResourceLimit != null && + !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Thread, 1)) + { + return KernelResult.ResLimitExceeded; + } + + KThread Thread = new KThread(System); + + KernelResult Result = CurrentProcess.InitializeThread( + Thread, + Entrypoint, + ArgsPtr, + StackTop, + Priority, + CpuCore); + + if (Result != KernelResult.Success) + { + CurrentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1); + + return Result; + } + + Result = Process.HandleTable.GenerateHandle(Thread, out Handle); + + if (Result != KernelResult.Success) + { + Thread.Terminate(); + + CurrentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1); + } + + return Result; + } + private void SvcStartThread(CpuThreadState ThreadState) { int Handle = (int)ThreadState.X0; @@ -57,11 +91,11 @@ namespace Ryujinx.HLE.HOS.Kernel if (Thread != null) { - long Result = Thread.Start(); + KernelResult Result = Thread.Start(); - if (Result != 0) + if (Result != KernelResult.Success) { - Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\"."); } ThreadState.X0 = (ulong)Result; @@ -78,9 +112,9 @@ namespace Ryujinx.HLE.HOS.Kernel { KThread CurrentThread = System.Scheduler.GetCurrentThread(); - CurrentThread.Exit(); + System.Scheduler.ExitThread(CurrentThread); - System.Scheduler.StopThread(CurrentThread); + CurrentThread.Exit(); } private void SvcSleepThread(CpuThreadState ThreadState) @@ -242,7 +276,7 @@ namespace Ryujinx.HLE.HOS.Kernel private void SvcGetCurrentProcessorNumber(CpuThreadState ThreadState) { - ThreadState.X0 = (ulong)Process.GetThread(ThreadState.Tpidr).CurrentCore; + ThreadState.X0 = (ulong)System.Scheduler.GetCurrentThread().CurrentCore; } private void SvcGetThreadId(CpuThreadState ThreadState) @@ -254,7 +288,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Thread != null) { ThreadState.X0 = 0; - ThreadState.X1 = (ulong)Thread.ThreadId; + ThreadState.X1 = (ulong)Thread.ThreadUid; } else { @@ -280,15 +314,24 @@ namespace Ryujinx.HLE.HOS.Kernel return; } - if (Thread.Owner != Process) + if (Thread.Owner != System.Scheduler.GetCurrentProcess()) { - Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread owner process!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread, it belongs to another process."); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); return; } + if (Thread == System.Scheduler.GetCurrentThread()) + { + Logger.PrintWarning(LogClass.KernelSvc, "Invalid thread, current thread is not accepted."); + + ThreadState.X0 = (ulong)KernelResult.InvalidThread; + + return; + } + long Result = Thread.SetActivity(Pause); if (Result != 0) @@ -304,6 +347,9 @@ namespace Ryujinx.HLE.HOS.Kernel long Position = (long)ThreadState.X0; int Handle = (int)ThreadState.X1; + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + KThread CurrentThread = System.Scheduler.GetCurrentThread(); + KThread Thread = Process.HandleTable.GetObject(Handle); if (Thread == null) @@ -315,9 +361,18 @@ namespace Ryujinx.HLE.HOS.Kernel return; } - if (Process.GetThread(ThreadState.Tpidr) == Thread) + if (Thread.Owner != CurrentProcess) { - Logger.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is current thread!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread, it belongs to another process."); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + + return; + } + + if (CurrentThread == Thread) + { + Logger.PrintWarning(LogClass.KernelSvc, "Invalid thread, current thread is not accepted."); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidThread); diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs index 318bd290f9..f444d1e749 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs @@ -116,12 +116,9 @@ namespace Ryujinx.HLE.HOS.Kernel return; } - long Result = System.AddressArbiter.ArbitrateLock( - Process, - Memory, - OwnerHandle, - MutexAddress, - RequesterHandle); + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + + long Result = CurrentProcess.AddressArbiter.ArbitrateLock(OwnerHandle, MutexAddress, RequesterHandle); if (Result != 0) { @@ -155,7 +152,9 @@ namespace Ryujinx.HLE.HOS.Kernel return; } - long Result = System.AddressArbiter.ArbitrateUnlock(Memory, MutexAddress); + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + + long Result = CurrentProcess.AddressArbiter.ArbitrateUnlock(MutexAddress); if (Result != 0) { @@ -196,8 +195,9 @@ namespace Ryujinx.HLE.HOS.Kernel return; } - long Result = System.AddressArbiter.WaitProcessWideKeyAtomic( - Memory, + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + + long Result = CurrentProcess.AddressArbiter.WaitProcessWideKeyAtomic( MutexAddress, CondVarAddress, ThreadHandle, @@ -227,7 +227,9 @@ namespace Ryujinx.HLE.HOS.Kernel "Address = 0x" + Address.ToString("x16") + ", " + "Count = 0x" + Count .ToString("x8")); - System.AddressArbiter.SignalProcessWideKey(Process, Memory, Address, Count); + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + + CurrentProcess.AddressArbiter.SignalProcessWideKey(Address, Count); ThreadState.X0 = 0; } @@ -263,20 +265,22 @@ namespace Ryujinx.HLE.HOS.Kernel return; } + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + long Result; switch (Type) { case ArbitrationType.WaitIfLessThan: - Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, false, Timeout); + Result = CurrentProcess.AddressArbiter.WaitForAddressIfLessThan(Address, Value, false, Timeout); break; case ArbitrationType.DecrementAndWaitIfLessThan: - Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, true, Timeout); + Result = CurrentProcess.AddressArbiter.WaitForAddressIfLessThan(Address, Value, true, Timeout); break; case ArbitrationType.WaitIfEqual: - Result = System.AddressArbiter.WaitForAddressIfEqual(Memory, Address, Value, Timeout); + Result = CurrentProcess.AddressArbiter.WaitForAddressIfEqual(Address, Value, Timeout); break; default: @@ -323,20 +327,22 @@ namespace Ryujinx.HLE.HOS.Kernel return; } + KProcess CurrentProcess = System.Scheduler.GetCurrentProcess(); + long Result; switch (Type) { case SignalType.Signal: - Result = System.AddressArbiter.Signal(Address, Count); + Result = CurrentProcess.AddressArbiter.Signal(Address, Count); break; case SignalType.SignalAndIncrementIfEqual: - Result = System.AddressArbiter.SignalAndIncrementIfEqual(Memory, Address, Value, Count); + Result = CurrentProcess.AddressArbiter.SignalAndIncrementIfEqual(Address, Value, Count); break; case SignalType.SignalAndModifyIfEqual: - Result = System.AddressArbiter.SignalAndModifyIfEqual(Memory, Address, Value, Count); + Result = CurrentProcess.AddressArbiter.SignalAndModifyIfEqual(Address, Value, Count); break; default: diff --git a/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs b/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs index 603446f3cf..37e5908a9c 100644 --- a/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs +++ b/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs @@ -1,11 +1,15 @@ namespace Ryujinx.HLE.HOS.Kernel { - enum ThreadSchedState : byte + enum ThreadSchedState : ushort { - LowNibbleMask = 0xf, - HighNibbleMask = 0xf0, - ExceptionalMask = 0x70, - ForcePauseFlag = 0x20, + LowMask = 0xf, + HighMask = 0xfff0, + ForcePauseMask = 0x70, + + ProcessPauseFlag = 1 << 4, + ThreadPauseFlag = 1 << 5, + ProcessDebugPauseFlag = 1 << 6, + KernelInitPauseFlag = 1 << 8, None = 0, Paused = 1, diff --git a/Ryujinx.HLE/HOS/Kernel/ThreadType.cs b/Ryujinx.HLE/HOS/Kernel/ThreadType.cs new file mode 100644 index 0000000000..0fe83423cf --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/ThreadType.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + enum ThreadType + { + Dummy, + Kernel, + Kernel2, + User + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Process.cs b/Ryujinx.HLE/HOS/Process.cs deleted file mode 100644 index 93b2d68d0c..0000000000 --- a/Ryujinx.HLE/HOS/Process.cs +++ /dev/null @@ -1,528 +0,0 @@ -using ChocolArm64; -using ChocolArm64.Events; -using ChocolArm64.Memory; -using ChocolArm64.State; -using LibHac; -using Ryujinx.Common.Logging; -using Ryujinx.HLE.Exceptions; -using Ryujinx.HLE.HOS.Diagnostics.Demangler; -using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.HOS.Services.Nv; -using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.HLE.Loaders; -using Ryujinx.HLE.Loaders.Executables; -using Ryujinx.HLE.Loaders.Npdm; -using Ryujinx.HLE.Utilities; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace Ryujinx.HLE.HOS -{ - class Process : IDisposable - { - private const int TickFreq = 19_200_000; - - public Switch Device { get; private set; } - - public bool NeedsHbAbi { get; private set; } - - public long HbAbiDataPosition { get; private set; } - - public int ProcessId { get; private set; } - - private Translator Translator; - - public MemoryManager Memory { get; private set; } - - public KMemoryManager MemoryManager { get; private set; } - - private List TlsPages; - - public Npdm MetaData { get; private set; } - - public Nacp ControlData { get; set; } - - public KProcessHandleTable HandleTable { get; private set; } - - public AppletStateMgr AppletState { get; private set; } - - private SvcHandler SvcHandler; - - private ConcurrentDictionary Threads; - - private List Executables; - - private long ImageBase; - - private bool Disposed; - - public Process(Switch Device, int ProcessId, Npdm MetaData) - { - this.Device = Device; - this.MetaData = MetaData; - this.ProcessId = ProcessId; - - Memory = new MemoryManager(Device.Memory.RamPointer); - - Memory.InvalidAccess += CpuInvalidAccessHandler; - - MemoryManager = new KMemoryManager(this); - - TlsPages = new List(); - - int HandleTableSize = 1024; - - if (MetaData != null) - { - foreach (KernelAccessControlItem Item in MetaData.ACI0.KernelAccessControl.Items) - { - if (Item.HasHandleTableSize) - { - HandleTableSize = Item.HandleTableSize; - - break; - } - } - } - - HandleTable = new KProcessHandleTable(Device.System, HandleTableSize); - - AppletState = new AppletStateMgr(Device.System); - - SvcHandler = new SvcHandler(Device, this); - - Threads = new ConcurrentDictionary(); - - Executables = new List(); - - ImageBase = MemoryManager.CodeRegionStart; - } - - public void LoadProgram(IExecutable Program) - { - if (Disposed) - { - throw new ObjectDisposedException(nameof(Process)); - } - - long ImageEnd = LoadProgram(Program, ImageBase); - - ImageBase = IntUtils.AlignUp(ImageEnd, KMemoryManager.PageSize); - } - - public long LoadProgram(IExecutable Program, long ExecutableBase) - { - if (Disposed) - { - throw new ObjectDisposedException(nameof(Process)); - } - - Logger.PrintInfo(LogClass.Loader, $"Image base at 0x{ExecutableBase:x16}."); - - Executable Executable = new Executable(Program, MemoryManager, Memory, ExecutableBase); - - Executables.Add(Executable); - - return Executable.ImageEnd; - } - - public void RemoveProgram(long ExecutableBase) - { - foreach (Executable Executable in Executables) - { - if (Executable.ImageBase == ExecutableBase) - { - Executables.Remove(Executable); - break; - } - } - } - - public void SetEmptyArgs() - { - //TODO: This should be part of Run. - ImageBase += KMemoryManager.PageSize; - } - - public bool Run(bool NeedsHbAbi = false) - { - if (Disposed) - { - throw new ObjectDisposedException(nameof(Process)); - } - - this.NeedsHbAbi = NeedsHbAbi; - - if (Executables.Count == 0) - { - return false; - } - - long MainStackTop = MemoryManager.CodeRegionEnd - KMemoryManager.PageSize; - - long MainStackSize = 1 * 1024 * 1024; - - long MainStackBottom = MainStackTop - MainStackSize; - - MemoryManager.HleMapCustom( - MainStackBottom, - MainStackSize, - MemoryState.MappedMemory, - MemoryPermission.ReadAndWrite); - - int Handle = MakeThread(Executables[0].ImageBase, MainStackTop, 0, 44, 0); - - if (Handle == -1) - { - return false; - } - - KThread MainThread = HandleTable.GetKThread(Handle); - - if (NeedsHbAbi) - { - HbAbiDataPosition = IntUtils.AlignUp(Executables[0].ImageEnd, KMemoryManager.PageSize); - - const long HbAbiDataSize = KMemoryManager.PageSize; - - MemoryManager.HleMapCustom( - HbAbiDataPosition, - HbAbiDataSize, - MemoryState.MappedMemory, - MemoryPermission.ReadAndWrite); - - string SwitchPath = Device.FileSystem.SystemPathToSwitchPath(Executables[0].FilePath); - - Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle, SwitchPath); - - MainThread.Context.ThreadState.X0 = (ulong)HbAbiDataPosition; - MainThread.Context.ThreadState.X1 = ulong.MaxValue; - } - - MainThread.TimeUp(); - - return true; - } - - private int ThreadIdCtr = 1; - - public int MakeThread( - long EntryPoint, - long StackTop, - long ArgsPtr, - int Priority, - int ProcessorId) - { - if (Disposed) - { - throw new ObjectDisposedException(nameof(Process)); - } - - CpuThread CpuThread = new CpuThread(GetTranslator(), Memory, EntryPoint); - - long Tpidr = GetFreeTls(); - - int ThreadId = ThreadIdCtr++; //(int)((Tpidr - MemoryManager.TlsIoRegionStart) / 0x200) + 1; - - KThread Thread = new KThread(CpuThread, this, Device.System, ProcessorId, Priority, ThreadId); - - Thread.LastPc = EntryPoint; - - HandleTable.GenerateHandle(Thread, out int Handle); - - CpuThread.ThreadState.CntfrqEl0 = TickFreq; - CpuThread.ThreadState.Tpidr = Tpidr; - - CpuThread.ThreadState.X0 = (ulong)ArgsPtr; - CpuThread.ThreadState.X1 = (ulong)Handle; - CpuThread.ThreadState.X31 = (ulong)StackTop; - - CpuThread.ThreadState.Interrupt += InterruptHandler; - CpuThread.ThreadState.Break += BreakHandler; - CpuThread.ThreadState.SvcCall += SvcHandler.SvcCall; - CpuThread.ThreadState.Undefined += UndefinedHandler; - - CpuThread.WorkFinished += ThreadFinished; - - Threads.TryAdd(CpuThread.ThreadState.Tpidr, Thread); - - return Handle; - } - - private long GetFreeTls() - { - long Position; - - lock (TlsPages) - { - for (int Index = 0; Index < TlsPages.Count; Index++) - { - if (TlsPages[Index].TryGetFreeTlsAddr(out Position)) - { - return Position; - } - } - - long PagePosition = MemoryManager.HleMapTlsPage(); - - KTlsPageManager TlsPage = new KTlsPageManager(PagePosition); - - TlsPages.Add(TlsPage); - - TlsPage.TryGetFreeTlsAddr(out Position); - } - - return Position; - } - - private void InterruptHandler(object sender, EventArgs e) - { - Device.System.Scheduler.ContextSwitch(); - } - - private void BreakHandler(object sender, InstExceptionEventArgs e) - { - PrintStackTraceForCurrentThread(); - - throw new GuestBrokeExecutionException(); - } - - private void UndefinedHandler(object sender, InstUndefinedEventArgs e) - { - PrintStackTraceForCurrentThread(); - - throw new UndefinedInstructionException(e.Position, e.RawOpCode); - } - - public void EnableCpuTracing() - { - Translator.EnableCpuTrace = true; - } - - public void DisableCpuTracing() - { - Translator.EnableCpuTrace = false; - } - - private void CpuTraceHandler(object sender, CpuTraceEventArgs e) - { - Executable Exe = GetExecutable(e.Position); - - if (Exe == null) - { - return; - } - - if (!TryGetSubName(Exe, e.Position, out string SubName)) - { - SubName = string.Empty; - } - - long Offset = e.Position - Exe.ImageBase; - - string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}"; - - Logger.PrintDebug(LogClass.Cpu, ExeNameWithAddr + " " + SubName); - } - - private Translator GetTranslator() - { - if (Translator == null) - { - Translator = new Translator(); - - Translator.CpuTrace += CpuTraceHandler; - } - - return Translator; - } - - private void CpuInvalidAccessHandler(object sender, InvalidAccessEventArgs e) - { - PrintStackTraceForCurrentThread(); - } - - private void PrintStackTraceForCurrentThread() - { - foreach (KThread Thread in Threads.Values) - { - if (Thread.Context.IsCurrentThread()) - { - PrintStackTrace(Thread.Context.ThreadState); - - break; - } - } - } - - public void PrintStackTrace(CpuThreadState ThreadState) - { - StringBuilder Trace = new StringBuilder(); - - Trace.AppendLine("Guest stack trace:"); - - void AppendTrace(long Position) - { - Executable Exe = GetExecutable(Position); - - if (Exe == null) - { - return; - } - - if (!TryGetSubName(Exe, Position, out string SubName)) - { - SubName = $"Sub{Position:x16}"; - } - else if (SubName.StartsWith("_Z")) - { - SubName = Demangler.Parse(SubName); - } - - long Offset = Position - Exe.ImageBase; - - string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}"; - - Trace.AppendLine(" " + ExeNameWithAddr + " " + SubName); - } - - long FramePointer = (long)ThreadState.X29; - - while (FramePointer != 0) - { - AppendTrace(Memory.ReadInt64(FramePointer + 8)); - - FramePointer = Memory.ReadInt64(FramePointer); - } - - Logger.PrintInfo(LogClass.Cpu, Trace.ToString()); - } - - private bool TryGetSubName(Executable Exe, long Position, out string Name) - { - Position -= Exe.ImageBase; - - int Left = 0; - int Right = Exe.SymbolTable.Count - 1; - - while (Left <= Right) - { - int Size = Right - Left; - - int Middle = Left + (Size >> 1); - - ElfSym Symbol = Exe.SymbolTable[Middle]; - - long EndPosition = Symbol.Value + Symbol.Size; - - if ((ulong)Position >= (ulong)Symbol.Value && (ulong)Position < (ulong)EndPosition) - { - Name = Symbol.Name; - - return true; - } - - if ((ulong)Position < (ulong)Symbol.Value) - { - Right = Middle - 1; - } - else - { - Left = Middle + 1; - } - } - - Name = null; - - return false; - } - - private Executable GetExecutable(long Position) - { - string Name = string.Empty; - - for (int Index = Executables.Count - 1; Index >= 0; Index--) - { - if ((ulong)Position >= (ulong)Executables[Index].ImageBase) - { - return Executables[Index]; - } - } - - return null; - } - - private void ThreadFinished(object sender, EventArgs e) - { - if (sender is CpuThread Thread) - { - if (Threads.TryRemove(Thread.ThreadState.Tpidr, out KThread KernelThread)) - { - Device.System.Scheduler.RemoveThread(KernelThread); - } - } - - if (Threads.Count == 0) - { - Device.System.ExitProcess(ProcessId); - } - } - - public KThread GetThread(long Tpidr) - { - if (!Threads.TryGetValue(Tpidr, out KThread Thread)) - { - throw new InvalidOperationException(); - } - - return Thread; - } - - private void Unload() - { - if (Disposed || Threads.Count > 0) - { - return; - } - - Disposed = true; - - HandleTable.Destroy(); - - INvDrvServices.UnloadProcess(this); - - if (NeedsHbAbi && Executables.Count > 0 && Executables[0].FilePath.EndsWith(Homebrew.TemporaryNroSuffix)) - { - File.Delete(Executables[0].FilePath); - } - - Logger.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting..."); - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool Disposing) - { - if (Disposing) - { - if (Threads.Count > 0) - { - foreach (KThread Thread in Threads.Values) - { - Device.System.Scheduler.StopThread(Thread); - } - } - else - { - Unload(); - } - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs new file mode 100644 index 0000000000..4b69b6d45b --- /dev/null +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -0,0 +1,129 @@ +using ChocolArm64.Memory; +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Kernel; +using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.HLE.Loaders.Npdm; + +namespace Ryujinx.HLE.HOS +{ + class ProgramLoader + { + private const int ArgsHeaderSize = 8; + private const int ArgsDataSize = 0x9000; + private const int ArgsTotalSize = ArgsHeaderSize + ArgsDataSize; + + public static bool LoadStaticObjects( + Horizon System, + Npdm MetaData, + IExecutable[] StaticObjects, + byte[] Arguments = null) + { + long ArgsStart = 0; + int ArgsSize = 0; + long CodeStart = 0x8000000; + int CodeSize = 0; + + long[] NsoBase = new long[StaticObjects.Length]; + + for (int Index = 0; Index < StaticObjects.Length; Index++) + { + IExecutable StaticObject = StaticObjects[Index]; + + int TextEnd = StaticObject.TextOffset + StaticObject.Text.Length; + int ROEnd = StaticObject.ROOffset + StaticObject.RO.Length; + int DataEnd = StaticObject.DataOffset + StaticObject.Data.Length + StaticObject.BssSize; + + int NsoSize = TextEnd; + + if ((uint)NsoSize < (uint)ROEnd) + { + NsoSize = ROEnd; + } + + if ((uint)NsoSize < (uint)DataEnd) + { + NsoSize = DataEnd; + } + + NsoSize = BitUtils.AlignUp(NsoSize, KMemoryManager.PageSize); + + NsoBase[Index] = CodeStart + CodeSize; + + CodeSize += NsoSize; + + if (Arguments != null && ArgsSize == 0) + { + ArgsStart = CodeSize; + + ArgsSize = BitUtils.AlignDown(Arguments.Length * 2 + ArgsTotalSize - 1, KMemoryManager.PageSize); + + CodeSize += ArgsSize; + } + } + + int CodePagesCount = CodeSize / KMemoryManager.PageSize; + + int PersonalMmHeapPagesCount = MetaData.PersonalMmHeapSize / KMemoryManager.PageSize; + + KProcess Process = new KProcess(System); + + ProcessCreationInfo CreationInfo = new ProcessCreationInfo( + MetaData.TitleName, + MetaData.ProcessCategory, + MetaData.ACI0.TitleId, + CodeStart, + CodePagesCount, + MetaData.MmuFlags, + 0, + PersonalMmHeapPagesCount); + + KernelResult Result = Process.Initialize( + CreationInfo, + MetaData.ACI0.KernelAccessControl.Capabilities, + System.ResourceLimit, + MemoryRegion.Application); + + if (Result != KernelResult.Success) + { + Logger.PrintError(LogClass.KernelSvc, $"Process initialization returned error \"{Result}\"."); + + return false; + } + + for (int Index = 0; Index < StaticObjects.Length; Index++) + { + IExecutable StaticObject = StaticObjects[Index]; + + long TextStart = NsoBase[Index] + StaticObject.TextOffset; + long ROStart = NsoBase[Index] + StaticObject.ROOffset; + long DataStart = NsoBase[Index] + StaticObject.DataOffset; + + long BssStart = DataStart + StaticObject.Data.Length; + + long BssEnd = BitUtils.AlignUp(BssStart + StaticObject.BssSize, KMemoryManager.PageSize); + + Process.CpuMemory.WriteBytes(TextStart, StaticObject.Text); + Process.CpuMemory.WriteBytes(ROStart, StaticObject.RO); + Process.CpuMemory.WriteBytes(DataStart, StaticObject.Data); + + MemoryHelper.FillWithZeros(Process.CpuMemory, BssStart, (int)(BssEnd - BssStart)); + + Process.MemoryManager.SetProcessMemoryPermission(TextStart, ROStart - TextStart, MemoryPermission.ReadAndExecute); + Process.MemoryManager.SetProcessMemoryPermission(ROStart, DataStart - ROStart, MemoryPermission.Read); + Process.MemoryManager.SetProcessMemoryPermission(DataStart, BssEnd - DataStart, MemoryPermission.ReadAndWrite); + } + + Result = Process.Start(MetaData.MainThreadPriority, MetaData.MainThreadStackSize); + + if (Result != KernelResult.Success) + { + Logger.PrintError(LogClass.KernelSvc, $"Process start returned error \"{Result}\"."); + + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/ServiceCtx.cs b/Ryujinx.HLE/HOS/ServiceCtx.cs index a591673e2c..76c426bcab 100644 --- a/Ryujinx.HLE/HOS/ServiceCtx.cs +++ b/Ryujinx.HLE/HOS/ServiceCtx.cs @@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS class ServiceCtx { public Switch Device { get; private set; } - public Process Process { get; private set; } + public KProcess Process { get; private set; } public MemoryManager Memory { get; private set; } public KSession Session { get; private set; } public IpcMessage Request { get; private set; } @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS public ServiceCtx( Switch Device, - Process Process, + KProcess Process, MemoryManager Memory, KSession Session, IpcMessage Request, diff --git a/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs b/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs index 6b012689c4..2feaf8fc02 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs @@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Am public long GetEventHandle(ServiceCtx Context) { - KEvent Event = Context.Process.AppletState.MessageEvent; + KEvent Event = Context.Device.System.AppletState.MessageEvent; if (Context.Process.HandleTable.GenerateHandle(Event.ReadableEvent, out int Handle) != KernelResult.Success) { @@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Services.Am public long ReceiveMessage(ServiceCtx Context) { - if (!Context.Process.AppletState.TryDequeueMessage(out MessageInfo Message)) + if (!Context.Device.System.AppletState.TryDequeueMessage(out MessageInfo Message)) { return MakeError(ErrorModule.Am, AmErr.NoMessages); } @@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Am public long GetCurrentFocusState(ServiceCtx Context) { - Context.ResponseData.Write((byte)Context.Process.AppletState.FocusState); + Context.ResponseData.Write((byte)Context.Device.System.AppletState.FocusState); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs index 4d595fde27..fccbaac440 100644 --- a/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs @@ -261,8 +261,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldr long HeapRegionStart = Context.Process.MemoryManager.HeapRegionStart; long HeapRegionEnd = Context.Process.MemoryManager.HeapRegionEnd; - long MapRegionStart = Context.Process.MemoryManager.MapRegionStart; - long MapRegionEnd = Context.Process.MemoryManager.MapRegionEnd; + long MapRegionStart = Context.Process.MemoryManager.AliasRegionStart; + long MapRegionEnd = Context.Process.MemoryManager.AliasRegionEnd; while (true) { @@ -285,7 +285,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldr TargetAddress += 0x1000; } - Context.Process.LoadProgram(Info.Executable, TargetAddress); + //Context.Process.LoadProgram(Info.Executable, TargetAddress); Info.NroMappedAddress = TargetAddress; NroMappedAddress = TargetAddress; @@ -316,7 +316,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldr { NroInfos.Remove(Info); - Context.Process.RemoveProgram(Info.NroMappedAddress); + //Context.Process.RemoveProgram(Info.NroMappedAddress); long Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress, Info.Executable.SourceAddress, Info.TotalSize - Info.Executable.BssSize); diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index b8ae11cea1..6786d0e2c8 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -205,7 +205,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv return ((Cmd >> 31) & 1) != 0; } - public static void UnloadProcess(Process Process) + public static void UnloadProcess(KProcess Process) { Fds.DeleteProcess(Process); diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs index fed4104229..7fe3bbedb0 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs @@ -1,6 +1,7 @@ using ChocolArm64.Memory; using Ryujinx.Common.Logging; using Ryujinx.Graphics.Memory; +using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Services.Nv.NvMap; using System; using System.Collections.Concurrent; @@ -13,11 +14,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS private const int FlagRemapSubRange = 0x100; - private static ConcurrentDictionary ASCtxs; + private static ConcurrentDictionary ASCtxs; static NvGpuASIoctl() { - ASCtxs = new ConcurrentDictionary(); + ASCtxs = new ConcurrentDictionary(); } public static int ProcessIoctl(ServiceCtx Context, int Cmd) @@ -321,7 +322,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS return ASCtxs.GetOrAdd(Context.Process, (Key) => new NvGpuASCtx(Context)); } - public static void UnloadProcess(Process Process) + public static void UnloadProcess(KProcess Process) { ASCtxs.TryRemove(Process, out _); } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs index 39f39d4569..d9f5602b4b 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs @@ -1,6 +1,7 @@ using ChocolArm64.Memory; using Ryujinx.Common.Logging; using Ryujinx.Graphics.Memory; +using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS; using System; using System.Collections.Concurrent; @@ -21,11 +22,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel } } - private static ConcurrentDictionary Channels; + private static ConcurrentDictionary Channels; static NvHostChannelIoctl() { - Channels = new ConcurrentDictionary(); + Channels = new ConcurrentDictionary(); } public static int ProcessIoctlGpu(ServiceCtx Context, int Cmd) @@ -194,7 +195,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel return Cpp.Channels[Channel]; } - public static void UnloadProcess(Process Process) + public static void UnloadProcess(KProcess Process) { Channels.TryRemove(Process, out _); } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs index 6cb1474105..bf92afb40d 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs @@ -1,5 +1,6 @@ using ChocolArm64.Memory; using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Kernel; using System; using System.Collections.Concurrent; using System.Text; @@ -9,13 +10,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl { class NvHostCtrlIoctl { - private static ConcurrentDictionary UserCtxs; + private static ConcurrentDictionary UserCtxs; private static bool IsProductionMode = true; static NvHostCtrlIoctl() { - UserCtxs = new ConcurrentDictionary(); + UserCtxs = new ConcurrentDictionary(); if (Set.NxSettings.Settings.TryGetValue("nv!rmos_set_production_mode", out object ProductionModeSetting)) { @@ -390,7 +391,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl return UserCtxs.GetOrAdd(Context.Process, (Key) => new NvHostCtrlUserCtx()); } - public static void UnloadProcess(Process Process) + public static void UnloadProcess(KProcess Process) { UserCtxs.TryRemove(Process, out _); } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs index f5378ef7fb..090c52fcd7 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs @@ -1,6 +1,7 @@ using ChocolArm64.Memory; using Ryujinx.Common.Logging; using Ryujinx.Graphics.Memory; +using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Utilities; using System.Collections.Concurrent; @@ -10,11 +11,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap { private const int FlagNotFreedYet = 1; - private static ConcurrentDictionary Maps; + private static ConcurrentDictionary Maps; static NvMapIoctl() { - Maps = new ConcurrentDictionary(); + Maps = new ConcurrentDictionary(); } public static int ProcessIoctl(ServiceCtx Context, int Cmd) @@ -294,7 +295,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap return null; } - public static void UnloadProcess(Process Process) + public static void UnloadProcess(KProcess Process) { Maps.TryRemove(Process, out _); } diff --git a/Ryujinx.HLE/Homebrew.npdm b/Ryujinx.HLE/Homebrew.npdm new file mode 100644 index 0000000000000000000000000000000000000000..814116146521574a55b96b6614fb0c55d132058f GIT binary patch literal 972 zcmeZu4RK_E0%issC<{gjFgO+zU+84}+AZ=bKvz_8%IJOjgjdkKdB^$bk^|FeO$IXZh9 sfEiE%=1wS=!GVE+L56{WA%Own-U0@Q`(g6P?ggm>iNk0Rn-cc{082ehU;qFB literal 0 HcmV?d00001 diff --git a/Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs b/Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs new file mode 100644 index 0000000000..928828b0d0 --- /dev/null +++ b/Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs @@ -0,0 +1,99 @@ +using System; +using System.IO; + +namespace Ryujinx.HLE.Loaders.Compression +{ + static class BackwardsLz + { + private class BackwardsReader + { + private Stream BaseStream; + + public BackwardsReader(Stream BaseStream) + { + this.BaseStream = BaseStream; + } + + public byte ReadByte() + { + BaseStream.Seek(-1, SeekOrigin.Current); + + byte Value = (byte)BaseStream.ReadByte(); + + BaseStream.Seek(-1, SeekOrigin.Current); + + return Value; + } + + public short ReadInt16() + { + return (short)((ReadByte() << 8) | (ReadByte() << 0)); + } + + public int ReadInt32() + { + return ((ReadByte() << 24) | + (ReadByte() << 16) | + (ReadByte() << 8) | + (ReadByte() << 0)); + } + } + + public static byte[] Decompress(Stream Input) + { + BackwardsReader Reader = new BackwardsReader(Input); + + int AdditionalDecLength = Reader.ReadInt32(); + int StartOffset = Reader.ReadInt32(); + int CompressedLength = Reader.ReadInt32(); + + Input.Seek(12 - StartOffset, SeekOrigin.Current); + + byte[] Dec = new byte[CompressedLength + AdditionalDecLength]; + + int DecPos = Dec.Length; + + byte Mask = 0; + byte Header = 0; + + while (DecPos > 0) + { + if ((Mask >>= 1) == 0) + { + Header = Reader.ReadByte(); + Mask = 0x80; + } + + if ((Header & Mask) == 0) + { + Dec[--DecPos] = Reader.ReadByte(); + } + else + { + ushort Pair = (ushort)Reader.ReadInt16(); + + int Length = (Pair >> 12) + 3; + int Position = (Pair & 0xfff) + 3; + + if (Position - Length >= DecPos) + { + int SrcPos = DecPos + Position; + + DecPos -= Length; + + Buffer.BlockCopy(Dec, SrcPos, Dec, DecPos, Length); + } + else + { + while (Length-- > 0) + { + Dec[--DecPos] = Dec[DecPos + Position + 1]; + } + } + } + } + + return Dec; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/Loaders/ElfRel.cs b/Ryujinx.HLE/Loaders/ElfRel.cs deleted file mode 100644 index cfc31d8910..0000000000 --- a/Ryujinx.HLE/Loaders/ElfRel.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Ryujinx.HLE.Loaders -{ - struct ElfRel - { - public long Offset { get; private set; } - public long Addend { get; private set; } - - public ElfSym Symbol { get; private set; } - public ElfRelType Type { get; private set; } - - public ElfRel(long Offset, long Addend, ElfSym Symbol, ElfRelType Type) - { - this.Offset = Offset; - this.Addend = Addend; - this.Symbol = Symbol; - this.Type = Type; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/Loaders/ElfRelType.cs b/Ryujinx.HLE/Loaders/ElfRelType.cs deleted file mode 100644 index 7da5eec363..0000000000 --- a/Ryujinx.HLE/Loaders/ElfRelType.cs +++ /dev/null @@ -1,128 +0,0 @@ -namespace Ryujinx.HLE.Loaders -{ - enum ElfRelType - { - R_AARCH64_NONE = 0, - R_AARCH64_ABS64 = 257, - R_AARCH64_ABS32 = 258, - R_AARCH64_ABS16 = 259, - R_AARCH64_PREL64 = 260, - R_AARCH64_PREL32 = 261, - R_AARCH64_PREL16 = 262, - R_AARCH64_MOVW_UABS_G0 = 263, - R_AARCH64_MOVW_UABS_G0_NC = 264, - R_AARCH64_MOVW_UABS_G1 = 265, - R_AARCH64_MOVW_UABS_G1_NC = 266, - R_AARCH64_MOVW_UABS_G2 = 267, - R_AARCH64_MOVW_UABS_G2_NC = 268, - R_AARCH64_MOVW_UABS_G3 = 269, - R_AARCH64_MOVW_SABS_G0 = 270, - R_AARCH64_MOVW_SABS_G1 = 271, - R_AARCH64_MOVW_SABS_G2 = 272, - R_AARCH64_LD_PREL_LO19 = 273, - R_AARCH64_ADR_PREL_LO21 = 274, - R_AARCH64_ADR_PREL_PG_HI21 = 275, - R_AARCH64_ADR_PREL_PG_HI21_NC = 276, - R_AARCH64_ADD_ABS_LO12_NC = 277, - R_AARCH64_LDST8_ABS_LO12_NC = 278, - R_AARCH64_TSTBR14 = 279, - R_AARCH64_CONDBR19 = 280, - R_AARCH64_JUMP26 = 282, - R_AARCH64_CALL26 = 283, - R_AARCH64_LDST16_ABS_LO12_NC = 284, - R_AARCH64_LDST32_ABS_LO12_NC = 285, - R_AARCH64_LDST64_ABS_LO12_NC = 286, - R_AARCH64_MOVW_PREL_G0 = 287, - R_AARCH64_MOVW_PREL_G0_NC = 288, - R_AARCH64_MOVW_PREL_G1 = 289, - R_AARCH64_MOVW_PREL_G1_NC = 290, - R_AARCH64_MOVW_PREL_G2 = 291, - R_AARCH64_MOVW_PREL_G2_NC = 292, - R_AARCH64_MOVW_PREL_G3 = 293, - R_AARCH64_LDST128_ABS_LO12_NC = 299, - R_AARCH64_MOVW_GOTOFF_G0 = 300, - R_AARCH64_MOVW_GOTOFF_G0_NC = 301, - R_AARCH64_MOVW_GOTOFF_G1 = 302, - R_AARCH64_MOVW_GOTOFF_G1_NC = 303, - R_AARCH64_MOVW_GOTOFF_G2 = 304, - R_AARCH64_MOVW_GOTOFF_G2_NC = 305, - R_AARCH64_MOVW_GOTOFF_G3 = 306, - R_AARCH64_GOTREL64 = 307, - R_AARCH64_GOTREL32 = 308, - R_AARCH64_GOT_LD_PREL19 = 309, - R_AARCH64_LD64_GOTOFF_LO15 = 310, - R_AARCH64_ADR_GOT_PAGE = 311, - R_AARCH64_LD64_GOT_LO12_NC = 312, - R_AARCH64_LD64_GOTPAGE_LO15 = 313, - R_AARCH64_TLSGD_ADR_PREL21 = 512, - R_AARCH64_TLSGD_ADR_PAGE21 = 513, - R_AARCH64_TLSGD_ADD_LO12_NC = 514, - R_AARCH64_TLSGD_MOVW_G1 = 515, - R_AARCH64_TLSGD_MOVW_G0_NC = 516, - R_AARCH64_TLSLD_ADR_PREL21 = 517, - R_AARCH64_TLSLD_ADR_PAGE21 = 518, - R_AARCH64_TLSLD_ADD_LO12_NC = 519, - R_AARCH64_TLSLD_MOVW_G1 = 520, - R_AARCH64_TLSLD_MOVW_G0_NC = 521, - R_AARCH64_TLSLD_LD_PREL19 = 522, - R_AARCH64_TLSLD_MOVW_DTPREL_G2 = 523, - R_AARCH64_TLSLD_MOVW_DTPREL_G1 = 524, - R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC = 525, - R_AARCH64_TLSLD_MOVW_DTPREL_G0 = 526, - R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC = 527, - R_AARCH64_TLSLD_ADD_DTPREL_HI12 = 528, - R_AARCH64_TLSLD_ADD_DTPREL_LO12 = 529, - R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC = 530, - R_AARCH64_TLSLD_LDST8_DTPREL_LO12 = 531, - R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC = 532, - R_AARCH64_TLSLD_LDST16_DTPREL_LO12 = 533, - R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC = 534, - R_AARCH64_TLSLD_LDST32_DTPREL_LO12 = 535, - R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC = 536, - R_AARCH64_TLSLD_LDST64_DTPREL_LO12 = 537, - R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC = 538, - R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 = 539, - R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC = 540, - R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 = 541, - R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC = 542, - R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 = 543, - R_AARCH64_TLSLE_MOVW_TPREL_G2 = 544, - R_AARCH64_TLSLE_MOVW_TPREL_G1 = 545, - R_AARCH64_TLSLE_MOVW_TPREL_G1_NC = 546, - R_AARCH64_TLSLE_MOVW_TPREL_G0 = 547, - R_AARCH64_TLSLE_MOVW_TPREL_G0_NC = 548, - R_AARCH64_TLSLE_ADD_TPREL_HI12 = 549, - R_AARCH64_TLSLE_ADD_TPREL_LO12 = 550, - R_AARCH64_TLSLE_ADD_TPREL_LO12_NC = 551, - R_AARCH64_TLSLE_LDST8_TPREL_LO12 = 552, - R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC = 553, - R_AARCH64_TLSLE_LDST16_TPREL_LO12 = 554, - R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC = 555, - R_AARCH64_TLSLE_LDST32_TPREL_LO12 = 556, - R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC = 557, - R_AARCH64_TLSLE_LDST64_TPREL_LO12 = 558, - R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC = 559, - R_AARCH64_TLSDESC_LD_PREL19 = 560, - R_AARCH64_TLSDESC_ADR_PREL21 = 561, - R_AARCH64_TLSDESC_ADR_PAGE21 = 562, - R_AARCH64_TLSDESC_LD64_LO12 = 563, - R_AARCH64_TLSDESC_ADD_LO12 = 564, - R_AARCH64_TLSDESC_OFF_G1 = 565, - R_AARCH64_TLSDESC_OFF_G0_NC = 566, - R_AARCH64_TLSDESC_LDR = 567, - R_AARCH64_TLSDESC_ADD = 568, - R_AARCH64_TLSDESC_CALL = 569, - R_AARCH64_TLSLE_LDST128_TPREL_LO12 = 570, - R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC = 571, - R_AARCH64_TLSLD_LDST128_DTPREL_LO12 = 572, - R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC = 573, - R_AARCH64_COPY = 1024, - R_AARCH64_GLOB_DAT = 1025, - R_AARCH64_JUMP_SLOT = 1026, - R_AARCH64_RELATIVE = 1027, - R_AARCH64_TLS_DTPMOD64 = 1028, - R_AARCH64_TLS_DTPREL64 = 1029, - R_AARCH64_TLS_TPREL64 = 1030, - R_AARCH64_TLSDESC = 1031 - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/Loaders/Executable.cs b/Ryujinx.HLE/Loaders/Executable.cs index d4d79073ec..ed2e066be9 100644 --- a/Ryujinx.HLE/Loaders/Executable.cs +++ b/Ryujinx.HLE/Loaders/Executable.cs @@ -1,12 +1,9 @@ using ChocolArm64.Memory; -using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Loaders.Executables; -using Ryujinx.HLE.Utilities; using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.IO; using System.Linq; namespace Ryujinx.HLE.Loaders @@ -32,72 +29,6 @@ namespace Ryujinx.HLE.Loaders { Dynamic = new List(); - FilePath = Exe.FilePath; - - if (FilePath != null) - { - Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, "")); - } - - this.Memory = Memory; - this.MemoryManager = MemoryManager; - this.ImageBase = ImageBase; - this.ImageEnd = ImageBase; - - long TextPosition = ImageBase + (uint)Exe.TextOffset; - long ROPosition = ImageBase + (uint)Exe.ROOffset; - long DataPosition = ImageBase + (uint)Exe.DataOffset; - - long TextSize = (uint)IntUtils.AlignUp(Exe.Text.Length, KMemoryManager.PageSize); - long ROSize = (uint)IntUtils.AlignUp(Exe.RO.Length, KMemoryManager.PageSize); - long DataSize = (uint)IntUtils.AlignUp(Exe.Data.Length, KMemoryManager.PageSize); - long BssSize = (uint)IntUtils.AlignUp(Exe.BssSize, KMemoryManager.PageSize); - - long DataAndBssSize = BssSize + DataSize; - - ImageEnd = DataPosition + DataAndBssSize; - - if (Exe.SourceAddress == 0) - { - MemoryManager.HleMapProcessCode(TextPosition, TextSize + ROSize + DataAndBssSize); - - MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read); - MemoryManager.SetProcessMemoryPermission(DataPosition, DataAndBssSize, MemoryPermission.ReadAndWrite); - - Memory.WriteBytes(TextPosition, Exe.Text); - Memory.WriteBytes(ROPosition, Exe.RO); - Memory.WriteBytes(DataPosition, Exe.Data); - } - else - { - long Result = MemoryManager.MapProcessCodeMemory(TextPosition, Exe.SourceAddress, TextSize + ROSize + DataSize); - - if (Result != 0) - { - throw new InvalidOperationException(); - } - - MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read); - MemoryManager.SetProcessMemoryPermission(DataPosition, DataSize, MemoryPermission.ReadAndWrite); - - if (Exe.BssAddress != 0 && Exe.BssSize != 0) - { - Result = MemoryManager.MapProcessCodeMemory(DataPosition + DataSize, Exe.BssAddress, BssSize); - - if (Result != 0) - { - throw new InvalidOperationException(); - } - - MemoryManager.SetProcessMemoryPermission(DataPosition + DataSize, BssSize, MemoryPermission.ReadAndWrite); - } - } - - if (Exe.Mod0Offset == 0) - { - return; - } - long Mod0Offset = ImageBase + Exe.Mod0Offset; int Mod0Magic = Memory.ReadInt32(Mod0Offset + 0x0); @@ -144,32 +75,6 @@ namespace Ryujinx.HLE.Loaders SymbolTable = Array.AsReadOnly(Symbols.OrderBy(x => x.Value).ToArray()); } - private ElfRel GetRelocation(long Position) - { - long Offset = Memory.ReadInt64(Position + 0); - long Info = Memory.ReadInt64(Position + 8); - long Addend = Memory.ReadInt64(Position + 16); - - int RelType = (int)(Info >> 0); - int SymIdx = (int)(Info >> 32); - - ElfSym Symbol = GetSymbol(SymIdx); - - return new ElfRel(Offset, Addend, Symbol, (ElfRelType)RelType); - } - - private ElfSym GetSymbol(int Index) - { - long StrTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_STRTAB); - long SymTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_SYMTAB); - - long SymEntSize = GetFirstValue(ElfDynTag.DT_SYMENT); - - long Position = SymTblAddr + Index * SymEntSize; - - return GetSymbol(Position, StrTblAddr); - } - private ElfSym GetSymbol(long Position, long StrTblAddr) { int NameIndex = Memory.ReadInt32(Position + 0); diff --git a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs index 6f0952abdb..82b284534c 100644 --- a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs +++ b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.Loaders.Executables { - public interface IExecutable + interface IExecutable { string FilePath { get; } @@ -8,9 +8,6 @@ namespace Ryujinx.HLE.Loaders.Executables byte[] RO { get; } byte[] Data { get; } - long SourceAddress { get; } - long BssAddress { get; } - int Mod0Offset { get; } int TextOffset { get; } int ROOffset { get; } diff --git a/Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs b/Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs new file mode 100644 index 0000000000..0653cbbfff --- /dev/null +++ b/Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs @@ -0,0 +1,128 @@ +using Ryujinx.HLE.Loaders.Compression; +using System.IO; + +namespace Ryujinx.HLE.Loaders.Executables +{ + class KernelInitialProcess + { + public string Name { get; private set; } + + public long TitleId { get; private set; } + + public int ProcessCategory { get; private set; } + + public byte MainThreadPriority { get; private set; } + public byte DefaultProcessorId { get; private set; } + + public byte[] Text { get; private set; } + public byte[] RO { get; private set; } + public byte[] Data { get; private set; } + + public int TextOffset { get; private set; } + public int ROOffset { get; private set; } + public int DataOffset { get; private set; } + public int BssOffset { get; private set; } + public int BssSize { get; private set; } + + private struct SegmentHeader + { + public int Offset { get; private set; } + public int DecompressedSize { get; private set; } + public int CompressedSize { get; private set; } + public int Attribute { get; private set; } + + public SegmentHeader( + int Offset, + int DecompressedSize, + int CompressedSize, + int Attribute) + { + this.Offset = Offset; + this.DecompressedSize = DecompressedSize; + this.CompressedSize = CompressedSize; + this.Attribute = Attribute; + } + } + + public KernelInitialProcess(Stream Input) + { + BinaryReader Reader = new BinaryReader(Input); + + string Magic = ReadString(Reader, 4); + + if (Magic != "KIP1") + { + + } + + Name = ReadString(Reader, 12); + + TitleId = Reader.ReadInt64(); + + ProcessCategory = Reader.ReadInt32(); + + MainThreadPriority = Reader.ReadByte(); + DefaultProcessorId = Reader.ReadByte(); + + byte Reserved = Reader.ReadByte(); + byte Flags = Reader.ReadByte(); + + SegmentHeader[] Segments = new SegmentHeader[6]; + + for (int Index = 0; Index < Segments.Length; Index++) + { + Segments[Index] = new SegmentHeader( + Reader.ReadInt32(), + Reader.ReadInt32(), + Reader.ReadInt32(), + Reader.ReadInt32()); + } + + Reader.ReadBytes(0x20); + + Text = ReadSegment(Segments[0], Input); + RO = ReadSegment(Segments[1], Input); + Data = ReadSegment(Segments[2], Input); + + TextOffset = Segments[0].Offset; + ROOffset = Segments[1].Offset; + DataOffset = Segments[2].Offset; + BssOffset = Segments[3].Offset; + BssSize = Segments[3].DecompressedSize; + } + + private byte[] ReadSegment(SegmentHeader Header, Stream Input) + { + long End = Input.Position + Header.CompressedSize; + + Input.Seek(End, SeekOrigin.Begin); + + byte[] Data = BackwardsLz.Decompress(Input); + + Input.Seek(End, SeekOrigin.Begin); + + return Data; + } + + private static string ReadString(BinaryReader Reader, int MaxSize) + { + string Value = string.Empty; + + for (int Index = 0; Index < MaxSize; Index++) + { + char Chr = (char)Reader.ReadByte(); + + if (Chr == '\0') + { + Reader.BaseStream.Seek(MaxSize - Index - 1, SeekOrigin.Current); + + break; + } + + Value += Chr; + } + + return Value; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/Loaders/Executables/Nso.cs b/Ryujinx.HLE/Loaders/Executables/Nso.cs index c7b48a5f35..fef9c4b853 100644 --- a/Ryujinx.HLE/Loaders/Executables/Nso.cs +++ b/Ryujinx.HLE/Loaders/Executables/Nso.cs @@ -18,9 +18,6 @@ namespace Ryujinx.HLE.Loaders.Executables public int DataOffset { get; private set; } public int BssSize { get; private set; } - public long SourceAddress { get; private set; } - public long BssAddress { get; private set; } - [Flags] private enum NsoFlags { @@ -36,9 +33,6 @@ namespace Ryujinx.HLE.Loaders.Executables { this.FilePath = FilePath; - SourceAddress = 0; - BssAddress = 0; - BinaryReader Reader = new BinaryReader(Input); Input.Seek(0, SeekOrigin.Begin); diff --git a/Ryujinx.HLE/Loaders/Npdm/ApplicationType.cs b/Ryujinx.HLE/Loaders/Npdm/ApplicationType.cs deleted file mode 100644 index ad27903280..0000000000 --- a/Ryujinx.HLE/Loaders/Npdm/ApplicationType.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.HLE.Loaders.Npdm -{ - enum ApplicationType - { - SystemModule, - Application, - Applet - } -} diff --git a/Ryujinx.HLE/Loaders/Npdm/FsPermissionBool.cs b/Ryujinx.HLE/Loaders/Npdm/FsPermissionBool.cs deleted file mode 100644 index 571b7b5a80..0000000000 --- a/Ryujinx.HLE/Loaders/Npdm/FsPermissionBool.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Ryujinx.HLE.Loaders.Npdm -{ - enum FsPermissionBool : ulong - { - BisCache = 0x8000000000000080, - EraseMmc = 0x8000000000000080, - GameCardCertificate = 0x8000000000000010, - GameCardIdSet = 0x8000000000000010, - GameCardDriver = 0x8000000000000200, - GameCardAsic = 0x8000000000000200, - SaveDataCreate = 0x8000000000002020, - SaveDataDelete0 = 0x8000000000000060, - SystemSaveDataCreate0 = 0x8000000000000028, - SystemSaveDataCreate1 = 0x8000000000000020, - SaveDataDelete1 = 0x8000000000004028, - SaveDataIterators0 = 0x8000000000000060, - SaveDataIterators1 = 0x8000000000004020, - SaveThumbnails = 0x8000000000020000, - PosixTime = 0x8000000000000400, - SaveDataExtraData = 0x8000000000004060, - GlobalMode = 0x8000000000080000, - SpeedEmulation = 0x8000000000080000, - NULL = 0, - PaddingFiles = 0xC000000000800000, - SaveData_Debug = 0xC000000001000000, - SaveData_SystemManagement = 0xC000000002000000, - Unknown0x16 = 0x8000000004000000, - Unknown0x17 = 0x8000000008000000, - Unknown0x18 = 0x8000000010000000, - Unknown0x19 = 0x8000000000000800, - Unknown0x1A = 0x8000000000004020 - } -} diff --git a/Ryujinx.HLE/Loaders/Npdm/FsPermissionRw.cs b/Ryujinx.HLE/Loaders/Npdm/FsPermissionRw.cs deleted file mode 100644 index ca21279b5e..0000000000 --- a/Ryujinx.HLE/Loaders/Npdm/FsPermissionRw.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace Ryujinx.HLE.Loaders.Npdm -{ - enum FsPermissionRw : ulong - { - MountContentType2 = 0x8000000000000801, - MountContentType5 = 0x8000000000000801, - MountContentType3 = 0x8000000000000801, - MountContentType4 = 0x8000000000000801, - MountContentType6 = 0x8000000000000801, - MountContentType7 = 0x8000000000000801, - Unknown0x6 = 0x8000000000000000, - ContentStorageAccess = 0x8000000000000800, - ImageDirectoryAccess = 0x8000000000001000, - MountBisType28 = 0x8000000000000084, - MountBisType29 = 0x8000000000000080, - MountBisType30 = 0x8000000000008080, - MountBisType31 = 0x8000000000008080, - Unknown0xD = 0x8000000000000080, - SdCardAccess = 0xC000000000200000, - GameCardUser = 0x8000000000000010, - SaveDataAccess0 = 0x8000000000040020, - SystemSaveDataAccess0 = 0x8000000000000028, - SaveDataAccess1 = 0x8000000000000020, - SystemSaveDataAccess1 = 0x8000000000000020, - BisPartition0 = 0x8000000000010082, - BisPartition10 = 0x8000000000010080, - BisPartition20 = 0x8000000000010080, - BisPartition21 = 0x8000000000010080, - BisPartition22 = 0x8000000000010080, - BisPartition23 = 0x8000000000010080, - BisPartition24 = 0x8000000000010080, - BisPartition25 = 0x8000000000010080, - BisPartition26 = 0x8000000000000080, - BisPartition27 = 0x8000000000000084, - BisPartition28 = 0x8000000000000084, - BisPartition29 = 0x8000000000000080, - BisPartition30 = 0x8000000000000080, - BisPartition31 = 0x8000000000000080, - BisPartition32 = 0x8000000000000080, - Unknown0x23 = 0xC000000000200000, - GameCard_System = 0x8000000000000100, - MountContent_System = 0x8000000000100008, - HostAccess = 0xC000000000400000 - } -} diff --git a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs b/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs index 0b45ebfbbd..cd3d325245 100644 --- a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs +++ b/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs @@ -1,173 +1,23 @@ -using Ryujinx.HLE.Exceptions; -using System; -using System.Collections.ObjectModel; -using System.IO; +using System.IO; namespace Ryujinx.HLE.Loaders.Npdm { class KernelAccessControl { - public ReadOnlyCollection Items; + public int[] Capabilities { get; private set; } public KernelAccessControl(Stream Stream, int Offset, int Size) { Stream.Seek(Offset, SeekOrigin.Begin); + Capabilities = new int[Size / 4]; + BinaryReader Reader = new BinaryReader(Stream); - KernelAccessControlItem[] Items = new KernelAccessControlItem[Size / 4]; - - for (int Index = 0; Index < Size / 4; Index++) + for (int Index = 0; Index < Capabilities.Length; Index++) { - uint Descriptor = Reader.ReadUInt32(); - - //Ignore the descriptor. - if (Descriptor == 0xffffffff) - { - continue; - } - - Items[Index] = new KernelAccessControlItem(); - - int LowBits = 0; - - while ((Descriptor & 1) != 0) - { - Descriptor >>= 1; - - LowBits++; - } - - Descriptor >>= 1; - - switch (LowBits) - { - //Kernel flags. - case 3: - { - Items[Index].HasKernelFlags = true; - - Items[Index].HighestThreadPriority = (Descriptor >> 0) & 0x3f; - Items[Index].LowestThreadPriority = (Descriptor >> 6) & 0x3f; - Items[Index].LowestCpuId = (Descriptor >> 12) & 0xff; - Items[Index].HighestCpuId = (Descriptor >> 20) & 0xff; - - break; - } - - //Syscall mask. - case 4: - { - Items[Index].HasSvcFlags = true; - - Items[Index].AllowedSvcs = new bool[0x80]; - - int SysCallBase = (int)(Descriptor >> 24) * 0x18; - - for (int SysCall = 0; SysCall < 0x18 && SysCallBase + SysCall < 0x80; SysCall++) - { - Items[Index].AllowedSvcs[SysCallBase + SysCall] = (Descriptor & 1) != 0; - - Descriptor >>= 1; - } - - break; - } - - //Map IO/Normal. - case 6: - { - ulong Address = (Descriptor & 0xffffff) << 12; - bool IsRo = (Descriptor >> 24) != 0; - - if (Index == Size / 4 - 1) - { - throw new InvalidNpdmException("Invalid Kernel Access Control Descriptors!"); - } - - Descriptor = Reader.ReadUInt32(); - - if ((Descriptor & 0x7f) != 0x3f) - { - throw new InvalidNpdmException("Invalid Kernel Access Control Descriptors!"); - } - - Descriptor >>= 7; - - ulong MmioSize = (Descriptor & 0xffffff) << 12; - bool IsNormal = (Descriptor >> 24) != 0; - - Items[Index].NormalMmio.Add(new KernelAccessControlMmio(Address, MmioSize, IsRo, IsNormal)); - - Index++; - - break; - } - - //Map Normal Page. - case 7: - { - ulong Address = Descriptor << 12; - - Items[Index].PageMmio.Add(new KernelAccessControlMmio(Address, 0x1000, false, false)); - - break; - } - - //IRQ Pair. - case 11: - { - Items[Index].Irq.Add(new KernelAccessControlIrq( - (Descriptor >> 0) & 0x3ff, - (Descriptor >> 10) & 0x3ff)); - - break; - } - - //Application Type. - case 13: - { - Items[Index].HasApplicationType = true; - - Items[Index].ApplicationType = (int)Descriptor & 7; - - break; - } - - //Kernel Release Version. - case 14: - { - Items[Index].HasKernelVersion = true; - - Items[Index].KernelVersionRelease = (int)Descriptor; - - break; - } - - //Handle Table Size. - case 15: - { - Items[Index].HasHandleTableSize = true; - - Items[Index].HandleTableSize = (int)Descriptor; - - break; - } - - //Debug Flags. - case 16: - { - Items[Index].HasDebugFlags = true; - - Items[Index].AllowDebug = ((Descriptor >> 0) & 1) != 0; - Items[Index].ForceDebug = ((Descriptor >> 1) & 1) != 0; - - break; - } - } + Capabilities[Index] = Reader.ReadInt32(); } - - this.Items = Array.AsReadOnly(Items); } } } diff --git a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlIrq.cs b/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlIrq.cs deleted file mode 100644 index 636713317a..0000000000 --- a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlIrq.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.HLE.Loaders.Npdm -{ - struct KernelAccessControlIrq - { - public uint Irq0 { get; private set; } - public uint Irq1 { get; private set; } - - public KernelAccessControlIrq(uint Irq0, uint Irq1) - { - this.Irq0 = Irq0; - this.Irq1 = Irq1; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlMmio.cs b/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlMmio.cs deleted file mode 100644 index 1ec79c8870..0000000000 --- a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlMmio.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Ryujinx.HLE.Loaders.Npdm -{ - struct KernelAccessControlMmio - { - public ulong Address { get; private set; } - public ulong Size { get; private set; } - public bool IsRo { get; private set; } - public bool IsNormal { get; private set; } - - public KernelAccessControlMmio( - ulong Address, - ulong Size, - bool IsRo, - bool IsNormal) - { - this.Address = Address; - this.Size = Size; - this.IsRo = IsRo; - this.IsNormal = IsNormal; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/Loaders/Npdm/KernelAccessItem.cs b/Ryujinx.HLE/Loaders/Npdm/KernelAccessItem.cs deleted file mode 100644 index 42015c3ea2..0000000000 --- a/Ryujinx.HLE/Loaders/Npdm/KernelAccessItem.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Collections.Generic; - -namespace Ryujinx.HLE.Loaders.Npdm -{ - struct KernelAccessControlItem - { - public bool HasKernelFlags { get; set; } - public uint LowestThreadPriority { get; set; } - public uint HighestThreadPriority { get; set; } - public uint LowestCpuId { get; set; } - public uint HighestCpuId { get; set; } - - public bool HasSvcFlags { get; set; } - public bool[] AllowedSvcs { get; set; } - - public List NormalMmio { get; set; } - public List PageMmio { get; set; } - public List Irq { get; set; } - - public bool HasApplicationType { get; set; } - public int ApplicationType { get; set; } - - public bool HasKernelVersion { get; set; } - public int KernelVersionRelease { get; set; } - - public bool HasHandleTableSize { get; set; } - public int HandleTableSize { get; set; } - - public bool HasDebugFlags { get; set; } - public bool AllowDebug { get; set; } - public bool ForceDebug { get; set; } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs b/Ryujinx.HLE/Loaders/Npdm/Npdm.cs index 8aacfd99ef..9c2fdb381b 100644 --- a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs +++ b/Ryujinx.HLE/Loaders/Npdm/Npdm.cs @@ -1,5 +1,4 @@ using Ryujinx.HLE.Exceptions; -using Ryujinx.HLE.Utilities; using System.IO; using System.Text; @@ -12,15 +11,15 @@ namespace Ryujinx.HLE.Loaders.Npdm { private const int MetaMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24; - public bool Is64Bits { get; private set; } - public int AddressSpaceWidth { get; private set; } - public byte MainThreadPriority { get; private set; } - public byte DefaultCpuId { get; private set; } - public int SystemResourceSize { get; private set; } - public int ProcessCategory { get; private set; } - public int MainEntrypointStackSize { get; private set; } - public string TitleName { get; private set; } - public byte[] ProductCode { get; private set; } + public byte MmuFlags { get; private set; } + public bool Is64Bits { get; private set; } + public byte MainThreadPriority { get; private set; } + public byte DefaultCpuId { get; private set; } + public int PersonalMmHeapSize { get; private set; } + public int ProcessCategory { get; private set; } + public int MainThreadStackSize { get; private set; } + public string TitleName { get; private set; } + public byte[] ProductCode { get; private set; } public ACI0 ACI0 { get; private set; } public ACID ACID { get; private set; } @@ -36,27 +35,22 @@ namespace Ryujinx.HLE.Loaders.Npdm Reader.ReadInt64(); - //MmuFlags, bit0: 64-bit instructions, bits1-3: address space width (1=64-bit, 2=32-bit). Needs to be <= 0xF. - byte MmuFlags = Reader.ReadByte(); + MmuFlags = Reader.ReadByte(); - Is64Bits = (MmuFlags & 1) != 0; - AddressSpaceWidth = (MmuFlags >> 1) & 7; + Is64Bits = (MmuFlags & 1) != 0; Reader.ReadByte(); - MainThreadPriority = Reader.ReadByte(); //(0-63). + MainThreadPriority = Reader.ReadByte(); DefaultCpuId = Reader.ReadByte(); Reader.ReadInt32(); - //System resource size (max size as of 5.x: 534773760). - SystemResourceSize = EndianSwap.Swap32(Reader.ReadInt32()); + PersonalMmHeapSize = Reader.ReadInt32(); - //ProcessCategory (0: regular title, 1: kernel built-in). Should be 0 here. - ProcessCategory = EndianSwap.Swap32(Reader.ReadInt32()); + ProcessCategory = Reader.ReadInt32(); - //Main entrypoint stack size. - MainEntrypointStackSize = Reader.ReadInt32(); + MainThreadStackSize = Reader.ReadInt32(); byte[] TempTitleName = Reader.ReadBytes(0x10); diff --git a/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs b/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs index 910eacb38b..b18538e5b4 100644 --- a/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs +++ b/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs @@ -28,8 +28,8 @@ namespace Ryujinx.HLE.Loaders.Npdm break; } - int Length = ((ControlByte & 0x07)) + 1; - bool RegisterAllowed = ((ControlByte & 0x80) != 0); + int Length = (ControlByte & 0x07) + 1; + bool RegisterAllowed = (ControlByte & 0x80) != 0; Services.Add(Encoding.ASCII.GetString(Reader.ReadBytes(Length), 0, Length), RegisterAllowed); diff --git a/Ryujinx.HLE/Loaders/Npdm/SvcName.cs b/Ryujinx.HLE/Loaders/Npdm/SvcName.cs deleted file mode 100644 index e519e05ec0..0000000000 --- a/Ryujinx.HLE/Loaders/Npdm/SvcName.cs +++ /dev/null @@ -1,134 +0,0 @@ -namespace Ryujinx.HLE.Loaders.Npdm -{ - enum SvcName - { - Reserved0, - SetHeapSize, - SetMemoryPermission, - SetMemoryAttribute, - MapMemory, - UnmapMemory, - QueryMemory, - ExitProcess, - CreateThread, - StartThread, - ExitThread, - SleepThread, - GetThreadPriority, - SetThreadPriority, - GetThreadCoreMask, - SetThreadCoreMask, - GetCurrentProcessorNumber, - SignalEvent, - ClearEvent, - MapSharedMemory, - UnmapSharedMemory, - CreateTransferMemory, - CloseHandle, - ResetSignal, - WaitSynchronization, - CancelSynchronization, - ArbitrateLock, - ArbitrateUnlock, - WaitProcessWideKeyAtomic, - SignalProcessWideKey, - GetSystemTick, - ConnectToNamedPort, - SendSyncRequestLight, - SendSyncRequest, - SendSyncRequestWithUserBuffer, - SendAsyncRequestWithUserBuffer, - GetProcessId, - GetThreadId, - Break, - OutputDebugString, - ReturnFromException, - GetInfo, - FlushEntireDataCache, - FlushDataCache, - MapPhysicalMemory, - UnmapPhysicalMemory, - GetFutureThreadInfo, - GetLastThreadInfo, - GetResourceLimitLimitValue, - GetResourceLimitCurrentValue, - SetThreadActivity, - GetThreadContext3, - WaitForAddress, - SignalToAddress, - Reserved1, - Reserved2, - Reserved3, - Reserved4, - Reserved5, - Reserved6, - DumpInfo, - DumpInfoNew, - Reserved7, - Reserved8, - CreateSession, - AcceptSession, - ReplyAndReceiveLight, - ReplyAndReceive, - ReplyAndReceiveWithUserBuffer, - CreateEvent, - Reserved9, - Reserved10, - MapPhysicalMemoryUnsafe, - UnmapPhysicalMemoryUnsafe, - SetUnsafeLimit, - CreateCodeMemory, - ControlCodeMemory, - SleepSystem, - ReadWriteRegister, - SetProcessActivity, - CreateSharedMemory, - MapTransferMemory, - UnmapTransferMemory, - CreateInterruptEvent, - QueryPhysicalAddress, - QueryIoMapping, - CreateDeviceAddressSpace, - AttachDeviceAddressSpace, - DetachDeviceAddressSpace, - MapDeviceAddressSpaceByForce, - MapDeviceAddressSpaceAligned, - MapDeviceAddressSpace, - UnmapDeviceAddressSpace, - InvalidateProcessDataCache, - StoreProcessDataCache, - FlushProcessDataCache, - DebugActiveProcess, - BreakDebugProcess, - TerminateDebugProcess, - GetDebugEvent, - ContinueDebugEvent, - GetProcessList, - GetThreadList, - GetDebugThreadContext, - SetDebugThreadContext, - QueryDebugProcessMemory, - ReadDebugProcessMemory, - WriteDebugProcessMemory, - SetHardwareBreakPoint, - GetDebugThreadParam, - Reserved11, - GetSystemInfo, - CreatePort, - ManageNamedPort, - ConnectToPort, - SetProcessMemoryPermission, - MapProcessMemory, - UnmapProcessMemory, - QueryProcessMemory, - MapProcessCodeMemory, - UnmapProcessCodeMemory, - CreateProcess, - StartProcess, - TerminateProcess, - GetProcessInfo, - CreateResourceLimit, - SetResourceLimitLimitValue, - CallSecureMonitor - } -} diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index 71a0cf1c47..6285825a5c 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -14,10 +14,12 @@ + + diff --git a/Ryujinx.HLE/Utilities/WSAError.cs b/Ryujinx.HLE/Utilities/WSAError.cs index 55c04f2284..ff0896d47a 100644 --- a/Ryujinx.HLE/Utilities/WSAError.cs +++ b/Ryujinx.HLE/Utilities/WSAError.cs @@ -1,17 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Ryujinx.HLE.Utilities +namespace Ryujinx.HLE.Utilities { - public enum WSAError + enum WSAError { /* * All Windows Sockets error constants are biased by WSABASEERR from * the "normal" */ WSABASEERR = 10000, - + /* * Windows Sockets definitions of regular Microsoft C error constants */