From 1f544bbba4bc4efdffe1efe34e297f6ae45abe0e Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 20 Nov 2018 19:44:10 -0300 Subject: [PATCH] Attempt at implementing ldr:ro with new KProcess --- Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs | 88 ++++++++--- .../HOS/Kernel/KMemoryRegionManager.cs | 2 - Ryujinx.HLE/HOS/Kernel/SvcMemory.cs | 8 +- Ryujinx.HLE/HOS/ProgramLoader.cs | 17 ++- Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs | 139 ++++++++++++++---- 5 files changed, 193 insertions(+), 61 deletions(-) diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs index b11c3679fb..1277383cf2 100644 --- a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs @@ -382,7 +382,7 @@ namespace Ryujinx.HLE.HOS.Kernel { KPageList CurrentPageList = new KPageList(); - AddVaRangeToPageList(CurrentPageList, Address, Address + Size); + AddVaRangeToPageList(CurrentPageList, Address, PagesCount); if (!CurrentPageList.IsEqual(PageList)) { @@ -585,6 +585,62 @@ namespace Ryujinx.HLE.HOS.Kernel } } + public KernelResult MapProcessCodeMemory(ulong Dst, ulong Src, ulong Size) + { + ulong PagesCount = Size / PageSize; + + lock (Blocks) + { + bool Success = CheckRange( + Src, + Size, + MemoryState.Mask, + MemoryState.Heap, + MemoryPermission.Mask, + MemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out MemoryState State, + out MemoryPermission Permission, + out _); + + Success &= IsUnmapped(Dst, Size); + + if (Success) + { + KPageList PageList = new KPageList(); + + AddVaRangeToPageList(PageList, Src, PagesCount); + + KernelResult Result = MmuChangePermission(Src, PagesCount, MemoryPermission.None); + + if (Result != KernelResult.Success) + { + return Result; + } + + Result = MapPages(Dst, PageList, MemoryPermission.None); + + if (Result != KernelResult.Success) + { + MmuChangePermission(Src, PagesCount, Permission); + + return Result; + } + + InsertBlock(Src, PagesCount, State, MemoryPermission.None, MemoryAttribute.Borrowed); + InsertBlock(Dst, PagesCount, MemoryState.ModCodeStatic); + + return KernelResult.Success; + } + else + { + return KernelResult.InvalidMemState; + } + } + } + public KernelResult UnmapProcessCodeMemory(ulong Dst, ulong Src, ulong Size) { ulong PagesCount = Size / PageSize; @@ -844,7 +900,7 @@ namespace Ryujinx.HLE.HOS.Kernel KPageList PageList = new KPageList(); - AddVaRangeToPageList(PageList, Src, Src + Size); + AddVaRangeToPageList(PageList, Src, PagesCount); KernelResult Result = MmuChangePermission(Src, PagesCount, MemoryPermission.None); @@ -954,8 +1010,8 @@ namespace Ryujinx.HLE.HOS.Kernel KPageList SrcPageList = new KPageList(); KPageList DstPageList = new KPageList(); - AddVaRangeToPageList(SrcPageList, Src, Src + Size); - AddVaRangeToPageList(DstPageList, Dst, Dst + Size); + AddVaRangeToPageList(SrcPageList, Src, PagesCount); + AddVaRangeToPageList(DstPageList, Dst, PagesCount); if (!DstPageList.IsEqual(SrcPageList)) { @@ -1220,7 +1276,7 @@ namespace Ryujinx.HLE.HOS.Kernel ulong BlockSize = GetSizeInRange(Info, Address, EndAddr); ulong BlockAddress = GetAddrInRange(Info, Address); - AddVaRangeToPageList(PageList, BlockAddress, BlockAddress + BlockSize); + AddVaRangeToPageList(PageList, BlockAddress, BlockSize / PageSize); HeapMappedSize += BlockSize; } @@ -1380,11 +1436,13 @@ namespace Ryujinx.HLE.HOS.Kernel return Info.Address; } - private void AddVaRangeToPageList(KPageList PageList, ulong Start, ulong End) + private void AddVaRangeToPageList(KPageList PageList, ulong Start, ulong PagesCount) { - while (Start < End) + ulong Address = Start; + + while (Address < Start + PagesCount * PageSize) { - KernelResult Result = ConvertVaToPa(Start, out ulong Pa); + KernelResult Result = ConvertVaToPa(Address, out ulong Pa); if (Result != KernelResult.Success) { @@ -1393,22 +1451,10 @@ namespace Ryujinx.HLE.HOS.Kernel PageList.AddRange(Pa, 1); - Start += PageSize; + Address += PageSize; } } - public bool HleIsUnmapped(ulong Address, ulong Size) - { - bool Result = false; - - lock (Blocks) - { - Result = IsUnmapped(Address, Size); - } - - return Result; - } - private bool IsUnmapped(ulong Address, ulong Size) { return CheckRange( diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs index 5c0517ea0e..81f8e06675 100644 --- a/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs @@ -95,8 +95,6 @@ namespace Ryujinx.HLE.HOS.Kernel private KernelResult AllocatePagesImpl(ulong PagesCount, bool Backwards, out KPageList PageList) { - Backwards = false; - PageList = new KPageList(); if (BlockOrdersCount > 0) diff --git a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs index 4927d1b0fc..d639602ab2 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs @@ -264,7 +264,7 @@ namespace Ryujinx.HLE.HOS.Kernel return; } - if ((ulong)(Address + Size) <= (ulong)Address) + if (Address + Size <= Address) { Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!"); @@ -347,7 +347,7 @@ namespace Ryujinx.HLE.HOS.Kernel return; } - if ((ulong)(Address + Size) <= (ulong)Address) + if (Address + Size <= Address) { Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!"); @@ -417,7 +417,7 @@ namespace Ryujinx.HLE.HOS.Kernel return; } - if ((ulong)(Address + Size) <= (ulong)Address) + if (Address + Size <= Address) { Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!"); @@ -533,7 +533,7 @@ namespace Ryujinx.HLE.HOS.Kernel return; } - if ((ulong)(Address + Size) <= (ulong)Address) + if (Address + Size <= Address) { Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!"); diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index 735255c87d..6f6e063abc 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS if (Result != KernelResult.Success) { - Logger.PrintError(LogClass.Loader, $"Resource limit initialization returned error \"{Result}\"."); + Logger.PrintError(LogClass.Loader, $"Process initialization failed setting resource limit values."); return false; } @@ -128,9 +128,18 @@ namespace Ryujinx.HLE.HOS MemoryHelper.FillWithZeros(Process.CpuMemory, (long)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); + KMemoryManager MemMgr = Process.MemoryManager; + + Result = MemMgr.SetProcessMemoryPermission(TextStart, ROStart - TextStart, MemoryPermission.ReadAndExecute); + Result |= MemMgr.SetProcessMemoryPermission(ROStart, DataStart - ROStart, MemoryPermission.Read); + Result |= MemMgr.SetProcessMemoryPermission(DataStart, BssEnd - DataStart, MemoryPermission.ReadAndWrite); + + if (Result != KernelResult.Success) + { + Logger.PrintError(LogClass.Loader, $"Process initialization failed setting memory permissions."); + + return false; + } } Result = Process.Start(MetaData.MainThreadPriority, (ulong)MetaData.MainThreadStackSize); diff --git a/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs index df16fe2a34..38236928e3 100644 --- a/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs @@ -1,4 +1,6 @@ -using Ryujinx.HLE.HOS.Ipc; +using ChocolArm64.Memory; +using Ryujinx.Common; +using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Utilities; @@ -63,16 +65,31 @@ namespace Ryujinx.HLE.HOS.Services.Ldr class NroInfo { - public NxRelocatableObject Executable { get; private set; } + public NxRelocatableObject Executable { get; private set; } + public byte[] Hash { get; private set; } public ulong NroAddress { get; private set; } + public ulong NroSize { get; private set; } + public ulong BssAddress { get; private set; } + public ulong BssSize { get; private set; } public ulong TotalSize { get; private set; } public ulong NroMappedAddress { get; set; } - public NroInfo(NxRelocatableObject Executable, byte[] Hash, ulong TotalSize) + public NroInfo( + NxRelocatableObject Executable, + byte[] Hash, + ulong NroAddress, + ulong NroSize, + ulong BssAddress, + ulong BssSize, + ulong TotalSize) { this.Executable = Executable; this.Hash = Hash; + this.NroAddress = NroAddress; + this.NroSize = NroSize; + this.BssAddress = BssAddress; + this.BssSize = BssSize; this.TotalSize = TotalSize; } } @@ -175,7 +192,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldr return false; } - public long ParseNro(out NroInfo Res, ServiceCtx Context, ulong NroHeapAddress, ulong NroSize, ulong BssHeapAddress, ulong BssSize) + public long ParseNro(out NroInfo Res, ServiceCtx Context, ulong NroAddress, ulong NroSize, ulong BssAddress, ulong BssSize) { Res = null; @@ -183,28 +200,28 @@ namespace Ryujinx.HLE.HOS.Services.Ldr { return MakeError(ErrorModule.Loader, LoaderErr.MaxNro); } - else if (NroSize == 0 || NroHeapAddress + NroSize <= NroHeapAddress || (NroSize & 0xFFF) != 0) + else if (NroSize == 0 || NroAddress + NroSize <= NroAddress || (NroSize & 0xFFF) != 0) { return MakeError(ErrorModule.Loader, LoaderErr.BadSize); } - else if (BssSize != 0 && (BssHeapAddress + BssSize) <= BssHeapAddress) + else if (BssSize != 0 && (BssAddress + BssSize) <= BssAddress) { return MakeError(ErrorModule.Loader, LoaderErr.BadSize); } - else if ((NroHeapAddress & 0xFFF) != 0) + else if ((NroAddress & 0xFFF) != 0) { return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress); } - uint Magic = Context.Memory.ReadUInt32((long)NroHeapAddress + 0x10); - uint NroFileSize = Context.Memory.ReadUInt32((long)NroHeapAddress + 0x18); + uint Magic = Context.Memory.ReadUInt32((long)NroAddress + 0x10); + uint NroFileSize = Context.Memory.ReadUInt32((long)NroAddress + 0x18); if (Magic != NroMagic || NroSize != NroFileSize) { return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro); } - byte[] NroData = Context.Memory.ReadBytes((long)NroHeapAddress, (long)NroSize); + byte[] NroData = Context.Memory.ReadBytes((long)NroAddress, (long)NroSize); byte[] NroHash = null; MemoryStream Stream = new MemoryStream(NroData); @@ -226,7 +243,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldr Stream.Position = 0; - NxRelocatableObject Executable = new NxRelocatableObject(Stream, NroHeapAddress, BssHeapAddress); + NxRelocatableObject Executable = new NxRelocatableObject(Stream, NroAddress, BssAddress); // check if everything is page align. if ((Executable.Text.Length & 0xFFF) != 0 || (Executable.RO.Length & 0xFFF) != 0 || @@ -251,7 +268,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldr int TotalSize = Executable.Text.Length + Executable.RO.Length + Executable.Data.Length + Executable.BssSize; - Res = new NroInfo(Executable, NroHash, (ulong)TotalSize); + Res = new NroInfo( + Executable, + NroHash, + NroAddress, + NroSize, + BssAddress, + BssSize, + (ulong)TotalSize); return 0; } @@ -260,36 +284,58 @@ namespace Ryujinx.HLE.HOS.Services.Ldr { NroMappedAddress = 0; - ulong TargetAddress = Context.Process.MemoryManager.AddrSpaceStart; + KMemoryManager MemMgr = Context.Process.MemoryManager; - ulong HeapRegionStart = Context.Process.MemoryManager.HeapRegionStart; - ulong HeapRegionEnd = Context.Process.MemoryManager.HeapRegionEnd; - - ulong MapRegionStart = Context.Process.MemoryManager.AliasRegionStart; - ulong MapRegionEnd = Context.Process.MemoryManager.AliasRegionEnd; + ulong TargetAddress = MemMgr.AddrSpaceStart; while (true) { - if (TargetAddress + Info.TotalSize >= Context.Process.MemoryManager.AddrSpaceEnd) + if (TargetAddress + Info.TotalSize >= MemMgr.AddrSpaceEnd) { return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState); } - bool IsValidAddress = !(HeapRegionStart > 0 && HeapRegionStart <= TargetAddress + Info.TotalSize - 1 - && TargetAddress <= HeapRegionEnd - 1) - && !(MapRegionStart > 0 - && MapRegionStart <= TargetAddress + Info.TotalSize - 1 - && TargetAddress <= MapRegionEnd - 1); + KMemoryInfo MemInfo = MemMgr.QueryMemory(TargetAddress); - if (IsValidAddress && Context.Process.MemoryManager.HleIsUnmapped(TargetAddress, Info.TotalSize)) + if (MemInfo.State == MemoryState.Unmapped && MemInfo.Size >= Info.TotalSize) { - break; + if (!MemMgr.InsideHeapRegion (TargetAddress, Info.TotalSize) && + !MemMgr.InsideAliasRegion(TargetAddress, Info.TotalSize)) + { + break; + } } - TargetAddress += 0x1000; + TargetAddress += MemInfo.Size; } - //Context.Process.LoadProgram(Info.Executable, TargetAddress); + KernelResult Result = MemMgr.MapProcessCodeMemory(TargetAddress, Info.NroAddress, Info.NroSize); + + if (Result != KernelResult.Success) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState); + } + + ulong BssTargetAddress = TargetAddress + Info.NroSize; + + Result = MemMgr.MapProcessCodeMemory(BssTargetAddress, Info.BssAddress, Info.BssSize); + + if (Result != KernelResult.Success) + { + MemMgr.UnmapProcessCodeMemory(TargetAddress, Info.NroAddress, Info.NroSize); + + return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState); + } + + Result = LoadNroIntoMemory(Context.Process, Info.Executable, TargetAddress); + + if (Result != KernelResult.Success) + { + MemMgr.UnmapProcessCodeMemory(TargetAddress, Info.NroAddress, Info.NroSize); + MemMgr.UnmapProcessCodeMemory(BssTargetAddress, Info.BssAddress, Info.BssSize); + + return 0; + } Info.NroMappedAddress = TargetAddress; NroMappedAddress = TargetAddress; @@ -297,6 +343,41 @@ namespace Ryujinx.HLE.HOS.Services.Ldr return 0; } + private KernelResult LoadNroIntoMemory(KProcess Process, IExecutable RelocatableObject, ulong BaseAddress) + { + ulong TextStart = BaseAddress + (ulong)RelocatableObject.TextOffset; + ulong ROStart = BaseAddress + (ulong)RelocatableObject.ROOffset; + ulong DataStart = BaseAddress + (ulong)RelocatableObject.DataOffset; + + ulong BssStart = DataStart + (ulong)RelocatableObject.Data.Length; + + ulong BssEnd = BitUtils.AlignUp(BssStart + (ulong)RelocatableObject.BssSize, KMemoryManager.PageSize); + + Process.CpuMemory.WriteBytes((long)TextStart, RelocatableObject.Text); + Process.CpuMemory.WriteBytes((long)ROStart, RelocatableObject.RO); + Process.CpuMemory.WriteBytes((long)DataStart, RelocatableObject.Data); + + MemoryHelper.FillWithZeros(Process.CpuMemory, (long)BssStart, (int)(BssEnd - BssStart)); + + KernelResult Result; + + Result = Process.MemoryManager.SetProcessMemoryPermission(TextStart, ROStart - TextStart, MemoryPermission.ReadAndExecute); + + if (Result != KernelResult.Success) + { + return Result; + } + + Result = Process.MemoryManager.SetProcessMemoryPermission(ROStart, DataStart - ROStart, MemoryPermission.Read); + + if (Result != KernelResult.Success) + { + return Result; + } + + return Process.MemoryManager.SetProcessMemoryPermission(DataStart, BssEnd - DataStart, MemoryPermission.ReadAndWrite); + } + private long RemoveNrrInfo(long NrrAddress) { foreach (NrrInfo Info in NrrInfos) @@ -320,8 +401,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldr { NroInfos.Remove(Info); - //Context.Process.RemoveProgram(Info.NroMappedAddress); - KernelResult Result = Context.Process.MemoryManager.UnmapProcessCodeMemory( Info.NroMappedAddress, Info.Executable.SourceAddress,