Attempt at implementing ldr:ro with new KProcess

This commit is contained in:
gdkchan 2018-11-20 19:44:10 -03:00
commit 1f544bbba4
5 changed files with 193 additions and 61 deletions

View file

@ -382,7 +382,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{ {
KPageList CurrentPageList = new KPageList(); KPageList CurrentPageList = new KPageList();
AddVaRangeToPageList(CurrentPageList, Address, Address + Size); AddVaRangeToPageList(CurrentPageList, Address, PagesCount);
if (!CurrentPageList.IsEqual(PageList)) 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) public KernelResult UnmapProcessCodeMemory(ulong Dst, ulong Src, ulong Size)
{ {
ulong PagesCount = Size / PageSize; ulong PagesCount = Size / PageSize;
@ -844,7 +900,7 @@ namespace Ryujinx.HLE.HOS.Kernel
KPageList PageList = new KPageList(); KPageList PageList = new KPageList();
AddVaRangeToPageList(PageList, Src, Src + Size); AddVaRangeToPageList(PageList, Src, PagesCount);
KernelResult Result = MmuChangePermission(Src, PagesCount, MemoryPermission.None); KernelResult Result = MmuChangePermission(Src, PagesCount, MemoryPermission.None);
@ -954,8 +1010,8 @@ namespace Ryujinx.HLE.HOS.Kernel
KPageList SrcPageList = new KPageList(); KPageList SrcPageList = new KPageList();
KPageList DstPageList = new KPageList(); KPageList DstPageList = new KPageList();
AddVaRangeToPageList(SrcPageList, Src, Src + Size); AddVaRangeToPageList(SrcPageList, Src, PagesCount);
AddVaRangeToPageList(DstPageList, Dst, Dst + Size); AddVaRangeToPageList(DstPageList, Dst, PagesCount);
if (!DstPageList.IsEqual(SrcPageList)) if (!DstPageList.IsEqual(SrcPageList))
{ {
@ -1220,7 +1276,7 @@ namespace Ryujinx.HLE.HOS.Kernel
ulong BlockSize = GetSizeInRange(Info, Address, EndAddr); ulong BlockSize = GetSizeInRange(Info, Address, EndAddr);
ulong BlockAddress = GetAddrInRange(Info, Address); ulong BlockAddress = GetAddrInRange(Info, Address);
AddVaRangeToPageList(PageList, BlockAddress, BlockAddress + BlockSize); AddVaRangeToPageList(PageList, BlockAddress, BlockSize / PageSize);
HeapMappedSize += BlockSize; HeapMappedSize += BlockSize;
} }
@ -1380,11 +1436,13 @@ namespace Ryujinx.HLE.HOS.Kernel
return Info.Address; 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) if (Result != KernelResult.Success)
{ {
@ -1393,22 +1451,10 @@ namespace Ryujinx.HLE.HOS.Kernel
PageList.AddRange(Pa, 1); 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) private bool IsUnmapped(ulong Address, ulong Size)
{ {
return CheckRange( return CheckRange(

View file

@ -95,8 +95,6 @@ namespace Ryujinx.HLE.HOS.Kernel
private KernelResult AllocatePagesImpl(ulong PagesCount, bool Backwards, out KPageList PageList) private KernelResult AllocatePagesImpl(ulong PagesCount, bool Backwards, out KPageList PageList)
{ {
Backwards = false;
PageList = new KPageList(); PageList = new KPageList();
if (BlockOrdersCount > 0) if (BlockOrdersCount > 0)

View file

@ -264,7 +264,7 @@ namespace Ryujinx.HLE.HOS.Kernel
return; 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}!"); Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
@ -347,7 +347,7 @@ namespace Ryujinx.HLE.HOS.Kernel
return; 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}!"); Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
@ -417,7 +417,7 @@ namespace Ryujinx.HLE.HOS.Kernel
return; 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}!"); Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
@ -533,7 +533,7 @@ namespace Ryujinx.HLE.HOS.Kernel
return; 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}!"); Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");

View file

@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS
if (Result != KernelResult.Success) 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; return false;
} }
@ -128,9 +128,18 @@ namespace Ryujinx.HLE.HOS
MemoryHelper.FillWithZeros(Process.CpuMemory, (long)BssStart, (int)(BssEnd - BssStart)); MemoryHelper.FillWithZeros(Process.CpuMemory, (long)BssStart, (int)(BssEnd - BssStart));
Process.MemoryManager.SetProcessMemoryPermission(TextStart, ROStart - TextStart, MemoryPermission.ReadAndExecute); KMemoryManager MemMgr = Process.MemoryManager;
Process.MemoryManager.SetProcessMemoryPermission(ROStart, DataStart - ROStart, MemoryPermission.Read);
Process.MemoryManager.SetProcessMemoryPermission(DataStart, BssEnd - DataStart, MemoryPermission.ReadAndWrite); 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); Result = Process.Start(MetaData.MainThreadPriority, (ulong)MetaData.MainThreadStackSize);

View file

@ -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.HOS.Kernel;
using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Utilities; using Ryujinx.HLE.Utilities;
@ -64,15 +66,30 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
class NroInfo class NroInfo
{ {
public NxRelocatableObject Executable { get; private set; } public NxRelocatableObject Executable { get; private set; }
public byte[] Hash { get; private set; } public byte[] Hash { get; private set; }
public ulong NroAddress { 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 TotalSize { get; private set; }
public ulong NroMappedAddress { get; 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.Executable = Executable;
this.Hash = Hash; this.Hash = Hash;
this.NroAddress = NroAddress;
this.NroSize = NroSize;
this.BssAddress = BssAddress;
this.BssSize = BssSize;
this.TotalSize = TotalSize; this.TotalSize = TotalSize;
} }
} }
@ -175,7 +192,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
return false; 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; Res = null;
@ -183,28 +200,28 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
{ {
return MakeError(ErrorModule.Loader, LoaderErr.MaxNro); 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); 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); return MakeError(ErrorModule.Loader, LoaderErr.BadSize);
} }
else if ((NroHeapAddress & 0xFFF) != 0) else if ((NroAddress & 0xFFF) != 0)
{ {
return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress); return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress);
} }
uint Magic = Context.Memory.ReadUInt32((long)NroHeapAddress + 0x10); uint Magic = Context.Memory.ReadUInt32((long)NroAddress + 0x10);
uint NroFileSize = Context.Memory.ReadUInt32((long)NroHeapAddress + 0x18); uint NroFileSize = Context.Memory.ReadUInt32((long)NroAddress + 0x18);
if (Magic != NroMagic || NroSize != NroFileSize) if (Magic != NroMagic || NroSize != NroFileSize)
{ {
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro); 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; byte[] NroHash = null;
MemoryStream Stream = new MemoryStream(NroData); MemoryStream Stream = new MemoryStream(NroData);
@ -226,7 +243,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
Stream.Position = 0; Stream.Position = 0;
NxRelocatableObject Executable = new NxRelocatableObject(Stream, NroHeapAddress, BssHeapAddress); NxRelocatableObject Executable = new NxRelocatableObject(Stream, NroAddress, BssAddress);
// check if everything is page align. // check if everything is page align.
if ((Executable.Text.Length & 0xFFF) != 0 || (Executable.RO.Length & 0xFFF) != 0 || 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; 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; return 0;
} }
@ -260,36 +284,58 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
{ {
NroMappedAddress = 0; NroMappedAddress = 0;
ulong TargetAddress = Context.Process.MemoryManager.AddrSpaceStart; KMemoryManager MemMgr = Context.Process.MemoryManager;
ulong HeapRegionStart = Context.Process.MemoryManager.HeapRegionStart; ulong TargetAddress = MemMgr.AddrSpaceStart;
ulong HeapRegionEnd = Context.Process.MemoryManager.HeapRegionEnd;
ulong MapRegionStart = Context.Process.MemoryManager.AliasRegionStart;
ulong MapRegionEnd = Context.Process.MemoryManager.AliasRegionEnd;
while (true) while (true)
{ {
if (TargetAddress + Info.TotalSize >= Context.Process.MemoryManager.AddrSpaceEnd) if (TargetAddress + Info.TotalSize >= MemMgr.AddrSpaceEnd)
{ {
return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState); return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState);
} }
bool IsValidAddress = !(HeapRegionStart > 0 && HeapRegionStart <= TargetAddress + Info.TotalSize - 1 KMemoryInfo MemInfo = MemMgr.QueryMemory(TargetAddress);
&& TargetAddress <= HeapRegionEnd - 1)
&& !(MapRegionStart > 0
&& MapRegionStart <= TargetAddress + Info.TotalSize - 1
&& TargetAddress <= MapRegionEnd - 1);
if (IsValidAddress && Context.Process.MemoryManager.HleIsUnmapped(TargetAddress, Info.TotalSize)) if (MemInfo.State == MemoryState.Unmapped && MemInfo.Size >= Info.TotalSize)
{
if (!MemMgr.InsideHeapRegion (TargetAddress, Info.TotalSize) &&
!MemMgr.InsideAliasRegion(TargetAddress, Info.TotalSize))
{ {
break; break;
} }
TargetAddress += 0x1000;
} }
//Context.Process.LoadProgram(Info.Executable, TargetAddress); TargetAddress += MemInfo.Size;
}
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; Info.NroMappedAddress = TargetAddress;
NroMappedAddress = TargetAddress; NroMappedAddress = TargetAddress;
@ -297,6 +343,41 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
return 0; 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) private long RemoveNrrInfo(long NrrAddress)
{ {
foreach (NrrInfo Info in NrrInfos) foreach (NrrInfo Info in NrrInfos)
@ -320,8 +401,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
{ {
NroInfos.Remove(Info); NroInfos.Remove(Info);
//Context.Process.RemoveProgram(Info.NroMappedAddress);
KernelResult Result = Context.Process.MemoryManager.UnmapProcessCodeMemory( KernelResult Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(
Info.NroMappedAddress, Info.NroMappedAddress,
Info.Executable.SourceAddress, Info.Executable.SourceAddress,