Better process implementation (#491)

* Initial implementation of KProcess

* Some improvements to the memory manager, implement back guest stack trace printing

* Better GetInfo implementation, improve checking in some places with information from process capabilities

* Allow the cpu to read/write from the correct memory locations for accesses crossing a page boundary

* Change long -> ulong for address/size on memory related methods to avoid unnecessary casts

* Attempt at implementing ldr:ro with new KProcess

* Allow BSS with size 0 on ldr:ro

* Add checking for memory block slab heap usage, return errors if full, exit gracefully

* Use KMemoryBlockSize const from KMemoryManager

* Allow all methods to read from non-contiguous locations

* Fix for TransactParcelAuto

* Address PR feedback, additionally fix some small issues related to the KIP loader and implement SVCs GetProcessId, GetProcessList, GetSystemInfo, CreatePort and ManageNamedPort

* Fix wrong check for source pages count from page list on MapPhysicalMemory

* Fix some issues with UnloadNro on ldr:ro
This commit is contained in:
gdkchan 2018-11-28 20:18:09 -02:00 committed by GitHub
parent e7fe7d7247
commit 00579927e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
119 changed files with 7998 additions and 3232 deletions

View file

@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
ulong Size = ThreadState.X1;
if ((Size & 0xFFFFFFFE001FFFFF) != 0)
if ((Size & 0xfffffffe001fffff) != 0)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!");
@ -20,24 +20,24 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
long Result = Process.MemoryManager.TrySetHeapSize((long)Size, out long Position);
KernelResult Result = Process.MemoryManager.SetHeapSize(Size, out ulong Position);
ThreadState.X0 = (ulong)Result;
if (Result == 0)
if (Result == KernelResult.Success)
{
ThreadState.X1 = (ulong)Position;
ThreadState.X1 = Position;
}
else
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
}
}
private void SvcSetMemoryAttribute(CpuThreadState ThreadState)
{
long Position = (long)ThreadState.X0;
long Size = (long)ThreadState.X1;
ulong Position = ThreadState.X0;
ulong Size = ThreadState.X1;
if (!PageAligned(Position))
{
@ -72,19 +72,19 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
long Result = Process.MemoryManager.SetMemoryAttribute(
KernelResult Result = Process.MemoryManager.SetMemoryAttribute(
Position,
Size,
AttributeMask,
AttributeValue);
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}\".");
}
else
{
Memory.StopObservingRegion(Position, Size);
Memory.StopObservingRegion((long)Position, (long)Size);
}
ThreadState.X0 = (ulong)Result;
@ -92,9 +92,9 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcMapMemory(CpuThreadState ThreadState)
{
long Dst = (long)ThreadState.X0;
long Src = (long)ThreadState.X1;
long Size = (long)ThreadState.X2;
ulong Dst = ThreadState.X0;
ulong Src = ThreadState.X1;
ulong Size = ThreadState.X2;
if (!PageAligned(Src | Dst))
{
@ -114,7 +114,7 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst)
if (Src + Size <= Src || Dst + Size <= Dst)
{
Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
@ -123,7 +123,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if (!InsideAddrSpace(Src, Size))
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
if (!CurrentProcess.MemoryManager.InsideAddrSpace(Src, Size))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
@ -132,7 +134,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if (!InsideNewMapRegion(Dst, Size))
if (CurrentProcess.MemoryManager.OutsideStackRegion(Dst, Size) ||
CurrentProcess.MemoryManager.InsideHeapRegion (Dst, Size) ||
CurrentProcess.MemoryManager.InsideAliasRegion (Dst, Size))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
@ -141,9 +145,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
long Result = Process.MemoryManager.Map(Src, Dst, Size);
KernelResult Result = Process.MemoryManager.Map(Dst, Src, Size);
if (Result != 0)
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
@ -153,9 +157,9 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcUnmapMemory(CpuThreadState ThreadState)
{
long Dst = (long)ThreadState.X0;
long Src = (long)ThreadState.X1;
long Size = (long)ThreadState.X2;
ulong Dst = ThreadState.X0;
ulong Src = ThreadState.X1;
ulong Size = ThreadState.X2;
if (!PageAligned(Src | Dst))
{
@ -175,7 +179,7 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst)
if (Src + Size <= Src || Dst + Size <= Dst)
{
Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
@ -184,7 +188,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if (!InsideAddrSpace(Src, Size))
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
if (!CurrentProcess.MemoryManager.InsideAddrSpace(Src, Size))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
@ -193,7 +199,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if (!InsideNewMapRegion(Dst, Size))
if (CurrentProcess.MemoryManager.OutsideStackRegion(Dst, Size) ||
CurrentProcess.MemoryManager.InsideHeapRegion (Dst, Size) ||
CurrentProcess.MemoryManager.InsideAliasRegion (Dst, Size))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
@ -202,9 +210,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
long Result = Process.MemoryManager.Unmap(Src, Dst, Size);
KernelResult Result = Process.MemoryManager.Unmap(Dst, Src, Size);
if (Result != 0)
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
@ -214,19 +222,19 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcQueryMemory(CpuThreadState ThreadState)
{
long InfoPtr = (long)ThreadState.X0;
long Position = (long)ThreadState.X2;
long InfoPtr = (long)ThreadState.X0;
ulong Position = ThreadState.X2;
KMemoryInfo BlkInfo = Process.MemoryManager.QueryMemory(Position);
Memory.WriteInt64(InfoPtr + 0x00, BlkInfo.Position);
Memory.WriteInt64(InfoPtr + 0x08, BlkInfo.Size);
Memory.WriteInt32(InfoPtr + 0x10, (int)BlkInfo.State & 0xff);
Memory.WriteInt32(InfoPtr + 0x14, (int)BlkInfo.Attribute);
Memory.WriteInt32(InfoPtr + 0x18, (int)BlkInfo.Permission);
Memory.WriteInt32(InfoPtr + 0x1c, BlkInfo.IpcRefCount);
Memory.WriteInt32(InfoPtr + 0x20, BlkInfo.DeviceRefCount);
Memory.WriteInt32(InfoPtr + 0x24, 0);
Memory.WriteUInt64(InfoPtr + 0x00, BlkInfo.Address);
Memory.WriteUInt64(InfoPtr + 0x08, BlkInfo.Size);
Memory.WriteInt32 (InfoPtr + 0x10, (int)BlkInfo.State & 0xff);
Memory.WriteInt32 (InfoPtr + 0x14, (int)BlkInfo.Attribute);
Memory.WriteInt32 (InfoPtr + 0x18, (int)BlkInfo.Permission);
Memory.WriteInt32 (InfoPtr + 0x1c, BlkInfo.IpcRefCount);
Memory.WriteInt32 (InfoPtr + 0x20, BlkInfo.DeviceRefCount);
Memory.WriteInt32 (InfoPtr + 0x24, 0);
ThreadState.X0 = 0;
ThreadState.X1 = 0;
@ -234,13 +242,13 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcMapSharedMemory(CpuThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
long Position = (long)ThreadState.X1;
long Size = (long)ThreadState.X2;
int Handle = (int)ThreadState.X0;
ulong Address = ThreadState.X1;
ulong Size = ThreadState.X2;
if (!PageAligned(Position))
if (!PageAligned(Address))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -256,9 +264,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if ((ulong)(Position + Size) <= (ulong)Position)
if (Address + Size <= Address)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -276,7 +284,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
KSharedMemory SharedMemory = Process.HandleTable.GetObject<KSharedMemory>(Handle);
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
KSharedMemory SharedMemory = CurrentProcess.HandleTable.GetObject<KSharedMemory>(Handle);
if (SharedMemory == null)
{
@ -287,29 +297,27 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size))
if (CurrentProcess.MemoryManager.IsInvalidRegion (Address, Size) ||
CurrentProcess.MemoryManager.InsideHeapRegion (Address, Size) ||
CurrentProcess.MemoryManager.InsideAliasRegion(Address, Size))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
if (SharedMemory.Size != Size)
KernelResult Result = SharedMemory.MapIntoProcess(
CurrentProcess.MemoryManager,
Address,
Size,
CurrentProcess,
Permission);
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} does not match shared memory size 0x{SharedMemory.Size:16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
return;
}
long Result = Process.MemoryManager.MapSharedMemory(SharedMemory, Permission, Position);
if (Result != 0)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
}
ThreadState.X0 = (ulong)Result;
@ -317,13 +325,13 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcUnmapSharedMemory(CpuThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
long Position = (long)ThreadState.X1;
long Size = (long)ThreadState.X2;
int Handle = (int)ThreadState.X0;
ulong Address = ThreadState.X1;
ulong Size = ThreadState.X2;
if (!PageAligned(Position))
if (!PageAligned(Address))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -339,16 +347,18 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if ((ulong)(Position + Size) <= (ulong)Position)
if (Address + Size <= Address)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
KSharedMemory SharedMemory = Process.HandleTable.GetObject<KSharedMemory>(Handle);
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
KSharedMemory SharedMemory = CurrentProcess.HandleTable.GetObject<KSharedMemory>(Handle);
if (SharedMemory == null)
{
@ -359,20 +369,26 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size))
if (CurrentProcess.MemoryManager.IsInvalidRegion (Address, Size) ||
CurrentProcess.MemoryManager.InsideHeapRegion (Address, Size) ||
CurrentProcess.MemoryManager.InsideAliasRegion(Address, Size))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
long Result = Process.MemoryManager.UnmapSharedMemory(Position, Size);
KernelResult Result = SharedMemory.UnmapFromProcess(
CurrentProcess.MemoryManager,
Address,
Size,
CurrentProcess);
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;
@ -380,12 +396,12 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcCreateTransferMemory(CpuThreadState ThreadState)
{
long Position = (long)ThreadState.X1;
long Size = (long)ThreadState.X2;
ulong Address = ThreadState.X1;
ulong Size = ThreadState.X2;
if (!PageAligned(Position))
if (!PageAligned(Address))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -401,9 +417,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if ((ulong)(Position + Size) <= (ulong)Position)
if (Address + Size <= Address)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@ -421,9 +437,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
Process.MemoryManager.ReserveTransferMemory(Position, Size, Permission);
Process.MemoryManager.ReserveTransferMemory(Address, Size, Permission);
KTransferMemory TransferMemory = new KTransferMemory(Position, Size);
KTransferMemory TransferMemory = new KTransferMemory(Address, Size);
KernelResult Result = Process.HandleTable.GenerateHandle(TransferMemory, out int Handle);
@ -433,12 +449,12 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcMapPhysicalMemory(CpuThreadState ThreadState)
{
long Position = (long)ThreadState.X0;
long Size = (long)ThreadState.X1;
ulong Address = ThreadState.X0;
ulong Size = ThreadState.X1;
if (!PageAligned(Position))
if (!PageAligned(Address))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -454,27 +470,39 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if ((ulong)(Position + Size) <= (ulong)Position)
if (Address + Size <= Address)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
if (!InsideAddrSpace(Position, Size))
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
if ((CurrentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"System resource size is zero.");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
return;
}
if (!CurrentProcess.MemoryManager.InsideAddrSpace (Address, Size) ||
CurrentProcess.MemoryManager.OutsideAliasRegion(Address, Size))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Address:x16}.");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
long Result = Process.MemoryManager.MapPhysicalMemory(Position, Size);
KernelResult Result = Process.MemoryManager.MapPhysicalMemory(Address, Size);
if (Result != 0)
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
@ -484,12 +512,12 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SvcUnmapPhysicalMemory(CpuThreadState ThreadState)
{
long Position = (long)ThreadState.X0;
long Size = (long)ThreadState.X1;
ulong Address = ThreadState.X0;
ulong Size = ThreadState.X1;
if (!PageAligned(Position))
if (!PageAligned(Address))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Address:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
@ -505,27 +533,39 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
if ((ulong)(Position + Size) <= (ulong)Position)
if (Address + Size <= Address)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Address:x16} / size 0x{Size:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
if (!InsideAddrSpace(Position, Size))
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
if ((CurrentProcess.PersonalMmHeapPagesCount & 0xfffffffffffff) == 0)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!");
Logger.PrintWarning(LogClass.KernelSvc, $"System resource size is zero.");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
return;
}
if (!CurrentProcess.MemoryManager.InsideAddrSpace (Address, Size) ||
CurrentProcess.MemoryManager.OutsideAliasRegion(Address, Size))
{
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Address:x16}.");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
long Result = Process.MemoryManager.UnmapPhysicalMemory(Position, Size);
KernelResult Result = Process.MemoryManager.UnmapPhysicalMemory(Address, Size);
if (Result != 0)
if (Result != KernelResult.Success)
{
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
@ -533,45 +573,9 @@ namespace Ryujinx.HLE.HOS.Kernel
ThreadState.X0 = (ulong)Result;
}
private static bool PageAligned(long Position)
private static bool PageAligned(ulong Position)
{
return (Position & (KMemoryManager.PageSize - 1)) == 0;
}
private bool InsideAddrSpace(long Position, long Size)
{
ulong Start = (ulong)Position;
ulong End = (ulong)Size + Start;
return Start >= (ulong)Process.MemoryManager.AddrSpaceStart &&
End < (ulong)Process.MemoryManager.AddrSpaceEnd;
}
private bool InsideMapRegion(long Position, long Size)
{
ulong Start = (ulong)Position;
ulong End = (ulong)Size + Start;
return Start >= (ulong)Process.MemoryManager.MapRegionStart &&
End < (ulong)Process.MemoryManager.MapRegionEnd;
}
private bool InsideHeapRegion(long Position, long Size)
{
ulong Start = (ulong)Position;
ulong End = (ulong)Size + Start;
return Start >= (ulong)Process.MemoryManager.HeapRegionStart &&
End < (ulong)Process.MemoryManager.HeapRegionEnd;
}
private bool InsideNewMapRegion(long Position, long Size)
{
ulong Start = (ulong)Position;
ulong End = (ulong)Size + Start;
return Start >= (ulong)Process.MemoryManager.NewMapRegionStart &&
End < (ulong)Process.MemoryManager.NewMapRegionEnd;
}
}
}