From 5116951222fe8aa934ac8277a9ff34926e8ac73f Mon Sep 17 00:00:00 2001 From: MysticExile <30736337+MysticExile@users.noreply.github.com> Date: Fri, 8 Nov 2019 05:43:00 +0100 Subject: [PATCH 1/5] Update README.md to reflect new changes in the Homebrew used and mentioned (#813) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2af97c3cf3..0ecb2769ad 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of Homebrew is available on many websites, such as the [Switch Appstore](https://www.switchbru.com/appstore/). - A hacked Switch is needed to dump games, which you can learn how to do [here](https://nh-server.github.io/switch-guide/). Once you've hacked your Switch, you need to dump your own games with [NxDumpTool](https://github.com/DarkMatterCore/nxdumptool) to get an XCI dump or [SwitchSDTool](https://github.com/CaitSith2/SwitchSDTool) to get an NSP dump. + A hacked Nintendo Switch is needed to dump games, which you can learn how to do [here](https://nh-server.github.io/switch-guide/). Once you have hacked your Nintendo Switch, you will need to dump your own games with [NxDumpTool](https://github.com/DarkMatterCore/nxdumptool/releases) to get an XCI or NSP dump. ## Features From 88593bf8727e0da05a8f319951dce19f27e287d6 Mon Sep 17 00:00:00 2001 From: Thomas Guillemard Date: Fri, 8 Nov 2019 15:49:28 +0100 Subject: [PATCH 2/5] Add detail of ZbcSetTableArguments (#810) * Add detail of ZbcSetTableArguments This is a missing part of the #800 PR that cause an assert to be triggered in debug mode. Also, remove Fence in SurfaceFlinger as it's a duplicate of NvFence. * Fix critical issue in size checking of ioctl oops --- Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs | 2 +- .../Types/ZbcSetTableArguments.cs | 43 ++++++++++++++++++- .../Services/SurfaceFlinger/Types/Fence.cs | 11 ----- .../SurfaceFlinger/Types/MultiFence.cs | 11 ++--- 4 files changed, 48 insertions(+), 19 deletions(-) delete mode 100644 Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Fence.cs diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index 9e22d17e77..2b9f09fa2f 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv if (isRead && isWrite) { - if (outputDataPosition < inputDataSize) + if (outputDataSize < inputDataSize) { arguments = null; diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs index e21e437e82..ed74cc2631 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrlGpu/Types/ZbcSetTableArguments.cs @@ -1,10 +1,49 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types { + [StructLayout(LayoutKind.Sequential)] + struct ZbcColorArray + { + private uint element0; + private uint element1; + private uint element2; + private uint element3; + + public uint this[int index] + { + get + { + if (index == 0) + { + return element0; + } + else if (index == 1) + { + return element1; + } + else if (index == 2) + { + return element2; + } + else if (index == 2) + { + return element3; + } + + throw new IndexOutOfRangeException(); + } + } + } + [StructLayout(LayoutKind.Sequential)] struct ZbcSetTableArguments { - // TODO + public ZbcColorArray ColorDs; + public ZbcColorArray ColorL2; + public uint Depth; + public uint Format; + public uint Type; } } diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Fence.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Fence.cs deleted file mode 100644 index 356a123227..0000000000 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/Fence.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger -{ - [StructLayout(LayoutKind.Sequential, Size = 0x8)] - struct Fence - { - public int Id; - public int Value; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/MultiFence.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/MultiFence.cs index 97ad3e20d3..20e0723b88 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/MultiFence.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/MultiFence.cs @@ -1,4 +1,5 @@ -using System.Runtime.InteropServices; +using Ryujinx.HLE.HOS.Services.Nv.Types; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { @@ -9,15 +10,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public int FenceCount; [FieldOffset(0x4)] - public Fence Fence0; + public NvFence Fence0; [FieldOffset(0xC)] - public Fence Fence1; + public NvFence Fence1; [FieldOffset(0x14)] - public Fence Fence2; + public NvFence Fence2; [FieldOffset(0x1C)] - public Fence Fence3; + public NvFence Fence3; } } \ No newline at end of file From 2ea8d5bd5ffd564f0c28b96846c3c3865adc93e2 Mon Sep 17 00:00:00 2001 From: Thomas Guillemard Date: Fri, 8 Nov 2019 15:49:48 +0100 Subject: [PATCH 3/5] Improve IRoInterface logic (#809) * hle: Improve IRoInterface logic This commit contains a little rewrite of IRoInterface to fix some issues that we were facing on some recent games (AC3 Remastered & Final Fantasy VIII Remastered) Related issues: - https://github.com/Ryujinx/Ryujinx-Games-List/issues/196 * Address comments --- Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs | 13 - .../Services/{Loader => Ro}/IRoInterface.cs | 313 +++++++++++------- Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs | 27 ++ .../Services/{Loader => Ro}/Types/NroInfo.cs | 2 +- .../{Loader => Ro}/Types/NrrHeader.cs | 2 +- .../Services/{Loader => Ro}/Types/NrrInfo.cs | 2 +- 6 files changed, 228 insertions(+), 131 deletions(-) rename Ryujinx.HLE/HOS/Services/{Loader => Ro}/IRoInterface.cs (57%) create mode 100644 Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs rename Ryujinx.HLE/HOS/Services/{Loader => Ro}/Types/NroInfo.cs (96%) rename Ryujinx.HLE/HOS/Services/{Loader => Ro}/Types/NrrHeader.cs (94%) rename Ryujinx.HLE/HOS/Services/{Loader => Ro}/Types/NrrInfo.cs (91%) diff --git a/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs index 4746ecc596..35fd4dcb54 100644 --- a/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs +++ b/Ryujinx.HLE/HOS/Services/Loader/ResultCode.cs @@ -6,18 +6,5 @@ ErrorCodeShift = 9, Success = 0, - - InvalidMemoryState = (51 << ErrorCodeShift) | ModuleId, - InvalidNro = (52 << ErrorCodeShift) | ModuleId, - InvalidNrr = (53 << ErrorCodeShift) | ModuleId, - MaxNro = (55 << ErrorCodeShift) | ModuleId, - MaxNrr = (56 << ErrorCodeShift) | ModuleId, - NroAlreadyLoaded = (57 << ErrorCodeShift) | ModuleId, - NroHashNotPresent = (54 << ErrorCodeShift) | ModuleId, - UnalignedAddress = (81 << ErrorCodeShift) | ModuleId, - BadSize = (82 << ErrorCodeShift) | ModuleId, - BadNroAddress = (84 << ErrorCodeShift) | ModuleId, - BadNrrAddress = (85 << ErrorCodeShift) | ModuleId, - BadInitialization = (87 << ErrorCodeShift) | ModuleId } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Loader/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs similarity index 57% rename from Ryujinx.HLE/HOS/Services/Loader/IRoInterface.cs rename to Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs index 3d58594b05..0153f4dd19 100644 --- a/Ryujinx.HLE/HOS/Services/Loader/IRoInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/IRoInterface.cs @@ -5,19 +5,22 @@ using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Utilities; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; -namespace Ryujinx.HLE.HOS.Services.Loader +namespace Ryujinx.HLE.HOS.Services.Ro { [Service("ldr:ro")] [Service("ro:1")] // 7.0.0+ - class IRoInterface : IpcService + class IRoInterface : IpcService, IDisposable { - private const int MaxNrr = 0x40; - private const int MaxNro = 0x40; + private const int MaxNrr = 0x40; + private const int MaxNro = 0x40; + private const int MaxMapRetries = 0x200; + private const int GuardPagesSize = 0x4000; private const uint NrrMagic = 0x3052524E; private const uint NroMagic = 0x304F524E; @@ -25,12 +28,15 @@ namespace Ryujinx.HLE.HOS.Services.Loader private List _nrrInfos; private List _nroInfos; - private bool _isInitialized; + private KProcess _owner; + + private static Random _random = new Random(); public IRoInterface(ServiceCtx context) { _nrrInfos = new List(MaxNrr); _nroInfos = new List(MaxNro); + _owner = null; } private ResultCode ParseNrr(out NrrInfo nrrInfo, ServiceCtx context, long nrrAddress, long nrrSize) @@ -39,11 +45,11 @@ namespace Ryujinx.HLE.HOS.Services.Loader if (nrrSize == 0 || nrrAddress + nrrSize <= nrrAddress || (nrrSize & 0xFFF) != 0) { - return ResultCode.BadSize; + return ResultCode.InvalidSize; } else if ((nrrAddress & 0xFFF) != 0) { - return ResultCode.UnalignedAddress; + return ResultCode.InvalidAddress; } StructReader reader = new StructReader(context.Memory, nrrAddress); @@ -55,7 +61,7 @@ namespace Ryujinx.HLE.HOS.Services.Loader } else if (header.NrrSize != nrrSize) { - return ResultCode.BadSize; + return ResultCode.InvalidSize; } List hashes = new List(); @@ -105,19 +111,19 @@ namespace Ryujinx.HLE.HOS.Services.Loader if (_nroInfos.Count >= MaxNro) { - return ResultCode.MaxNro; + return ResultCode.TooManyNro; } else if (nroSize == 0 || nroAddress + nroSize <= nroAddress || (nroSize & 0xFFF) != 0) { - return ResultCode.BadSize; + return ResultCode.InvalidSize; } else if (bssSize != 0 && bssAddress + bssSize <= bssAddress) { - return ResultCode.BadSize; + return ResultCode.InvalidSize; } else if ((nroAddress & 0xFFF) != 0) { - return ResultCode.UnalignedAddress; + return ResultCode.InvalidAddress; } uint magic = context.Memory.ReadUInt32((long)nroAddress + 0x10); @@ -140,12 +146,12 @@ namespace Ryujinx.HLE.HOS.Services.Loader if (!IsNroHashPresent(nroHash)) { - return ResultCode.NroHashNotPresent; + return ResultCode.NotRegistered; } if (IsNroLoaded(nroHash)) { - return ResultCode.NroAlreadyLoaded; + return ResultCode.AlreadyLoaded; } stream.Position = 0; @@ -187,77 +193,120 @@ namespace Ryujinx.HLE.HOS.Services.Loader return ResultCode.Success; } - private ResultCode MapNro(ServiceCtx context, NroInfo info, out ulong nroMappedAddress) + private ResultCode MapNro(KProcess process, NroInfo info, out ulong nroMappedAddress) { + KMemoryManager memMgr = process.MemoryManager; + + int retryCount = 0; + nroMappedAddress = 0; - KMemoryManager memMgr = context.Process.MemoryManager; - - ulong targetAddress = memMgr.GetAddrSpaceBaseAddr(); - - while (true) + while (retryCount++ < MaxMapRetries) { - if (targetAddress + info.TotalSize >= memMgr.AddrSpaceEnd) + ResultCode result = MapCodeMemoryInProcess(process, info.NroAddress, info.NroSize, out nroMappedAddress); + + if (result != ResultCode.Success) { - return ResultCode.InvalidMemoryState; + return result; } - KMemoryInfo memInfo = memMgr.QueryMemory(targetAddress); - - if (memInfo.State == MemoryState.Unmapped && memInfo.Size >= info.TotalSize) + if (info.BssSize > 0) { - if (!memMgr.InsideHeapRegion (targetAddress, info.TotalSize) && - !memMgr.InsideAliasRegion(targetAddress, info.TotalSize)) + KernelResult bssMappingResult = memMgr.MapProcessCodeMemory(nroMappedAddress + info.NroSize, info.BssAddress, info.BssSize); + + if (bssMappingResult == KernelResult.InvalidMemState) + { + memMgr.UnmapProcessCodeMemory(nroMappedAddress + info.NroSize, info.BssAddress, info.BssSize); + memMgr.UnmapProcessCodeMemory(nroMappedAddress, info.NroAddress, info.NroSize); + + continue; + } + else if (bssMappingResult != KernelResult.Success) + { + memMgr.UnmapProcessCodeMemory(nroMappedAddress + info.NroSize, info.BssAddress, info.BssSize); + memMgr.UnmapProcessCodeMemory(nroMappedAddress, info.NroAddress, info.NroSize); + + return (ResultCode)bssMappingResult; + } + } + + if (CanAddGuardRegionsInProcess(process, nroMappedAddress, info.TotalSize)) + { + return ResultCode.Success; + } + } + + return ResultCode.InsufficientAddressSpace; + } + + private bool CanAddGuardRegionsInProcess(KProcess process, ulong baseAddress, ulong size) + { + KMemoryManager memMgr = process.MemoryManager; + + KMemoryInfo memInfo = memMgr.QueryMemory(baseAddress - 1); + + if (memInfo.State == MemoryState.Unmapped && baseAddress - GuardPagesSize >= memInfo.Address) + { + memInfo = memMgr.QueryMemory(baseAddress + size); + + if (memInfo.State == MemoryState.Unmapped) + { + return baseAddress + size + GuardPagesSize <= memInfo.Address + memInfo.Size; + } + } + return false; + } + + private ResultCode MapCodeMemoryInProcess(KProcess process, ulong baseAddress, ulong size, out ulong targetAddress) + { + KMemoryManager memMgr = process.MemoryManager; + + targetAddress = 0; + + int retryCount; + + int addressSpacePageLimit = (int)((memMgr.GetAddrSpaceSize() - size) >> 12); + + for (retryCount = 0; retryCount < MaxMapRetries; retryCount++) + { + while (true) + { + targetAddress = memMgr.GetAddrSpaceBaseAddr() + (ulong)(_random.Next(addressSpacePageLimit) << 12); + + if (memMgr.InsideAddrSpace(targetAddress, size) && !memMgr.InsideHeapRegion(targetAddress, size) && !memMgr.InsideAliasRegion(targetAddress, size)) { break; } } - targetAddress += memInfo.Size; - } + KernelResult result = memMgr.MapProcessCodeMemory(targetAddress, baseAddress, size); - KernelResult result = memMgr.MapProcessCodeMemory(targetAddress, info.NroAddress, info.NroSize); - - if (result != KernelResult.Success) - { - return ResultCode.InvalidMemoryState; - } - - ulong bssTargetAddress = targetAddress + info.NroSize; - - if (info.BssSize != 0) - { - result = memMgr.MapProcessCodeMemory(bssTargetAddress, info.BssAddress, info.BssSize); - - if (result != KernelResult.Success) + if (result == KernelResult.InvalidMemState) { - memMgr.UnmapProcessCodeMemory(targetAddress, info.NroAddress, info.NroSize); - - return ResultCode.InvalidMemoryState; + continue; } - } - - result = LoadNroIntoMemory(context.Process, info.Executable, targetAddress); - - if (result != KernelResult.Success) - { - memMgr.UnmapProcessCodeMemory(targetAddress, info.NroAddress, info.NroSize); - - if (info.BssSize != 0) + else if (result != KernelResult.Success) { - memMgr.UnmapProcessCodeMemory(bssTargetAddress, info.BssAddress, info.BssSize); + return (ResultCode)result; + } + + if (!CanAddGuardRegionsInProcess(process, targetAddress, size)) + { + continue; } return ResultCode.Success; } - info.NroMappedAddress = targetAddress; - nroMappedAddress = targetAddress; + if (retryCount == MaxMapRetries) + { + return ResultCode.InsufficientAddressSpace; + } return ResultCode.Success; } - private KernelResult LoadNroIntoMemory(KProcess process, IExecutable relocatableObject, ulong baseAddress) + private KernelResult SetNroMemoryPermissions(KProcess process, IExecutable relocatableObject, ulong baseAddress) { ulong textStart = baseAddress + (ulong)relocatableObject.TextOffset; ulong roStart = baseAddress + (ulong)relocatableObject.RoOffset; @@ -304,10 +353,10 @@ namespace Ryujinx.HLE.HOS.Services.Loader } } - return ResultCode.BadNrrAddress; + return ResultCode.NotLoaded; } - private ResultCode RemoveNroInfo(ServiceCtx context, ulong nroMappedAddress) + private ResultCode RemoveNroInfo(ulong nroMappedAddress) { foreach (NroInfo info in _nroInfos) { @@ -315,49 +364,64 @@ namespace Ryujinx.HLE.HOS.Services.Loader { _nroInfos.Remove(info); - ulong textSize = (ulong)info.Executable.Text.Length; - ulong roSize = (ulong)info.Executable.Ro.Length; - ulong dataSize = (ulong)info.Executable.Data.Length; - ulong bssSize = (ulong)info.Executable.BssSize; - - KernelResult result = KernelResult.Success; - - if (info.Executable.BssSize != 0) - { - result = context.Process.MemoryManager.UnmapProcessCodeMemory( - info.NroMappedAddress + textSize + roSize + dataSize, - info.Executable.BssAddress, - bssSize); - } - - if (result == KernelResult.Success) - { - result = context.Process.MemoryManager.UnmapProcessCodeMemory( - info.NroMappedAddress + textSize + roSize, - info.Executable.SourceAddress + textSize + roSize, - dataSize); - - if (result == KernelResult.Success) - { - result = context.Process.MemoryManager.UnmapProcessCodeMemory( - info.NroMappedAddress, - info.Executable.SourceAddress, - textSize + roSize); - } - } - - return (ResultCode)result; + return UnmapNroFromInfo(info); } } - return ResultCode.BadNroAddress; + return ResultCode.NotLoaded; + } + + private ResultCode UnmapNroFromInfo(NroInfo info) + { + ulong textSize = (ulong)info.Executable.Text.Length; + ulong roSize = (ulong)info.Executable.Ro.Length; + ulong dataSize = (ulong)info.Executable.Data.Length; + ulong bssSize = (ulong)info.Executable.BssSize; + + KernelResult result = KernelResult.Success; + + if (info.Executable.BssSize != 0) + { + result = _owner.MemoryManager.UnmapProcessCodeMemory( + info.NroMappedAddress + textSize + roSize + dataSize, + info.Executable.BssAddress, + bssSize); + } + + if (result == KernelResult.Success) + { + result = _owner.MemoryManager.UnmapProcessCodeMemory( + info.NroMappedAddress + textSize + roSize, + info.Executable.SourceAddress + textSize + roSize, + dataSize); + + if (result == KernelResult.Success) + { + result = _owner.MemoryManager.UnmapProcessCodeMemory( + info.NroMappedAddress, + info.Executable.SourceAddress, + textSize + roSize); + } + } + + return (ResultCode)result; + } + + private ResultCode IsInitialized(KProcess process) + { + if (_owner != null && _owner.Pid == process.Pid) + { + return ResultCode.Success; + } + + return ResultCode.InvalidProcess; } [Command(0)] // LoadNro(u64, u64, u64, u64, u64, pid) -> u64 public ResultCode LoadNro(ServiceCtx context) { - ResultCode result = ResultCode.BadInitialization; + ResultCode result = IsInitialized(context.Process); // Zero context.RequestData.ReadUInt64(); @@ -369,19 +433,24 @@ namespace Ryujinx.HLE.HOS.Services.Loader ulong nroMappedAddress = 0; - if (_isInitialized) + if (result == ResultCode.Success) { NroInfo info; result = ParseNro(out info, context, nroHeapAddress, nroSize, bssHeapAddress, bssSize); - if (result == 0) + if (result == ResultCode.Success) { - result = MapNro(context, info, out nroMappedAddress); + result = MapNro(context.Process, info, out nroMappedAddress); - if (result == 0) + if (result == ResultCode.Success) { - _nroInfos.Add(info); + result = (ResultCode)SetNroMemoryPermissions(context.Process, info.Executable, nroMappedAddress); + + if (result == ResultCode.Success) + { + _nroInfos.Add(info); + } } } } @@ -395,21 +464,21 @@ namespace Ryujinx.HLE.HOS.Services.Loader // UnloadNro(u64, u64, pid) public ResultCode UnloadNro(ServiceCtx context) { - ResultCode result = ResultCode.BadInitialization; + ResultCode result = IsInitialized(context.Process); // Zero context.RequestData.ReadUInt64(); ulong nroMappedAddress = context.RequestData.ReadUInt64(); - if (_isInitialized) + if (result == ResultCode.Success) { if ((nroMappedAddress & 0xFFF) != 0) { - return ResultCode.UnalignedAddress; + return ResultCode.InvalidAddress; } - result = RemoveNroInfo(context, nroMappedAddress); + result = RemoveNroInfo(nroMappedAddress); } return result; @@ -419,24 +488,24 @@ namespace Ryujinx.HLE.HOS.Services.Loader // LoadNrr(u64, u64, u64, pid) public ResultCode LoadNrr(ServiceCtx context) { - ResultCode result = ResultCode.BadInitialization; + ResultCode result = IsInitialized(context.Process); - // Zero + // pid placeholder, zero context.RequestData.ReadUInt64(); long nrrAddress = context.RequestData.ReadInt64(); long nrrSize = context.RequestData.ReadInt64(); - if (_isInitialized) + if (result == ResultCode.Success) { NrrInfo info; result = ParseNrr(out info, context, nrrAddress, nrrSize); - if (result == 0) + if (result == ResultCode.Success) { if (_nrrInfos.Count >= MaxNrr) { - result = ResultCode.MaxNrr; + result = ResultCode.NotLoaded; } else { @@ -452,18 +521,18 @@ namespace Ryujinx.HLE.HOS.Services.Loader // UnloadNrr(u64, u64, pid) public ResultCode UnloadNrr(ServiceCtx context) { - ResultCode result = ResultCode.BadInitialization; + ResultCode result = IsInitialized(context.Process); - // Zero + // pid placeholder, zero context.RequestData.ReadUInt64(); long nrrHeapAddress = context.RequestData.ReadInt64(); - if (_isInitialized) + if (result == ResultCode.Success) { if ((nrrHeapAddress & 0xFFF) != 0) { - return ResultCode.UnalignedAddress; + return ResultCode.InvalidAddress; } result = RemoveNrrInfo(nrrHeapAddress); @@ -476,10 +545,24 @@ namespace Ryujinx.HLE.HOS.Services.Loader // Initialize(u64, pid, KObject) public ResultCode Initialize(ServiceCtx context) { - // TODO: we actually ignore the pid and process handle receive, we will need to use them when we will have multi process support. - _isInitialized = true; + if (_owner != null) + { + return ResultCode.InvalidSession; + } + + _owner = context.Process; return ResultCode.Success; } + + public void Dispose() + { + foreach (NroInfo info in _nroInfos) + { + UnmapNroFromInfo(info); + } + + _nroInfos.Clear(); + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs new file mode 100644 index 0000000000..ab1c6ac8c4 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ro/ResultCode.cs @@ -0,0 +1,27 @@ +namespace Ryujinx.HLE.HOS.Services.Ro +{ + enum ResultCode + { + ModuleId = 22, + ErrorCodeShift = 22, + + Success = 0, + + InsufficientAddressSpace = (2 << ErrorCodeShift) | ModuleId, + AlreadyLoaded = (3 << ErrorCodeShift) | ModuleId, + InvalidNro = (4 << ErrorCodeShift) | ModuleId, + InvalidNrr = (6 << ErrorCodeShift) | ModuleId, + TooManyNro = (7 << ErrorCodeShift) | ModuleId, + TooManyNrr = (8 << ErrorCodeShift) | ModuleId, + NotAuthorized = (9 << ErrorCodeShift) | ModuleId, + + InvalidNrrType = (10 << ErrorCodeShift) | ModuleId, + + InvalidAddress = (1025 << ErrorCodeShift) | ModuleId, + InvalidSize = (1026 << ErrorCodeShift) | ModuleId, + NotLoaded = (1028 << ErrorCodeShift) | ModuleId, + NotRegistered = (1029 << ErrorCodeShift) | ModuleId, + InvalidSession = (1030 << ErrorCodeShift) | ModuleId, + InvalidProcess = (1031 << ErrorCodeShift) | ModuleId, + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Loader/Types/NroInfo.cs b/Ryujinx.HLE/HOS/Services/Ro/Types/NroInfo.cs similarity index 96% rename from Ryujinx.HLE/HOS/Services/Loader/Types/NroInfo.cs rename to Ryujinx.HLE/HOS/Services/Ro/Types/NroInfo.cs index a71d4c0829..a09116272c 100644 --- a/Ryujinx.HLE/HOS/Services/Loader/Types/NroInfo.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/Types/NroInfo.cs @@ -1,6 +1,6 @@ using Ryujinx.HLE.Loaders.Executables; -namespace Ryujinx.HLE.HOS.Services.Loader +namespace Ryujinx.HLE.HOS.Services.Ro { class NroInfo { diff --git a/Ryujinx.HLE/HOS/Services/Loader/Types/NrrHeader.cs b/Ryujinx.HLE/HOS/Services/Ro/Types/NrrHeader.cs similarity index 94% rename from Ryujinx.HLE/HOS/Services/Loader/Types/NrrHeader.cs rename to Ryujinx.HLE/HOS/Services/Ro/Types/NrrHeader.cs index 1521719666..ec06feb436 100644 --- a/Ryujinx.HLE/HOS/Services/Loader/Types/NrrHeader.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/Types/NrrHeader.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace Ryujinx.HLE.HOS.Services.Loader +namespace Ryujinx.HLE.HOS.Services.Ro { [StructLayout(LayoutKind.Explicit, Size = 0x350)] unsafe struct NrrHeader diff --git a/Ryujinx.HLE/HOS/Services/Loader/Types/NrrInfo.cs b/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs similarity index 91% rename from Ryujinx.HLE/HOS/Services/Loader/Types/NrrInfo.cs rename to Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs index 2c60360a1f..8e038fcb6b 100644 --- a/Ryujinx.HLE/HOS/Services/Loader/Types/NrrInfo.cs +++ b/Ryujinx.HLE/HOS/Services/Ro/Types/NrrInfo.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Ryujinx.HLE.HOS.Services.Loader +namespace Ryujinx.HLE.HOS.Services.Ro { class NrrInfo { From eefe2b20fceb56da2b4507717108b80d935cfc2e Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Sun, 10 Nov 2019 03:21:03 +0100 Subject: [PATCH 4/5] Fix Fcmge_S/V & Fcmgt_S/V Inst.s (#815) * Fix Fcmge_S/V & Fcmgt_S/V. Follow-up Fcm**_S/V & Fc*mp*_S. Improve CmpCondition enum. Nits. * Optimize Fccmp*_S & Fcmp*_S. * Fix cvtsd2si opcode. * Address PR feedback. --- ARMeilleure/CodeGen/X86/Assembler.cs | 2 +- ARMeilleure/CodeGen/X86/CodeGenerator.cs | 16 +++- .../CodeGen/X86/HardwareCapabilities.cs | 2 +- ARMeilleure/CodeGen/X86/IntrinsicTable.cs | 1 + ARMeilleure/Instructions/InstEmitSimdCmp.cs | 77 ++++++++++--------- ARMeilleure/Instructions/InstEmitSimdCvt.cs | 34 ++++---- .../Instructions/InstEmitSimdHelper.cs | 15 ++++ .../IntermediateRepresentation/Intrinsic.cs | 1 + ARMeilleure/Optimizations.cs | 2 + .../Ryujinx.Tests.Unicorn.csproj | 2 +- Ryujinx.Tests/Ryujinx.Tests.csproj | 2 +- 11 files changed, 92 insertions(+), 62 deletions(-) diff --git a/ARMeilleure/CodeGen/X86/Assembler.cs b/ARMeilleure/CodeGen/X86/Assembler.cs index c64838945f..ee80d892b0 100644 --- a/ARMeilleure/CodeGen/X86/Assembler.cs +++ b/ARMeilleure/CodeGen/X86/Assembler.cs @@ -94,7 +94,7 @@ namespace ARMeilleure.CodeGen.X86 Add(X86Instruction.Cvtpd2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Cvtps2dq, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5b, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Cvtps2pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex)); - Add(X86Instruction.Cvtsd2si, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2c, InstructionFlags.Vex | InstructionFlags.PrefixF2)); + Add(X86Instruction.Cvtsd2si, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2d, InstructionFlags.Vex | InstructionFlags.PrefixF2)); Add(X86Instruction.Cvtsd2ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f5a, InstructionFlags.Vex | InstructionFlags.PrefixF2)); Add(X86Instruction.Cvtsi2sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2a, InstructionFlags.Vex | InstructionFlags.PrefixF2)); Add(X86Instruction.Cvtsi2ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2a, InstructionFlags.Vex | InstructionFlags.PrefixF3)); diff --git a/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/ARMeilleure/CodeGen/X86/CodeGenerator.cs index ae24b5631a..33fc2aee3e 100644 --- a/ARMeilleure/CodeGen/X86/CodeGenerator.cs +++ b/ARMeilleure/CodeGen/X86/CodeGenerator.cs @@ -265,7 +265,21 @@ namespace ARMeilleure.CodeGen.X86 Debug.Assert(dest.Type.IsInteger() && !source.Type.IsInteger()); - context.Assembler.WriteInstruction(info.Inst, dest, source, dest.Type); + if (intrinOp.Intrinsic == Intrinsic.X86Cvtsi2si) + { + if (dest.Type == OperandType.I32) + { + context.Assembler.Movd(dest, source); // int _mm_cvtsi128_si32 + } + else /* if (dest.Type == OperandType.I64) */ + { + context.Assembler.Movq(dest, source); // __int64 _mm_cvtsi128_si64 + } + } + else + { + context.Assembler.WriteInstruction(info.Inst, dest, source, dest.Type); + } break; } diff --git a/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs b/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs index 7f930d6b9c..ed81482928 100644 --- a/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs +++ b/ARMeilleure/CodeGen/X86/HardwareCapabilities.cs @@ -26,7 +26,7 @@ namespace ARMeilleure.CodeGen.X86 public static bool ForceLegacySse { get; set; } - public static bool SupportsVexEncoding => !ForceLegacySse && SupportsAvx; + public static bool SupportsVexEncoding => SupportsAvx && !ForceLegacySse; static HardwareCapabilities() { diff --git a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs index e225f2542c..73fb5fd1b6 100644 --- a/ARMeilleure/CodeGen/X86/IntrinsicTable.cs +++ b/ARMeilleure/CodeGen/X86/IntrinsicTable.cs @@ -37,6 +37,7 @@ namespace ARMeilleure.CodeGen.X86 Add(Intrinsic.X86Cvtps2pd, new IntrinsicInfo(X86Instruction.Cvtps2pd, IntrinsicType.Unary)); Add(Intrinsic.X86Cvtsd2si, new IntrinsicInfo(X86Instruction.Cvtsd2si, IntrinsicType.UnaryToGpr)); Add(Intrinsic.X86Cvtsd2ss, new IntrinsicInfo(X86Instruction.Cvtsd2ss, IntrinsicType.Binary)); + Add(Intrinsic.X86Cvtsi2si, new IntrinsicInfo(X86Instruction.Movd, IntrinsicType.UnaryToGpr)); Add(Intrinsic.X86Cvtss2sd, new IntrinsicInfo(X86Instruction.Cvtss2sd, IntrinsicType.Binary)); Add(Intrinsic.X86Divpd, new IntrinsicInfo(X86Instruction.Divpd, IntrinsicType.Binary)); Add(Intrinsic.X86Divps, new IntrinsicInfo(X86Instruction.Divps, IntrinsicType.Binary)); diff --git a/ARMeilleure/Instructions/InstEmitSimdCmp.cs b/ARMeilleure/Instructions/InstEmitSimdCmp.cs index f27121bb33..ac1bffcb2e 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCmp.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCmp.cs @@ -322,7 +322,7 @@ namespace ARMeilleure.Instructions public static void Fcmge_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: true); } @@ -334,7 +334,7 @@ namespace ARMeilleure.Instructions public static void Fcmge_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: false); } @@ -346,7 +346,7 @@ namespace ARMeilleure.Instructions public static void Fcmgt_S(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: true); } @@ -358,7 +358,7 @@ namespace ARMeilleure.Instructions public static void Fcmgt_V(ArmEmitterContext context) { - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && Optimizations.UseAvx) { EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: false); } @@ -372,7 +372,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: true, isLeOrLt: true); + EmitCmpSseOrSse2OpF(context, CmpCondition.LessThanOrEqual, scalar: true); } else { @@ -384,7 +384,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThanOrEqual, scalar: false, isLeOrLt: true); + EmitCmpSseOrSse2OpF(context, CmpCondition.LessThanOrEqual, scalar: false); } else { @@ -396,7 +396,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: true, isLeOrLt: true); + EmitCmpSseOrSse2OpF(context, CmpCondition.LessThan, scalar: true); } else { @@ -408,7 +408,7 @@ namespace ARMeilleure.Instructions { if (Optimizations.FastFP && Optimizations.UseSse2) { - EmitCmpSseOrSse2OpF(context, CmpCondition.GreaterThan, scalar: false, isLeOrLt: true); + EmitCmpSseOrSse2OpF(context, CmpCondition.LessThan, scalar: false); } else { @@ -426,7 +426,7 @@ namespace ARMeilleure.Instructions EmitFcmpOrFcmpe(context, signalNaNs: true); } - public static void EmitFccmpOrFccmpe(ArmEmitterContext context, bool signalNaNs) + private static void EmitFccmpOrFccmpe(ArmEmitterContext context, bool signalNaNs) { OpCodeSimdFcond op = (OpCodeSimdFcond)context.CurrOp; @@ -435,7 +435,7 @@ namespace ARMeilleure.Instructions context.BranchIfTrue(lblTrue, InstEmitFlowHelper.GetCondTrue(context, op.Cond)); - EmitSetNzcv(context, Const(op.Nzcv)); + EmitSetNzcv(context, op.Nzcv); context.Branch(lblEnd); @@ -446,27 +446,47 @@ namespace ARMeilleure.Instructions context.MarkLabel(lblEnd); } + private static void EmitSetNzcv(ArmEmitterContext context, int nzcv) + { + Operand Extract(int value, int bit) + { + if (bit != 0) + { + value >>= bit; + } + + value &= 1; + + return Const(value); + } + + SetFlag(context, PState.VFlag, Extract(nzcv, 0)); + SetFlag(context, PState.CFlag, Extract(nzcv, 1)); + SetFlag(context, PState.ZFlag, Extract(nzcv, 2)); + SetFlag(context, PState.NFlag, Extract(nzcv, 3)); + } + private static void EmitFcmpOrFcmpe(ArmEmitterContext context, bool signalNaNs) { OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; - const int cmpOrdered = 7; - bool cmpWithZero = !(op is OpCodeSimdFcond) ? op.Bit3 : false; - if (Optimizations.FastFP && Optimizations.UseSse2) + if (Optimizations.FastFP && (signalNaNs ? Optimizations.UseAvx : Optimizations.UseSse2)) { Operand n = GetVec(op.Rn); Operand m = cmpWithZero ? context.VectorZero() : GetVec(op.Rm); + CmpCondition cmpOrdered = signalNaNs ? CmpCondition.OrderedS : CmpCondition.OrderedQ; + Operand lblNaN = Label(); Operand lblEnd = Label(); if (op.Size == 0) { - Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpss, n, m, Const(cmpOrdered)); + Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpss, n, m, Const((int)cmpOrdered)); - Operand isOrdered = context.VectorExtract16(ordMask, 0); + Operand isOrdered = context.AddIntrinsicInt(Intrinsic.X86Cvtsi2si, ordMask); context.BranchIfFalse(lblNaN, isOrdered); @@ -481,9 +501,9 @@ namespace ARMeilleure.Instructions } else /* if (op.Size == 1) */ { - Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, m, Const(cmpOrdered)); + Operand ordMask = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, m, Const((int)cmpOrdered)); - Operand isOrdered = context.VectorExtract16(ordMask, 0); + Operand isOrdered = context.AddIntrinsicLong(Intrinsic.X86Cvtsi2si, ordMask); context.BranchIfFalse(lblNaN, isOrdered); @@ -653,18 +673,7 @@ namespace ARMeilleure.Instructions context.Copy(GetVec(op.Rd), res); } - private enum CmpCondition - { - Equal = 0, - GreaterThanOrEqual = 5, - GreaterThan = 6 - } - - private static void EmitCmpSseOrSse2OpF( - ArmEmitterContext context, - CmpCondition cond, - bool scalar, - bool isLeOrLt = false) + private static void EmitCmpSseOrSse2OpF(ArmEmitterContext context, CmpCondition cond, bool scalar) { OpCodeSimd op = (OpCodeSimd)context.CurrOp; @@ -677,9 +686,7 @@ namespace ARMeilleure.Instructions { Intrinsic inst = scalar ? Intrinsic.X86Cmpss : Intrinsic.X86Cmpps; - Operand res = isLeOrLt - ? context.AddIntrinsic(inst, m, n, Const((int)cond)) - : context.AddIntrinsic(inst, n, m, Const((int)cond)); + Operand res = context.AddIntrinsic(inst, n, m, Const((int)cond)); if (scalar) { @@ -696,9 +703,7 @@ namespace ARMeilleure.Instructions { Intrinsic inst = scalar ? Intrinsic.X86Cmpsd : Intrinsic.X86Cmppd; - Operand res = isLeOrLt - ? context.AddIntrinsic(inst, m, n, Const((int)cond)) - : context.AddIntrinsic(inst, n, m, Const((int)cond)); + Operand res = context.AddIntrinsic(inst, n, m, Const((int)cond)); if (scalar) { @@ -709,4 +714,4 @@ namespace ARMeilleure.Instructions } } } -} \ No newline at end of file +} diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt.cs b/ARMeilleure/Instructions/InstEmitSimdCvt.cs index 012bfcce2a..e2b6dbd74c 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCvt.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCvt.cs @@ -732,8 +732,7 @@ namespace ARMeilleure.Instructions Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64); Debug.Assert((uint)size < 2); - OperandType type = size == 0 ? OperandType.FP32 - : OperandType.FP64; + OperandType type = size == 0 ? OperandType.FP32 : OperandType.FP64; if (signed) { @@ -837,15 +836,12 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); - const int cmpGreaterThanOrEqual = 5; - const int cmpOrdered = 7; - // sizeF == ((OpCodeSimdShImm64)op).Size - 2 int sizeF = op.Size & 1; if (sizeF == 0) { - Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmpps, n, n, Const(cmpOrdered)); + Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmpps, n, n, Const((int)CmpCondition.OrderedQ)); Operand nScaled = context.AddIntrinsic(Intrinsic.X86Pand, nMask, n); @@ -867,7 +863,7 @@ namespace ARMeilleure.Instructions Operand mask = X86GetAllElements(context, 0x4F000000); // 2.14748365E9f (2147483648) - Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmpps, nRnd, mask, Const(cmpGreaterThanOrEqual)); + Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmpps, nRnd, mask, Const((int)CmpCondition.NotLessThan)); Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, nInt, mask2); @@ -884,7 +880,7 @@ namespace ARMeilleure.Instructions } else /* if (sizeF == 1) */ { - Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmppd, n, n, Const(cmpOrdered)); + Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmppd, n, n, Const((int)CmpCondition.OrderedQ)); Operand nScaled = context.AddIntrinsic(Intrinsic.X86Pand, nMask, n); @@ -920,7 +916,7 @@ namespace ARMeilleure.Instructions Operand mask = X86GetAllElements(context, 0x43E0000000000000L); // 9.2233720368547760E18d (9223372036854775808) - Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmppd, nRnd, mask, Const(cmpGreaterThanOrEqual)); + Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmppd, nRnd, mask, Const((int)CmpCondition.NotLessThan)); Operand res = context.AddIntrinsic(Intrinsic.X86Pxor, nInt, mask2); @@ -939,16 +935,12 @@ namespace ARMeilleure.Instructions Operand n = GetVec(op.Rn); - const int cmpGreaterThanOrEqual = 5; - const int cmpGreaterThan = 6; - const int cmpOrdered = 7; - // sizeF == ((OpCodeSimdShImm)op).Size - 2 int sizeF = op.Size & 1; if (sizeF == 0) { - Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmpps, n, n, Const(cmpOrdered)); + Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmpps, n, n, Const((int)CmpCondition.OrderedQ)); Operand nScaled = context.AddIntrinsic(Intrinsic.X86Pand, nMask, n); @@ -966,7 +958,7 @@ namespace ARMeilleure.Instructions Operand nRnd = context.AddIntrinsic(Intrinsic.X86Roundps, nScaled, Const(X86GetRoundControl(roundMode))); - Operand nRndMask = context.AddIntrinsic(Intrinsic.X86Cmpps, nRnd, context.VectorZero(), Const(cmpGreaterThan)); + Operand nRndMask = context.AddIntrinsic(Intrinsic.X86Cmpps, nRnd, context.VectorZero(), Const((int)CmpCondition.NotLessThanOrEqual)); Operand nRndMasked = context.AddIntrinsic(Intrinsic.X86Pand, nRnd, nRndMask); @@ -976,13 +968,13 @@ namespace ARMeilleure.Instructions Operand res = context.AddIntrinsic(Intrinsic.X86Subps, nRndMasked, mask); - Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmpps, res, context.VectorZero(), Const(cmpGreaterThan)); + Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmpps, res, context.VectorZero(), Const((int)CmpCondition.NotLessThanOrEqual)); Operand resMasked = context.AddIntrinsic(Intrinsic.X86Pand, res, mask2); res = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, resMasked); - Operand mask3 = context.AddIntrinsic(Intrinsic.X86Cmpps, resMasked, mask, Const(cmpGreaterThanOrEqual)); + Operand mask3 = context.AddIntrinsic(Intrinsic.X86Cmpps, resMasked, mask, Const((int)CmpCondition.NotLessThan)); res = context.AddIntrinsic(Intrinsic.X86Pxor, res, mask3); res = context.AddIntrinsic(Intrinsic.X86Paddd, res, nInt); @@ -1000,7 +992,7 @@ namespace ARMeilleure.Instructions } else /* if (sizeF == 1) */ { - Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmppd, n, n, Const(cmpOrdered)); + Operand nMask = context.AddIntrinsic(Intrinsic.X86Cmppd, n, n, Const((int)CmpCondition.OrderedQ)); Operand nScaled = context.AddIntrinsic(Intrinsic.X86Pand, nMask, n); @@ -1018,7 +1010,7 @@ namespace ARMeilleure.Instructions Operand nRnd = context.AddIntrinsic(Intrinsic.X86Roundpd, nScaled, Const(X86GetRoundControl(roundMode))); - Operand nRndMask = context.AddIntrinsic(Intrinsic.X86Cmppd, nRnd, context.VectorZero(), Const(cmpGreaterThan)); + Operand nRndMask = context.AddIntrinsic(Intrinsic.X86Cmppd, nRnd, context.VectorZero(), Const((int)CmpCondition.NotLessThanOrEqual)); Operand nRndMasked = context.AddIntrinsic(Intrinsic.X86Pand, nRnd, nRndMask); @@ -1042,7 +1034,7 @@ namespace ARMeilleure.Instructions Operand res = context.AddIntrinsic(Intrinsic.X86Subpd, nRndMasked, mask); - Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmppd, res, context.VectorZero(), Const(cmpGreaterThan)); + Operand mask2 = context.AddIntrinsic(Intrinsic.X86Cmppd, res, context.VectorZero(), Const((int)CmpCondition.NotLessThanOrEqual)); Operand resMasked = context.AddIntrinsic(Intrinsic.X86Pand, res, mask2); @@ -1056,7 +1048,7 @@ namespace ARMeilleure.Instructions res = EmitVectorLongCreate(context, low, high); - Operand mask3 = context.AddIntrinsic(Intrinsic.X86Cmppd, resMasked, mask, Const(cmpGreaterThanOrEqual)); + Operand mask3 = context.AddIntrinsic(Intrinsic.X86Cmppd, resMasked, mask, Const((int)CmpCondition.NotLessThan)); res = context.AddIntrinsic(Intrinsic.X86Pxor, res, mask3); res = context.AddIntrinsic(Intrinsic.X86Paddq, res, nInt); diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper.cs b/ARMeilleure/Instructions/InstEmitSimdHelper.cs index a3da80fb0d..f0880079e4 100644 --- a/ARMeilleure/Instructions/InstEmitSimdHelper.cs +++ b/ARMeilleure/Instructions/InstEmitSimdHelper.cs @@ -1108,6 +1108,21 @@ namespace ARMeilleure.Instructions } } + public enum CmpCondition + { + // Legacy Sse. + Equal = 0, // Ordered, non-signaling. + LessThan = 1, // Ordered, signaling. + LessThanOrEqual = 2, // Ordered, signaling. + NotLessThan = 5, // Unordered, signaling. + NotLessThanOrEqual = 6, // Unordered, signaling. + OrderedQ = 7, // Non-signaling. + + // Vex. + GreaterThanOrEqual = 13, // Ordered, signaling. + GreaterThan = 14, // Ordered, signaling. + OrderedS = 23 // Signaling. + } [Flags] public enum SaturatingFlags diff --git a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs index 1fe29e8558..e2d3c6dbaf 100644 --- a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs +++ b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs @@ -26,6 +26,7 @@ namespace ARMeilleure.IntermediateRepresentation X86Cvtps2pd, X86Cvtsd2si, X86Cvtsd2ss, + X86Cvtsi2si, X86Cvtss2sd, X86Divpd, X86Divps, diff --git a/ARMeilleure/Optimizations.cs b/ARMeilleure/Optimizations.cs index 0b9885dc95..28af0936c8 100644 --- a/ARMeilleure/Optimizations.cs +++ b/ARMeilleure/Optimizations.cs @@ -15,6 +15,7 @@ namespace ARMeilleure public static bool UseSse41IfAvailable { get; set; } = true; public static bool UseSse42IfAvailable { get; set; } = true; public static bool UsePopCntIfAvailable { get; set; } = true; + public static bool UseAvxIfAvailable { get; set; } = true; public static bool ForceLegacySse { @@ -29,5 +30,6 @@ namespace ARMeilleure internal static bool UseSse41 => UseSse41IfAvailable && HardwareCapabilities.SupportsSse41; internal static bool UseSse42 => UseSse42IfAvailable && HardwareCapabilities.SupportsSse42; internal static bool UsePopCnt => UsePopCntIfAvailable && HardwareCapabilities.SupportsPopcnt; + internal static bool UseAvx => UseAvxIfAvailable && HardwareCapabilities.SupportsAvx && !ForceLegacySse; } } \ No newline at end of file diff --git a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj index f01e8ced5f..36310f3d2a 100644 --- a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj +++ b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj @@ -22,7 +22,7 @@ - + diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj index 143c4cec84..83ec2e9647 100644 --- a/Ryujinx.Tests/Ryujinx.Tests.csproj +++ b/Ryujinx.Tests/Ryujinx.Tests.csproj @@ -27,7 +27,7 @@ - + From f0188bb34502c94b6826c986ce1d5e11aeb4ce56 Mon Sep 17 00:00:00 2001 From: MysticExile <30736337+MysticExile@users.noreply.github.com> Date: Sun, 10 Nov 2019 14:02:29 +0100 Subject: [PATCH 5/5] Update KEYS.md to use a new dumping guide and correct title.key formatting (#814) * Update title.keys formatting and add key dumping guide * Update README.md to reflect changes in KEYS.md * Address feedback and minor style changes * Remove key templates * Fix Co-Authored-By: Ac_K * Fix 2 Co-Authored-By: jduncanator <1518948+jduncanator@users.noreply.github.com> * Add a description of what keys are * Minor changes and style fixes * Fix 3 --- KEYS.md | 112 ++++++++++++------------------------------------------ README.md | 2 +- 2 files changed, 25 insertions(+), 89 deletions(-) diff --git a/KEYS.md b/KEYS.md index a2867ddca2..2250cd3e27 100644 --- a/KEYS.md +++ b/KEYS.md @@ -2,103 +2,39 @@ Keys are required for decrypting most of the file formats used by the Nintendo Switch. -Keysets are stored as text files. These 3 filenames are automatically read: -`prod.keys` - Contains common keys usedy by all Switch devices. -`console.keys` - Contains console-unique keys. -`title.keys` - Contains game-specific keys. + Keysets are stored as text files. These 2 filenames are automatically read: +* `prod.keys` - Contains common keys used by all Nintendo Switch devices. +* `title.keys` - Contains game-specific keys. Ryujinx will first look for keys in `RyuFS/system`, and if it doesn't find any there it will look in `$HOME/.switch`. - -A guide to assist with dumping your own keys can be found [here](https://gist.github.com/roblabla/d8358ab058bbe3b00614740dcba4f208). - -## Common keys - -Here is a template for a key file containing the main keys Ryujinx uses to read content files. -Both `prod.keys` and `console.keys` use this format. - -``` -master_key_00 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -master_key_01 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -master_key_02 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -master_key_03 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -master_key_04 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -master_key_05 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - -titlekek_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -key_area_key_application_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -key_area_key_ocean_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -key_area_key_system_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -aes_kek_generation_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -aes_key_generation_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -header_kek_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -header_key_source = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -``` +To dump your `prod.keys` and `title.keys` please follow these following steps. +1. First off learn how to boot into RCM mode and inject payloads if you haven't already. This can be done [here](https://nh-server.github.io/switch-guide/). +2. Make sure you have an SD card with the latest release of [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere/releases) inserted into your Nintendo Switch. +3. Download the latest release of [Lockpick_RCM](https://github.com/shchmue/Lockpick_RCM/releases). +4. Boot into RCM mode. +5. Inject the `Lockpick_RCM.bin` that you have downloaded at `Step 3.` using your preferred payload injector. We recommend [TegraRCMGUI](https://github.com/eliboa/TegraRcmGUI/releases) as it is easy to use and has a decent feature set. +6. Using the `Vol+/-` buttons to navigate and the `Power` button to select, select `Dump from SysNAND | Key generation: X` ("X" depends on your Nintendo Switch's firmware version) +7. The dumping process may take a while depending on how many titles you have installed. +8. After its completion press any button to return to the main menu of Lockpick_RCM. +9. Navigate to and select `Power off` if you have an SD card reader. Or you could Navigate and select `Reboot (RCM)` if you want to mount your SD card using `TegraRCMGUI > Tools > Memloader V3 > MMC - SD Card`. +10. You can find your keys in `sd:/switch/prod.keys` and `sd:/switch/title.keys` respectively. +11. Copy these files and paste them in `RyuFS/system`. +And you're done! ## Title keys -Title keys are stored in the format `rights_id,key`. +These are only used for games that are not dumped from cartridges but from games downloaded from the Nintendo eShop, these are also only used if the eShop dump does *not* have a `ticket`. If the game does have a ticket, Ryujinx will read the key directly from that ticket. + +Title keys are stored in the format `rights_id = key`. For example: ``` -01000000000100000000000000000003,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -01000000000108000000000000000003,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -01000000000108000000000000000004,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +01000000000100000000000000000003 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +01000000000108000000000000000003 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +01000000000108000000000000000004 = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ``` -## Complete key list -Below is a complete list of keys that are currently recognized. -\## represents a hexadecimal number between 00 and 1F -@@ represents a hexadecimal number between 00 and 03 +## Prod keys -### Common keys - -``` -master_key_source -keyblob_mac_key_source -package2_key_source -aes_kek_generation_source -aes_key_generation_source -key_area_key_application_source -key_area_key_ocean_source -key_area_key_system_source -titlekek_source -header_kek_source -header_key_source -sd_card_kek_source -sd_card_nca_key_source -sd_card_save_key_source -retail_specific_aes_key_source -per_console_key_source -bis_kek_source -bis_key_source_@@ - -header_key -xci_header_key -eticket_rsa_kek - -master_key_## -package1_key_## -package2_key_## -titlekek_## -key_area_key_application_## -key_area_key_ocean_## -key_area_key_system_## -keyblob_key_source_## -keyblob_## -``` - -### Console-unique keys - -``` -secure_boot_key -tsec_key -device_key -bis_key_@@ - -keyblob_key_## -keyblob_mac_key_## -encrypted_keyblob_## - -sd_seed -``` +These are typically used to decrypt system files and encrypted game files. These keys get changed in about every major system update, so make sure to keep your keys up-to-date if you want to play newer games! \ No newline at end of file diff --git a/README.md b/README.md index 0ecb2769ad..9fdfadc079 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of - **Switch Keys** - Everything on the Switch is encrypted, so if you want to run anything other than homebrew, you have to dump encryption keys from your console. To get more information please take a look at our [Keys Documentation](KEYS.md) *(Outdated)*. + Everything on the Switch is encrypted, so if you want to run anything other than homebrew, you have to dump encryption keys from your console. To get more information please take a look at our [Keys Documentation](KEYS.md). - **FFmpeg Dependencies**