aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChocolArm64/CpuThread.cs4
-rw-r--r--ChocolArm64/Exceptions/VmmOutOfMemoryException.cs13
-rw-r--r--ChocolArm64/Memory/MemoryHelper.cs18
-rw-r--r--ChocolArm64/Memory/MemoryManager.cs239
-rw-r--r--Ryujinx.Common/BitUtils.cs104
-rw-r--r--Ryujinx.Graphics/Gal/GalVertexAttrib.cs8
-rw-r--r--Ryujinx.Graphics/Gal/IGalConstBuffer.cs1
-rw-r--r--Ryujinx.Graphics/Gal/IGalRasterizer.cs1
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs8
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs215
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs16
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs7
-rw-r--r--Ryujinx.Graphics/Memory/NvGpuVmm.cs4
-rw-r--r--Ryujinx.Graphics/NvGpuEngine3d.cs65
-rw-r--r--Ryujinx.HLE/DeviceMemory.cs (renamed from Ryujinx.HLE/Memory/DeviceMemory.cs)6
-rw-r--r--Ryujinx.HLE/FileSystem/SaveHelper.cs5
-rw-r--r--Ryujinx.HLE/HOS/Font/SharedFontManager.cs2
-rw-r--r--Ryujinx.HLE/HOS/GlobalStateTable.cs17
-rw-r--r--Ryujinx.HLE/HOS/Horizon.cs263
-rw-r--r--Ryujinx.HLE/HOS/Ipc/IpcHandler.cs6
-rw-r--r--Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs15
-rw-r--r--Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs51
-rw-r--r--Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs310
-rw-r--r--Ryujinx.HLE/HOS/Kernel/HleScheduler.cs16
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs244
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KAutoObject.cs42
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KClientPort.cs31
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs71
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs83
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KCoreContext.cs31
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KCriticalSection.cs (renamed from Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs)8
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KHandleTable.cs (renamed from Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs)48
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs22
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs16
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs22
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryBlockAllocator.cs19
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs10
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs2155
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs43
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs428
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KPageList.cs80
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KPageNode.cs14
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KPort.cs26
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KProcess.cs1013
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs311
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs8
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs146
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KScheduler.cs11
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KServerPort.cs14
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs64
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs50
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KSynchronization.cs18
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs8
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KThread.cs337
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KTimeManager.cs53
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs73
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KTransferMemory.cs10
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KernelInit.cs136
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KernelResult.cs29
-rw-r--r--Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs71
-rw-r--r--Ryujinx.HLE/HOS/Kernel/LimitableResource.cs13
-rw-r--r--Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs12
-rw-r--r--Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs10
-rw-r--r--Ryujinx.HLE/HOS/Kernel/MemoryState.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs128
-rw-r--r--Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs37
-rw-r--r--Ryujinx.HLE/HOS/Kernel/ProcessState.cs14
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SvcHandler.cs34
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SvcMemory.cs278
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SvcSystem.cs584
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SvcThread.cs198
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs40
-rw-r--r--Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs14
-rw-r--r--Ryujinx.HLE/HOS/Kernel/ThreadType.cs10
-rw-r--r--Ryujinx.HLE/HOS/Process.cs528
-rw-r--r--Ryujinx.HLE/HOS/ProgramLoader.cs292
-rw-r--r--Ryujinx.HLE/HOS/ServiceCtx.cs4
-rw-r--r--Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs6
-rw-r--r--Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs226
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs7
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs7
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs7
-rw-r--r--Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs3
-rw-r--r--Ryujinx.HLE/Homebrew.npdmbin0 -> 972 bytes
-rw-r--r--Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs105
-rw-r--r--Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs15
-rw-r--r--Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs (renamed from Ryujinx.HLE/Loaders/ElfDynTag.cs)4
-rw-r--r--Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs40
-rw-r--r--Ryujinx.HLE/Loaders/Elf/ElfSymbolBinding.cs (renamed from Ryujinx.HLE/Loaders/ElfSymBinding.cs)4
-rw-r--r--Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs (renamed from Ryujinx.HLE/Loaders/ElfSymType.cs)4
-rw-r--r--Ryujinx.HLE/Loaders/Elf/ElfSymbolVisibility.cs (renamed from Ryujinx.HLE/Loaders/ElfSymVisibility.cs)4
-rw-r--r--Ryujinx.HLE/Loaders/ElfDyn.cs15
-rw-r--r--Ryujinx.HLE/Loaders/ElfRel.cs19
-rw-r--r--Ryujinx.HLE/Loaders/ElfRelType.cs128
-rw-r--r--Ryujinx.HLE/Loaders/ElfSym.cs40
-rw-r--r--Ryujinx.HLE/Loaders/Executable.cs205
-rw-r--r--Ryujinx.HLE/Loaders/Executables/IExecutable.cs9
-rw-r--r--Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs149
-rw-r--r--Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs (renamed from Ryujinx.HLE/Loaders/Executables/Nro.cs)13
-rw-r--r--Ryujinx.HLE/Loaders/Executables/NxStaticObject.cs (renamed from Ryujinx.HLE/Loaders/Executables/Nso.cs)30
-rw-r--r--Ryujinx.HLE/Loaders/Npdm/ApplicationType.cs9
-rw-r--r--Ryujinx.HLE/Loaders/Npdm/FsPermissionBool.cs33
-rw-r--r--Ryujinx.HLE/Loaders/Npdm/FsPermissionRw.cs45
-rw-r--r--Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs162
-rw-r--r--Ryujinx.HLE/Loaders/Npdm/KernelAccessControlIrq.cs14
-rw-r--r--Ryujinx.HLE/Loaders/Npdm/KernelAccessControlMmio.cs22
-rw-r--r--Ryujinx.HLE/Loaders/Npdm/KernelAccessItem.cs33
-rw-r--r--Ryujinx.HLE/Loaders/Npdm/Npdm.cs36
-rw-r--r--Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs4
-rw-r--r--Ryujinx.HLE/Loaders/Npdm/SvcName.cs134
-rw-r--r--Ryujinx.HLE/Memory/ArenaAllocator.cs150
-rw-r--r--Ryujinx.HLE/Ryujinx.HLE.csproj2
-rw-r--r--Ryujinx.HLE/Switch.cs3
-rw-r--r--Ryujinx.HLE/Utilities/WSAError.cs10
-rw-r--r--Ryujinx/Ui/ConsoleLog.cs4
119 files changed, 7911 insertions, 3145 deletions
diff --git a/ChocolArm64/CpuThread.cs b/ChocolArm64/CpuThread.cs
index 11f41236..dac376a1 100644
--- a/ChocolArm64/CpuThread.cs
+++ b/ChocolArm64/CpuThread.cs
@@ -18,7 +18,7 @@ namespace ChocolArm64
private int _isExecuting;
- public CpuThread(Translator translator, MemoryManager memory, long entryPoint)
+ public CpuThread(Translator translator, MemoryManager memory, long entrypoint)
{
_translator = translator;
Memory = memory;
@@ -31,7 +31,7 @@ namespace ChocolArm64
Work = new Thread(delegate()
{
- translator.ExecuteSubroutine(this, entryPoint);
+ translator.ExecuteSubroutine(this, entrypoint);
memory.RemoveMonitor(ThreadState.Core);
diff --git a/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs b/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs
deleted file mode 100644
index d6ddf752..00000000
--- a/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-
-namespace ChocolArm64.Exceptions
-{
- public class VmmAccessException : Exception
- {
- private const string ExMsg = "Memory region at 0x{0} with size 0x{1} is not contiguous!";
-
- public VmmAccessException() { }
-
- public VmmAccessException(long position, long size) : base(string.Format(ExMsg, position, size)) { }
- }
-} \ No newline at end of file
diff --git a/ChocolArm64/Memory/MemoryHelper.cs b/ChocolArm64/Memory/MemoryHelper.cs
index 2e721afa..1c436dd8 100644
--- a/ChocolArm64/Memory/MemoryHelper.cs
+++ b/ChocolArm64/Memory/MemoryHelper.cs
@@ -26,22 +26,26 @@ namespace ChocolArm64.Memory
{
long size = Marshal.SizeOf<T>();
- memory.EnsureRangeIsValid(position, size);
+ byte[] data = memory.ReadBytes(position, size);
- IntPtr ptr = (IntPtr)memory.Translate(position);
-
- return Marshal.PtrToStructure<T>(ptr);
+ fixed (byte* ptr = data)
+ {
+ return Marshal.PtrToStructure<T>((IntPtr)ptr);
+ }
}
public unsafe static void Write<T>(MemoryManager memory, long position, T value) where T : struct
{
long size = Marshal.SizeOf<T>();
- memory.EnsureRangeIsValid(position, size);
+ byte[] data = new byte[size];
- IntPtr ptr = (IntPtr)memory.TranslateWrite(position);
+ fixed (byte* ptr = data)
+ {
+ Marshal.StructureToPtr<T>(value, (IntPtr)ptr, false);
+ }
- Marshal.StructureToPtr<T>(value, ptr, false);
+ memory.WriteBytes(position, data);
}
public static string ReadAsciiString(MemoryManager memory, long position, long maxSize = -1)
diff --git a/ChocolArm64/Memory/MemoryManager.cs b/ChocolArm64/Memory/MemoryManager.cs
index ef3fb006..68d9100b 100644
--- a/ChocolArm64/Memory/MemoryManager.cs
+++ b/ChocolArm64/Memory/MemoryManager.cs
@@ -1,5 +1,6 @@
using ChocolArm64.Events;
using ChocolArm64.Exceptions;
+using ChocolArm64.Instructions;
using ChocolArm64.State;
using System;
using System.Collections.Concurrent;
@@ -197,17 +198,41 @@ namespace ChocolArm64.Memory
public ushort ReadUInt16(long position)
{
- return *((ushort*)Translate(position));
+ if ((position & 1) == 0)
+ {
+ return *((ushort*)Translate(position));
+ }
+ else
+ {
+ return (ushort)(ReadByte(position + 0) << 0 |
+ ReadByte(position + 1) << 8);
+ }
}
public uint ReadUInt32(long position)
{
- return *((uint*)Translate(position));
+ if ((position & 3) == 0)
+ {
+ return *((uint*)Translate(position));
+ }
+ else
+ {
+ return (uint)(ReadUInt16(position + 0) << 0 |
+ ReadUInt16(position + 2) << 16);
+ }
}
public ulong ReadUInt64(long position)
{
- return *((ulong*)Translate(position));
+ if ((position & 7) == 0)
+ {
+ return *((ulong*)Translate(position));
+ }
+ else
+ {
+ return (ulong)ReadUInt32(position + 0) << 0 |
+ (ulong)ReadUInt32(position + 4) << 32;
+ }
}
public Vector128<float> ReadVector8(long position)
@@ -218,74 +243,117 @@ namespace ChocolArm64.Memory
}
else
{
- throw new PlatformNotSupportedException();
+ Vector128<float> value = VectorHelper.VectorSingleZero();
+
+ value = VectorHelper.VectorInsertInt(ReadByte(position), value, 0, 0);
+
+ return value;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector128<float> ReadVector16(long position)
{
- if (Sse2.IsSupported)
+ if (Sse2.IsSupported && (position & 1) == 0)
{
return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse2.SetZeroVector128<ushort>(), ReadUInt16(position), 0));
}
else
{
- throw new PlatformNotSupportedException();
+ Vector128<float> value = VectorHelper.VectorSingleZero();
+
+ value = VectorHelper.VectorInsertInt(ReadUInt16(position), value, 0, 1);
+
+ return value;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector128<float> ReadVector32(long position)
{
- if (Sse.IsSupported)
+ if (Sse.IsSupported && (position & 3) == 0)
{
return Sse.LoadScalarVector128((float*)Translate(position));
}
else
{
- throw new PlatformNotSupportedException();
+ Vector128<float> value = VectorHelper.VectorSingleZero();
+
+ value = VectorHelper.VectorInsertInt(ReadUInt32(position), value, 0, 2);
+
+ return value;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector128<float> ReadVector64(long position)
{
- if (Sse2.IsSupported)
+ if (Sse2.IsSupported && (position & 7) == 0)
{
return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)Translate(position)));
}
else
{
- throw new PlatformNotSupportedException();
+ Vector128<float> value = VectorHelper.VectorSingleZero();
+
+ value = VectorHelper.VectorInsertInt(ReadUInt64(position), value, 0, 3);
+
+ return value;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector128<float> ReadVector128(long position)
{
- if (Sse.IsSupported)
+ if (Sse.IsSupported && (position & 15) == 0)
{
return Sse.LoadVector128((float*)Translate(position));
}
else
{
- throw new PlatformNotSupportedException();
+ Vector128<float> value = VectorHelper.VectorSingleZero();
+
+ value = VectorHelper.VectorInsertInt(ReadUInt64(position + 0), value, 0, 3);
+ value = VectorHelper.VectorInsertInt(ReadUInt64(position + 8), value, 1, 3);
+
+ return value;
}
}
public byte[] ReadBytes(long position, long size)
{
- if ((uint)size > int.MaxValue)
+ long endAddr = position + size;
+
+ if ((ulong)size > int.MaxValue)
{
throw new ArgumentOutOfRangeException(nameof(size));
}
- EnsureRangeIsValid(position, size);
+ if ((ulong)endAddr < (ulong)position)
+ {
+ throw new ArgumentOutOfRangeException(nameof(position));
+ }
byte[] data = new byte[size];
- Marshal.Copy((IntPtr)Translate(position), data, 0, (int)size);
+ int offset = 0;
+
+ while ((ulong)position < (ulong)endAddr)
+ {
+ long pageLimit = (position + PageSize) & ~(long)PageMask;
+
+ if ((ulong)pageLimit > (ulong)endAddr)
+ {
+ pageLimit = endAddr;
+ }
+
+ int copySize = (int)(pageLimit - position);
+
+ Marshal.Copy((IntPtr)Translate(position), data, offset, copySize);
+
+ position += copySize;
+ offset += copySize;
+ }
return data;
}
@@ -293,9 +361,36 @@ namespace ChocolArm64.Memory
public void ReadBytes(long position, byte[] data, int startIndex, int size)
{
//Note: This will be moved later.
- EnsureRangeIsValid(position, (uint)size);
+ long endAddr = position + size;
+
+ if ((ulong)size > int.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException(nameof(size));
+ }
- Marshal.Copy((IntPtr)Translate(position), data, startIndex, size);
+ if ((ulong)endAddr < (ulong)position)
+ {
+ throw new ArgumentOutOfRangeException(nameof(position));
+ }
+
+ int offset = startIndex;
+
+ while ((ulong)position < (ulong)endAddr)
+ {
+ long pageLimit = (position + PageSize) & ~(long)PageMask;
+
+ if ((ulong)pageLimit > (ulong)endAddr)
+ {
+ pageLimit = endAddr;
+ }
+
+ int copySize = (int)(pageLimit - position);
+
+ Marshal.Copy((IntPtr)Translate(position), data, offset, copySize);
+
+ position += copySize;
+ offset += copySize;
+ }
}
public void WriteSByte(long position, sbyte value)
@@ -325,17 +420,41 @@ namespace ChocolArm64.Memory
public void WriteUInt16(long position, ushort value)
{
- *((ushort*)TranslateWrite(position)) = value;
+ if ((position & 1) == 0)
+ {
+ *((ushort*)TranslateWrite(position)) = value;
+ }
+ else
+ {
+ WriteByte(position + 0, (byte)(value >> 0));
+ WriteByte(position + 1, (byte)(value >> 8));
+ }
}
public void WriteUInt32(long position, uint value)
{
- *((uint*)TranslateWrite(position)) = value;
+ if ((position & 3) == 0)
+ {
+ *((uint*)TranslateWrite(position)) = value;
+ }
+ else
+ {
+ WriteUInt16(position + 0, (ushort)(value >> 0));
+ WriteUInt16(position + 2, (ushort)(value >> 16));
+ }
}
public void WriteUInt64(long position, ulong value)
{
- *((ulong*)TranslateWrite(position)) = value;
+ if ((position & 7) == 0)
+ {
+ *((ulong*)TranslateWrite(position)) = value;
+ }
+ else
+ {
+ WriteUInt32(position + 0, (uint)(value >> 0));
+ WriteUInt32(position + 4, (uint)(value >> 32));
+ }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -351,7 +470,7 @@ namespace ChocolArm64.Memory
}
else
{
- throw new PlatformNotSupportedException();
+ WriteByte(position, (byte)VectorHelper.VectorExtractIntZx(value, 0, 0));
}
}
@@ -364,46 +483,47 @@ namespace ChocolArm64.Memory
}
else
{
- throw new PlatformNotSupportedException();
+ WriteUInt16(position, (ushort)VectorHelper.VectorExtractIntZx(value, 0, 1));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector32(long position, Vector128<float> value)
{
- if (Sse.IsSupported)
+ if (Sse.IsSupported && (position & 3) == 0)
{
Sse.StoreScalar((float*)TranslateWrite(position), value);
}
else
{
- throw new PlatformNotSupportedException();
+ WriteUInt32(position, (uint)VectorHelper.VectorExtractIntZx(value, 0, 2));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector64(long position, Vector128<float> value)
{
- if (Sse2.IsSupported)
+ if (Sse2.IsSupported && (position & 7) == 0)
{
Sse2.StoreScalar((double*)TranslateWrite(position), Sse.StaticCast<float, double>(value));
}
else
{
- throw new PlatformNotSupportedException();
+ WriteUInt64(position, VectorHelper.VectorExtractIntZx(value, 0, 3));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector128(long position, Vector128<float> value)
{
- if (Sse.IsSupported)
+ if (Sse.IsSupported && (position & 15) == 0)
{
Sse.Store((float*)TranslateWrite(position), value);
}
else
{
- throw new PlatformNotSupportedException();
+ WriteUInt64(position + 0, VectorHelper.VectorExtractIntZx(value, 0, 3));
+ WriteUInt64(position + 8, VectorHelper.VectorExtractIntZx(value, 1, 3));
}
}
@@ -439,22 +559,48 @@ namespace ChocolArm64.Memory
public void WriteBytes(long position, byte[] data, int startIndex, int size)
{
//Note: This will be moved later.
- //Using Translate instead of TranslateWrite is on purpose.
- EnsureRangeIsValid(position, (uint)size);
+ long endAddr = position + size;
- Marshal.Copy(data, startIndex, (IntPtr)Translate(position), size);
+ if ((ulong)endAddr < (ulong)position)
+ {
+ throw new ArgumentOutOfRangeException(nameof(position));
+ }
+
+ int offset = startIndex;
+
+ while ((ulong)position < (ulong)endAddr)
+ {
+ long pageLimit = (position + PageSize) & ~(long)PageMask;
+
+ if ((ulong)pageLimit > (ulong)endAddr)
+ {
+ pageLimit = endAddr;
+ }
+
+ int copySize = (int)(pageLimit - position);
+
+ Marshal.Copy(data, offset, (IntPtr)TranslateWrite(position), copySize);
+
+ position += copySize;
+ offset += copySize;
+ }
}
public void CopyBytes(long src, long dst, long size)
{
//Note: This will be moved later.
- EnsureRangeIsValid(src, size);
- EnsureRangeIsValid(dst, size);
-
- byte* srcPtr = Translate(src);
- byte* dstPtr = TranslateWrite(dst);
+ if (IsContiguous(src, size) &&
+ IsContiguous(dst, size))
+ {
+ byte* srcPtr = Translate(src);
+ byte* dstPtr = TranslateWrite(dst);
- Buffer.MemoryCopy(srcPtr, dstPtr, size, size);
+ Buffer.MemoryCopy(srcPtr, dstPtr, size, size);
+ }
+ else
+ {
+ WriteBytes(dst, ReadBytes(src, size));
+ }
}
public void Map(long va, long pa, long size)
@@ -703,14 +849,21 @@ Unmapped:
}
}
- public IntPtr GetHostAddress(long position, long size)
+ public bool TryGetHostAddress(long position, long size, out IntPtr ptr)
{
- EnsureRangeIsValid(position, size);
+ if (IsContiguous(position, size))
+ {
+ ptr = (IntPtr)Translate(position);
- return (IntPtr)Translate(position);
+ return true;
+ }
+
+ ptr = IntPtr.Zero;
+
+ return false;
}
- internal void EnsureRangeIsValid(long position, long size)
+ private bool IsContiguous(long position, long size)
{
long endPos = position + size;
@@ -724,12 +877,14 @@ Unmapped:
if (pa != expectedPa)
{
- throw new VmmAccessException(position, size);
+ return false;
}
position += PageSize;
expectedPa += PageSize;
}
+
+ return true;
}
public bool IsValidPosition(long position)
diff --git a/Ryujinx.Common/BitUtils.cs b/Ryujinx.Common/BitUtils.cs
new file mode 100644
index 00000000..5c858029
--- /dev/null
+++ b/Ryujinx.Common/BitUtils.cs
@@ -0,0 +1,104 @@
+namespace Ryujinx.Common
+{
+ public static class BitUtils
+ {
+ public static int AlignUp(int Value, int Size)
+ {
+ return (Value + (Size - 1)) & -Size;
+ }
+
+ public static ulong AlignUp(ulong Value, int Size)
+ {
+ return (ulong)AlignUp((long)Value, Size);
+ }
+
+ public static long AlignUp(long Value, int Size)
+ {
+ return (Value + (Size - 1)) & -(long)Size;
+ }
+
+ public static int AlignDown(int Value, int Size)
+ {
+ return Value & -Size;
+ }
+
+ public static ulong AlignDown(ulong Value, int Size)
+ {
+ return (ulong)AlignDown((long)Value, Size);
+ }
+
+ public static long AlignDown(long Value, int Size)
+ {
+ return Value & -(long)Size;
+ }
+
+ public static ulong DivRoundUp(ulong Value, uint Dividend)
+ {
+ return (Value + Dividend - 1) / Dividend;
+ }
+
+ public static long DivRoundUp(long Value, int Dividend)
+ {
+ return (Value + Dividend - 1) / Dividend;
+ }
+
+ public static bool IsPowerOfTwo32(int Value)
+ {
+ return Value != 0 && (Value & (Value - 1)) == 0;
+ }
+
+ public static bool IsPowerOfTwo64(long Value)
+ {
+ return Value != 0 && (Value & (Value - 1)) == 0;
+ }
+
+ public static int CountLeadingZeros32(int Value)
+ {
+ return (int)CountLeadingZeros((ulong)Value, 32);
+ }
+
+ public static int CountLeadingZeros64(long Value)
+ {
+ return (int)CountLeadingZeros((ulong)Value, 64);
+ }
+
+ private static readonly byte[] ClzNibbleTbl = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ private static ulong CountLeadingZeros(ulong Value, int Size) // Size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.).
+ {
+ if (Value == 0ul)
+ {
+ return (ulong)Size;
+ }
+
+ int NibbleIdx = Size;
+ int PreCount, Count = 0;
+
+ do
+ {
+ NibbleIdx -= 4;
+ PreCount = ClzNibbleTbl[(Value >> NibbleIdx) & 0b1111];
+ Count += PreCount;
+ }
+ while (PreCount == 4);
+
+ return (ulong)Count;
+ }
+
+ public static long ReverseBits64(long Value)
+ {
+ return (long)ReverseBits64((ulong)Value);
+ }
+
+ private static ulong ReverseBits64(ulong Value)
+ {
+ Value = ((Value & 0xaaaaaaaaaaaaaaaa) >> 1 ) | ((Value & 0x5555555555555555) << 1 );
+ Value = ((Value & 0xcccccccccccccccc) >> 2 ) | ((Value & 0x3333333333333333) << 2 );
+ Value = ((Value & 0xf0f0f0f0f0f0f0f0) >> 4 ) | ((Value & 0x0f0f0f0f0f0f0f0f) << 4 );
+ Value = ((Value & 0xff00ff00ff00ff00) >> 8 ) | ((Value & 0x00ff00ff00ff00ff) << 8 );
+ Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16);
+
+ return (Value >> 32) | (Value << 32);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
index fa9a391f..31b648df 100644
--- a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
+++ b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
@@ -1,5 +1,3 @@
-using System;
-
namespace Ryujinx.Graphics.Gal
{
public struct GalVertexAttrib
@@ -7,7 +5,7 @@ namespace Ryujinx.Graphics.Gal
public int Index { get; private set; }
public bool IsConst { get; private set; }
public int Offset { get; private set; }
- public IntPtr Pointer { get; private set; }
+ public byte[] Data { get; private set; }
public GalVertexAttribSize Size { get; private set; }
public GalVertexAttribType Type { get; private set; }
@@ -18,14 +16,14 @@ namespace Ryujinx.Graphics.Gal
int Index,
bool IsConst,
int Offset,
- IntPtr Pointer,
+ byte[] Data,
GalVertexAttribSize Size,
GalVertexAttribType Type,
bool IsBgra)
{
this.Index = Index;
this.IsConst = IsConst;
- this.Pointer = Pointer;
+ this.Data = Data;
this.Offset = Offset;
this.Size = Size;
this.Type = Type;
diff --git a/Ryujinx.Graphics/Gal/IGalConstBuffer.cs b/Ryujinx.Graphics/Gal/IGalConstBuffer.cs
index 37545b2a..0cdcc237 100644
--- a/Ryujinx.Graphics/Gal/IGalConstBuffer.cs
+++ b/Ryujinx.Graphics/Gal/IGalConstBuffer.cs
@@ -12,5 +12,6 @@ namespace Ryujinx.Graphics.Gal
bool IsCached(long Key, long Size);
void SetData(long Key, long Size, IntPtr HostAddress);
+ void SetData(long Key, byte[] Data);
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs
index 052e3f35..04f7aae5 100644
--- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs
+++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs
@@ -22,6 +22,7 @@ namespace Ryujinx.Graphics.Gal
bool IsIboCached(long Key, long DataSize);
void CreateVbo(long Key, int DataSize, IntPtr HostAddress);
+ void CreateVbo(long Key, byte[] Data);
void CreateIbo(long Key, int DataSize, IntPtr HostAddress);
void CreateIbo(long Key, int DataSize, byte[] Buffer);
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs
index e04190e0..a12681c7 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs
@@ -44,6 +44,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
}
+ public void SetData(long Key, byte[] Data)
+ {
+ if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
+ {
+ Buffer.SetData(Data);
+ }
+ }
+
public bool TryGetUbo(long Key, out int UboHandle)
{
if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
index e81cf8a3..ac12314c 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
@@ -600,122 +600,125 @@ namespace Ryujinx.Graphics.Gal.OpenGL
ThrowUnsupportedAttrib(Attrib);
}
- if (Attrib.Type == GalVertexAttribType.Unorm)
+ fixed (byte* Ptr = Attrib.Data)
{
- switch (Attrib.Size)
+ if (Attrib.Type == GalVertexAttribType.Unorm)
{
- case GalVertexAttribSize._8:
- case GalVertexAttribSize._8_8:
- case GalVertexAttribSize._8_8_8:
- case GalVertexAttribSize._8_8_8_8:
- GL.VertexAttrib4N((uint)Attrib.Index, (byte*)Attrib.Pointer);
- break;
-
- case GalVertexAttribSize._16:
- case GalVertexAttribSize._16_16:
- case GalVertexAttribSize._16_16_16:
- case GalVertexAttribSize._16_16_16_16:
- GL.VertexAttrib4N((uint)Attrib.Index, (ushort*)Attrib.Pointer);
- break;
-
- case GalVertexAttribSize._32:
- case GalVertexAttribSize._32_32:
- case GalVertexAttribSize._32_32_32:
- case GalVertexAttribSize._32_32_32_32:
- GL.VertexAttrib4N((uint)Attrib.Index, (uint*)Attrib.Pointer);
- break;
+ switch (Attrib.Size)
+ {
+ case GalVertexAttribSize._8:
+ case GalVertexAttribSize._8_8:
+ case GalVertexAttribSize._8_8_8:
+ case GalVertexAttribSize._8_8_8_8:
+ GL.VertexAttrib4N((uint)Attrib.Index, Ptr);
+ break;
+
+ case GalVertexAttribSize._16:
+ case GalVertexAttribSize._16_16:
+ case GalVertexAttribSize._16_16_16:
+ case GalVertexAttribSize._16_16_16_16:
+ GL.VertexAttrib4N((uint)Attrib.Index, (ushort*)Ptr);
+ break;
+
+ case GalVertexAttribSize._32:
+ case GalVertexAttribSize._32_32:
+ case GalVertexAttribSize._32_32_32:
+ case GalVertexAttribSize._32_32_32_32:
+ GL.VertexAttrib4N((uint)Attrib.Index, (uint*)Ptr);
+ break;
+ }
}
- }
- else if (Attrib.Type == GalVertexAttribType.Snorm)
- {
- switch (Attrib.Size)
+ else if (Attrib.Type == GalVertexAttribType.Snorm)
{
- case GalVertexAttribSize._8:
- case GalVertexAttribSize._8_8:
- case GalVertexAttribSize._8_8_8:
- case GalVertexAttribSize._8_8_8_8:
- GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Attrib.Pointer);
- break;
-
- case GalVertexAttribSize._16:
- case GalVertexAttribSize._16_16:
- case GalVertexAttribSize._16_16_16:
- case GalVertexAttribSize._16_16_16_16:
- GL.VertexAttrib4N((uint)Attrib.Index, (short*)Attrib.Pointer);
- break;
-
- case GalVertexAttribSize._32:
- case GalVertexAttribSize._32_32:
- case GalVertexAttribSize._32_32_32:
- case GalVertexAttribSize._32_32_32_32:
- GL.VertexAttrib4N((uint)Attrib.Index, (int*)Attrib.Pointer);
- break;
+ switch (Attrib.Size)
+ {
+ case GalVertexAttribSize._8:
+ case GalVertexAttribSize._8_8:
+ case GalVertexAttribSize._8_8_8:
+ case GalVertexAttribSize._8_8_8_8:
+ GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Ptr);
+ break;
+
+ case GalVertexAttribSize._16:
+ case GalVertexAttribSize._16_16:
+ case GalVertexAttribSize._16_16_16:
+ case GalVertexAttribSize._16_16_16_16:
+ GL.VertexAttrib4N((uint)Attrib.Index, (short*)Ptr);
+ break;
+
+ case GalVertexAttribSize._32:
+ case GalVertexAttribSize._32_32:
+ case GalVertexAttribSize._32_32_32:
+ case GalVertexAttribSize._32_32_32_32:
+ GL.VertexAttrib4N((uint)Attrib.Index, (int*)Ptr);
+ break;
+ }
}
- }
- else if (Attrib.Type == GalVertexAttribType.Uint)
- {
- switch (Attrib.Size)
+ else if (Attrib.Type == GalVertexAttribType.Uint)
{
- case GalVertexAttribSize._8:
- case GalVertexAttribSize._8_8:
- case GalVertexAttribSize._8_8_8:
- case GalVertexAttribSize._8_8_8_8:
- GL.VertexAttribI4((uint)Attrib.Index, (byte*)Attrib.Pointer);
- break;
-
- case GalVertexAttribSize._16:
- case GalVertexAttribSize._16_16:
- case GalVertexAttribSize._16_16_16:
- case GalVertexAttribSize._16_16_16_16:
- GL.VertexAttribI4((uint)Attrib.Index, (ushort*)Attrib.Pointer);
- break;
-
- case GalVertexAttribSize._32:
- case GalVertexAttribSize._32_32:
- case GalVertexAttribSize._32_32_32:
- case GalVertexAttribSize._32_32_32_32:
- GL.VertexAttribI4((uint)Attrib.Index, (uint*)Attrib.Pointer);
- break;
+ switch (Attrib.Size)
+ {
+ case GalVertexAttribSize._8:
+ case GalVertexAttribSize._8_8:
+ case GalVertexAttribSize._8_8_8:
+ case GalVertexAttribSize._8_8_8_8:
+ GL.VertexAttribI4((uint)Attrib.Index, Ptr);
+ break;
+
+ case GalVertexAttribSize._16:
+ case GalVertexAttribSize._16_16:
+ case GalVertexAttribSize._16_16_16:
+ case GalVertexAttribSize._16_16_16_16:
+ GL.VertexAttribI4((uint)Attrib.Index, (ushort*)Ptr);
+ break;
+
+ case GalVertexAttribSize._32:
+ case GalVertexAttribSize._32_32:
+ case GalVertexAttribSize._32_32_32:
+ case GalVertexAttribSize._32_32_32_32:
+ GL.VertexAttribI4((uint)Attrib.Index, (uint*)Ptr);
+ break;
+ }
}
- }
- else if (Attrib.Type == GalVertexAttribType.Sint)
- {
- switch (Attrib.Size)
+ else if (Attrib.Type == GalVertexAttribType.Sint)
{
- case GalVertexAttribSize._8:
- case GalVertexAttribSize._8_8:
- case GalVertexAttribSize._8_8_8:
- case GalVertexAttribSize._8_8_8_8:
- GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Attrib.Pointer);
- break;
-
- case GalVertexAttribSize._16:
- case GalVertexAttribSize._16_16:
- case GalVertexAttribSize._16_16_16:
- case GalVertexAttribSize._16_16_16_16:
- GL.VertexAttribI4((uint)Attrib.Index, (short*)Attrib.Pointer);
- break;
-
- case GalVertexAttribSize._32:
- case GalVertexAttribSize._32_32:
- case GalVertexAttribSize._32_32_32:
- case GalVertexAttribSize._32_32_32_32:
- GL.VertexAttribI4((uint)Attrib.Index, (int*)Attrib.Pointer);
- break;
+ switch (Attrib.Size)
+ {
+ case GalVertexAttribSize._8:
+ case GalVertexAttribSize._8_8:
+ case GalVertexAttribSize._8_8_8:
+ case GalVertexAttribSize._8_8_8_8:
+ GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Ptr);
+ break;
+
+ case GalVertexAttribSize._16:
+ case GalVertexAttribSize._16_16:
+ case GalVertexAttribSize._16_16_16:
+ case GalVertexAttribSize._16_16_16_16:
+ GL.VertexAttribI4((uint)Attrib.Index, (short*)Ptr);
+ break;
+
+ case GalVertexAttribSize._32:
+ case GalVertexAttribSize._32_32:
+ case GalVertexAttribSize._32_32_32:
+ case GalVertexAttribSize._32_32_32_32:
+ GL.VertexAttribI4((uint)Attrib.Index, (int*)Ptr);
+ break;
+ }
}
- }
- else if (Attrib.Type == GalVertexAttribType.Float)
- {
- switch (Attrib.Size)
+ else if (Attrib.Type == GalVertexAttribType.Float)
{
- case GalVertexAttribSize._32:
- case GalVertexAttribSize._32_32:
- case GalVertexAttribSize._32_32_32:
- case GalVertexAttribSize._32_32_32_32:
- GL.VertexAttrib4(Attrib.Index, (float*)Attrib.Pointer);
- break;
-
- default: ThrowUnsupportedAttrib(Attrib); break;
+ switch (Attrib.Size)
+ {
+ case GalVertexAttribSize._32:
+ case GalVertexAttribSize._32_32:
+ case GalVertexAttribSize._32_32_32:
+ case GalVertexAttribSize._32_32_32_32:
+ GL.VertexAttrib4(Attrib.Index, (float*)Ptr);
+ break;
+
+ default: ThrowUnsupportedAttrib(Attrib); break;
+ }
}
}
}
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
index cd6292f7..c4015d02 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
@@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
int Handle = GL.GenBuffer();
- VboCache.AddOrUpdate(Key, Handle, (uint)DataSize);
+ VboCache.AddOrUpdate(Key, Handle, DataSize);
IntPtr Length = new IntPtr(DataSize);
@@ -100,6 +100,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.BufferData(BufferTarget.ArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw);
}
+ public void CreateVbo(long Key, byte[] Data)
+ {
+ int Handle = GL.GenBuffer();
+
+ VboCache.AddOrUpdate(Key, Handle, Data.Length);
+
+ IntPtr Length = new IntPtr(Data.Length);
+
+ GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
+ GL.BufferData(BufferTarget.ArrayBuffer, Length, Data, BufferUsageHint.StreamDraw);
+ }
+
public void CreateIbo(long Key, int DataSize, IntPtr HostAddress)
{
int Handle = GL.GenBuffer();
@@ -116,7 +128,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
int Handle = GL.GenBuffer();
- IboCache.AddOrUpdate(Key, Handle, (uint)DataSize);
+ IboCache.AddOrUpdate(Key, Handle, DataSize);
IntPtr Length = new IntPtr(Buffer.Length);
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs
index 94639405..411d33aa 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs
@@ -30,6 +30,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Size, HostAddress);
}
+ public void SetData(byte[] Data)
+ {
+ GL.BindBuffer(Target, Handle);
+
+ GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Data.Length, Data);
+ }
+
public void Dispose()
{
Dispose(true);
diff --git a/Ryujinx.Graphics/Memory/NvGpuVmm.cs b/Ryujinx.Graphics/Memory/NvGpuVmm.cs
index ec660bf0..ccb21950 100644
--- a/Ryujinx.Graphics/Memory/NvGpuVmm.cs
+++ b/Ryujinx.Graphics/Memory/NvGpuVmm.cs
@@ -243,9 +243,9 @@ namespace Ryujinx.Graphics.Memory
return Cache.IsRegionModified(Memory, BufferType, PA, Size);
}
- public IntPtr GetHostAddress(long Position, long Size)
+ public bool TryGetHostAddress(long Position, long Size, out IntPtr Ptr)
{
- return Memory.GetHostAddress(GetPhysicalAddress(Position), Size);
+ return Memory.TryGetHostAddress(GetPhysicalAddress(Position), Size, out Ptr);
}
public byte ReadByte(long Position)
diff --git a/Ryujinx.Graphics/NvGpuEngine3d.cs b/Ryujinx.Graphics/NvGpuEngine3d.cs
index 4a0310fb..918409e2 100644
--- a/Ryujinx.Graphics/NvGpuEngine3d.cs
+++ b/Ryujinx.Graphics/NvGpuEngine3d.cs
@@ -615,9 +615,14 @@ namespace Ryujinx.Graphics
if (Gpu.ResourceManager.MemoryRegionModified(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer))
{
- IntPtr Source = Vmm.GetHostAddress(Cb.Position, Cb.Size);
-
- Gpu.Renderer.Buffer.SetData(Key, Cb.Size, Source);
+ if (Vmm.TryGetHostAddress(Cb.Position, Cb.Size, out IntPtr CbPtr))
+ {
+ Gpu.Renderer.Buffer.SetData(Key, Cb.Size, CbPtr);
+ }
+ else
+ {
+ Gpu.Renderer.Buffer.SetData(Key, Vmm.ReadBytes(Cb.Position, Cb.Size));
+ }
}
State.ConstBufferKeys[Stage][DeclInfo.Cbuf] = Key;
@@ -660,9 +665,14 @@ namespace Ryujinx.Graphics
{
if (!UsesLegacyQuads)
{
- IntPtr DataAddress = Vmm.GetHostAddress(IbPosition, IbSize);
-
- Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress);
+ if (Vmm.TryGetHostAddress(IbPosition, IbSize, out IntPtr IbPtr))
+ {
+ Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, IbPtr);
+ }
+ else
+ {
+ Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, Vmm.ReadBytes(IbPosition, IbSize));
+ }
}
else
{
@@ -711,22 +721,22 @@ namespace Ryujinx.Graphics
Attribs[ArrayIndex] = new List<GalVertexAttrib>();
}
- long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + ArrayIndex * 4);
+ long VbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + ArrayIndex * 4);
+
+ bool IsConst = ((Packed >> 6) & 1) != 0;
int Offset = (Packed >> 7) & 0x3fff;
+ GalVertexAttribSize Size = (GalVertexAttribSize)((Packed >> 21) & 0x3f);
+ GalVertexAttribType Type = (GalVertexAttribType)((Packed >> 27) & 0x7);
+
+ bool IsRgba = ((Packed >> 31) & 1) != 0;
+
//Note: 16 is the maximum size of an attribute,
//having a component size of 32-bits with 4 elements (a vec4).
- IntPtr Pointer = Vmm.GetHostAddress(VertexPosition + Offset, 16);
+ byte[] Data = Vmm.ReadBytes(VbPosition + Offset, 16);
- Attribs[ArrayIndex].Add(new GalVertexAttrib(
- Attr,
- ((Packed >> 6) & 0x1) != 0,
- Offset,
- Pointer,
- (GalVertexAttribSize)((Packed >> 21) & 0x3f),
- (GalVertexAttribType)((Packed >> 27) & 0x7),
- ((Packed >> 31) & 0x1) != 0));
+ Attribs[ArrayIndex].Add(new GalVertexAttrib(Attr, IsConst, Offset, Data, Size, Type, IsRgba));
}
State.VertexBindings = new GalVertexBinding[32];
@@ -747,8 +757,8 @@ namespace Ryujinx.Graphics
continue;
}
- long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
- long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2);
+ long VbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
+ long VbEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2);
int VertexDivisor = ReadRegister(NvGpuEngine3dReg.VertexArrayNDivisor + Index * 4);
@@ -758,26 +768,31 @@ namespace Ryujinx.Graphics
if (Instanced && VertexDivisor != 0)
{
- VertexPosition += Stride * (CurrentInstance / VertexDivisor);
+ VbPosition += Stride * (CurrentInstance / VertexDivisor);
}
- if (VertexPosition > VertexEndPos)
+ if (VbPosition > VbEndPos)
{
//Instance is invalid, ignore the draw call
continue;
}
- long VboKey = Vmm.GetPhysicalAddress(VertexPosition);
+ long VboKey = Vmm.GetPhysicalAddress(VbPosition);
- long VbSize = (VertexEndPos - VertexPosition) + 1;
+ long VbSize = (VbEndPos - VbPosition) + 1;
bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize);
if (!VboCached || Gpu.ResourceManager.MemoryRegionModified(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex))
{
- IntPtr DataAddress = Vmm.GetHostAddress(VertexPosition, VbSize);
-
- Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, DataAddress);
+ if (Vmm.TryGetHostAddress(VbPosition, VbSize, out IntPtr VbPtr))
+ {
+ Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, VbPtr);
+ }
+ else
+ {
+ Gpu.Renderer.Rasterizer.CreateVbo(VboKey, Vmm.ReadBytes(VbPosition, VbSize));
+ }
}
State.VertexBindings[Index].Enabled = true;
diff --git a/Ryujinx.HLE/Memory/DeviceMemory.cs b/Ryujinx.HLE/DeviceMemory.cs
index 3c5f2e5f..edc70911 100644
--- a/Ryujinx.HLE/Memory/DeviceMemory.cs
+++ b/Ryujinx.HLE/DeviceMemory.cs
@@ -1,22 +1,18 @@
using System;
using System.Runtime.InteropServices;
-namespace Ryujinx.HLE.Memory
+namespace Ryujinx.HLE
{
class DeviceMemory : IDisposable
{
public const long RamSize = 4L * 1024 * 1024 * 1024;
- public ArenaAllocator Allocator { get; private set; }
-
public IntPtr RamPointer { get; private set; }
private unsafe byte* RamPtr;
public unsafe DeviceMemory()
{
- Allocator = new ArenaAllocator(RamSize);
-
RamPointer = Marshal.AllocHGlobal(new IntPtr(RamSize));
RamPtr = (byte*)RamPointer;
diff --git a/Ryujinx.HLE/FileSystem/SaveHelper.cs b/Ryujinx.HLE/FileSystem/SaveHelper.cs
index b74d853c..20138c8c 100644
--- a/Ryujinx.HLE/FileSystem/SaveHelper.cs
+++ b/Ryujinx.HLE/FileSystem/SaveHelper.cs
@@ -29,10 +29,7 @@ namespace Ryujinx.HLE.FileSystem
if (SaveMetaData.TitleId == 0 && SaveMetaData.SaveDataType == SaveDataType.SaveData)
{
- if (Context.Process.MetaData != null)
- {
- CurrentTitleId = Context.Process.MetaData.ACI0.TitleId;
- }
+ CurrentTitleId = Context.Process.TitleId;
}
string SaveAccount = SaveMetaData.UserId.IsZero() ? "savecommon" : SaveMetaData.UserId.ToString();
diff --git a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs
index 313db345..55adf46a 100644
--- a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs
+++ b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs
@@ -1,4 +1,4 @@
-using LibHac;
+using LibHac;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.Resource;
diff --git a/Ryujinx.HLE/HOS/GlobalStateTable.cs b/Ryujinx.HLE/HOS/GlobalStateTable.cs
index faf47b2e..d7d83453 100644
--- a/Ryujinx.HLE/HOS/GlobalStateTable.cs
+++ b/Ryujinx.HLE/HOS/GlobalStateTable.cs
@@ -1,3 +1,4 @@
+using Ryujinx.HLE.HOS.Kernel;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -5,28 +6,28 @@ namespace Ryujinx.HLE.HOS
{
class GlobalStateTable
{
- private ConcurrentDictionary<Process, IdDictionary> DictByProcess;
+ private ConcurrentDictionary<KProcess, IdDictionary> DictByProcess;
public GlobalStateTable()
{
- DictByProcess = new ConcurrentDictionary<Process, IdDictionary>();
+ DictByProcess = new ConcurrentDictionary<KProcess, IdDictionary>();
}
- public bool Add(Process Process, int Id, object Data)
+ public bool Add(KProcess Process, int Id, object Data)
{
IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary());
return Dict.Add(Id, Data);
}
- public int Add(Process Process, object Data)
+ public int Add(KProcess Process, object Data)
{
IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary());
return Dict.Add(Data);
}
- public object GetData(Process Process, int Id)
+ public object GetData(KProcess Process, int Id)
{
if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
{
@@ -36,7 +37,7 @@ namespace Ryujinx.HLE.HOS
return null;
}
- public T GetData<T>(Process Process, int Id)
+ public T GetData<T>(KProcess Process, int Id)
{
if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
{
@@ -46,7 +47,7 @@ namespace Ryujinx.HLE.HOS
return default(T);
}
- public object Delete(Process Process, int Id)
+ public object Delete(KProcess Process, int Id)
{
if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
{
@@ -56,7 +57,7 @@ namespace Ryujinx.HLE.HOS
return null;
}
- public ICollection<object> DeleteProcess(Process Process)
+ public ICollection<object> DeleteProcess(KProcess Process)
{
if (DictByProcess.TryRemove(Process, out IdDictionary Dict))
{
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index d967c896..46c27649 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -11,32 +11,68 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using Nso = Ryujinx.HLE.Loaders.Executables.Nso;
+using System.Reflection;
+using System.Threading;
+
+using NxStaticObject = Ryujinx.HLE.Loaders.Executables.NxStaticObject;
namespace Ryujinx.HLE.HOS
{
public class Horizon : IDisposable
{
+ internal const int InitialKipId = 1;
+ internal const int InitialProcessId = 0x51;
+
internal const int HidSize = 0x40000;
internal const int FontSize = 0x1100000;
- private Switch Device;
+ private const int MemoryBlockAllocatorSize = 0x2710;
+
+ private const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
+ private const ulong UserSlabHeapItemSize = KMemoryManager.PageSize;
+ private const ulong UserSlabHeapSize = 0x3de000;
- private ConcurrentDictionary<int, Process> Processes;
+ internal long PrivilegedProcessLowestId { get; set; } = 1;
+ internal long PrivilegedProcessHighestId { get; set; } = 8;
+
+ internal Switch Device { get; private set; }
public SystemStateMgr State { get; private set; }
- internal KRecursiveLock CriticalSectionLock { get; private set; }
+ internal bool KernelInitialized { get; private set; }
+
+ internal KResourceLimit ResourceLimit { get; private set; }
+
+ internal KMemoryRegionManager[] MemoryRegions { get; private set; }
+
+ internal KMemoryBlockAllocator LargeMemoryBlockAllocator { get; private set; }
+ internal KMemoryBlockAllocator SmallMemoryBlockAllocator { get; private set; }
+
+ internal KSlabHeap UserSlabHeapPages { get; private set; }
+
+ internal KCriticalSection CriticalSection { get; private set; }
internal KScheduler Scheduler { get; private set; }
internal KTimeManager TimeManager { get; private set; }
- internal KAddressArbiter AddressArbiter { get; private set; }
-
internal KSynchronization Synchronization { get; private set; }
- internal LinkedList<KThread> Withholders { get; private set; }
+ internal KContextIdManager ContextIdManager { get; private set; }
+
+ private long KipId;
+ private long ProcessId;
+ private long ThreadUid;
+
+ internal CountdownEvent ThreadCounter;
+
+ internal SortedDictionary<long, KProcess> Processes;
+
+ internal ConcurrentDictionary<string, KAutoObject> AutoObjectNames;
+
+ internal bool EnableVersionChecks { get; private set; }
+
+ internal AppletStateMgr AppletState { get; private set; }
internal KSharedMemory HidSharedMem { get; private set; }
internal KSharedMemory FontSharedMem { get; private set; }
@@ -57,38 +93,74 @@ namespace Ryujinx.HLE.HOS
public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; }
+ internal long HidBaseAddress { get; private set; }
+
public Horizon(Switch Device)
{
this.Device = Device;
- Processes = new ConcurrentDictionary<int, Process>();
-
State = new SystemStateMgr();
- CriticalSectionLock = new KRecursiveLock(this);
+ ResourceLimit = new KResourceLimit(this);
+
+ KernelInit.InitializeResourceLimit(ResourceLimit);
+
+ MemoryRegions = KernelInit.GetMemoryRegions();
+
+ LargeMemoryBlockAllocator = new KMemoryBlockAllocator(MemoryBlockAllocatorSize * 2);
+ SmallMemoryBlockAllocator = new KMemoryBlockAllocator(MemoryBlockAllocatorSize);
+
+ UserSlabHeapPages = new KSlabHeap(
+ UserSlabHeapBase,
+ UserSlabHeapItemSize,
+ UserSlabHeapSize);
+
+ CriticalSection = new KCriticalSection(this);
Scheduler = new KScheduler(this);
TimeManager = new KTimeManager();
- AddressArbiter = new KAddressArbiter(this);
-
Synchronization = new KSynchronization(this);
- Withholders = new LinkedList<KThread>();
+ ContextIdManager = new KContextIdManager();
+
+ KipId = InitialKipId;
+ ProcessId = InitialProcessId;
Scheduler.StartAutoPreemptionThread();
- if (!Device.Memory.Allocator.TryAllocate(HidSize, out long HidPA) ||
- !Device.Memory.Allocator.TryAllocate(FontSize, out long FontPA))
- {
- throw new InvalidOperationException();
- }
+ KernelInitialized = true;
+
+ ThreadCounter = new CountdownEvent(1);
+
+ Processes = new SortedDictionary<long, KProcess>();
+
+ AutoObjectNames = new ConcurrentDictionary<string, KAutoObject>();
+
+ //Note: This is not really correct, but with HLE of services, the only memory
+ //region used that is used is Application, so we can use the other ones for anything.
+ KMemoryRegionManager Region = MemoryRegions[(int)MemoryRegion.NvServices];
+
+ ulong HidPa = Region.Address;
+ ulong FontPa = Region.Address + HidSize;
+
+ HidBaseAddress = (long)(HidPa - DramMemoryMap.DramBase);
- HidSharedMem = new KSharedMemory(HidPA, HidSize);
- FontSharedMem = new KSharedMemory(FontPA, FontSize);
+ KPageList HidPageList = new KPageList();
+ KPageList FontPageList = new KPageList();
- Font = new SharedFontManager(Device, FontSharedMem.PA);
+ HidPageList .AddRange(HidPa, HidSize / KMemoryManager.PageSize);
+ FontPageList.AddRange(FontPa, FontSize / KMemoryManager.PageSize);
+
+ HidSharedMem = new KSharedMemory(HidPageList, 0, 0, MemoryPermission.Read);
+ FontSharedMem = new KSharedMemory(FontPageList, 0, 0, MemoryPermission.Read);
+
+ AppletState = new AppletStateMgr(this);
+
+ AppletState.SetFocus(true);
+
+ Font = new SharedFontManager(Device, (long)(FontPa - DramMemoryMap.DramBase));
VsyncEvent = new KEvent(this);
@@ -120,13 +192,15 @@ namespace Ryujinx.HLE.HOS
else
{
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
+
+ MetaData = GetDefaultNpdm();
}
- Process MainProcess = MakeProcess(MetaData);
+ List<IExecutable> StaticObjects = new List<IExecutable>();
- void LoadNso(string FileName)
+ void LoadNso(string SearchPattern)
{
- foreach (string File in Directory.GetFiles(ExeFsDir, FileName))
+ foreach (string File in Directory.GetFiles(ExeFsDir, SearchPattern))
{
if (Path.GetExtension(File) != string.Empty)
{
@@ -137,33 +211,28 @@ namespace Ryujinx.HLE.HOS
using (FileStream Input = new FileStream(File, FileMode.Open))
{
- string Name = Path.GetFileNameWithoutExtension(File);
+ NxStaticObject StaticObject = new NxStaticObject(Input);
- Nso Program = new Nso(Input, Name);
-
- MainProcess.LoadProgram(Program);
+ StaticObjects.Add(StaticObject);
}
}
}
- if (!(MainProcess.MetaData?.Is64Bits ?? true))
+ if (!MetaData.Is64Bits)
{
throw new NotImplementedException("32-bit titles are unsupported!");
}
- CurrentTitle = MainProcess.MetaData.ACI0.TitleId.ToString("x16");
+ CurrentTitle = MetaData.ACI0.TitleId.ToString("x16");
LoadNso("rtld");
-
- MainProcess.SetEmptyArgs();
-
LoadNso("main");
LoadNso("subsdk*");
LoadNso("sdk");
ContentManager.LoadEntries();
- MainProcess.Run();
+ ProgramLoader.LoadStaticObjects(this, MetaData, StaticObjects.ToArray());
}
public void LoadXci(string XciFile)
@@ -356,9 +425,11 @@ namespace Ryujinx.HLE.HOS
else
{
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
+
+ MetaData = GetDefaultNpdm();
}
- Process MainProcess = MakeProcess(MetaData);
+ List<IExecutable> StaticObjects = new List<IExecutable>();
void LoadNso(string Filename)
{
@@ -371,11 +442,9 @@ namespace Ryujinx.HLE.HOS
Logger.PrintInfo(LogClass.Loader, $"Loading {Filename}...");
- string Name = Path.GetFileNameWithoutExtension(File.Name);
-
- Nso Program = new Nso(Exefs.OpenFile(File), Name);
+ NxStaticObject StaticObject = new NxStaticObject(Exefs.OpenFile(File));
- MainProcess.LoadProgram(Program);
+ StaticObjects.Add(StaticObject);
}
}
@@ -401,69 +470,52 @@ namespace Ryujinx.HLE.HOS
if (ControlNca != null)
{
- MainProcess.ControlData = ReadControlData();
+ ReadControlData();
}
else
{
- CurrentTitle = MainProcess.MetaData.ACI0.TitleId.ToString("x16");
+ CurrentTitle = MetaData.ACI0.TitleId.ToString("x16");
}
- if (!MainProcess.MetaData.Is64Bits)
+ if (!MetaData.Is64Bits)
{
- throw new NotImplementedException("32-bit titles are unsupported!");
+ throw new NotImplementedException("32-bit titles are not supported!");
}
LoadNso("rtld");
-
- MainProcess.SetEmptyArgs();
-
LoadNso("main");
LoadNso("subsdk");
LoadNso("sdk");
ContentManager.LoadEntries();
- MainProcess.Run();
+ ProgramLoader.LoadStaticObjects(this, MetaData, StaticObjects.ToArray());
}
public void LoadProgram(string FilePath)
{
- bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro";
+ Npdm MetaData = GetDefaultNpdm();
- string Name = Path.GetFileNameWithoutExtension(FilePath);
- string SwitchFilePath = Device.FileSystem.SystemPathToSwitchPath(FilePath);
+ bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro";
- if (IsNro && (SwitchFilePath == null || !SwitchFilePath.StartsWith("sdmc:/")))
+ using (FileStream Input = new FileStream(FilePath, FileMode.Open))
{
- string SwitchPath = $"sdmc:/switch/{Name}{Homebrew.TemporaryNroSuffix}";
- string TempPath = Device.FileSystem.SwitchPathToSystemPath(SwitchPath);
-
- string SwitchDir = Path.GetDirectoryName(TempPath);
-
- if (!Directory.Exists(SwitchDir))
- {
- Directory.CreateDirectory(SwitchDir);
- }
-
- File.Copy(FilePath, TempPath, true);
+ IExecutable StaticObject = IsNro
+ ? (IExecutable)new NxRelocatableObject(Input)
+ : (IExecutable)new NxStaticObject(Input);
- FilePath = TempPath;
+ ProgramLoader.LoadStaticObjects(this, MetaData, new IExecutable[] { StaticObject });
}
+ }
- Process MainProcess = MakeProcess();
+ private Npdm GetDefaultNpdm()
+ {
+ Assembly Asm = Assembly.GetCallingAssembly();
- using (FileStream Input = new FileStream(FilePath, FileMode.Open))
+ using (Stream NpdmStream = Asm.GetManifestResourceStream("Ryujinx.HLE.Homebrew.npdm"))
{
- MainProcess.LoadProgram(IsNro
- ? (IExecutable)new Nro(Input, FilePath)
- : (IExecutable)new Nso(Input, FilePath));
+ return new Npdm(NpdmStream);
}
-
- MainProcess.SetEmptyArgs();
-
- ContentManager.LoadEntries();
-
- MainProcess.Run(IsNro);
}
public void LoadKeySet()
@@ -507,51 +559,19 @@ namespace Ryujinx.HLE.HOS
VsyncEvent.ReadableEvent.Signal();
}
- private Process MakeProcess(Npdm MetaData = null)
+ internal long GetThreadUid()
{
- HasStarted = true;
-
- Process Process;
-
- lock (Processes)
- {
- int ProcessId = 0;
-
- while (Processes.ContainsKey(ProcessId))
- {
- ProcessId++;
- }
-
- Process = new Process(Device, ProcessId, MetaData);
-
- Processes.TryAdd(ProcessId, Process);
- }
-
- InitializeProcess(Process);
-
- return Process;
+ return Interlocked.Increment(ref ThreadUid) - 1;
}
- private void InitializeProcess(Process Process)
+ internal long GetKipId()
{
- Process.AppletState.SetFocus(true);
+ return Interlocked.Increment(ref KipId) - 1;
}
- internal void ExitProcess(int ProcessId)
+ internal long GetProcessId()
{
- if (Processes.TryRemove(ProcessId, out Process Process))
- {
- Process.Dispose();
-
- if (Processes.Count == 0)
- {
- Scheduler.Dispose();
-
- TimeManager.Dispose();
-
- Device.Unload();
- }
- }
+ return Interlocked.Increment(ref ProcessId) - 1;
}
public void EnableMultiCoreScheduling()
@@ -579,10 +599,25 @@ namespace Ryujinx.HLE.HOS
{
if (Disposing)
{
- foreach (Process Process in Processes.Values)
+ //Force all threads to exit.
+ lock (Processes)
{
- Process.Dispose();
+ foreach (KProcess Process in Processes.Values)
+ {
+ Process.StopAllThreads();
+ }
}
+
+ //It's only safe to release resources once all threads
+ //have exited.
+ ThreadCounter.Signal();
+ ThreadCounter.Wait();
+
+ Scheduler.Dispose();
+
+ TimeManager.Dispose();
+
+ Device.Unload();
}
}
}
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs b/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs
index ec27a6ea..860c8242 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs
@@ -8,8 +8,8 @@ namespace Ryujinx.HLE.HOS.Ipc
static class IpcHandler
{
public static long IpcCall(
- Switch Ns,
- Process Process,
+ Switch Device,
+ KProcess Process,
MemoryManager Memory,
KSession Session,
IpcMessage Request,
@@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Ipc
BinaryWriter ResWriter = new BinaryWriter(ResMS);
ServiceCtx Context = new ServiceCtx(
- Ns,
+ Device,
Process,
Memory,
Session,
diff --git a/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs b/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs
index c97caf42..6f7b230e 100644
--- a/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs
+++ b/Ryujinx.HLE/HOS/Kernel/AddressSpaceType.cs
@@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
Addr32Bits = 0,
Addr36Bits = 1,
- Addr36BitsNoMap = 2,
+ Addr32BitsNoMap = 2,
Addr39Bits = 3
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs b/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs
index 8a2d47f7..b584d719 100644
--- a/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs
+++ b/Ryujinx.HLE/HOS/Kernel/ArbitrationType.cs
@@ -6,4 +6,4 @@ namespace Ryujinx.HLE.HOS.Kernel
DecrementAndWaitIfLessThan = 1,
WaitIfEqual = 2
}
-}
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs b/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs
new file mode 100644
index 00000000..b20a83e2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ static class DramMemoryMap
+ {
+ public const ulong DramBase = 0x80000000;
+ public const ulong DramSize = 0x100000000;
+ public const ulong DramEnd = DramBase + DramSize;
+
+ public const ulong KernelReserveBase = DramBase + 0x60000;
+
+ public const ulong SlabHeapBase = KernelReserveBase + 0x85000;
+ public const ulong SlapHeapSize = 0xa21000;
+ public const ulong SlabHeapEnd = SlabHeapBase + SlapHeapSize;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs b/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs
index 0bfa2710..6a424cf2 100644
--- a/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/HleCoreManager.cs
@@ -5,24 +5,61 @@ namespace Ryujinx.HLE.HOS.Kernel
{
class HleCoreManager
{
- private ConcurrentDictionary<Thread, ManualResetEvent> Threads;
+ private class PausableThread
+ {
+ public ManualResetEvent Event { get; private set; }
+
+ public bool IsExiting { get; set; }
+
+ public PausableThread()
+ {
+ Event = new ManualResetEvent(false);
+ }
+ }
+
+ private ConcurrentDictionary<Thread, PausableThread> Threads;
public HleCoreManager()
{
- Threads = new ConcurrentDictionary<Thread, ManualResetEvent>();
+ Threads = new ConcurrentDictionary<Thread, PausableThread>();
+ }
+
+ public void Set(Thread Thread)
+ {
+ GetThread(Thread).Event.Set();
+ }
+
+ public void Reset(Thread Thread)
+ {
+ GetThread(Thread).Event.Reset();
+ }
+
+ public void Wait(Thread Thread)
+ {
+ PausableThread PausableThread = GetThread(Thread);
+
+ if (!PausableThread.IsExiting)
+ {
+ PausableThread.Event.WaitOne();
+ }
+ }
+
+ public void Exit(Thread Thread)
+ {
+ GetThread(Thread).IsExiting = true;
}
- public ManualResetEvent GetThread(Thread Thread)
+ private PausableThread GetThread(Thread Thread)
{
- return Threads.GetOrAdd(Thread, (Key) => new ManualResetEvent(false));
+ return Threads.GetOrAdd(Thread, (Key) => new PausableThread());
}
public void RemoveThread(Thread Thread)
{
- if (Threads.TryRemove(Thread, out ManualResetEvent Event))
+ if (Threads.TryRemove(Thread, out PausableThread PausableThread))
{
- Event.Set();
- Event.Dispose();
+ PausableThread.Event.Set();
+ PausableThread.Event.Dispose();
}
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs b/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs
new file mode 100644
index 00000000..a6053b1b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs
@@ -0,0 +1,310 @@
+using ChocolArm64.Memory;
+using ChocolArm64.State;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Diagnostics.Demangler;
+using Ryujinx.HLE.Loaders.Elf;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class HleProcessDebugger
+ {
+ private const int Mod0 = 'M' << 0 | 'O' << 8 | 'D' << 16 | '0' << 24;
+
+ private KProcess Owner;
+
+ private class Image
+ {
+ public long BaseAddress { get; private set; }
+
+ public ElfSymbol[] Symbols { get; private set; }
+
+ public Image(long BaseAddress, ElfSymbol[] Symbols)
+ {
+ this.BaseAddress = BaseAddress;
+ this.Symbols = Symbols;
+ }
+ }
+
+ private List<Image> Images;
+
+ private int Loaded;
+
+ public HleProcessDebugger(KProcess Owner)
+ {
+ this.Owner = Owner;
+
+ Images = new List<Image>();
+ }
+
+ public void PrintGuestStackTrace(CpuThreadState ThreadState)
+ {
+ EnsureLoaded();
+
+ StringBuilder Trace = new StringBuilder();
+
+ Trace.AppendLine("Guest stack trace:");
+
+ void AppendTrace(long Address)
+ {
+ Image Image = GetImage(Address, out int ImageIndex);
+
+ if (Image == null || !TryGetSubName(Image, Address, out string SubName))
+ {
+ SubName = $"Sub{Address:x16}";
+ }
+ else if (SubName.StartsWith("_Z"))
+ {
+ SubName = Demangler.Parse(SubName);
+ }
+
+ if (Image != null)
+ {
+ long Offset = Address - Image.BaseAddress;
+
+ string ImageName = GetGuessedNsoNameFromIndex(ImageIndex);
+
+ string ImageNameAndOffset = $"[{Owner.Name}] {ImageName}:0x{Offset:x8}";
+
+ Trace.AppendLine($" {ImageNameAndOffset} {SubName}");
+ }
+ else
+ {
+ Trace.AppendLine($" [{Owner.Name}] ??? {SubName}");
+ }
+ }
+
+ long FramePointer = (long)ThreadState.X29;
+
+ while (FramePointer != 0)
+ {
+ if ((FramePointer & 7) != 0 ||
+ !Owner.CpuMemory.IsMapped(FramePointer) ||
+ !Owner.CpuMemory.IsMapped(FramePointer + 8))
+ {
+ break;
+ }
+
+ //Note: This is the return address, we need to subtract one instruction
+ //worth of bytes to get the branch instruction address.
+ AppendTrace(Owner.CpuMemory.ReadInt64(FramePointer + 8) - 4);
+
+ FramePointer = Owner.CpuMemory.ReadInt64(FramePointer);
+ }
+
+ Logger.PrintInfo(LogClass.Cpu, Trace.ToString());
+ }
+
+ private bool TryGetSubName(Image Image, long Address, out string Name)
+ {
+ Address -= Image.BaseAddress;
+
+ int Left = 0;
+ int Right = Image.Symbols.Length - 1;
+
+ while (Left <= Right)
+ {
+ int Size = Right - Left;
+
+ int Middle = Left + (Size >> 1);
+
+ ElfSymbol Symbol = Image.Symbols[Middle];
+
+ long EndAddr = Symbol.Value + Symbol.Size;
+
+ if ((ulong)Address >= (ulong)Symbol.Value && (ulong)Address < (ulong)EndAddr)
+ {
+ Name = Symbol.Name;
+
+ return true;
+ }
+
+ if ((ulong)Address < (ulong)Symbol.Value)
+ {
+ Right = Middle - 1;
+ }
+ else
+ {
+ Left = Middle + 1;
+ }
+ }
+
+ Name = null;
+
+ return false;
+ }
+
+ private Image GetImage(long Address, out int Index)
+ {
+ lock (Images)
+ {
+ for (Index = Images.Count - 1; Index >= 0; Index--)
+ {
+ if ((ulong)Address >= (ulong)Images[Index].BaseAddress)
+ {
+ return Images[Index];
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private string GetGuessedNsoNameFromIndex(int Index)
+ {
+ if ((uint)Index > 11)
+ {
+ return "???";
+ }
+
+ if (Index == 0)
+ {
+ return "rtld";
+ }
+ else if (Index == 1)
+ {
+ return "main";
+ }
+ else if (Index == GetImagesCount() - 1)
+ {
+ return "sdk";
+ }
+ else
+ {
+ return "subsdk" + (Index - 2);
+ }
+ }
+
+ private int GetImagesCount()
+ {
+ lock (Images)
+ {
+ return Images.Count;
+ }
+ }
+
+ private void EnsureLoaded()
+ {
+ if (Interlocked.CompareExchange(ref Loaded, 1, 0) == 0)
+ {
+ ScanMemoryForTextSegments();
+ }
+ }
+
+ private void ScanMemoryForTextSegments()
+ {
+ ulong OldAddress = 0;
+ ulong Address = 0;
+
+ while (Address >= OldAddress)
+ {
+ KMemoryInfo Info = Owner.MemoryManager.QueryMemory(Address);
+
+ if (Info.State == MemoryState.Reserved)
+ {
+ break;
+ }
+
+ if (Info.State == MemoryState.CodeStatic && Info.Permission == MemoryPermission.ReadAndExecute)
+ {
+ LoadMod0Symbols(Owner.CpuMemory, (long)Info.Address);
+ }
+
+ OldAddress = Address;
+
+ Address = Info.Address + Info.Size;
+ }
+ }
+
+ private void LoadMod0Symbols(MemoryManager Memory, long TextOffset)
+ {
+ long Mod0Offset = TextOffset + Memory.ReadUInt32(TextOffset + 4);
+
+ if (Mod0Offset < TextOffset || !Memory.IsMapped(Mod0Offset) || (Mod0Offset & 3) != 0)
+ {
+ return;
+ }
+
+ Dictionary<ElfDynamicTag, long> Dynamic = new Dictionary<ElfDynamicTag, long>();
+
+ int Mod0Magic = Memory.ReadInt32(Mod0Offset + 0x0);
+
+ if (Mod0Magic != Mod0)
+ {
+ return;
+ }
+
+ long DynamicOffset = Memory.ReadInt32(Mod0Offset + 0x4) + Mod0Offset;
+ long BssStartOffset = Memory.ReadInt32(Mod0Offset + 0x8) + Mod0Offset;
+ long BssEndOffset = Memory.ReadInt32(Mod0Offset + 0xc) + Mod0Offset;
+ long EhHdrStartOffset = Memory.ReadInt32(Mod0Offset + 0x10) + Mod0Offset;
+ long EhHdrEndOffset = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset;
+ long ModObjOffset = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset;
+
+ while (true)
+ {
+ long TagVal = Memory.ReadInt64(DynamicOffset + 0);
+ long Value = Memory.ReadInt64(DynamicOffset + 8);
+
+ DynamicOffset += 0x10;
+
+ ElfDynamicTag Tag = (ElfDynamicTag)TagVal;
+
+ if (Tag == ElfDynamicTag.DT_NULL)
+ {
+ break;
+ }
+
+ Dynamic[Tag] = Value;
+ }
+
+ if (!Dynamic.TryGetValue(ElfDynamicTag.DT_STRTAB, out long StrTab) ||
+ !Dynamic.TryGetValue(ElfDynamicTag.DT_SYMTAB, out long SymTab) ||
+ !Dynamic.TryGetValue(ElfDynamicTag.DT_SYMENT, out long SymEntSize))
+ {
+ return;
+ }
+
+ long StrTblAddr = TextOffset + StrTab;
+ long SymTblAddr = TextOffset + SymTab;
+
+ List<ElfSymbol> Symbols = new List<ElfSymbol>();
+
+ while ((ulong)SymTblAddr < (ulong)StrTblAddr)
+ {
+ ElfSymbol Sym = GetSymbol(Memory, SymTblAddr, StrTblAddr);
+
+ Symbols.Add(Sym);
+
+ SymTblAddr += SymEntSize;
+ }
+
+ lock (Images)
+ {
+ Images.Add(new Image(TextOffset, Symbols.OrderBy(x => x.Value).ToArray()));
+ }
+ }
+
+ private ElfSymbol GetSymbol(MemoryManager Memory, long Address, long StrTblAddr)
+ {
+ int NameIndex = Memory.ReadInt32(Address + 0);
+ int Info = Memory.ReadByte (Address + 4);
+ int Other = Memory.ReadByte (Address + 5);
+ int SHIdx = Memory.ReadInt16(Address + 6);
+ long Value = Memory.ReadInt64(Address + 8);
+ long Size = Memory.ReadInt64(Address + 16);
+
+ string Name = string.Empty;
+
+ for (int Chr; (Chr = Memory.ReadByte(StrTblAddr + NameIndex++)) != 0;)
+ {
+ Name += (char)Chr;
+ }
+
+ return new ElfSymbol(Name, Info, Other, SHIdx, Value, Size);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs b/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs
index e0cb158c..87dbe553 100644
--- a/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs
@@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Kernel
public bool MultiCoreScheduling { get; set; }
- private HleCoreManager CoreManager;
+ public HleCoreManager CoreManager { get; private set; }
private bool KeepPreempting;
@@ -49,11 +49,11 @@ namespace Ryujinx.HLE.HOS.Kernel
if (SelectedCount == 0)
{
- CoreManager.GetThread(Thread.CurrentThread).Reset();
+ CoreManager.Reset(Thread.CurrentThread);
}
else if (SelectedCount == 1)
{
- CoreManager.GetThread(Thread.CurrentThread).Set();
+ CoreManager.Set(Thread.CurrentThread);
}
else
{
@@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- CoreManager.GetThread(CurrentThread.Context.Work).Reset();
+ CoreManager.Reset(CurrentThread.Context.Work);
}
//Advance current core and try picking a thread,
@@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
CoreContext.CurrentThread.ClearExclusive();
- CoreManager.GetThread(CoreContext.CurrentThread.Context.Work).Set();
+ CoreManager.Set(CoreContext.CurrentThread.Context.Work);
CoreContext.CurrentThread.Context.Execute();
@@ -111,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- CoreManager.GetThread(Thread.CurrentThread).WaitOne();
+ CoreManager.Wait(Thread.CurrentThread);
}
private void PreemptCurrentThread()
@@ -134,11 +134,11 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- public void StopThread(KThread Thread)
+ public void ExitThread(KThread Thread)
{
Thread.Context.StopExecution();
- CoreManager.GetThread(Thread.Context.Work).Set();
+ CoreManager.Exit(Thread.Context.Work);
}
public void RemoveThread(KThread Thread)
diff --git a/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs b/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs
index 4a0f955f..cc637be0 100644
--- a/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs
@@ -1,4 +1,3 @@
-using ChocolArm64.Memory;
using System.Collections.Generic;
using System.Linq;
@@ -23,39 +22,36 @@ namespace Ryujinx.HLE.HOS.Kernel
ArbiterThreads = new List<KThread>();
}
- public long ArbitrateLock(
- Process Process,
- MemoryManager Memory,
- int OwnerHandle,
- long MutexAddress,
- int RequesterHandle)
+ public long ArbitrateLock(int OwnerHandle, long MutexAddress, int RequesterHandle)
{
- System.CriticalSectionLock.Lock();
-
KThread CurrentThread = System.Scheduler.GetCurrentThread();
+ System.CriticalSection.Enter();
+
CurrentThread.SignaledObj = null;
CurrentThread.ObjSyncResult = 0;
- if (!UserToKernelInt32(Memory, MutexAddress, out int MutexValue))
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (!KernelTransfer.UserToKernelInt32(System, MutexAddress, out int MutexValue))
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);;
}
if (MutexValue != (OwnerHandle | HasListenersMask))
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return 0;
}
- KThread MutexOwner = Process.HandleTable.GetObject<KThread>(OwnerHandle);
+ KThread MutexOwner = CurrentProcess.HandleTable.GetObject<KThread>(OwnerHandle);
if (MutexOwner == null)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
}
@@ -67,26 +63,26 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentThread.Reschedule(ThreadSchedState.Paused);
- System.CriticalSectionLock.Unlock();
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Leave();
+ System.CriticalSection.Enter();
if (CurrentThread.MutexOwner != null)
{
CurrentThread.MutexOwner.RemoveMutexWaiter(CurrentThread);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return (uint)CurrentThread.ObjSyncResult;
}
- public long ArbitrateUnlock(MemoryManager Memory, long MutexAddress)
+ public long ArbitrateUnlock(long MutexAddress)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
KThread CurrentThread = System.Scheduler.GetCurrentThread();
- (long Result, KThread NewOwnerThread) = MutexUnlock(Memory, CurrentThread, MutexAddress);
+ (long Result, KThread NewOwnerThread) = MutexUnlock(CurrentThread, MutexAddress);
if (Result != 0 && NewOwnerThread != null)
{
@@ -94,19 +90,18 @@ namespace Ryujinx.HLE.HOS.Kernel
NewOwnerThread.ObjSyncResult = (int)Result;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return Result;
}
public long WaitProcessWideKeyAtomic(
- MemoryManager Memory,
- long MutexAddress,
- long CondVarAddress,
- int ThreadHandle,
- long Timeout)
+ long MutexAddress,
+ long CondVarAddress,
+ int ThreadHandle,
+ long Timeout)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
KThread CurrentThread = System.Scheduler.GetCurrentThread();
@@ -116,16 +111,16 @@ namespace Ryujinx.HLE.HOS.Kernel
if (CurrentThread.ShallBeTerminated ||
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
}
- (long Result, _) = MutexUnlock(Memory, CurrentThread, MutexAddress);
+ (long Result, _) = MutexUnlock(CurrentThread, MutexAddress);
if (Result != 0)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return Result;
}
@@ -146,14 +141,14 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
if (Timeout > 0)
{
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
}
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (CurrentThread.MutexOwner != null)
{
@@ -162,12 +157,12 @@ namespace Ryujinx.HLE.HOS.Kernel
CondVarThreads.Remove(CurrentThread);
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return (uint)CurrentThread.ObjSyncResult;
}
- private (long, KThread) MutexUnlock(MemoryManager Memory, KThread CurrentThread, long MutexAddress)
+ private (long, KThread) MutexUnlock(KThread CurrentThread, long MutexAddress)
{
KThread NewOwnerThread = CurrentThread.RelinquishMutex(MutexAddress, out int Count);
@@ -190,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Kernel
long Result = 0;
- if (!KernelToUserInt32(Memory, MutexAddress, MutexValue))
+ if (!KernelTransfer.KernelToUserInt32(System, MutexAddress, MutexValue))
{
Result = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
@@ -198,17 +193,17 @@ namespace Ryujinx.HLE.HOS.Kernel
return (Result, NewOwnerThread);
}
- public void SignalProcessWideKey(Process Process, MemoryManager Memory, long Address, int Count)
+ public void SignalProcessWideKey(long Address, int Count)
{
Queue<KThread> SignaledThreads = new Queue<KThread>();
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
IOrderedEnumerable<KThread> SortedThreads = CondVarThreads.OrderBy(x => x.DynamicPriority);
foreach (KThread Thread in SortedThreads.Where(x => x.CondVarAddress == Address))
{
- TryAcquireMutex(Process, Memory, Thread);
+ TryAcquireMutex(Thread);
SignaledThreads.Enqueue(Thread);
@@ -224,19 +219,21 @@ namespace Ryujinx.HLE.HOS.Kernel
CondVarThreads.Remove(Thread);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
- private KThread TryAcquireMutex(Process Process, MemoryManager Memory, KThread Requester)
+ private KThread TryAcquireMutex(KThread Requester)
{
long Address = Requester.MutexAddress;
- Memory.SetExclusive(0, Address);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
- if (!UserToKernelInt32(Memory, Address, out int MutexValue))
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
+
+ if (!KernelTransfer.UserToKernelInt32(System, Address, out int MutexValue))
{
//Invalid address.
- Memory.ClearExclusive(0);
+ CurrentProcess.CpuMemory.ClearExclusive(0);
Requester.SignaledObj = null;
Requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
@@ -246,27 +243,27 @@ namespace Ryujinx.HLE.HOS.Kernel
while (true)
{
- if (Memory.TestExclusive(0, Address))
+ if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
{
if (MutexValue != 0)
{
//Update value to indicate there is a mutex waiter now.
- Memory.WriteInt32(Address, MutexValue | HasListenersMask);
+ CurrentProcess.CpuMemory.WriteInt32(Address, MutexValue | HasListenersMask);
}
else
{
//No thread owning the mutex, assign to requesting thread.
- Memory.WriteInt32(Address, Requester.ThreadHandleForUserMutex);
+ CurrentProcess.CpuMemory.WriteInt32(Address, Requester.ThreadHandleForUserMutex);
}
- Memory.ClearExclusiveForStore(0);
+ CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
break;
}
- Memory.SetExclusive(0, Address);
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
- MutexValue = Memory.ReadInt32(Address);
+ MutexValue = CurrentProcess.CpuMemory.ReadInt32(Address);
}
if (MutexValue == 0)
@@ -282,7 +279,7 @@ namespace Ryujinx.HLE.HOS.Kernel
MutexValue &= ~HasListenersMask;
- KThread MutexOwner = Process.HandleTable.GetObject<KThread>(MutexValue);
+ KThread MutexOwner = CurrentProcess.HandleTable.GetObject<KThread>(MutexValue);
if (MutexOwner != null)
{
@@ -301,16 +298,16 @@ namespace Ryujinx.HLE.HOS.Kernel
return MutexOwner;
}
- public long WaitForAddressIfEqual(MemoryManager Memory, long Address, int Value, long Timeout)
+ public long WaitForAddressIfEqual(long Address, int Value, long Timeout)
{
KThread CurrentThread = System.Scheduler.GetCurrentThread();
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (CurrentThread.ShallBeTerminated ||
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
}
@@ -318,9 +315,9 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentThread.SignaledObj = null;
CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
- if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
+ if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
@@ -329,7 +326,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
if (Timeout == 0)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
}
@@ -346,14 +343,14 @@ namespace Ryujinx.HLE.HOS.Kernel
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
if (Timeout > 0)
{
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
}
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (CurrentThread.WaitingInArbitration)
{
@@ -362,31 +359,26 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentThread.WaitingInArbitration = false;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return CurrentThread.ObjSyncResult;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
}
- public long WaitForAddressIfLessThan(
- MemoryManager Memory,
- long Address,
- int Value,
- bool ShouldDecrement,
- long Timeout)
+ public long WaitForAddressIfLessThan(long Address, int Value, bool ShouldDecrement, long Timeout)
{
KThread CurrentThread = System.Scheduler.GetCurrentThread();
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (CurrentThread.ShallBeTerminated ||
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
}
@@ -394,12 +386,14 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentThread.SignaledObj = null;
CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
//If ShouldDecrement is true, do atomic decrement of the value at Address.
- Memory.SetExclusive(0, Address);
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
- if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
+ if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
@@ -408,28 +402,28 @@ namespace Ryujinx.HLE.HOS.Kernel
{
while (CurrentValue < Value)
{
- if (Memory.TestExclusive(0, Address))
+ if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
{
- Memory.WriteInt32(Address, CurrentValue - 1);
+ CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue - 1);
- Memory.ClearExclusiveForStore(0);
+ CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
break;
}
- Memory.SetExclusive(0, Address);
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
- CurrentValue = Memory.ReadInt32(Address);
+ CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
}
}
- Memory.ClearExclusive(0);
+ CurrentProcess.CpuMemory.ClearExclusive(0);
if (CurrentValue < Value)
{
if (Timeout == 0)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
}
@@ -446,14 +440,14 @@ namespace Ryujinx.HLE.HOS.Kernel
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
if (Timeout > 0)
{
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
}
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (CurrentThread.WaitingInArbitration)
{
@@ -462,12 +456,12 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentThread.WaitingInArbitration = false;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return CurrentThread.ObjSyncResult;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
}
@@ -498,63 +492,65 @@ namespace Ryujinx.HLE.HOS.Kernel
public long Signal(long Address, int Count)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
WakeArbiterThreads(Address, Count);
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return 0;
}
- public long SignalAndIncrementIfEqual(MemoryManager Memory, long Address, int Value, int Count)
+ public long SignalAndIncrementIfEqual(long Address, int Value, int Count)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
+
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
- Memory.SetExclusive(0, Address);
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
- if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
+ if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
while (CurrentValue == Value)
{
- if (Memory.TestExclusive(0, Address))
+ if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
{
- Memory.WriteInt32(Address, CurrentValue + 1);
+ CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue + 1);
- Memory.ClearExclusiveForStore(0);
+ CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
break;
}
- Memory.SetExclusive(0, Address);
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
- CurrentValue = Memory.ReadInt32(Address);
+ CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
}
- Memory.ClearExclusive(0);
+ CurrentProcess.CpuMemory.ClearExclusive(0);
if (CurrentValue != Value)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
}
WakeArbiterThreads(Address, Count);
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return 0;
}
- public long SignalAndModifyIfEqual(MemoryManager Memory, long Address, int Value, int Count)
+ public long SignalAndModifyIfEqual(long Address, int Value, int Count)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
int Offset;
@@ -580,43 +576,45 @@ namespace Ryujinx.HLE.HOS.Kernel
Offset = 1;
}
- Memory.SetExclusive(0, Address);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
- if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
+
+ if (!KernelTransfer.UserToKernelInt32(System, Address, out int CurrentValue))
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
while (CurrentValue == Value)
{
- if (Memory.TestExclusive(0, Address))
+ if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
{
- Memory.WriteInt32(Address, CurrentValue + Offset);
+ CurrentProcess.CpuMemory.WriteInt32(Address, CurrentValue + Offset);
- Memory.ClearExclusiveForStore(0);
+ CurrentProcess.CpuMemory.ClearExclusiveForStore(0);
break;
}
- Memory.SetExclusive(0, Address);
+ CurrentProcess.CpuMemory.SetExclusive(0, Address);
- CurrentValue = Memory.ReadInt32(Address);
+ CurrentValue = CurrentProcess.CpuMemory.ReadInt32(Address);
}
- Memory.ClearExclusive(0);
+ CurrentProcess.CpuMemory.ClearExclusive(0);
if (CurrentValue != Value)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
}
WakeArbiterThreads(Address, Count);
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return 0;
}
@@ -648,31 +646,5 @@ namespace Ryujinx.HLE.HOS.Kernel
ArbiterThreads.Remove(Thread);
}
}
-
- private bool UserToKernelInt32(MemoryManager Memory, long Address, out int Value)
- {
- if (Memory.IsMapped(Address))
- {
- Value = Memory.ReadInt32(Address);
-
- return true;
- }
-
- Value = 0;
-
- return false;
- }
-
- private bool KernelToUserInt32(MemoryManager Memory, long Address, int Value)
- {
- if (Memory.IsMapped(Address))
- {
- Memory.WriteInt32ToSharedAddr(Address, Value);
-
- return true;
- }
-
- return false;
- }
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/KAutoObject.cs b/Ryujinx.HLE/HOS/Kernel/KAutoObject.cs
new file mode 100644
index 00000000..a91bf9a8
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KAutoObject.cs
@@ -0,0 +1,42 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KAutoObject
+ {
+ protected Horizon System;
+
+ public KAutoObject(Horizon System)
+ {
+ this.System = System;
+ }
+
+ public virtual KernelResult SetName(string Name)
+ {
+ if (!System.AutoObjectNames.TryAdd(Name, this))
+ {
+ return KernelResult.InvalidState;
+ }
+
+ return KernelResult.Success;
+ }
+
+ public static KernelResult RemoveName(Horizon System, string Name)
+ {
+ if (!System.AutoObjectNames.TryRemove(Name, out _))
+ {
+ return KernelResult.NotFound;
+ }
+
+ return KernelResult.Success;
+ }
+
+ public static KAutoObject FindNamedObject(Horizon System, string Name)
+ {
+ if (System.AutoObjectNames.TryGetValue(Name, out KAutoObject Obj))
+ {
+ return Obj;
+ }
+
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KClientPort.cs b/Ryujinx.HLE/HOS/Kernel/KClientPort.cs
new file mode 100644
index 00000000..e3f8128b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KClientPort.cs
@@ -0,0 +1,31 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KClientPort : KSynchronizationObject
+ {
+ private int SessionsCount;
+ private int CurrentCapacity;
+ private int MaxSessions;
+
+ private KPort Parent;
+
+ public KClientPort(Horizon System) : base(System) { }
+
+ public void Initialize(KPort Parent, int MaxSessions)
+ {
+ this.MaxSessions = MaxSessions;
+ this.Parent = Parent;
+ }
+
+ public new static KernelResult RemoveName(Horizon System, string Name)
+ {
+ KAutoObject FoundObj = KAutoObject.FindNamedObject(System, Name);
+
+ if (!(FoundObj is KClientPort))
+ {
+ return KernelResult.NotFound;
+ }
+
+ return KAutoObject.RemoveName(System, Name);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs b/Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs
new file mode 100644
index 00000000..1c95f811
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs
@@ -0,0 +1,71 @@
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ static class KConditionVariable
+ {
+ public static void Wait(Horizon System, LinkedList<KThread> ThreadList, object Mutex, long Timeout)
+ {
+ KThread CurrentThread = System.Scheduler.GetCurrentThread();
+
+ System.CriticalSection.Enter();
+
+ Monitor.Exit(Mutex);
+
+ CurrentThread.Withholder = ThreadList;
+
+ CurrentThread.Reschedule(ThreadSchedState.Paused);
+
+ CurrentThread.WithholderNode = ThreadList.AddLast(CurrentThread);
+
+ if (CurrentThread.ShallBeTerminated ||
+ CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
+ {
+ ThreadList.Remove(CurrentThread.WithholderNode);
+
+ CurrentThread.Reschedule(ThreadSchedState.Running);
+
+ CurrentThread.Withholder = null;
+
+ System.CriticalSection.Leave();
+ }
+ else
+ {
+ if (Timeout > 0)
+ {
+ System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
+ }
+
+ System.CriticalSection.Leave();
+
+ if (Timeout > 0)
+ {
+ System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
+ }
+ }
+
+ Monitor.Enter(Mutex);
+ }
+
+ public static void NotifyAll(Horizon System, LinkedList<KThread> ThreadList)
+ {
+ System.CriticalSection.Enter();
+
+ LinkedListNode<KThread> Node = ThreadList.First;
+
+ for (; Node != null; Node = ThreadList.First)
+ {
+ KThread Thread = Node.Value;
+
+ ThreadList.Remove(Thread.WithholderNode);
+
+ Thread.Withholder = null;
+
+ Thread.Reschedule(ThreadSchedState.Running);
+ }
+
+ System.CriticalSection.Leave();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs b/Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs
new file mode 100644
index 00000000..03e7dddf
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs
@@ -0,0 +1,83 @@
+using Ryujinx.Common;
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KContextIdManager
+ {
+ private const int IdMasksCount = 8;
+
+ private int[] IdMasks;
+
+ private int NextFreeBitHint;
+
+ public KContextIdManager()
+ {
+ IdMasks = new int[IdMasksCount];
+ }
+
+ public int GetId()
+ {
+ lock (IdMasks)
+ {
+ int Id = 0;
+
+ if (!TestBit(NextFreeBitHint))
+ {
+ Id = NextFreeBitHint;
+ }
+ else
+ {
+ for (int Index = 0; Index < IdMasksCount; Index++)
+ {
+ int Mask = IdMasks[Index];
+
+ int FirstFreeBit = BitUtils.CountLeadingZeros32((Mask + 1) & ~Mask);
+
+ if (FirstFreeBit < 32)
+ {
+ int BaseBit = Index * 32 + 31;
+
+ Id = BaseBit - FirstFreeBit;
+
+ break;
+ }
+ else if (Index == IdMasksCount - 1)
+ {
+ throw new InvalidOperationException("Maximum number of Ids reached!");
+ }
+ }
+ }
+
+ NextFreeBitHint = Id + 1;
+
+ SetBit(Id);
+
+ return Id;
+ }
+ }
+
+ public void PutId(int Id)
+ {
+ lock (IdMasks)
+ {
+ ClearBit(Id);
+ }
+ }
+
+ private bool TestBit(int Bit)
+ {
+ return (IdMasks[NextFreeBitHint / 32] & (1 << (NextFreeBitHint & 31))) != 0;
+ }
+
+ private void SetBit(int Bit)
+ {
+ IdMasks[NextFreeBitHint / 32] |= (1 << (NextFreeBitHint & 31));
+ }
+
+ private void ClearBit(int Bit)
+ {
+ IdMasks[NextFreeBitHint / 32] &= ~(1 << (NextFreeBitHint & 31));
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs b/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs
index 02354e16..638dde9e 100644
--- a/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KCoreContext.cs
@@ -1,5 +1,4 @@
using Ryujinx.Common;
-using System;
namespace Ryujinx.HLE.HOS.Kernel
{
@@ -11,6 +10,10 @@ namespace Ryujinx.HLE.HOS.Kernel
public bool ContextSwitchNeeded { get; private set; }
+ public long LastContextSwitchTime { get; private set; }
+
+ public long TotalIdleTimeTicks { get; private set; } //TODO
+
public KThread CurrentThread { get; private set; }
public KThread SelectedThread { get; private set; }
@@ -24,11 +27,6 @@ namespace Ryujinx.HLE.HOS.Kernel
{
SelectedThread = Thread;
- if (Thread != null)
- {
- Thread.LastScheduledTicks = PerformanceCounter.ElapsedMilliseconds;
- }
-
if (SelectedThread != CurrentThread)
{
ContextSwitchNeeded = true;
@@ -39,25 +37,42 @@ namespace Ryujinx.HLE.HOS.Kernel
{
ContextSwitchNeeded = false;
+ LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
+
CurrentThread = SelectedThread;
+
+ if (CurrentThread != null)
+ {
+ long CurrentTime = PerformanceCounter.ElapsedMilliseconds;
+
+ CurrentThread.TotalTimeRunning += CurrentTime - CurrentThread.LastScheduledTime;
+ CurrentThread.LastScheduledTime = CurrentTime;
+ }
}
public void ContextSwitch()
{
ContextSwitchNeeded = false;
+ LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
+
if (CurrentThread != null)
{
- CoreManager.GetThread(CurrentThread.Context.Work).Reset();
+ CoreManager.Reset(CurrentThread.Context.Work);
}
CurrentThread = SelectedThread;
if (CurrentThread != null)
{
+ long CurrentTime = PerformanceCounter.ElapsedMilliseconds;
+
+ CurrentThread.TotalTimeRunning += CurrentTime - CurrentThread.LastScheduledTime;
+ CurrentThread.LastScheduledTime = CurrentTime;
+
CurrentThread.ClearExclusive();
- CoreManager.GetThread(CurrentThread.Context.Work).Set();
+ CoreManager.Set(CurrentThread.Context.Work);
CurrentThread.Context.Execute();
}
diff --git a/Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs b/Ryujinx.HLE/HOS/Kernel/KCriticalSection.cs
index 30c1a880..b02a1195 100644
--- a/Ryujinx.HLE/HOS/Kernel/KRecursiveLock.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KCriticalSection.cs
@@ -3,7 +3,7 @@ using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel
{
- class KRecursiveLock
+ class KCriticalSection
{
private Horizon System;
@@ -11,21 +11,21 @@ namespace Ryujinx.HLE.HOS.Kernel
private int RecursionCount;
- public KRecursiveLock(Horizon System)
+ public KCriticalSection(Horizon System)
{
this.System = System;
LockObj = new object();
}
- public void Lock()
+ public void Enter()
{
Monitor.Enter(LockObj);
RecursionCount++;
}
- public void Unlock()
+ public void Leave()
{
if (RecursionCount == 0)
{
diff --git a/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs b/Ryujinx.HLE/HOS/Kernel/KHandleTable.cs
index 682f08d4..e39dfb67 100644
--- a/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KHandleTable.cs
@@ -2,7 +2,7 @@ using System;
namespace Ryujinx.HLE.HOS.Kernel
{
- class KProcessHandleTable
+ class KHandleTable
{
private const int SelfThreadHandle = (0x1ffff << 15) | 0;
private const int SelfProcessHandle = (0x1ffff << 15) | 1;
@@ -20,12 +20,24 @@ namespace Ryujinx.HLE.HOS.Kernel
private ushort IdCounter;
- private object LockObj;
-
- public KProcessHandleTable(Horizon System, int Size = 1024)
+ public KHandleTable(Horizon System)
{
this.System = System;
- this.Size = Size;
+ }
+
+ public KernelResult Initialize(int Size)
+ {
+ if ((uint)Size > 1024)
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ if (Size < 1)
+ {
+ Size = 1024;
+ }
+
+ this.Size = Size;
IdCounter = 1;
@@ -48,14 +60,14 @@ namespace Ryujinx.HLE.HOS.Kernel
NextFreeEntry = TableHead;
- LockObj = new object();
+ return KernelResult.Success;
}
public KernelResult GenerateHandle(object Obj, out int Handle)
{
Handle = 0;
- lock (LockObj)
+ lock (Table)
{
if (ActiveSlotsCount >= Size)
{
@@ -95,12 +107,12 @@ namespace Ryujinx.HLE.HOS.Kernel
return false;
}
- int Index = (Handle >> 0) & 0x7fff;
+ int Index = (Handle >> 0) & 0x7fff;
int HandleId = (Handle >> 15);
bool Result = false;
- lock (LockObj)
+ lock (Table)
{
if (HandleId != 0 && Index < Size)
{
@@ -125,10 +137,10 @@ namespace Ryujinx.HLE.HOS.Kernel
public T GetObject<T>(int Handle)
{
- int Index = (Handle >> 0) & 0x7fff;
+ int Index = (Handle >> 0) & 0x7fff;
int HandleId = (Handle >> 15);
- lock (LockObj)
+ lock (Table)
{
if ((Handle >> 30) == 0 && HandleId != 0)
{
@@ -156,9 +168,21 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
+ public KProcess GetKProcess(int Handle)
+ {
+ if (Handle == SelfProcessHandle)
+ {
+ return System.Scheduler.GetCurrentProcess();
+ }
+ else
+ {
+ return GetObject<KProcess>(Handle);
+ }
+ }
+
public void Destroy()
{
- lock (LockObj)
+ lock (Table)
{
for (int Index = 0; Index < Size; Index++)
{
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs
new file mode 100644
index 00000000..af393b68
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs
@@ -0,0 +1,22 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KMemoryArrange
+ {
+ public KMemoryArrangeRegion Service { get; private set; }
+ public KMemoryArrangeRegion NvServices { get; private set; }
+ public KMemoryArrangeRegion Applet { get; private set; }
+ public KMemoryArrangeRegion Application { get; private set; }
+
+ public KMemoryArrange(
+ KMemoryArrangeRegion Service,
+ KMemoryArrangeRegion NvServices,
+ KMemoryArrangeRegion Applet,
+ KMemoryArrangeRegion Application)
+ {
+ this.Service = Service;
+ this.NvServices = NvServices;
+ this.Applet = Applet;
+ this.Application = Application;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs
new file mode 100644
index 00000000..7d66e291
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs
@@ -0,0 +1,16 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ struct KMemoryArrangeRegion
+ {
+ public ulong Address { get; private set; }
+ public ulong Size { get; private set; }
+
+ public ulong EndAddr => Address + Size;
+
+ public KMemoryArrangeRegion(ulong Address, ulong Size)
+ {
+ this.Address = Address;
+ this.Size = Size;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs
index 6100741b..08190236 100644
--- a/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryBlock.cs
@@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Kernel
{
class KMemoryBlock
{
- public long BasePosition { get; set; }
- public long PagesCount { get; set; }
+ public ulong BaseAddress { get; set; }
+ public ulong PagesCount { get; set; }
public MemoryState State { get; set; }
public MemoryPermission Permission { get; set; }
@@ -13,25 +13,25 @@ namespace Ryujinx.HLE.HOS.Kernel
public int DeviceRefCount { get; set; }
public KMemoryBlock(
- long BasePosition,
- long PagesCount,
+ ulong BaseAddress,
+ ulong PagesCount,
MemoryState State,
MemoryPermission Permission,
MemoryAttribute Attribute)
{
- this.BasePosition = BasePosition;
- this.PagesCount = PagesCount;
- this.State = State;
- this.Attribute = Attribute;
- this.Permission = Permission;
+ this.BaseAddress = BaseAddress;
+ this.PagesCount = PagesCount;
+ this.State = State;
+ this.Attribute = Attribute;
+ this.Permission = Permission;
}
public KMemoryInfo GetInfo()
{
- long Size = PagesCount * KMemoryManager.PageSize;
+ ulong Size = PagesCount * KMemoryManager.PageSize;
return new KMemoryInfo(
- BasePosition,
+ BaseAddress,
Size,
State,
Permission,
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryBlockAllocator.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryBlockAllocator.cs
new file mode 100644
index 00000000..08512e12
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryBlockAllocator.cs
@@ -0,0 +1,19 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KMemoryBlockAllocator
+ {
+ private ulong CapacityElements;
+
+ public int Count { get; set; }
+
+ public KMemoryBlockAllocator(ulong CapacityElements)
+ {
+ this.CapacityElements = CapacityElements;
+ }
+
+ public bool CanAllocate(int Count)
+ {
+ return (ulong)(this.Count + Count) <= CapacityElements;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs
index 9b73b32b..09ba88f2 100644
--- a/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryInfo.cs
@@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Kernel
{
class KMemoryInfo
{
- public long Position { get; private set; }
- public long Size { get; private set; }
+ public ulong Address { get; private set; }
+ public ulong Size { get; private set; }
public MemoryState State { get; private set; }
public MemoryPermission Permission { get; private set; }
@@ -13,15 +13,15 @@ namespace Ryujinx.HLE.HOS.Kernel
public int DeviceRefCount { get; private set; }
public KMemoryInfo(
- long Position,
- long Size,
+ ulong Address,
+ ulong Size,
MemoryState State,
MemoryPermission Permission,
MemoryAttribute Attribute,
int IpcRefCount,
int DeviceRefCount)
{
- this.Position = Position;
+ this.Address = Address;
this.Size = Size;
this.State = State;
this.Attribute = Attribute;
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
index 4cfb2aa2..0aa21e3f 100644
--- a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs
@@ -1,331 +1,858 @@
using ChocolArm64.Memory;
-using Ryujinx.HLE.Memory;
+using Ryujinx.Common;
using System;
using System.Collections.Generic;
-using static Ryujinx.HLE.HOS.ErrorCode;
-
namespace Ryujinx.HLE.HOS.Kernel
{
class KMemoryManager
{
public const int PageSize = 0x1000;
+ private const int KMemoryBlockSize = 0x40;
+
+ //We need 2 blocks for the case where a big block
+ //needs to be split in 2, plus one block that will be the new one inserted.
+ private const int MaxBlocksNeededForInsertion = 2;
+
private LinkedList<KMemoryBlock> Blocks;
private MemoryManager CpuMemory;
- private ArenaAllocator Allocator;
+ private Horizon System;
+
+ public ulong AddrSpaceStart { get; private set; }
+ public ulong AddrSpaceEnd { get; private set; }
+
+ public ulong CodeRegionStart { get; private set; }
+ public ulong CodeRegionEnd { get; private set; }
+
+ public ulong HeapRegionStart { get; private set; }
+ public ulong HeapRegionEnd { get; private set; }
+
+ private ulong CurrentHeapAddr;
- public long AddrSpaceStart { get; private set; }
- public long AddrSpaceEnd { get; private set; }
+ public ulong AliasRegionStart { get; private set; }
+ public ulong AliasRegionEnd { get; private set; }
- public long CodeRegionStart { get; private set; }
- public long CodeRegionEnd { get; private set; }
+ public ulong StackRegionStart { get; private set; }
+ public ulong StackRegionEnd { get; private set; }
- public long MapRegionStart { get; private set; }
- public long MapRegionEnd { get; private set; }
+ public ulong TlsIoRegionStart { get; private set; }
+ public ulong TlsIoRegionEnd { get; private set; }
- public long HeapRegionStart { get; private set; }
- public long HeapRegionEnd { get; private set; }
+ private ulong HeapCapacity;
- public long NewMapRegionStart { get; private set; }
- public long NewMapRegionEnd { get; private set; }
+ public ulong PhysicalMemoryUsage { get; private set; }
- public long TlsIoRegionStart { get; private set; }
- public long TlsIoRegionEnd { get; private set; }
+ private MemoryRegion MemRegion;
- public long PersonalMmHeapUsage { get; private set; }
+ private bool AslrDisabled;
- private long CurrentHeapAddr;
+ public int AddrSpaceWidth { get; private set; }
- public KMemoryManager(Process Process)
+ private bool IsKernel;
+ private bool AslrEnabled;
+
+ private KMemoryBlockAllocator BlockAllocator;
+
+ private int ContextId;
+
+ private MersenneTwister RandomNumberGenerator;
+
+ public KMemoryManager(Horizon System, MemoryManager CpuMemory)
{
- CpuMemory = Process.Memory;
- Allocator = Process.Device.Memory.Allocator;
+ this.System = System;
+ this.CpuMemory = CpuMemory;
- long CodeRegionSize;
- long MapRegionSize;
- long HeapRegionSize;
- long NewMapRegionSize;
- long TlsIoRegionSize;
- int AddrSpaceWidth;
+ Blocks = new LinkedList<KMemoryBlock>();
+ }
- AddressSpaceType AddrType = AddressSpaceType.Addr39Bits;
+ private static readonly int[] AddrSpaceSizes = new int[] { 32, 36, 32, 39 };
- if (Process.MetaData != null)
+ public KernelResult InitializeForProcess(
+ AddressSpaceType AddrSpaceType,
+ bool AslrEnabled,
+ bool AslrDisabled,
+ MemoryRegion MemRegion,
+ ulong Address,
+ ulong Size,
+ KMemoryBlockAllocator BlockAllocator)
+ {
+ if ((uint)AddrSpaceType > (uint)AddressSpaceType.Addr39Bits)
{
- AddrType = (AddressSpaceType)Process.MetaData.AddressSpaceWidth;
+ throw new ArgumentException(nameof(AddrSpaceType));
}
- switch (AddrType)
+ ContextId = System.ContextIdManager.GetId();
+
+ ulong AddrSpaceBase = 0;
+ ulong AddrSpaceSize = 1UL << AddrSpaceSizes[(int)AddrSpaceType];
+
+ KernelResult Result = CreateUserAddressSpace(
+ AddrSpaceType,
+ AslrEnabled,
+ AslrDisabled,
+ AddrSpaceBase,
+ AddrSpaceSize,
+ MemRegion,
+ Address,
+ Size,
+ BlockAllocator);
+
+ if (Result != KernelResult.Success)
+ {
+ System.ContextIdManager.PutId(ContextId);
+ }
+
+ return Result;
+ }
+
+ private class Region
+ {
+ public ulong Start;
+ public ulong End;
+ public ulong Size;
+ public ulong AslrOffset;
+ }
+
+ private KernelResult CreateUserAddressSpace(
+ AddressSpaceType AddrSpaceType,
+ bool AslrEnabled,
+ bool AslrDisabled,
+ ulong AddrSpaceStart,
+ ulong AddrSpaceEnd,
+ MemoryRegion MemRegion,
+ ulong Address,
+ ulong Size,
+ KMemoryBlockAllocator BlockAllocator)
+ {
+ ulong EndAddr = Address + Size;
+
+ Region AliasRegion = new Region();
+ Region HeapRegion = new Region();
+ Region StackRegion = new Region();
+ Region TlsIoRegion = new Region();
+
+ ulong CodeRegionSize;
+ ulong StackAndTlsIoStart;
+ ulong StackAndTlsIoEnd;
+ ulong BaseAddress;
+
+ switch (AddrSpaceType)
{
case AddressSpaceType.Addr32Bits:
- CodeRegionStart = 0x200000;
- CodeRegionSize = 0x3fe00000;
- MapRegionSize = 0x40000000;
- HeapRegionSize = 0x40000000;
- NewMapRegionSize = 0;
- TlsIoRegionSize = 0;
- AddrSpaceWidth = 32;
+ AliasRegion.Size = 0x40000000;
+ HeapRegion.Size = 0x40000000;
+ StackRegion.Size = 0;
+ TlsIoRegion.Size = 0;
+ CodeRegionStart = 0x200000;
+ CodeRegionSize = 0x3fe00000;
+ StackAndTlsIoStart = 0x200000;
+ StackAndTlsIoEnd = 0x40000000;
+ BaseAddress = 0x200000;
+ AddrSpaceWidth = 32;
break;
case AddressSpaceType.Addr36Bits:
- CodeRegionStart = 0x8000000;
- CodeRegionSize = 0x78000000;
- MapRegionSize = 0x180000000;
- HeapRegionSize = 0x180000000;
- NewMapRegionSize = 0;
- TlsIoRegionSize = 0;
- AddrSpaceWidth = 36;
+ AliasRegion.Size = 0x180000000;
+ HeapRegion.Size = 0x180000000;
+ StackRegion.Size = 0;
+ TlsIoRegion.Size = 0;
+ CodeRegionStart = 0x8000000;
+ CodeRegionSize = 0x78000000;
+ StackAndTlsIoStart = 0x8000000;
+ StackAndTlsIoEnd = 0x80000000;
+ BaseAddress = 0x8000000;
+ AddrSpaceWidth = 36;
break;
- case AddressSpaceType.Addr36BitsNoMap:
- CodeRegionStart = 0x200000;
- CodeRegionSize = 0x3fe00000;
- MapRegionSize = 0;
- HeapRegionSize = 0x80000000;
- NewMapRegionSize = 0;
- TlsIoRegionSize = 0;
- AddrSpaceWidth = 36;
+ case AddressSpaceType.Addr32BitsNoMap:
+ AliasRegion.Size = 0;
+ HeapRegion.Size = 0x80000000;
+ StackRegion.Size = 0;
+ TlsIoRegion.Size = 0;
+ CodeRegionStart = 0x200000;
+ CodeRegionSize = 0x3fe00000;
+ StackAndTlsIoStart = 0x200000;
+ StackAndTlsIoEnd = 0x40000000;
+ BaseAddress = 0x200000;
+ AddrSpaceWidth = 32;
break;
case AddressSpaceType.Addr39Bits:
- CodeRegionStart = 0x8000000;
- CodeRegionSize = 0x80000000;
- MapRegionSize = 0x1000000000;
- HeapRegionSize = 0x180000000;
- NewMapRegionSize = 0x80000000;
- TlsIoRegionSize = 0x1000000000;
- AddrSpaceWidth = 39;
+ AliasRegion.Size = 0x1000000000;
+ HeapRegion.Size = 0x180000000;
+ StackRegion.Size = 0x80000000;
+ TlsIoRegion.Size = 0x1000000000;
+ CodeRegionStart = BitUtils.AlignDown(Address, 0x200000);
+ CodeRegionSize = BitUtils.AlignUp (EndAddr, 0x200000) - CodeRegionStart;
+ StackAndTlsIoStart = 0;
+ StackAndTlsIoEnd = 0;
+ BaseAddress = 0x8000000;
+ AddrSpaceWidth = 39;
break;
- default: throw new InvalidOperationException();
+ default: throw new ArgumentException(nameof(AddrSpaceType));
+ }
+
+ CodeRegionEnd = CodeRegionStart + CodeRegionSize;
+
+ ulong MapBaseAddress;
+ ulong MapAvailableSize;
+
+ if (CodeRegionStart - BaseAddress >= AddrSpaceEnd - CodeRegionEnd)
+ {
+ //Has more space before the start of the code region.
+ MapBaseAddress = BaseAddress;
+ MapAvailableSize = CodeRegionStart - BaseAddress;
+ }
+ else
+ {
+ //Has more space after the end of the code region.
+ MapBaseAddress = CodeRegionEnd;
+ MapAvailableSize = AddrSpaceEnd - CodeRegionEnd;
}
- AddrSpaceStart = 0;
- AddrSpaceEnd = 1L << AddrSpaceWidth;
+ ulong MapTotalSize = AliasRegion.Size + HeapRegion.Size + StackRegion.Size + TlsIoRegion.Size;
+
+ ulong AslrMaxOffset = MapAvailableSize - MapTotalSize;
- CodeRegionEnd = CodeRegionStart + CodeRegionSize;
- MapRegionStart = CodeRegionEnd;
- MapRegionEnd = CodeRegionEnd + MapRegionSize;
- HeapRegionStart = MapRegionEnd;
- HeapRegionEnd = MapRegionEnd + HeapRegionSize;
- NewMapRegionStart = HeapRegionEnd;
- NewMapRegionEnd = HeapRegionEnd + NewMapRegionSize;
- TlsIoRegionStart = NewMapRegionEnd;
- TlsIoRegionEnd = NewMapRegionEnd + TlsIoRegionSize;
+ this.AslrEnabled = AslrEnabled;
- CurrentHeapAddr = HeapRegionStart;
+ this.AddrSpaceStart = AddrSpaceStart;
+ this.AddrSpaceEnd = AddrSpaceEnd;
- if (NewMapRegionSize == 0)
+ this.BlockAllocator = BlockAllocator;
+
+ if (MapAvailableSize < MapTotalSize)
{
- NewMapRegionStart = AddrSpaceStart;
- NewMapRegionEnd = AddrSpaceEnd;
+ return KernelResult.OutOfMemory;
}
- Blocks = new LinkedList<KMemoryBlock>();
+ if (AslrEnabled)
+ {
+ AliasRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+ HeapRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+ StackRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+ TlsIoRegion.AslrOffset = GetRandomValue(0, AslrMaxOffset >> 21) << 21;
+ }
+
+ //Regions are sorted based on ASLR offset.
+ //When ASLR is disabled, the order is Map, Heap, NewMap and TlsIo.
+ AliasRegion.Start = MapBaseAddress + AliasRegion.AslrOffset;
+ AliasRegion.End = AliasRegion.Start + AliasRegion.Size;
+ HeapRegion.Start = MapBaseAddress + HeapRegion.AslrOffset;
+ HeapRegion.End = HeapRegion.Start + HeapRegion.Size;
+ StackRegion.Start = MapBaseAddress + StackRegion.AslrOffset;
+ StackRegion.End = StackRegion.Start + StackRegion.Size;
+ TlsIoRegion.Start = MapBaseAddress + TlsIoRegion.AslrOffset;
+ TlsIoRegion.End = TlsIoRegion.Start + TlsIoRegion.Size;
- long AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
+ SortRegion(HeapRegion, AliasRegion);
+
+ if (StackRegion.Size != 0)
+ {
+ SortRegion(StackRegion, AliasRegion);
+ SortRegion(StackRegion, HeapRegion);
+ }
+ else
+ {
+ StackRegion.Start = StackAndTlsIoStart;
+ StackRegion.End = StackAndTlsIoEnd;
+ }
+
+ if (TlsIoRegion.Size != 0)
+ {
+ SortRegion(TlsIoRegion, AliasRegion);
+ SortRegion(TlsIoRegion, HeapRegion);
+ SortRegion(TlsIoRegion, StackRegion);
+ }
+ else
+ {
+ TlsIoRegion.Start = StackAndTlsIoStart;
+ TlsIoRegion.End = StackAndTlsIoEnd;
+ }
+
+ AliasRegionStart = AliasRegion.Start;
+ AliasRegionEnd = AliasRegion.End;
+ HeapRegionStart = HeapRegion.Start;
+ HeapRegionEnd = HeapRegion.End;
+ StackRegionStart = StackRegion.Start;
+ StackRegionEnd = StackRegion.End;
+ TlsIoRegionStart = TlsIoRegion.Start;
+ TlsIoRegionEnd = TlsIoRegion.End;
+
+ CurrentHeapAddr = HeapRegionStart;
+ HeapCapacity = 0;
+ PhysicalMemoryUsage = 0;
+
+ this.MemRegion = MemRegion;
+ this.AslrDisabled = AslrDisabled;
+
+ return InitializeBlocks(AddrSpaceStart, AddrSpaceEnd);
+ }
+
+ private ulong GetRandomValue(ulong Min, ulong Max)
+ {
+ return (ulong)GetRandomValue((long)Min, (long)Max);
+ }
+
+ private long GetRandomValue(long Min, long Max)
+ {
+ if (RandomNumberGenerator == null)
+ {
+ RandomNumberGenerator = new MersenneTwister(0);
+ }
+
+ return RandomNumberGenerator.GenRandomNumber(Min, Max);
+ }
+
+ private static void SortRegion(Region Lhs, Region Rhs)
+ {
+ if (Lhs.AslrOffset < Rhs.AslrOffset)
+ {
+ Rhs.Start += Lhs.Size;
+ Rhs.End += Lhs.Size;
+ }
+ else
+ {
+ Lhs.Start += Rhs.Size;
+ Lhs.End += Rhs.Size;
+ }
+ }
+
+ private KernelResult InitializeBlocks(ulong AddrSpaceStart, ulong AddrSpaceEnd)
+ {
+ //First insertion will always need only a single block,
+ //because there's nothing else to split.
+ if (!BlockAllocator.CanAllocate(1))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
InsertBlock(AddrSpaceStart, AddrSpacePagesCount, MemoryState.Unmapped);
+
+ return KernelResult.Success;
}
- public void HleMapProcessCode(long Position, long Size)
+ public KernelResult MapPages(
+ ulong Address,
+ KPageList PageList,
+ MemoryState State,
+ MemoryPermission Permission)
{
- long PagesCount = Size / PageSize;
+ ulong PagesCount = PageList.GetPagesCount();
+
+ ulong Size = PagesCount * PageSize;
- if (!Allocator.TryAllocate(Size, out long PA))
+ if (!ValidateRegionForState(Address, Size, State))
{
- throw new InvalidOperationException();
+ return KernelResult.InvalidMemState;
}
lock (Blocks)
{
- InsertBlock(Position, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute);
+ if (!IsUnmapped(Address, PagesCount * PageSize))
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ KernelResult Result = MapPages(Address, PageList, Permission);
+
+ if (Result == KernelResult.Success)
+ {
+ InsertBlock(Address, PagesCount, State, Permission);
+ }
- CpuMemory.Map(Position, PA, Size);
+ return Result;
}
}
- public long MapProcessCodeMemory(long Dst, long Src, long Size)
+ public KernelResult UnmapPages(ulong Address, KPageList PageList, MemoryState StateExpected)
{
+ ulong PagesCount = PageList.GetPagesCount();
+
+ ulong Size = PagesCount * PageSize;
+
+ ulong EndAddr = Address + Size;
+
+ ulong AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
+
+ if (AddrSpaceStart > Address)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (AddrSpacePagesCount < PagesCount)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (EndAddr - 1 > AddrSpaceEnd - 1)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
lock (Blocks)
{
- long PagesCount = Size / PageSize;
+ KPageList CurrentPageList = new KPageList();
- bool Success = IsUnmapped(Dst, Size);
+ AddVaRangeToPageList(CurrentPageList, Address, PagesCount);
- Success &= CheckRange(
- Src,
- Size,
- MemoryState.Mask,
- MemoryState.Heap,
- MemoryPermission.Mask,
- MemoryPermission.ReadAndWrite,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out _,
- out _,
- out _);
+ if (!CurrentPageList.IsEqual(PageList))
+ {
+ return KernelResult.InvalidMemRange;
+ }
- if (Success)
+ if (CheckRange(
+ Address,
+ Size,
+ MemoryState.Mask,
+ StateExpected,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out MemoryState State,
+ out _,
+ out _))
{
- long PA = CpuMemory.GetPhysicalAddress(Src);
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
- InsertBlock(Dst, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute);
- InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.None);
+ KernelResult Result = MmuUnmap(Address, PagesCount);
- CpuMemory.Map(Dst, PA, Size);
+ if (Result == KernelResult.Success)
+ {
+ InsertBlock(Address, PagesCount, MemoryState.Unmapped);
+ }
- return 0;
+ return Result;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
+ }
+
+ public KernelResult MapNormalMemory(long Address, long Size, MemoryPermission Permission)
+ {
+ //TODO.
+ return KernelResult.Success;
+ }
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ public KernelResult MapIoMemory(long Address, long Size, MemoryPermission Permission)
+ {
+ //TODO.
+ return KernelResult.Success;
}
- public long UnmapProcessCodeMemory(long Dst, long Src, long Size)
+ public KernelResult AllocateOrMapPa(
+ ulong NeededPagesCount,
+ int Alignment,
+ ulong SrcPa,
+ bool Map,
+ ulong RegionStart,
+ ulong RegionPagesCount,
+ MemoryState State,
+ MemoryPermission Permission,
+ out ulong Address)
{
+ Address = 0;
+
+ ulong RegionSize = RegionPagesCount * PageSize;
+
+ ulong RegionEndAddr = RegionStart + RegionSize;
+
+ if (!ValidateRegionForState(RegionStart, RegionSize, State))
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (RegionPagesCount <= NeededPagesCount)
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ ulong ReservedPagesCount = IsKernel ? 1UL : 4UL;
+
lock (Blocks)
{
- long PagesCount = Size / PageSize;
+ if (AslrEnabled)
+ {
+ ulong TotalNeededSize = (ReservedPagesCount + NeededPagesCount) * PageSize;
- bool Success = CheckRange(
- Dst,
- Size,
- MemoryState.Mask,
- MemoryState.CodeStatic,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out _,
- out _,
- out _);
+ ulong RemainingPages = RegionPagesCount - NeededPagesCount;
- Success &= CheckRange(
- Src,
- Size,
- MemoryState.Mask,
- MemoryState.Heap,
- MemoryPermission.Mask,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out _,
- out _,
- out _);
+ ulong AslrMaxOffset = ((RemainingPages + ReservedPagesCount) * PageSize) / (ulong)Alignment;
- if (Success)
+ for (int Attempt = 0; Attempt < 8; Attempt++)
+ {
+ Address = BitUtils.AlignDown(RegionStart + GetRandomValue(0, AslrMaxOffset) * (ulong)Alignment, Alignment);
+
+ ulong EndAddr = Address + TotalNeededSize;
+
+ KMemoryInfo Info = FindBlock(Address).GetInfo();
+
+ if (Info.State != MemoryState.Unmapped)
+ {
+ continue;
+ }
+
+ ulong CurrBaseAddr = Info.Address + ReservedPagesCount * PageSize;
+ ulong CurrEndAddr = Info.Address + Info.Size;
+
+ if (Address >= RegionStart &&
+ Address >= CurrBaseAddr &&
+ EndAddr - 1 <= RegionEndAddr - 1 &&
+ EndAddr - 1 <= CurrEndAddr - 1)
+ {
+ break;
+ }
+ }
+
+ if (Address == 0)
+ {
+ ulong AslrPage = GetRandomValue(0, AslrMaxOffset);
+
+ Address = FindFirstFit(
+ RegionStart + AslrPage * PageSize,
+ RegionPagesCount - AslrPage,
+ NeededPagesCount,
+ Alignment,
+ 0,
+ ReservedPagesCount);
+ }
+ }
+
+ if (Address == 0)
{
- InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
- InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
+ Address = FindFirstFit(
+ RegionStart,
+ RegionPagesCount,
+ NeededPagesCount,
+ Alignment,
+ 0,
+ ReservedPagesCount);
+ }
- CpuMemory.Unmap(Dst, Size);
+ if (Address == 0)
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ MemoryOperation Operation = Map
+ ? MemoryOperation.MapPa
+ : MemoryOperation.Allocate;
- return 0;
+ KernelResult Result = DoMmuOperation(
+ Address,
+ NeededPagesCount,
+ SrcPa,
+ Map,
+ Permission,
+ Operation);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
}
+
+ InsertBlock(Address, NeededPagesCount, State, Permission);
}
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ return KernelResult.Success;
}
- public void HleMapCustom(long Position, long Size, MemoryState State, MemoryPermission Permission)
+ public KernelResult MapNewProcessCode(
+ ulong Address,
+ ulong PagesCount,
+ MemoryState State,
+ MemoryPermission Permission)
{
- long PagesCount = Size / PageSize;
+ ulong Size = PagesCount * PageSize;
- if (!Allocator.TryAllocate(Size, out long PA))
+ if (!ValidateRegionForState(Address, Size, State))
{
- throw new InvalidOperationException();
+ return KernelResult.InvalidMemState;
}
lock (Blocks)
{
- InsertBlock(Position, PagesCount, State, Permission);
+ if (!IsUnmapped(Address, Size))
+ {
+ return KernelResult.InvalidMemState;
+ }
- CpuMemory.Map(Position, PA, Size);
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ KernelResult Result = DoMmuOperation(
+ Address,
+ PagesCount,
+ 0,
+ false,
+ Permission,
+ MemoryOperation.Allocate);
+
+ if (Result == KernelResult.Success)
+ {
+ InsertBlock(Address, PagesCount, State, Permission);
+ }
+
+ return Result;
}
}
- public long HleMapTlsPage()
+ public KernelResult MapProcessCodeMemory(ulong Dst, ulong Src, ulong Size)
{
- bool HasTlsIoRegion = TlsIoRegionStart != TlsIoRegionEnd;
-
- long Position = HasTlsIoRegion ? TlsIoRegionStart : CodeRegionStart;
+ ulong PagesCount = Size / PageSize;
lock (Blocks)
{
- while (Position < (HasTlsIoRegion ? TlsIoRegionEnd : CodeRegionEnd))
+ 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)
{
- if (FindBlock(Position).State == MemoryState.Unmapped)
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
{
- InsertBlock(Position, 1, MemoryState.ThreadLocal, MemoryPermission.ReadAndWrite);
+ return KernelResult.OutOfResource;
+ }
- if (!Allocator.TryAllocate(PageSize, out long PA))
- {
- throw new InvalidOperationException();
- }
+ KPageList PageList = new KPageList();
+
+ AddVaRangeToPageList(PageList, Src, PagesCount);
- CpuMemory.Map(Position, PA, PageSize);
+ 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 Position;
+ return Result;
}
- Position += PageSize;
+ 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;
+
+ lock (Blocks)
+ {
+ bool Success = CheckRange(
+ Src,
+ Size,
+ MemoryState.Mask,
+ MemoryState.Heap,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.Borrowed,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out _,
+ out _,
+ out _);
- throw new InvalidOperationException();
+ Success &= CheckRange(
+ Dst,
+ PageSize,
+ MemoryState.UnmapProcessCodeMemoryAllowed,
+ MemoryState.UnmapProcessCodeMemoryAllowed,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out MemoryState State,
+ out _,
+ out _);
+
+ Success &= CheckRange(
+ Dst,
+ Size,
+ MemoryState.Mask,
+ State,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None);
+
+ if (Success)
+ {
+ KernelResult Result = MmuUnmap(Dst, PagesCount);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ //TODO: Missing some checks here.
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
+ InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
+ }
}
}
- public long TrySetHeapSize(long Size, out long Position)
+ public KernelResult SetHeapSize(ulong Size, out ulong Address)
{
- Position = 0;
+ Address = 0;
- if ((ulong)Size > (ulong)(HeapRegionEnd - HeapRegionStart))
+ if (Size > HeapRegionEnd - HeapRegionStart)
{
- return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
+ return KernelResult.OutOfMemory;
}
- bool Success = false;
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
- long CurrentHeapSize = GetHeapSize();
+ ulong CurrentHeapSize = GetHeapSize();
- if ((ulong)CurrentHeapSize <= (ulong)Size)
+ if (CurrentHeapSize <= Size)
{
//Expand.
- long DiffSize = Size - CurrentHeapSize;
+ ulong DiffSize = Size - CurrentHeapSize;
lock (Blocks)
{
- if (Success = IsUnmapped(CurrentHeapAddr, DiffSize))
+ if (CurrentProcess.ResourceLimit != null && DiffSize != 0 &&
+ !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Memory, DiffSize))
+ {
+ return KernelResult.ResLimitExceeded;
+ }
+
+ ulong PagesCount = DiffSize / PageSize;
+
+ KMemoryRegionManager Region = GetMemoryRegionManager();
+
+ KernelResult Result = Region.AllocatePages(PagesCount, AslrDisabled, out KPageList PageList);
+
+ void CleanUpForError()
{
- if (!Allocator.TryAllocate(DiffSize, out long PA))
+ if (PageList != null)
+ {
+ Region.FreePages(PageList);
+ }
+
+ if (CurrentProcess.ResourceLimit != null && DiffSize != 0)
{
- return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
+ CurrentProcess.ResourceLimit.Release(LimitableResource.Memory, DiffSize);
}
+ }
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
- long PagesCount = DiffSize / PageSize;
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ CleanUpForError();
- InsertBlock(CurrentHeapAddr, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
+ return KernelResult.OutOfResource;
+ }
- CpuMemory.Map(CurrentHeapAddr, PA, DiffSize);
+ if (!IsUnmapped(CurrentHeapAddr, DiffSize))
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidMemState;
}
+
+ Result = DoMmuOperation(
+ CurrentHeapAddr,
+ PagesCount,
+ PageList,
+ MemoryPermission.ReadAndWrite,
+ MemoryOperation.MapVa);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ InsertBlock(CurrentHeapAddr, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite);
}
}
else
{
//Shrink.
- long FreeAddr = HeapRegionStart + Size;
- long DiffSize = CurrentHeapSize - Size;
+ ulong FreeAddr = HeapRegionStart + Size;
+ ulong DiffSize = CurrentHeapSize - Size;
lock (Blocks)
{
- Success = CheckRange(
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ if (!CheckRange(
FreeAddr,
DiffSize,
MemoryState.Mask,
@@ -337,48 +864,66 @@ namespace Ryujinx.HLE.HOS.Kernel
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
- out _);
-
- if (Success)
+ out _))
{
- long PagesCount = DiffSize / PageSize;
+ return KernelResult.InvalidMemState;
+ }
- InsertBlock(FreeAddr, PagesCount, MemoryState.Unmapped);
+ ulong PagesCount = DiffSize / PageSize;
- FreePages(FreeAddr, PagesCount);
+ KernelResult Result = MmuUnmap(FreeAddr, PagesCount);
- CpuMemory.Unmap(FreeAddr, DiffSize);
+ if (Result != KernelResult.Success)
+ {
+ return Result;
}
+
+ CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, BitUtils.AlignDown(DiffSize, PageSize));
+
+ InsertBlock(FreeAddr, PagesCount, MemoryState.Unmapped);
}
}
CurrentHeapAddr = HeapRegionStart + Size;
- if (Success)
- {
- Position = HeapRegionStart;
+ Address = HeapRegionStart;
- return 0;
- }
+ return KernelResult.Success;
+ }
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ public ulong GetTotalHeapSize()
+ {
+ lock (Blocks)
+ {
+ return GetHeapSize() + PhysicalMemoryUsage;
+ }
}
- public long GetHeapSize()
+ private ulong GetHeapSize()
{
return CurrentHeapAddr - HeapRegionStart;
}
- public long SetMemoryAttribute(
- long Position,
- long Size,
+ public KernelResult SetHeapCapacity(ulong Capacity)
+ {
+ lock (Blocks)
+ {
+ HeapCapacity = Capacity;
+ }
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult SetMemoryAttribute(
+ ulong Address,
+ ulong Size,
MemoryAttribute AttributeMask,
MemoryAttribute AttributeValue)
{
lock (Blocks)
{
if (CheckRange(
- Position,
+ Address,
Size,
MemoryState.AttributeChangeAllowed,
MemoryState.AttributeChangeAllowed,
@@ -391,35 +936,42 @@ namespace Ryujinx.HLE.HOS.Kernel
out MemoryPermission Permission,
out MemoryAttribute Attribute))
{
- long PagesCount = Size / PageSize;
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong PagesCount = Size / PageSize;
Attribute &= ~AttributeMask;
Attribute |= AttributeMask & AttributeValue;
- InsertBlock(Position, PagesCount, State, Permission, Attribute);
+ InsertBlock(Address, PagesCount, State, Permission, Attribute);
- return 0;
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public KMemoryInfo QueryMemory(long Position)
+ public KMemoryInfo QueryMemory(ulong Address)
{
- if ((ulong)Position >= (ulong)AddrSpaceStart &&
- (ulong)Position < (ulong)AddrSpaceEnd)
+ if (Address >= AddrSpaceStart &&
+ Address < AddrSpaceEnd)
{
lock (Blocks)
{
- return FindBlock(Position).GetInfo();
+ return FindBlock(Address).GetInfo();
}
}
else
{
return new KMemoryInfo(
AddrSpaceEnd,
- -AddrSpaceEnd,
+ ~AddrSpaceEnd + 1,
MemoryState.Reserved,
MemoryPermission.None,
MemoryAttribute.None,
@@ -428,7 +980,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- public long Map(long Src, long Dst, long Size)
+ public KernelResult Map(ulong Dst, ulong Src, ulong Size)
{
bool Success;
@@ -452,22 +1004,90 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Success)
{
- long PagesCount = Size / PageSize;
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
+ {
+ return KernelResult.OutOfResource;
+ }
- InsertBlock(Src, PagesCount, SrcState, MemoryPermission.None, MemoryAttribute.Borrowed);
+ ulong PagesCount = Size / PageSize;
- InsertBlock(Dst, PagesCount, MemoryState.MappedMemory, MemoryPermission.ReadAndWrite);
+ KPageList PageList = new KPageList();
- long PA = CpuMemory.GetPhysicalAddress(Src);
+ AddVaRangeToPageList(PageList, Src, PagesCount);
- CpuMemory.Map(Dst, PA, Size);
+ KernelResult Result = MmuChangePermission(Src, PagesCount, MemoryPermission.None);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ Result = MapPages(Dst, PageList, MemoryPermission.ReadAndWrite);
+
+ if (Result != KernelResult.Success)
+ {
+ if (MmuChangePermission(Src, PagesCount, MemoryPermission.ReadAndWrite) != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Unexpected failure reverting memory permission.");
+ }
+
+ return Result;
+ }
+
+ InsertBlock(Src, PagesCount, SrcState, MemoryPermission.None, MemoryAttribute.Borrowed);
+ InsertBlock(Dst, PagesCount, MemoryState.Stack, MemoryPermission.ReadAndWrite);
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
+ }
+
+ public KernelResult UnmapForKernel(ulong Address, ulong PagesCount, MemoryState StateExpected)
+ {
+ ulong Size = PagesCount * PageSize;
+
+ lock (Blocks)
+ {
+ if (CheckRange(
+ Address,
+ Size,
+ MemoryState.Mask,
+ StateExpected,
+ MemoryPermission.None,
+ MemoryPermission.None,
+ MemoryAttribute.Mask,
+ MemoryAttribute.None,
+ MemoryAttribute.IpcAndDeviceMapped,
+ out _,
+ out _,
+ out _))
+ {
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ KernelResult Result = MmuUnmap(Address, PagesCount);
- return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ if (Result == KernelResult.Success)
+ {
+ InsertBlock(Address, PagesCount, MemoryState.Unmapped);
+ }
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
+ }
+ }
}
- public long Unmap(long Src, long Dst, long Size)
+ public KernelResult Unmap(ulong Dst, ulong Src, ulong Size)
{
bool Success;
@@ -491,87 +1111,70 @@ namespace Ryujinx.HLE.HOS.Kernel
Dst,
Size,
MemoryState.Mask,
- MemoryState.MappedMemory,
+ MemoryState.Stack,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
- out _,
+ out MemoryPermission DstPermission,
out _);
if (Success)
{
- long PagesCount = Size / PageSize;
-
- InsertBlock(Src, PagesCount, SrcState, MemoryPermission.ReadAndWrite);
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion * 2))
+ {
+ return KernelResult.OutOfResource;
+ }
- InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
+ ulong PagesCount = Size / PageSize;
- CpuMemory.Unmap(Dst, Size);
- }
- }
+ KPageList SrcPageList = new KPageList();
+ KPageList DstPageList = new KPageList();
- return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
- }
+ AddVaRangeToPageList(SrcPageList, Src, PagesCount);
+ AddVaRangeToPageList(DstPageList, Dst, PagesCount);
- public long MapSharedMemory(KSharedMemory SharedMemory, MemoryPermission Permission, long Position)
- {
- lock (Blocks)
- {
- if (IsUnmapped(Position, SharedMemory.Size))
- {
- long PagesCount = SharedMemory.Size / PageSize;
-
- InsertBlock(Position, PagesCount, MemoryState.SharedMemory, Permission);
+ if (!DstPageList.IsEqual(SrcPageList))
+ {
+ return KernelResult.InvalidMemRange;
+ }
- CpuMemory.Map(Position, SharedMemory.PA, SharedMemory.Size);
+ KernelResult Result = MmuUnmap(Dst, PagesCount);
- return 0;
- }
- }
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
- }
+ Result = MmuChangePermission(Src, PagesCount, MemoryPermission.ReadAndWrite);
- public long UnmapSharedMemory(long Position, long Size)
- {
- lock (Blocks)
- {
- if (CheckRange(
- Position,
- Size,
- MemoryState.Mask,
- MemoryState.SharedMemory,
- MemoryPermission.None,
- MemoryPermission.None,
- MemoryAttribute.Mask,
- MemoryAttribute.None,
- MemoryAttribute.IpcAndDeviceMapped,
- out MemoryState State,
- out _,
- out _))
- {
- long PagesCount = Size / PageSize;
+ if (Result != KernelResult.Success)
+ {
+ MapPages(Dst, DstPageList, DstPermission);
- InsertBlock(Position, PagesCount, MemoryState.Unmapped);
+ return Result;
+ }
- CpuMemory.Unmap(Position, Size);
+ InsertBlock(Src, PagesCount, SrcState, MemoryPermission.ReadAndWrite);
+ InsertBlock(Dst, PagesCount, MemoryState.Unmapped);
- return 0;
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public long ReserveTransferMemory(long Position, long Size, MemoryPermission Permission)
+ public KernelResult ReserveTransferMemory(ulong Address, ulong Size, MemoryPermission Permission)
{
lock (Blocks)
{
if (CheckRange(
- Position,
+ Address,
Size,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
@@ -584,25 +1187,34 @@ namespace Ryujinx.HLE.HOS.Kernel
out _,
out MemoryAttribute Attribute))
{
- long PagesCount = Size / PageSize;
+ //TODO: Missing checks.
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong PagesCount = Size / PageSize;
Attribute |= MemoryAttribute.Borrowed;
- InsertBlock(Position, PagesCount, State, Permission, Attribute);
+ InsertBlock(Address, PagesCount, State, Permission, Attribute);
- return 0;
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public long ResetTransferMemory(long Position, long Size)
+ public KernelResult ResetTransferMemory(ulong Address, ulong Size)
{
lock (Blocks)
{
if (CheckRange(
- Position,
+ Address,
Size,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
@@ -615,23 +1227,30 @@ namespace Ryujinx.HLE.HOS.Kernel
out _,
out _))
{
- long PagesCount = Size / PageSize;
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
+
+ ulong PagesCount = Size / PageSize;
- InsertBlock(Position, PagesCount, State, MemoryPermission.ReadAndWrite);
+ InsertBlock(Address, PagesCount, State, MemoryPermission.ReadAndWrite);
- return 0;
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public long SetProcessMemoryPermission(long Position, long Size, MemoryPermission Permission)
+ public KernelResult SetProcessMemoryPermission(ulong Address, ulong Size, MemoryPermission Permission)
{
lock (Blocks)
{
if (CheckRange(
- Position,
+ Address,
Size,
MemoryState.ProcessPermissionChangeAllowed,
MemoryState.ProcessPermissionChangeAllowed,
@@ -640,47 +1259,73 @@ namespace Ryujinx.HLE.HOS.Kernel
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
- out MemoryState State,
- out _,
+ out MemoryState OldState,
+ out MemoryPermission OldPermission,
out _))
{
- if (State == MemoryState.CodeStatic)
- {
- State = MemoryState.CodeMutable;
- }
- else if (State == MemoryState.ModCodeStatic)
+ MemoryState NewState = OldState;
+
+ //If writing into the code region is allowed, then we need
+ //to change it to mutable.
+ if ((Permission & MemoryPermission.Write) != 0)
{
- State = MemoryState.ModCodeMutable;
+ if (OldState == MemoryState.CodeStatic)
+ {
+ NewState = MemoryState.CodeMutable;
+ }
+ else if (OldState == MemoryState.ModCodeStatic)
+ {
+ NewState = MemoryState.ModCodeMutable;
+ }
+ else
+ {
+ throw new InvalidOperationException($"Memory state \"{OldState}\" not valid for this operation.");
+ }
}
- else
+
+ if (NewState != OldState || Permission != OldPermission)
{
- throw new InvalidOperationException();
- }
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
+ }
- long PagesCount = Size / PageSize;
+ ulong PagesCount = Size / PageSize;
- InsertBlock(Position, PagesCount, State, Permission);
+ MemoryOperation Operation = (Permission & MemoryPermission.Execute) != 0
+ ? MemoryOperation.ChangePermsAndAttributes
+ : MemoryOperation.ChangePermRw;
- return 0;
+ KernelResult Result = DoMmuOperation(Address, PagesCount, 0, false, Permission, Operation);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ InsertBlock(Address, PagesCount, NewState, Permission);
+ }
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidMemState;
}
}
-
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
- public long MapPhysicalMemory(long Position, long Size)
+ public KernelResult MapPhysicalMemory(ulong Address, ulong Size)
{
- long End = Position + Size;
+ ulong EndAddr = Address + Size;
lock (Blocks)
{
- long MappedSize = 0;
+ ulong MappedSize = 0;
KMemoryInfo Info;
- LinkedListNode<KMemoryBlock> BaseNode = FindBlockNode(Position);
-
- LinkedListNode<KMemoryBlock> Node = BaseNode;
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
do
{
@@ -688,57 +1333,66 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Info.State != MemoryState.Unmapped)
{
- MappedSize += GetSizeInRange(Info, Position, End);
+ MappedSize += GetSizeInRange(Info, Address, EndAddr);
}
Node = Node.Next;
}
- while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
+ while (Info.Address + Info.Size < EndAddr && Node != null);
if (MappedSize == Size)
{
- return 0;
+ return KernelResult.Success;
}
- long RemainingSize = Size - MappedSize;
+ ulong RemainingSize = Size - MappedSize;
+
+ ulong RemainingPages = RemainingSize / PageSize;
- if (!Allocator.TryAllocate(RemainingSize, out long PA))
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (CurrentProcess.ResourceLimit != null &&
+ !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Memory, RemainingSize))
{
- return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
+ return KernelResult.ResLimitExceeded;
}
- Node = BaseNode;
+ KMemoryRegionManager Region = GetMemoryRegionManager();
- do
- {
- Info = Node.Value.GetInfo();
+ KernelResult Result = Region.AllocatePages(RemainingPages, AslrDisabled, out KPageList PageList);
- if (Info.State == MemoryState.Unmapped)
+ void CleanUpForError()
+ {
+ if (PageList != null)
{
- long CurrSize = GetSizeInRange(Info, Position, End);
+ Region.FreePages(PageList);
+ }
- long MapPosition = Info.Position;
+ CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, RemainingSize);
+ }
- if ((ulong)MapPosition < (ulong)Position)
- {
- MapPosition = Position;
- }
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
- CpuMemory.Map(MapPosition, PA, CurrSize);
+ return Result;
+ }
- PA += CurrSize;
- }
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ CleanUpForError();
- Node = Node.Next;
+ return KernelResult.OutOfResource;
}
- while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
- PersonalMmHeapUsage += RemainingSize;
+ MapPhysicalMemory(PageList, Address, EndAddr);
- long PagesCount = Size / PageSize;
+ PhysicalMemoryUsage += RemainingSize;
+
+ ulong PagesCount = Size / PageSize;
InsertBlock(
- Position,
+ Address,
PagesCount,
MemoryState.Unmapped,
MemoryPermission.None,
@@ -748,22 +1402,26 @@ namespace Ryujinx.HLE.HOS.Kernel
MemoryAttribute.None);
}
- return 0;
+ return KernelResult.Success;
}
- public long UnmapPhysicalMemory(long Position, long Size)
+ public KernelResult UnmapPhysicalMemory(ulong Address, ulong Size)
{
- long End = Position + Size;
+ ulong EndAddr = Address + Size;
lock (Blocks)
{
- long HeapMappedSize = 0;
+ //Scan, ensure that the region can be unmapped (all blocks are heap or
+ //already unmapped), fill pages list for freeing memory.
+ ulong HeapMappedSize = 0;
- long CurrPosition = Position;
+ KPageList PageList = new KPageList();
KMemoryInfo Info;
- LinkedListNode<KMemoryBlock> Node = FindBlockNode(CurrPosition);
+ LinkedListNode<KMemoryBlock> BaseNode = FindBlockNode(Address);
+
+ LinkedListNode<KMemoryBlock> Node = BaseNode;
do
{
@@ -773,90 +1431,200 @@ namespace Ryujinx.HLE.HOS.Kernel
{
if (Info.Attribute != MemoryAttribute.None)
{
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ return KernelResult.InvalidMemState;
}
- HeapMappedSize += GetSizeInRange(Info, Position, End);
+ ulong BlockSize = GetSizeInRange(Info, Address, EndAddr);
+ ulong BlockAddress = GetAddrInRange(Info, Address);
+
+ AddVaRangeToPageList(PageList, BlockAddress, BlockSize / PageSize);
+
+ HeapMappedSize += BlockSize;
}
else if (Info.State != MemoryState.Unmapped)
{
- return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ return KernelResult.InvalidMemState;
}
Node = Node.Next;
}
- while ((ulong)(Info.Position + Info.Size) < (ulong)End && Node != null);
+ while (Info.Address + Info.Size < EndAddr && Node != null);
if (HeapMappedSize == 0)
{
- return 0;
+ return KernelResult.Success;
+ }
+
+ if (!BlockAllocator.CanAllocate(MaxBlocksNeededForInsertion))
+ {
+ return KernelResult.OutOfResource;
}
- PersonalMmHeapUsage -= HeapMappedSize;
+ //Try to unmap all the heap mapped memory inside range.
+ KernelResult Result = KernelResult.Success;
- long PagesCount = Size / PageSize;
+ Node = BaseNode;
- InsertBlock(Position, PagesCount, MemoryState.Unmapped);
+ do
+ {
+ Info = Node.Value.GetInfo();
+
+ if (Info.State == MemoryState.Heap)
+ {
+ ulong BlockSize = GetSizeInRange(Info, Address, EndAddr);
+ ulong BlockAddress = GetAddrInRange(Info, Address);
- FreePages(Position, PagesCount);
+ ulong BlockPagesCount = BlockSize / PageSize;
- CpuMemory.Unmap(Position, Size);
+ Result = MmuUnmap(BlockAddress, BlockPagesCount);
- return 0;
+ if (Result != KernelResult.Success)
+ {
+ //If we failed to unmap, we need to remap everything back again.
+ MapPhysicalMemory(PageList, Address, BlockAddress + BlockSize);
+
+ break;
+ }
+ }
+
+ Node = Node.Next;
+ }
+ while (Info.Address + Info.Size < EndAddr && Node != null);
+
+ if (Result == KernelResult.Success)
+ {
+ GetMemoryRegionManager().FreePages(PageList);
+
+ PhysicalMemoryUsage -= HeapMappedSize;
+
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ CurrentProcess.ResourceLimit?.Release(LimitableResource.Memory, HeapMappedSize);
+
+ ulong PagesCount = Size / PageSize;
+
+ InsertBlock(Address, PagesCount, MemoryState.Unmapped);
+ }
+
+ return Result;
}
}
- private long GetSizeInRange(KMemoryInfo Info, long Start, long End)
+ private void MapPhysicalMemory(KPageList PageList, ulong Address, ulong EndAddr)
{
- long CurrEnd = Info.Size + Info.Position;
- long CurrSize = Info.Size;
+ KMemoryInfo Info;
- if ((ulong)Info.Position < (ulong)Start)
- {
- CurrSize -= Start - Info.Position;
- }
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
- if ((ulong)CurrEnd > (ulong)End)
+ LinkedListNode<KPageNode> PageListNode = PageList.Nodes.First;
+
+ KPageNode PageNode = PageListNode.Value;
+
+ ulong SrcPa = PageNode.Address;
+ ulong SrcPaPages = PageNode.PagesCount;
+
+ do
{
- CurrSize -= CurrEnd - End;
- }
+ Info = Node.Value.GetInfo();
+
+ if (Info.State == MemoryState.Unmapped)
+ {
+ ulong BlockSize = GetSizeInRange(Info, Address, EndAddr);
+
+ ulong DstVaPages = BlockSize / PageSize;
+
+ ulong DstVa = GetAddrInRange(Info, Address);
+
+ while (DstVaPages > 0)
+ {
+ if (SrcPaPages == 0)
+ {
+ PageListNode = PageListNode.Next;
+
+ PageNode = PageListNode.Value;
+
+ SrcPa = PageNode.Address;
+ SrcPaPages = PageNode.PagesCount;
+ }
+
+ ulong PagesCount = SrcPaPages;
+
+ if (PagesCount > DstVaPages)
+ {
+ PagesCount = DstVaPages;
+ }
+
+ DoMmuOperation(
+ DstVa,
+ PagesCount,
+ SrcPa,
+ true,
+ MemoryPermission.ReadAndWrite,
+ MemoryOperation.MapPa);
+
+ DstVa += PagesCount * PageSize;
+ SrcPa += PagesCount * PageSize;
+ SrcPaPages -= PagesCount;
+ DstVaPages -= PagesCount;
+ }
+ }
- return CurrSize;
+ Node = Node.Next;
+ }
+ while (Info.Address + Info.Size < EndAddr && Node != null);
}
- private void FreePages(long Position, long PagesCount)
+ private static ulong GetSizeInRange(KMemoryInfo Info, ulong Start, ulong End)
{
- for (long Page = 0; Page < PagesCount; Page++)
+ ulong EndAddr = Info.Size + Info.Address;
+ ulong Size = Info.Size;
+
+ if (Info.Address < Start)
{
- long VA = Position + Page * PageSize;
+ Size -= Start - Info.Address;
+ }
- if (!CpuMemory.IsMapped(VA))
- {
- continue;
- }
+ if (EndAddr > End)
+ {
+ Size -= EndAddr - End;
+ }
- long PA = CpuMemory.GetPhysicalAddress(VA);
+ return Size;
+ }
- Allocator.Free(PA, PageSize);
+ private static ulong GetAddrInRange(KMemoryInfo Info, ulong Start)
+ {
+ if (Info.Address < Start)
+ {
+ return Start;
}
+
+ return Info.Address;
}
- public bool HleIsUnmapped(long Position, long Size)
+ private void AddVaRangeToPageList(KPageList PageList, ulong Start, ulong PagesCount)
{
- bool Result = false;
+ ulong Address = Start;
- lock (Blocks)
+ while (Address < Start + PagesCount * PageSize)
{
- Result = IsUnmapped(Position, Size);
- }
+ KernelResult Result = ConvertVaToPa(Address, out ulong Pa);
- return Result;
+ if (Result != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Unexpected failure translating virtual address.");
+ }
+
+ PageList.AddRange(Pa, 1);
+
+ Address += PageSize;
+ }
}
- private bool IsUnmapped(long Position, long Size)
+ private bool IsUnmapped(ulong Address, ulong Size)
{
return CheckRange(
- Position,
+ Address,
Size,
MemoryState.Mask,
MemoryState.Unmapped,
@@ -871,8 +1639,8 @@ namespace Ryujinx.HLE.HOS.Kernel
}
private bool CheckRange(
- long Position,
- long Size,
+ ulong Address,
+ ulong Size,
MemoryState StateMask,
MemoryState StateExpected,
MemoryPermission PermissionMask,
@@ -884,24 +1652,44 @@ namespace Ryujinx.HLE.HOS.Kernel
out MemoryPermission OutPermission,
out MemoryAttribute OutAttribute)
{
- KMemoryInfo BlkInfo = FindBlock(Position).GetInfo();
+ ulong EndAddr = Address + Size - 1;
+
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
+
+ KMemoryInfo Info = Node.Value.GetInfo();
- ulong Start = (ulong)Position;
- ulong End = (ulong)Size + Start;
+ MemoryState FirstState = Info.State;
+ MemoryPermission FirstPermission = Info.Permission;
+ MemoryAttribute FirstAttribute = Info.Attribute;
- if (End <= (ulong)(BlkInfo.Position + BlkInfo.Size))
+ do
{
- if ((BlkInfo.Attribute & AttributeMask) == AttributeExpected &&
- (BlkInfo.State & StateMask) == StateExpected &&
- (BlkInfo.Permission & PermissionMask) == PermissionExpected)
+ Info = Node.Value.GetInfo();
+
+ //Check if the block state matches what we expect.
+ if ( FirstState != Info.State ||
+ FirstPermission != Info.Permission ||
+ (Info.Attribute & AttributeMask) != AttributeExpected ||
+ (FirstAttribute | AttributeIgnoreMask) != (Info.Attribute | AttributeIgnoreMask) ||
+ (FirstState & StateMask) != StateExpected ||
+ (FirstPermission & PermissionMask) != PermissionExpected)
+ {
+ break;
+ }
+
+ //Check if this is the last block on the range, if so return success.
+ if (EndAddr <= Info.Address + Info.Size - 1)
{
- OutState = BlkInfo.State;
- OutPermission = BlkInfo.Permission;
- OutAttribute = BlkInfo.Attribute & ~AttributeIgnoreMask;
+ OutState = FirstState;
+ OutPermission = FirstPermission;
+ OutAttribute = FirstAttribute & ~AttributeIgnoreMask;
return true;
}
+
+ Node = Node.Next;
}
+ while (Node != null);
OutState = MemoryState.Unmapped;
OutPermission = MemoryPermission.None;
@@ -910,9 +1698,48 @@ namespace Ryujinx.HLE.HOS.Kernel
return false;
}
+ private bool CheckRange(
+ ulong Address,
+ ulong Size,
+ MemoryState StateMask,
+ MemoryState StateExpected,
+ MemoryPermission PermissionMask,
+ MemoryPermission PermissionExpected,
+ MemoryAttribute AttributeMask,
+ MemoryAttribute AttributeExpected)
+ {
+ ulong EndAddr = Address + Size - 1;
+
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(Address);
+
+ do
+ {
+ KMemoryInfo Info = Node.Value.GetInfo();
+
+ //Check if the block state matches what we expect.
+ if ((Info.State & StateMask) != StateExpected ||
+ (Info.Permission & PermissionMask) != PermissionExpected ||
+ (Info.Attribute & AttributeMask) != AttributeExpected)
+ {
+ break;
+ }
+
+ //Check if this is the last block on the range, if so return success.
+ if (EndAddr <= Info.Address + Info.Size - 1)
+ {
+ return true;
+ }
+
+ Node = Node.Next;
+ }
+ while (Node != null);
+
+ return false;
+ }
+
private void InsertBlock(
- long BasePosition,
- long PagesCount,
+ ulong BaseAddress,
+ ulong PagesCount,
MemoryState OldState,
MemoryPermission OldPermission,
MemoryAttribute OldAttribute,
@@ -923,10 +1750,11 @@ namespace Ryujinx.HLE.HOS.Kernel
//Insert new block on the list only on areas where the state
//of the block matches the state specified on the Old* state
//arguments, otherwise leave it as is.
+ int OldCount = Blocks.Count;
+
OldAttribute |= MemoryAttribute.IpcAndDeviceMapped;
- ulong Start = (ulong)BasePosition;
- ulong End = (ulong)PagesCount * PageSize + Start;
+ ulong EndAddr = PagesCount * PageSize + BaseAddress;
LinkedListNode<KMemoryBlock> Node = Blocks.First;
@@ -937,10 +1765,10 @@ namespace Ryujinx.HLE.HOS.Kernel
KMemoryBlock CurrBlock = Node.Value;
- ulong CurrStart = (ulong)CurrBlock.BasePosition;
- ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart;
+ ulong CurrBaseAddr = CurrBlock.BaseAddress;
+ ulong CurrEndAddr = CurrBlock.PagesCount * PageSize + CurrBaseAddr;
- if (Start < CurrEnd && CurrStart < End)
+ if (BaseAddress < CurrEndAddr && CurrBaseAddr < EndAddr)
{
MemoryAttribute CurrBlockAttr = CurrBlock.Attribute | MemoryAttribute.IpcAndDeviceMapped;
@@ -953,36 +1781,36 @@ namespace Ryujinx.HLE.HOS.Kernel
continue;
}
- if (CurrStart >= Start && CurrEnd <= End)
+ if (CurrBaseAddr >= BaseAddress && CurrEndAddr <= EndAddr)
{
CurrBlock.State = NewState;
CurrBlock.Permission = NewPermission;
CurrBlock.Attribute &= ~MemoryAttribute.IpcAndDeviceMapped;
CurrBlock.Attribute |= NewAttribute;
}
- else if (CurrStart >= Start)
+ else if (CurrBaseAddr >= BaseAddress)
{
- CurrBlock.BasePosition = (long)End;
+ CurrBlock.BaseAddress = EndAddr;
- CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize);
+ CurrBlock.PagesCount = (CurrEndAddr - EndAddr) / PageSize;
- long NewPagesCount = (long)((End - CurrStart) / PageSize);
+ ulong NewPagesCount = (EndAddr - CurrBaseAddr) / PageSize;
NewNode = Blocks.AddBefore(Node, new KMemoryBlock(
- (long)CurrStart,
+ CurrBaseAddr,
NewPagesCount,
NewState,
NewPermission,
NewAttribute));
}
- else if (CurrEnd <= End)
+ else if (CurrEndAddr <= EndAddr)
{
- CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+ CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
- long NewPagesCount = (long)((CurrEnd - Start) / PageSize);
+ ulong NewPagesCount = (CurrEndAddr - BaseAddress) / PageSize;
NewNode = Blocks.AddAfter(Node, new KMemoryBlock(
- BasePosition,
+ BaseAddress,
NewPagesCount,
NewState,
NewPermission,
@@ -990,19 +1818,19 @@ namespace Ryujinx.HLE.HOS.Kernel
}
else
{
- CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+ CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
- long NextPagesCount = (long)((CurrEnd - End) / PageSize);
+ ulong NextPagesCount = (CurrEndAddr - EndAddr) / PageSize;
NewNode = Blocks.AddAfter(Node, new KMemoryBlock(
- BasePosition,
+ BaseAddress,
PagesCount,
NewState,
NewPermission,
NewAttribute));
Blocks.AddAfter(NewNode, new KMemoryBlock(
- (long)End,
+ EndAddr,
NextPagesCount,
CurrBlock.State,
CurrBlock.Permission,
@@ -1016,21 +1844,24 @@ namespace Ryujinx.HLE.HOS.Kernel
Node = NextNode;
}
+
+ BlockAllocator.Count += Blocks.Count - OldCount;
}
private void InsertBlock(
- long BasePosition,
- long PagesCount,
+ ulong BaseAddress,
+ ulong PagesCount,
MemoryState State,
MemoryPermission Permission = MemoryPermission.None,
MemoryAttribute Attribute = MemoryAttribute.None)
{
//Inserts new block at the list, replacing and spliting
//existing blocks as needed.
- KMemoryBlock Block = new KMemoryBlock(BasePosition, PagesCount, State, Permission, Attribute);
+ KMemoryBlock Block = new KMemoryBlock(BaseAddress, PagesCount, State, Permission, Attribute);
+
+ int OldCount = Blocks.Count;
- ulong Start = (ulong)BasePosition;
- ulong End = (ulong)PagesCount * PageSize + Start;
+ ulong EndAddr = PagesCount * PageSize + BaseAddress;
LinkedListNode<KMemoryBlock> NewNode = null;
@@ -1042,26 +1873,26 @@ namespace Ryujinx.HLE.HOS.Kernel
LinkedListNode<KMemoryBlock> NextNode = Node.Next;
- ulong CurrStart = (ulong)CurrBlock.BasePosition;
- ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart;
+ ulong CurrBaseAddr = CurrBlock.BaseAddress;
+ ulong CurrEndAddr = CurrBlock.PagesCount * PageSize + CurrBaseAddr;
- if (Start < CurrEnd && CurrStart < End)
+ if (BaseAddress < CurrEndAddr && CurrBaseAddr < EndAddr)
{
- if (Start >= CurrStart && End <= CurrEnd)
+ if (BaseAddress >= CurrBaseAddr && EndAddr <= CurrEndAddr)
{
Block.Attribute |= CurrBlock.Attribute & MemoryAttribute.IpcAndDeviceMapped;
}
- if (Start > CurrStart && End < CurrEnd)
+ if (BaseAddress > CurrBaseAddr && EndAddr < CurrEndAddr)
{
- CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+ CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
- long NextPagesCount = (long)((CurrEnd - End) / PageSize);
+ ulong NextPagesCount = (CurrEndAddr - EndAddr) / PageSize;
NewNode = Blocks.AddAfter(Node, Block);
Blocks.AddAfter(NewNode, new KMemoryBlock(
- (long)End,
+ EndAddr,
NextPagesCount,
CurrBlock.State,
CurrBlock.Permission,
@@ -1069,20 +1900,20 @@ namespace Ryujinx.HLE.HOS.Kernel
break;
}
- else if (Start <= CurrStart && End < CurrEnd)
+ else if (BaseAddress <= CurrBaseAddr && EndAddr < CurrEndAddr)
{
- CurrBlock.BasePosition = (long)End;
+ CurrBlock.BaseAddress = EndAddr;
- CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize);
+ CurrBlock.PagesCount = (CurrEndAddr - EndAddr) / PageSize;
if (NewNode == null)
{
NewNode = Blocks.AddBefore(Node, Block);
}
}
- else if (Start > CurrStart && End >= CurrEnd)
+ else if (BaseAddress > CurrBaseAddr && EndAddr >= CurrEndAddr)
{
- CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
+ CurrBlock.PagesCount = (BaseAddress - CurrBaseAddr) / PageSize;
if (NewNode == null)
{
@@ -1109,14 +1940,15 @@ namespace Ryujinx.HLE.HOS.Kernel
}
MergeEqualStateNeighbours(NewNode);
+
+ BlockAllocator.Count += Blocks.Count - OldCount;
}
private void MergeEqualStateNeighbours(LinkedListNode<KMemoryBlock> Node)
{
KMemoryBlock Block = Node.Value;
- ulong Start = (ulong)Block.BasePosition;
- ulong End = (ulong)Block.PagesCount * PageSize + Start;
+ ulong EndAddr = Block.PagesCount * PageSize + Block.BaseAddress;
if (Node.Previous != null)
{
@@ -1126,9 +1958,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
Blocks.Remove(Node.Previous);
- Block.BasePosition = Previous.BasePosition;
-
- Start = (ulong)Block.BasePosition;
+ Block.BaseAddress = Previous.BaseAddress;
}
}
@@ -1140,31 +1970,84 @@ namespace Ryujinx.HLE.HOS.Kernel
{
Blocks.Remove(Node.Next);
- End = (ulong)(Next.BasePosition + Next.PagesCount * PageSize);
+ EndAddr = Next.BaseAddress + Next.PagesCount * PageSize;
}
}
- Block.PagesCount = (long)((End - Start) / PageSize);
+ Block.PagesCount = (EndAddr - Block.BaseAddress) / PageSize;
}
- private static bool BlockStateEquals(KMemoryBlock LHS, KMemoryBlock RHS)
+ private static bool BlockStateEquals(KMemoryBlock Lhs, KMemoryBlock Rhs)
{
- return LHS.State == RHS.State &&
- LHS.Permission == RHS.Permission &&
- LHS.Attribute == RHS.Attribute &&
- LHS.DeviceRefCount == RHS.DeviceRefCount &&
- LHS.IpcRefCount == RHS.IpcRefCount;
+ return Lhs.State == Rhs.State &&
+ Lhs.Permission == Rhs.Permission &&
+ Lhs.Attribute == Rhs.Attribute &&
+ Lhs.DeviceRefCount == Rhs.DeviceRefCount &&
+ Lhs.IpcRefCount == Rhs.IpcRefCount;
}
- private KMemoryBlock FindBlock(long Position)
+ private ulong FindFirstFit(
+ ulong RegionStart,
+ ulong RegionPagesCount,
+ ulong NeededPagesCount,
+ int Alignment,
+ ulong ReservedStart,
+ ulong ReservedPagesCount)
{
- return FindBlockNode(Position)?.Value;
+ ulong ReservedSize = ReservedPagesCount * PageSize;
+
+ ulong TotalNeededSize = ReservedSize + NeededPagesCount * PageSize;
+
+ ulong RegionEndAddr = RegionStart + RegionPagesCount * PageSize;
+
+ LinkedListNode<KMemoryBlock> Node = FindBlockNode(RegionStart);
+
+ KMemoryInfo Info = Node.Value.GetInfo();
+
+ while (RegionEndAddr >= Info.Address)
+ {
+ if (Info.State == MemoryState.Unmapped)
+ {
+ ulong CurrBaseAddr = Info.Address + ReservedSize;
+ ulong CurrEndAddr = Info.Address + Info.Size - 1;
+
+ ulong Address = BitUtils.AlignDown(CurrBaseAddr, Alignment) + ReservedStart;
+
+ if (CurrBaseAddr > Address)
+ {
+ Address += (ulong)Alignment;
+ }
+
+ ulong AllocationEndAddr = Address + TotalNeededSize - 1;
+
+ if (AllocationEndAddr <= RegionEndAddr &&
+ AllocationEndAddr <= CurrEndAddr &&
+ Address < AllocationEndAddr)
+ {
+ return Address;
+ }
+ }
+
+ Node = Node.Next;
+
+ if (Node == null)
+ {
+ break;
+ }
+
+ Info = Node.Value.GetInfo();
+ }
+
+ return 0;
}
- private LinkedListNode<KMemoryBlock> FindBlockNode(long Position)
+ private KMemoryBlock FindBlock(ulong Address)
{
- ulong Addr = (ulong)Position;
+ return FindBlockNode(Address)?.Value;
+ }
+ private LinkedListNode<KMemoryBlock> FindBlockNode(ulong Address)
+ {
lock (Blocks)
{
LinkedListNode<KMemoryBlock> Node = Blocks.First;
@@ -1173,10 +2056,9 @@ namespace Ryujinx.HLE.HOS.Kernel
{
KMemoryBlock Block = Node.Value;
- ulong Start = (ulong)Block.BasePosition;
- ulong End = (ulong)Block.PagesCount * PageSize + Start;
+ ulong CurrEndAddr = Block.PagesCount * PageSize + Block.BaseAddress;
- if (Start <= Addr && End - 1 >= Addr)
+ if (Block.BaseAddress <= Address && CurrEndAddr - 1 >= Address)
{
return Node;
}
@@ -1187,5 +2069,390 @@ namespace Ryujinx.HLE.HOS.Kernel
return null;
}
+
+ private bool ValidateRegionForState(ulong Address, ulong Size, MemoryState State)
+ {
+ ulong EndAddr = Address + Size;
+
+ ulong RegionBaseAddr = GetBaseAddrForState(State);
+
+ ulong RegionEndAddr = RegionBaseAddr + GetSizeForState(State);
+
+ bool InsideRegion()
+ {
+ return RegionBaseAddr <= Address &&
+ EndAddr > Address &&
+ EndAddr - 1 <= RegionEndAddr - 1;
+ }
+
+ bool OutsideHeapRegion()
+ {
+ return EndAddr <= HeapRegionStart ||
+ Address >= HeapRegionEnd;
+ }
+
+ bool OutsideMapRegion()
+ {
+ return EndAddr <= AliasRegionStart ||
+ Address >= AliasRegionEnd;
+ }
+
+ switch (State)
+ {
+ case MemoryState.Io:
+ case MemoryState.Normal:
+ case MemoryState.CodeStatic:
+ case MemoryState.CodeMutable:
+ case MemoryState.SharedMemory:
+ case MemoryState.ModCodeStatic:
+ case MemoryState.ModCodeMutable:
+ case MemoryState.Stack:
+ case MemoryState.ThreadLocal:
+ case MemoryState.TransferMemoryIsolated:
+ case MemoryState.TransferMemory:
+ case MemoryState.ProcessMemory:
+ case MemoryState.CodeReadOnly:
+ case MemoryState.CodeWritable:
+ return InsideRegion() && OutsideHeapRegion() && OutsideMapRegion();
+
+ case MemoryState.Heap:
+ return InsideRegion() && OutsideMapRegion();
+
+ case MemoryState.IpcBuffer0:
+ case MemoryState.IpcBuffer1:
+ case MemoryState.IpcBuffer3:
+ return InsideRegion() && OutsideHeapRegion();
+
+ case MemoryState.KernelStack:
+ return InsideRegion();
+ }
+
+ throw new ArgumentException($"Invalid state value \"{State}\".");
+ }
+
+ private ulong GetBaseAddrForState(MemoryState State)
+ {
+ switch (State)
+ {
+ case MemoryState.Io:
+ case MemoryState.Normal:
+ case MemoryState.ThreadLocal:
+ return TlsIoRegionStart;
+
+ case MemoryState.CodeStatic:
+ case MemoryState.CodeMutable:
+ case MemoryState.SharedMemory:
+ case MemoryState.ModCodeStatic:
+ case MemoryState.ModCodeMutable:
+ case MemoryState.TransferMemoryIsolated:
+ case MemoryState.TransferMemory:
+ case MemoryState.ProcessMemory:
+ case MemoryState.CodeReadOnly:
+ case MemoryState.CodeWritable:
+ return GetAddrSpaceBaseAddr();
+
+ case MemoryState.Heap:
+ return HeapRegionStart;
+
+ case MemoryState.IpcBuffer0:
+ case MemoryState.IpcBuffer1:
+ case MemoryState.IpcBuffer3:
+ return AliasRegionStart;
+
+ case MemoryState.Stack:
+ return StackRegionStart;
+
+ case MemoryState.KernelStack:
+ return AddrSpaceStart;
+ }
+
+ throw new ArgumentException($"Invalid state value \"{State}\".");
+ }
+
+ private ulong GetSizeForState(MemoryState State)
+ {
+ switch (State)
+ {
+ case MemoryState.Io:
+ case MemoryState.Normal:
+ case MemoryState.ThreadLocal:
+ return TlsIoRegionEnd - TlsIoRegionStart;
+
+ case MemoryState.CodeStatic:
+ case MemoryState.CodeMutable:
+ case MemoryState.SharedMemory:
+ case MemoryState.ModCodeStatic:
+ case MemoryState.ModCodeMutable:
+ case MemoryState.TransferMemoryIsolated:
+ case MemoryState.TransferMemory:
+ case MemoryState.ProcessMemory:
+ case MemoryState.CodeReadOnly:
+ case MemoryState.CodeWritable:
+ return GetAddrSpaceSize();
+
+ case MemoryState.Heap:
+ return HeapRegionEnd - HeapRegionStart;
+
+ case MemoryState.IpcBuffer0:
+ case MemoryState.IpcBuffer1:
+ case MemoryState.IpcBuffer3:
+ return AliasRegionEnd - AliasRegionStart;
+
+ case MemoryState.Stack:
+ return StackRegionEnd - StackRegionStart;
+
+ case MemoryState.KernelStack:
+ return AddrSpaceEnd - AddrSpaceStart;
+ }
+
+ throw new ArgumentException($"Invalid state value \"{State}\".");
+ }
+
+ public ulong GetAddrSpaceBaseAddr()
+ {
+ if (AddrSpaceWidth == 36 || AddrSpaceWidth == 39)
+ {
+ return 0x8000000;
+ }
+ else if (AddrSpaceWidth == 32)
+ {
+ return 0x200000;
+ }
+ else
+ {
+ throw new InvalidOperationException("Invalid address space width!");
+ }
+ }
+
+ public ulong GetAddrSpaceSize()
+ {
+ if (AddrSpaceWidth == 36)
+ {
+ return 0xff8000000;
+ }
+ else if (AddrSpaceWidth == 39)
+ {
+ return 0x7ff8000000;
+ }
+ else if (AddrSpaceWidth == 32)
+ {
+ return 0xffe00000;
+ }
+ else
+ {
+ throw new InvalidOperationException("Invalid address space width!");
+ }
+ }
+
+ private KernelResult MapPages(ulong Address, KPageList PageList, MemoryPermission Permission)
+ {
+ ulong CurrAddr = Address;
+
+ KernelResult Result = KernelResult.Success;
+
+ foreach (KPageNode PageNode in PageList)
+ {
+ Result = DoMmuOperation(
+ CurrAddr,
+ PageNode.PagesCount,
+ PageNode.Address,
+ true,
+ Permission,
+ MemoryOperation.MapPa);
+
+ if (Result != KernelResult.Success)
+ {
+ KMemoryInfo Info = FindBlock(CurrAddr).GetInfo();
+
+ ulong PagesCount = (Address - CurrAddr) / PageSize;
+
+ Result = MmuUnmap(Address, PagesCount);
+
+ break;
+ }
+
+ CurrAddr += PageNode.PagesCount * PageSize;
+ }
+
+ return Result;
+ }
+
+ private KernelResult MmuUnmap(ulong Address, ulong PagesCount)
+ {
+ return DoMmuOperation(
+ Address,
+ PagesCount,
+ 0,
+ false,
+ MemoryPermission.None,
+ MemoryOperation.Unmap);
+ }
+
+ private KernelResult MmuChangePermission(ulong Address, ulong PagesCount, MemoryPermission Permission)
+ {
+ return DoMmuOperation(
+ Address,
+ PagesCount,
+ 0,
+ false,
+ Permission,
+ MemoryOperation.ChangePermRw);
+ }
+
+ private KernelResult DoMmuOperation(
+ ulong DstVa,
+ ulong PagesCount,
+ ulong SrcPa,
+ bool Map,
+ MemoryPermission Permission,
+ MemoryOperation Operation)
+ {
+ if (Map != (Operation == MemoryOperation.MapPa))
+ {
+ throw new ArgumentException(nameof(Map) + " value is invalid for this operation.");
+ }
+
+ KernelResult Result;
+
+ switch (Operation)
+ {
+ case MemoryOperation.MapPa:
+ {
+ ulong Size = PagesCount * PageSize;
+
+ CpuMemory.Map((long)DstVa, (long)(SrcPa - DramMemoryMap.DramBase), (long)Size);
+
+ Result = KernelResult.Success;
+
+ break;
+ }
+
+ case MemoryOperation.Allocate:
+ {
+ KMemoryRegionManager Region = GetMemoryRegionManager();
+
+ Result = Region.AllocatePages(PagesCount, AslrDisabled, out KPageList PageList);
+
+ if (Result == KernelResult.Success)
+ {
+ Result = MmuMapPages(DstVa, PageList);
+ }
+
+ break;
+ }
+
+ case MemoryOperation.Unmap:
+ {
+ ulong Size = PagesCount * PageSize;
+
+ CpuMemory.Unmap((long)DstVa, (long)Size);
+
+ Result = KernelResult.Success;
+
+ break;
+ }
+
+ case MemoryOperation.ChangePermRw: Result = KernelResult.Success; break;
+ case MemoryOperation.ChangePermsAndAttributes: Result = KernelResult.Success; break;
+
+ default: throw new ArgumentException($"Invalid operation \"{Operation}\".");
+ }
+
+ return Result;
+ }
+
+ private KernelResult DoMmuOperation(
+ ulong Address,
+ ulong PagesCount,
+ KPageList PageList,
+ MemoryPermission Permission,
+ MemoryOperation Operation)
+ {
+ if (Operation != MemoryOperation.MapVa)
+ {
+ throw new ArgumentException($"Invalid memory operation \"{Operation}\" specified.");
+ }
+
+ return MmuMapPages(Address, PageList);
+ }
+
+ private KMemoryRegionManager GetMemoryRegionManager()
+ {
+ return System.MemoryRegions[(int)MemRegion];
+ }
+
+ private KernelResult MmuMapPages(ulong Address, KPageList PageList)
+ {
+ foreach (KPageNode PageNode in PageList)
+ {
+ ulong Size = PageNode.PagesCount * PageSize;
+
+ CpuMemory.Map((long)Address, (long)(PageNode.Address - DramMemoryMap.DramBase), (long)Size);
+
+ Address += Size;
+ }
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult ConvertVaToPa(ulong Va, out ulong Pa)
+ {
+ Pa = DramMemoryMap.DramBase + (ulong)CpuMemory.GetPhysicalAddress((long)Va);
+
+ return KernelResult.Success;
+ }
+
+ public long GetMmUsedPages()
+ {
+ lock (Blocks)
+ {
+ return BitUtils.DivRoundUp(GetMmUsedSize(), PageSize);
+ }
+ }
+
+ private long GetMmUsedSize()
+ {
+ return Blocks.Count * KMemoryBlockSize;
+ }
+
+ public bool IsInvalidRegion(ulong Address, ulong Size)
+ {
+ return Address + Size - 1 > GetAddrSpaceBaseAddr() + GetAddrSpaceSize() - 1;
+ }
+
+ public bool InsideAddrSpace(ulong Address, ulong Size)
+ {
+ return AddrSpaceStart <= Address && Address + Size - 1 <= AddrSpaceEnd - 1;
+ }
+
+ public bool InsideAliasRegion(ulong Address, ulong Size)
+ {
+ return Address + Size > AliasRegionStart && AliasRegionEnd > Address;
+ }
+
+ public bool InsideHeapRegion(ulong Address, ulong Size)
+ {
+ return Address + Size > HeapRegionStart && HeapRegionEnd > Address;
+ }
+
+ public bool InsideStackRegion(ulong Address, ulong Size)
+ {
+ return Address + Size > StackRegionStart && StackRegionEnd > Address;
+ }
+
+ public bool OutsideAliasRegion(ulong Address, ulong Size)
+ {
+ return AliasRegionStart > Address || Address + Size - 1 > AliasRegionEnd - 1;
+ }
+
+ public bool OutsideAddrSpace(ulong Address, ulong Size)
+ {
+ return AddrSpaceStart > Address || Address + Size - 1 > AddrSpaceEnd - 1;
+ }
+
+ public bool OutsideStackRegion(ulong Address, ulong Size)
+ {
+ return StackRegionStart > Address || Address + Size - 1 > StackRegionEnd - 1;
+ }
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs
new file mode 100644
index 00000000..1f334e65
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs
@@ -0,0 +1,43 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KMemoryRegionBlock
+ {
+ public long[][] Masks;
+
+ public ulong FreeCount;
+ public int MaxLevel;
+ public ulong StartAligned;
+ public ulong SizeInBlocksTruncated;
+ public ulong SizeInBlocksRounded;
+ public int Order;
+ public int NextOrder;
+
+ public bool TryCoalesce(int Index, int Size)
+ {
+ long Mask = ((1L << Size) - 1) << (Index & 63);
+
+ Index /= 64;
+
+ if ((Mask & ~Masks[MaxLevel - 1][Index]) != 0)
+ {
+ return false;
+ }
+
+ Masks[MaxLevel - 1][Index] &= ~Mask;
+
+ for (int Level = MaxLevel - 2; Level >= 0; Level--, Index /= 64)
+ {
+ Masks[Level][Index / 64] &= ~(1L << (Index & 63));
+
+ if (Masks[Level][Index / 64] != 0)
+ {
+ break;
+ }
+ }
+
+ FreeCount -= (ulong)Size;
+
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs
new file mode 100644
index 00000000..10db0753
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs
@@ -0,0 +1,428 @@
+using Ryujinx.Common;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KMemoryRegionManager
+ {
+ private static readonly int[] BlockOrders = new int[] { 12, 16, 21, 22, 25, 29, 30 };
+
+ public ulong Address { get; private set; }
+ public ulong EndAddr { get; private set; }
+ public ulong Size { get; private set; }
+
+ private int BlockOrdersCount;
+
+ private KMemoryRegionBlock[] Blocks;
+
+ public KMemoryRegionManager(ulong Address, ulong Size, ulong EndAddr)
+ {
+ Blocks = new KMemoryRegionBlock[BlockOrders.Length];
+
+ this.Address = Address;
+ this.Size = Size;
+ this.EndAddr = EndAddr;
+
+ BlockOrdersCount = BlockOrders.Length;
+
+ for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++)
+ {
+ Blocks[BlockIndex] = new KMemoryRegionBlock();
+
+ Blocks[BlockIndex].Order = BlockOrders[BlockIndex];
+
+ int NextOrder = BlockIndex == BlockOrdersCount - 1 ? 0 : BlockOrders[BlockIndex + 1];
+
+ Blocks[BlockIndex].NextOrder = NextOrder;
+
+ int CurrBlockSize = 1 << BlockOrders[BlockIndex];
+ int NextBlockSize = CurrBlockSize;
+
+ if (NextOrder != 0)
+ {
+ NextBlockSize = 1 << NextOrder;
+ }
+
+ ulong StartAligned = BitUtils.AlignDown(Address, NextBlockSize);
+ ulong EndAddrAligned = BitUtils.AlignDown(EndAddr, CurrBlockSize);
+
+ ulong SizeInBlocksTruncated = (EndAddrAligned - StartAligned) >> BlockOrders[BlockIndex];
+
+ ulong EndAddrRounded = BitUtils.AlignUp(Address + Size, NextBlockSize);
+
+ ulong SizeInBlocksRounded = (EndAddrRounded - StartAligned) >> BlockOrders[BlockIndex];
+
+ Blocks[BlockIndex].StartAligned = StartAligned;
+ Blocks[BlockIndex].SizeInBlocksTruncated = SizeInBlocksTruncated;
+ Blocks[BlockIndex].SizeInBlocksRounded = SizeInBlocksRounded;
+
+ ulong CurrSizeInBlocks = SizeInBlocksRounded;
+
+ int MaxLevel = 0;
+
+ do
+ {
+ MaxLevel++;
+ }
+ while ((CurrSizeInBlocks /= 64) != 0);
+
+ Blocks[BlockIndex].MaxLevel = MaxLevel;
+
+ Blocks[BlockIndex].Masks = new long[MaxLevel][];
+
+ CurrSizeInBlocks = SizeInBlocksRounded;
+
+ for (int Level = MaxLevel - 1; Level >= 0; Level--)
+ {
+ CurrSizeInBlocks = (CurrSizeInBlocks + 63) / 64;
+
+ Blocks[BlockIndex].Masks[Level] = new long[CurrSizeInBlocks];
+ }
+ }
+
+ if (Size != 0)
+ {
+ FreePages(Address, Size / KMemoryManager.PageSize);
+ }
+ }
+
+ public KernelResult AllocatePages(ulong PagesCount, bool Backwards, out KPageList PageList)
+ {
+ lock (Blocks)
+ {
+ return AllocatePagesImpl(PagesCount, Backwards, out PageList);
+ }
+ }
+
+ private KernelResult AllocatePagesImpl(ulong PagesCount, bool Backwards, out KPageList PageList)
+ {
+ PageList = new KPageList();
+
+ if (BlockOrdersCount > 0)
+ {
+ if (GetFreePagesImpl() < PagesCount)
+ {
+ return KernelResult.OutOfMemory;
+ }
+ }
+ else if (PagesCount != 0)
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ for (int BlockIndex = BlockOrdersCount - 1; BlockIndex >= 0; BlockIndex--)
+ {
+ KMemoryRegionBlock Block = Blocks[BlockIndex];
+
+ ulong BestFitBlockSize = 1UL << Block.Order;
+
+ ulong BlockPagesCount = BestFitBlockSize / KMemoryManager.PageSize;
+
+ //Check if this is the best fit for this page size.
+ //If so, try allocating as much requested pages as possible.
+ while (BlockPagesCount <= PagesCount)
+ {
+ ulong Address = 0;
+
+ for (int CurrBlockIndex = BlockIndex;
+ CurrBlockIndex < BlockOrdersCount && Address == 0;
+ CurrBlockIndex++)
+ {
+ Block = Blocks[CurrBlockIndex];
+
+ int Index = 0;
+
+ bool ZeroMask = false;
+
+ for (int Level = 0; Level < Block.MaxLevel; Level++)
+ {
+ long Mask = Block.Masks[Level][Index];
+
+ if (Mask == 0)
+ {
+ ZeroMask = true;
+
+ break;
+ }
+
+ if (Backwards)
+ {
+ Index = (Index * 64 + 63) - BitUtils.CountLeadingZeros64(Mask);
+ }
+ else
+ {
+ Index = Index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(Mask));
+ }
+ }
+
+ if (Block.SizeInBlocksTruncated <= (ulong)Index || ZeroMask)
+ {
+ continue;
+ }
+
+ Block.FreeCount--;
+
+ int TempIdx = Index;
+
+ for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, TempIdx /= 64)
+ {
+ Block.Masks[Level][TempIdx / 64] &= ~(1L << (TempIdx & 63));
+
+ if (Block.Masks[Level][TempIdx / 64] != 0)
+ {
+ break;
+ }
+ }
+
+ Address = Block.StartAligned + ((ulong)Index << Block.Order);
+ }
+
+ for (int CurrBlockIndex = BlockIndex;
+ CurrBlockIndex < BlockOrdersCount && Address == 0;
+ CurrBlockIndex++)
+ {
+ Block = Blocks[CurrBlockIndex];
+
+ int Index = 0;
+
+ bool ZeroMask = false;
+
+ for (int Level = 0; Level < Block.MaxLevel; Level++)
+ {
+ long Mask = Block.Masks[Level][Index];
+
+ if (Mask == 0)
+ {
+ ZeroMask = true;
+
+ break;
+ }
+
+ if (Backwards)
+ {
+ Index = Index * 64 + BitUtils.CountLeadingZeros64(BitUtils.ReverseBits64(Mask));
+ }
+ else
+ {
+ Index = (Index * 64 + 63) - BitUtils.CountLeadingZeros64(Mask);
+ }
+ }
+
+ if (Block.SizeInBlocksTruncated <= (ulong)Index || ZeroMask)
+ {
+ continue;
+ }
+
+ Block.FreeCount--;
+
+ int TempIdx = Index;
+
+ for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, TempIdx /= 64)
+ {
+ Block.Masks[Level][TempIdx / 64] &= ~(1L << (TempIdx & 63));
+
+ if (Block.Masks[Level][TempIdx / 64] != 0)
+ {
+ break;
+ }
+ }
+
+ Address = Block.StartAligned + ((ulong)Index << Block.Order);
+ }
+
+ //The address being zero means that no free space was found on that order,
+ //just give up and try with the next one.
+ if (Address == 0)
+ {
+ break;
+ }
+
+ //If we are using a larger order than best fit, then we should
+ //split it into smaller blocks.
+ ulong FirstFreeBlockSize = 1UL << Block.Order;
+
+ if (FirstFreeBlockSize > BestFitBlockSize)
+ {
+ FreePages(Address + BestFitBlockSize, (FirstFreeBlockSize - BestFitBlockSize) / KMemoryManager.PageSize);
+ }
+
+ //Add new allocated page(s) to the pages list.
+ //If an error occurs, then free all allocated pages and fail.
+ KernelResult Result = PageList.AddRange(Address, BlockPagesCount);
+
+ if (Result != KernelResult.Success)
+ {
+ FreePages(Address, BlockPagesCount);
+
+ foreach (KPageNode PageNode in PageList)
+ {
+ FreePages(PageNode.Address, PageNode.PagesCount);
+ }
+
+ return Result;
+ }
+
+ PagesCount -= BlockPagesCount;
+ }
+ }
+
+ //Success case, all requested pages were allocated successfully.
+ if (PagesCount == 0)
+ {
+ return KernelResult.Success;
+ }
+
+ //Error case, free allocated pages and return out of memory.
+ foreach (KPageNode PageNode in PageList)
+ {
+ FreePages(PageNode.Address, PageNode.PagesCount);
+ }
+
+ PageList = null;
+
+ return KernelResult.OutOfMemory;
+ }
+
+ public void FreePages(KPageList PageList)
+ {
+ lock (Blocks)
+ {
+ foreach (KPageNode PageNode in PageList)
+ {
+ FreePages(PageNode.Address, PageNode.PagesCount);
+ }
+ }
+ }
+
+ private void FreePages(ulong Address, ulong PagesCount)
+ {
+ ulong EndAddr = Address + PagesCount * KMemoryManager.PageSize;
+
+ int BlockIndex = BlockOrdersCount - 1;
+
+ ulong AddressRounded = 0;
+ ulong EndAddrTruncated = 0;
+
+ for (; BlockIndex >= 0; BlockIndex--)
+ {
+ KMemoryRegionBlock AllocInfo = Blocks[BlockIndex];
+
+ int BlockSize = 1 << AllocInfo.Order;
+
+ AddressRounded = BitUtils.AlignUp (Address, BlockSize);
+ EndAddrTruncated = BitUtils.AlignDown(EndAddr, BlockSize);
+
+ if (AddressRounded < EndAddrTruncated)
+ {
+ break;
+ }
+ }
+
+ void FreeRegion(ulong CurrAddress)
+ {
+ for (int CurrBlockIndex = BlockIndex;
+ CurrBlockIndex < BlockOrdersCount && CurrAddress != 0;
+ CurrBlockIndex++)
+ {
+ KMemoryRegionBlock Block = Blocks[CurrBlockIndex];
+
+ Block.FreeCount++;
+
+ ulong FreedBlocks = (CurrAddress - Block.StartAligned) >> Block.Order;
+
+ int Index = (int)FreedBlocks;
+
+ for (int Level = Block.MaxLevel - 1; Level >= 0; Level--, Index /= 64)
+ {
+ long Mask = Block.Masks[Level][Index / 64];
+
+ Block.Masks[Level][Index / 64] = Mask | (1L << (Index & 63));
+
+ if (Mask != 0)
+ {
+ break;
+ }
+ }
+
+ int BlockSizeDelta = 1 << (Block.NextOrder - Block.Order);
+
+ int FreedBlocksTruncated = BitUtils.AlignDown((int)FreedBlocks, BlockSizeDelta);
+
+ if (!Block.TryCoalesce(FreedBlocksTruncated, BlockSizeDelta))
+ {
+ break;
+ }
+
+ CurrAddress = Block.StartAligned + ((ulong)FreedBlocksTruncated << Block.Order);
+ }
+ }
+
+ //Free inside aligned region.
+ ulong BaseAddress = AddressRounded;
+
+ while (BaseAddress < EndAddrTruncated)
+ {
+ ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
+
+ FreeRegion(BaseAddress);
+
+ BaseAddress += BlockSize;
+ }
+
+ int NextBlockIndex = BlockIndex - 1;
+
+ //Free region between Address and aligned region start.
+ BaseAddress = AddressRounded;
+
+ for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--)
+ {
+ ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
+
+ while (BaseAddress - BlockSize >= Address)
+ {
+ BaseAddress -= BlockSize;
+
+ FreeRegion(BaseAddress);
+ }
+ }
+
+ //Free region between aligned region end and End Address.
+ BaseAddress = EndAddrTruncated;
+
+ for (BlockIndex = NextBlockIndex; BlockIndex >= 0; BlockIndex--)
+ {
+ ulong BlockSize = 1UL << Blocks[BlockIndex].Order;
+
+ while (BaseAddress + BlockSize <= EndAddr)
+ {
+ FreeRegion(BaseAddress);
+
+ BaseAddress += BlockSize;
+ }
+ }
+ }
+
+ public ulong GetFreePages()
+ {
+ lock (Blocks)
+ {
+ return GetFreePagesImpl();
+ }
+ }
+
+ private ulong GetFreePagesImpl()
+ {
+ ulong AvailablePages = 0;
+
+ for (int BlockIndex = 0; BlockIndex < BlockOrdersCount; BlockIndex++)
+ {
+ KMemoryRegionBlock Block = Blocks[BlockIndex];
+
+ ulong BlockPagesCount = (1UL << Block.Order) / KMemoryManager.PageSize;
+
+ AvailablePages += BlockPagesCount * Block.FreeCount;
+ }
+
+ return AvailablePages;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KPageList.cs b/Ryujinx.HLE/HOS/Kernel/KPageList.cs
new file mode 100644
index 00000000..05162323
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KPageList.cs
@@ -0,0 +1,80 @@
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KPageList : IEnumerable<KPageNode>
+ {
+ public LinkedList<KPageNode> Nodes { get; private set; }
+
+ public KPageList()
+ {
+ Nodes = new LinkedList<KPageNode>();
+ }
+
+ public KernelResult AddRange(ulong Address, ulong PagesCount)
+ {
+ if (PagesCount != 0)
+ {
+ if (Nodes.Last != null)
+ {
+ KPageNode LastNode = Nodes.Last.Value;
+
+ if (LastNode.Address + LastNode.PagesCount * KMemoryManager.PageSize == Address)
+ {
+ Address = LastNode.Address;
+ PagesCount += LastNode.PagesCount;
+
+ Nodes.RemoveLast();
+ }
+ }
+
+ Nodes.AddLast(new KPageNode(Address, PagesCount));
+ }
+
+ return KernelResult.Success;
+ }
+
+ public ulong GetPagesCount()
+ {
+ ulong Sum = 0;
+
+ foreach (KPageNode Node in Nodes)
+ {
+ Sum += Node.PagesCount;
+ }
+
+ return Sum;
+ }
+
+ public bool IsEqual(KPageList Other)
+ {
+ LinkedListNode<KPageNode> ThisNode = Nodes.First;
+ LinkedListNode<KPageNode> OtherNode = Other.Nodes.First;
+
+ while (ThisNode != null && OtherNode != null)
+ {
+ if (ThisNode.Value.Address != OtherNode.Value.Address ||
+ ThisNode.Value.PagesCount != OtherNode.Value.PagesCount)
+ {
+ return false;
+ }
+
+ ThisNode = ThisNode.Next;
+ OtherNode = OtherNode.Next;
+ }
+
+ return ThisNode == null && OtherNode == null;
+ }
+
+ public IEnumerator<KPageNode> GetEnumerator()
+ {
+ return Nodes.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KPageNode.cs b/Ryujinx.HLE/HOS/Kernel/KPageNode.cs
new file mode 100644
index 00000000..6cecab2e
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KPageNode.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ struct KPageNode
+ {
+ public ulong Address;
+ public ulong PagesCount;
+
+ public KPageNode(ulong Address, ulong PagesCount)
+ {
+ this.Address = Address;
+ this.PagesCount = PagesCount;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KPort.cs b/Ryujinx.HLE/HOS/Kernel/KPort.cs
new file mode 100644
index 00000000..598f3a32
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KPort.cs
@@ -0,0 +1,26 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KPort : KAutoObject
+ {
+ public KServerPort ServerPort { get; private set; }
+ public KClientPort ClientPort { get; private set; }
+
+ private long NameAddress;
+ private bool IsLight;
+
+ public KPort(Horizon System) : base(System)
+ {
+ ServerPort = new KServerPort(System);
+ ClientPort = new KClientPort(System);
+ }
+
+ public void Initialize(int MaxSessions, bool IsLight, long NameAddress)
+ {
+ ServerPort.Initialize(this);
+ ClientPort.Initialize(this, MaxSessions);
+
+ this.IsLight = IsLight;
+ this.NameAddress = NameAddress;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/KProcess.cs
new file mode 100644
index 00000000..094ef222
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KProcess.cs
@@ -0,0 +1,1013 @@
+using ChocolArm64;
+using ChocolArm64.Events;
+using ChocolArm64.Memory;
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KProcess : KSynchronizationObject
+ {
+ public const int KernelVersionMajor = 10;
+ public const int KernelVersionMinor = 4;
+ public const int KernelVersionRevision = 0;
+
+ public const int KernelVersionPacked =
+ (KernelVersionMajor << 19) |
+ (KernelVersionMinor << 15) |
+ (KernelVersionRevision << 0);
+
+ public KMemoryManager MemoryManager { get; private set; }
+
+ private SortedDictionary<ulong, KTlsPageInfo> FullTlsPages;
+ private SortedDictionary<ulong, KTlsPageInfo> FreeTlsPages;
+
+ public int DefaultCpuCore { get; private set; }
+
+ public bool Debug { get; private set; }
+
+ public KResourceLimit ResourceLimit { get; private set; }
+
+ public ulong PersonalMmHeapPagesCount { get; private set; }
+
+ private ProcessState State;
+
+ private object ProcessLock;
+ private object ThreadingLock;
+
+ public KAddressArbiter AddressArbiter { get; private set; }
+
+ public long[] RandomEntropy { get; private set; }
+
+ private bool Signaled;
+ private bool UseSystemMemBlocks;
+
+ public string Name { get; private set; }
+
+ private int ThreadCount;
+
+ public int MmuFlags { get; private set; }
+
+ private MemoryRegion MemRegion;
+
+ public KProcessCapabilities Capabilities { get; private set; }
+
+ public long TitleId { get; private set; }
+ public long Pid { get; private set; }
+
+ private long CreationTimestamp;
+ private ulong Entrypoint;
+ private ulong ImageSize;
+ private ulong MainThreadStackSize;
+ private ulong MemoryUsageCapacity;
+ private int Category;
+
+ public KHandleTable HandleTable { get; private set; }
+
+ public ulong UserExceptionContextAddress { get; private set; }
+
+ private LinkedList<KThread> Threads;
+
+ public bool IsPaused { get; private set; }
+
+ public Translator Translator { get; private set; }
+
+ public MemoryManager CpuMemory { get; private set; }
+
+ private SvcHandler SvcHandler;
+
+ public HleProcessDebugger Debugger { get; private set; }
+
+ public KProcess(Horizon System) : base(System)
+ {
+ ProcessLock = new object();
+ ThreadingLock = new object();
+
+ CpuMemory = new MemoryManager(System.Device.Memory.RamPointer);
+
+ CpuMemory.InvalidAccess += InvalidAccessHandler;
+
+ AddressArbiter = new KAddressArbiter(System);
+
+ MemoryManager = new KMemoryManager(System, CpuMemory);
+
+ FullTlsPages = new SortedDictionary<ulong, KTlsPageInfo>();
+ FreeTlsPages = new SortedDictionary<ulong, KTlsPageInfo>();
+
+ Capabilities = new KProcessCapabilities();
+
+ RandomEntropy = new long[KScheduler.CpuCoresCount];
+
+ Threads = new LinkedList<KThread>();
+
+ Translator = new Translator();
+
+ Translator.CpuTrace += CpuTraceHandler;
+
+ SvcHandler = new SvcHandler(System.Device, this);
+
+ Debugger = new HleProcessDebugger(this);
+ }
+
+ public KernelResult InitializeKip(
+ ProcessCreationInfo CreationInfo,
+ int[] Caps,
+ KPageList PageList,
+ KResourceLimit ResourceLimit,
+ MemoryRegion MemRegion)
+ {
+ this.ResourceLimit = ResourceLimit;
+ this.MemRegion = MemRegion;
+
+ AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7);
+
+ bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0;
+
+ ulong CodeAddress = CreationInfo.CodeAddress;
+
+ ulong CodeSize = (ulong)CreationInfo.CodePagesCount * KMemoryManager.PageSize;
+
+ KMemoryBlockAllocator MemoryBlockAllocator = (MmuFlags & 0x40) != 0
+ ? System.LargeMemoryBlockAllocator
+ : System.SmallMemoryBlockAllocator;
+
+ KernelResult Result = MemoryManager.InitializeForProcess(
+ AddrSpaceType,
+ AslrEnabled,
+ !AslrEnabled,
+ MemRegion,
+ CodeAddress,
+ CodeSize,
+ MemoryBlockAllocator);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ if (!ValidateCodeAddressAndSize(CodeAddress, CodeSize))
+ {
+ return KernelResult.InvalidMemRange;
+ }
+
+ Result = MemoryManager.MapPages(
+ CodeAddress,
+ PageList,
+ MemoryState.CodeStatic,
+ MemoryPermission.None);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ Result = Capabilities.InitializeForKernel(Caps, MemoryManager);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ Pid = System.GetKipId();
+
+ if (Pid == 0 || (ulong)Pid >= Horizon.InitialProcessId)
+ {
+ throw new InvalidOperationException($"Invalid KIP Id {Pid}.");
+ }
+
+ Result = ParseProcessInfo(CreationInfo);
+
+ return Result;
+ }
+
+ public KernelResult Initialize(
+ ProcessCreationInfo CreationInfo,
+ int[] Caps,
+ KResourceLimit ResourceLimit,
+ MemoryRegion MemRegion)
+ {
+ this.ResourceLimit = ResourceLimit;
+ this.MemRegion = MemRegion;
+
+ ulong PersonalMmHeapSize = GetPersonalMmHeapSize((ulong)CreationInfo.PersonalMmHeapPagesCount, MemRegion);
+
+ ulong CodePagesCount = (ulong)CreationInfo.CodePagesCount;
+
+ ulong NeededSizeForProcess = PersonalMmHeapSize + CodePagesCount * KMemoryManager.PageSize;
+
+ if (NeededSizeForProcess != 0 && ResourceLimit != null)
+ {
+ if (!ResourceLimit.Reserve(LimitableResource.Memory, NeededSizeForProcess))
+ {
+ return KernelResult.ResLimitExceeded;
+ }
+ }
+
+ void CleanUpForError()
+ {
+ if (NeededSizeForProcess != 0 && ResourceLimit != null)
+ {
+ ResourceLimit.Release(LimitableResource.Memory, NeededSizeForProcess);
+ }
+ }
+
+ PersonalMmHeapPagesCount = (ulong)CreationInfo.PersonalMmHeapPagesCount;
+
+ KMemoryBlockAllocator MemoryBlockAllocator;
+
+ if (PersonalMmHeapPagesCount != 0)
+ {
+ MemoryBlockAllocator = new KMemoryBlockAllocator(PersonalMmHeapPagesCount * KMemoryManager.PageSize);
+ }
+ else
+ {
+ MemoryBlockAllocator = (MmuFlags & 0x40) != 0
+ ? System.LargeMemoryBlockAllocator
+ : System.SmallMemoryBlockAllocator;
+ }
+
+ AddressSpaceType AddrSpaceType = (AddressSpaceType)((CreationInfo.MmuFlags >> 1) & 7);
+
+ bool AslrEnabled = ((CreationInfo.MmuFlags >> 5) & 1) != 0;
+
+ ulong CodeAddress = CreationInfo.CodeAddress;
+
+ ulong CodeSize = CodePagesCount * KMemoryManager.PageSize;
+
+ KernelResult Result = MemoryManager.InitializeForProcess(
+ AddrSpaceType,
+ AslrEnabled,
+ !AslrEnabled,
+ MemRegion,
+ CodeAddress,
+ CodeSize,
+ MemoryBlockAllocator);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ if (!ValidateCodeAddressAndSize(CodeAddress, CodeSize))
+ {
+ CleanUpForError();
+
+ return KernelResult.InvalidMemRange;
+ }
+
+ Result = MemoryManager.MapNewProcessCode(
+ CodeAddress,
+ CodePagesCount,
+ MemoryState.CodeStatic,
+ MemoryPermission.None);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ Result = Capabilities.InitializeForUser(Caps, MemoryManager);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ Pid = System.GetProcessId();
+
+ if (Pid == -1 || (ulong)Pid < Horizon.InitialProcessId)
+ {
+ throw new InvalidOperationException($"Invalid Process Id {Pid}.");
+ }
+
+ Result = ParseProcessInfo(CreationInfo);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+ }
+
+ return Result;
+ }
+
+ private bool ValidateCodeAddressAndSize(ulong Address, ulong Size)
+ {
+ ulong CodeRegionStart;
+ ulong CodeRegionSize;
+
+ switch (MemoryManager.AddrSpaceWidth)
+ {
+ case 32:
+ CodeRegionStart = 0x200000;
+ CodeRegionSize = 0x3fe00000;
+ break;
+
+ case 36:
+ CodeRegionStart = 0x8000000;
+ CodeRegionSize = 0x78000000;
+ break;
+
+ case 39:
+ CodeRegionStart = 0x8000000;
+ CodeRegionSize = 0x7ff8000000;
+ break;
+
+ default: throw new InvalidOperationException("Invalid address space width on memory manager.");
+ }
+
+ ulong EndAddr = Address + Size;
+
+ ulong CodeRegionEnd = CodeRegionStart + CodeRegionSize;
+
+ if (EndAddr <= Address ||
+ EndAddr - 1 > CodeRegionEnd - 1)
+ {
+ return false;
+ }
+
+ if (MemoryManager.InsideHeapRegion (Address, Size) ||
+ MemoryManager.InsideAliasRegion(Address, Size))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private KernelResult ParseProcessInfo(ProcessCreationInfo CreationInfo)
+ {
+ //Ensure that the current kernel version is equal or above to the minimum required.
+ uint RequiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19;
+ uint RequiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf;
+
+ if (System.EnableVersionChecks)
+ {
+ if (RequiredKernelVersionMajor > KernelVersionMajor)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ if (RequiredKernelVersionMajor != KernelVersionMajor && RequiredKernelVersionMajor < 3)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ if (RequiredKernelVersionMinor > KernelVersionMinor)
+ {
+ return KernelResult.InvalidCombination;
+ }
+ }
+
+ KernelResult Result = AllocateThreadLocalStorage(out ulong UserExceptionContextAddress);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ this.UserExceptionContextAddress = UserExceptionContextAddress;
+
+ MemoryHelper.FillWithZeros(CpuMemory, (long)UserExceptionContextAddress, KTlsPageInfo.TlsEntrySize);
+
+ Name = CreationInfo.Name;
+
+ State = ProcessState.Created;
+
+ CreationTimestamp = PerformanceCounter.ElapsedMilliseconds;
+
+ MmuFlags = CreationInfo.MmuFlags;
+ Category = CreationInfo.Category;
+ TitleId = CreationInfo.TitleId;
+ Entrypoint = CreationInfo.CodeAddress;
+ ImageSize = (ulong)CreationInfo.CodePagesCount * KMemoryManager.PageSize;
+
+ UseSystemMemBlocks = ((MmuFlags >> 6) & 1) != 0;
+
+ switch ((AddressSpaceType)((MmuFlags >> 1) & 7))
+ {
+ case AddressSpaceType.Addr32Bits:
+ case AddressSpaceType.Addr36Bits:
+ case AddressSpaceType.Addr39Bits:
+ MemoryUsageCapacity = MemoryManager.HeapRegionEnd -
+ MemoryManager.HeapRegionStart;
+ break;
+
+ case AddressSpaceType.Addr32BitsNoMap:
+ MemoryUsageCapacity = MemoryManager.HeapRegionEnd -
+ MemoryManager.HeapRegionStart +
+ MemoryManager.AliasRegionEnd -
+ MemoryManager.AliasRegionStart;
+ break;
+
+ default: throw new InvalidOperationException($"Invalid MMU flags value 0x{MmuFlags:x2}.");
+ }
+
+ GenerateRandomEntropy();
+
+ return KernelResult.Success;
+ }
+
+ public KernelResult AllocateThreadLocalStorage(out ulong Address)
+ {
+ System.CriticalSection.Enter();
+
+ KernelResult Result;
+
+ if (FreeTlsPages.Count > 0)
+ {
+ //If we have free TLS pages available, just use the first one.
+ KTlsPageInfo PageInfo = FreeTlsPages.Values.First();
+
+ if (!PageInfo.TryGetFreePage(out Address))
+ {
+ throw new InvalidOperationException("Unexpected failure getting free TLS page!");
+ }
+
+ if (PageInfo.IsFull())
+ {
+ FreeTlsPages.Remove(PageInfo.PageAddr);
+
+ FullTlsPages.Add(PageInfo.PageAddr, PageInfo);
+ }
+
+ Result = KernelResult.Success;
+ }
+ else
+ {
+ //Otherwise, we need to create a new one.
+ Result = AllocateTlsPage(out KTlsPageInfo PageInfo);
+
+ if (Result == KernelResult.Success)
+ {
+ if (!PageInfo.TryGetFreePage(out Address))
+ {
+ throw new InvalidOperationException("Unexpected failure getting free TLS page!");
+ }
+
+ FreeTlsPages.Add(PageInfo.PageAddr, PageInfo);
+ }
+ else
+ {
+ Address = 0;
+ }
+ }
+
+ System.CriticalSection.Leave();
+
+ return Result;
+ }
+
+ private KernelResult AllocateTlsPage(out KTlsPageInfo PageInfo)
+ {
+ PageInfo = default(KTlsPageInfo);
+
+ if (!System.UserSlabHeapPages.TryGetItem(out ulong TlsPagePa))
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ ulong RegionStart = MemoryManager.TlsIoRegionStart;
+ ulong RegionSize = MemoryManager.TlsIoRegionEnd - RegionStart;
+
+ ulong RegionPagesCount = RegionSize / KMemoryManager.PageSize;
+
+ KernelResult Result = MemoryManager.AllocateOrMapPa(
+ 1,
+ KMemoryManager.PageSize,
+ TlsPagePa,
+ true,
+ RegionStart,
+ RegionPagesCount,
+ MemoryState.ThreadLocal,
+ MemoryPermission.ReadAndWrite,
+ out ulong TlsPageVa);
+
+ if (Result != KernelResult.Success)
+ {
+ System.UserSlabHeapPages.Free(TlsPagePa);
+ }
+ else
+ {
+ PageInfo = new KTlsPageInfo(TlsPageVa);
+
+ MemoryHelper.FillWithZeros(CpuMemory, (long)TlsPageVa, KMemoryManager.PageSize);
+ }
+
+ return Result;
+ }
+
+ public KernelResult FreeThreadLocalStorage(ulong TlsSlotAddr)
+ {
+ ulong TlsPageAddr = BitUtils.AlignDown(TlsSlotAddr, KMemoryManager.PageSize);
+
+ System.CriticalSection.Enter();
+
+ KernelResult Result = KernelResult.Success;
+
+ KTlsPageInfo PageInfo = null;
+
+ if (FullTlsPages.TryGetValue(TlsPageAddr, out PageInfo))
+ {
+ //TLS page was full, free slot and move to free pages tree.
+ FullTlsPages.Remove(TlsPageAddr);
+
+ FreeTlsPages.Add(TlsPageAddr, PageInfo);
+ }
+ else if (!FreeTlsPages.TryGetValue(TlsPageAddr, out PageInfo))
+ {
+ Result = KernelResult.InvalidAddress;
+ }
+
+ if (PageInfo != null)
+ {
+ PageInfo.FreeTlsSlot(TlsSlotAddr);
+
+ if (PageInfo.IsEmpty())
+ {
+ //TLS page is now empty, we should ensure it is removed
+ //from all trees, and free the memory it was using.
+ FreeTlsPages.Remove(TlsPageAddr);
+
+ System.CriticalSection.Leave();
+
+ FreeTlsPage(PageInfo);
+
+ return KernelResult.Success;
+ }
+ }
+
+ System.CriticalSection.Leave();
+
+ return Result;
+ }
+
+ private KernelResult FreeTlsPage(KTlsPageInfo PageInfo)
+ {
+ KernelResult Result = MemoryManager.ConvertVaToPa(PageInfo.PageAddr, out ulong TlsPagePa);
+
+ if (Result != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Unexpected failure translating virtual address to physical.");
+ }
+
+ Result = MemoryManager.UnmapForKernel(PageInfo.PageAddr, 1, MemoryState.ThreadLocal);
+
+ if (Result == KernelResult.Success)
+ {
+ System.UserSlabHeapPages.Free(TlsPagePa);
+ }
+
+ return Result;
+ }
+
+ private void GenerateRandomEntropy()
+ {
+ //TODO.
+ }
+
+ public KernelResult Start(int MainThreadPriority, ulong StackSize)
+ {
+ lock (ProcessLock)
+ {
+ if (State > ProcessState.CreatedAttached)
+ {
+ return KernelResult.InvalidState;
+ }
+
+ if (ResourceLimit != null && !ResourceLimit.Reserve(LimitableResource.Thread, 1))
+ {
+ return KernelResult.ResLimitExceeded;
+ }
+
+ KResourceLimit ThreadResourceLimit = ResourceLimit;
+ KResourceLimit MemoryResourceLimit = null;
+
+ if (MainThreadStackSize != 0)
+ {
+ throw new InvalidOperationException("Trying to start a process with a invalid state!");
+ }
+
+ ulong StackSizeRounded = BitUtils.AlignUp(StackSize, KMemoryManager.PageSize);
+
+ ulong NeededSize = StackSizeRounded + ImageSize;
+
+ //Check if the needed size for the code and the stack will fit on the
+ //memory usage capacity of this Process. Also check for possible overflow
+ //on the above addition.
+ if (NeededSize > MemoryUsageCapacity ||
+ NeededSize < StackSizeRounded)
+ {
+ ThreadResourceLimit?.Release(LimitableResource.Thread, 1);
+
+ return KernelResult.OutOfMemory;
+ }
+
+ if (StackSizeRounded != 0 && ResourceLimit != null)
+ {
+ MemoryResourceLimit = ResourceLimit;
+
+ if (!MemoryResourceLimit.Reserve(LimitableResource.Memory, StackSizeRounded))
+ {
+ ThreadResourceLimit?.Release(LimitableResource.Thread, 1);
+
+ return KernelResult.ResLimitExceeded;
+ }
+ }
+
+ KernelResult Result;
+
+ KThread MainThread = null;
+
+ ulong StackTop = 0;
+
+ void CleanUpForError()
+ {
+ MainThread?.Terminate();
+ HandleTable.Destroy();
+
+ if (MainThreadStackSize != 0)
+ {
+ ulong StackBottom = StackTop - MainThreadStackSize;
+
+ ulong StackPagesCount = MainThreadStackSize / KMemoryManager.PageSize;
+
+ MemoryManager.UnmapForKernel(StackBottom, StackPagesCount, MemoryState.Stack);
+ }
+
+ MemoryResourceLimit?.Release(LimitableResource.Memory, StackSizeRounded);
+ ThreadResourceLimit?.Release(LimitableResource.Thread, 1);
+ }
+
+ if (StackSizeRounded != 0)
+ {
+ ulong StackPagesCount = StackSizeRounded / KMemoryManager.PageSize;
+
+ ulong RegionStart = MemoryManager.StackRegionStart;
+ ulong RegionSize = MemoryManager.StackRegionEnd - RegionStart;
+
+ ulong RegionPagesCount = RegionSize / KMemoryManager.PageSize;
+
+ Result = MemoryManager.AllocateOrMapPa(
+ StackPagesCount,
+ KMemoryManager.PageSize,
+ 0,
+ false,
+ RegionStart,
+ RegionPagesCount,
+ MemoryState.Stack,
+ MemoryPermission.ReadAndWrite,
+ out ulong StackBottom);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ MainThreadStackSize += StackSizeRounded;
+
+ StackTop = StackBottom + StackSizeRounded;
+ }
+
+ ulong HeapCapacity = MemoryUsageCapacity - MainThreadStackSize - ImageSize;
+
+ Result = MemoryManager.SetHeapCapacity(HeapCapacity);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ HandleTable = new KHandleTable(System);
+
+ Result = HandleTable.Initialize(Capabilities.HandleTableSize);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ MainThread = new KThread(System);
+
+ Result = MainThread.Initialize(
+ Entrypoint,
+ 0,
+ StackTop,
+ MainThreadPriority,
+ DefaultCpuCore,
+ this);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ Result = HandleTable.GenerateHandle(MainThread, out int MainThreadHandle);
+
+ if (Result != KernelResult.Success)
+ {
+ CleanUpForError();
+
+ return Result;
+ }
+
+ MainThread.SetEntryArguments(0, MainThreadHandle);
+
+ ProcessState OldState = State;
+ ProcessState NewState = State != ProcessState.Created
+ ? ProcessState.Attached
+ : ProcessState.Started;
+
+ SetState(NewState);
+
+ //TODO: We can't call KThread.Start from a non-guest thread.
+ //We will need to make some changes to allow the creation of
+ //dummy threads that will be used to initialize the current
+ //thread on KCoreContext so that GetCurrentThread doesn't fail.
+ /* Result = MainThread.Start();
+
+ if (Result != KernelResult.Success)
+ {
+ SetState(OldState);
+
+ CleanUpForError();
+ } */
+
+ MainThread.Reschedule(ThreadSchedState.Running);
+
+ return Result;
+ }
+ }
+
+ private void SetState(ProcessState NewState)
+ {
+ if (State != NewState)
+ {
+ State = NewState;
+ Signaled = true;
+
+ Signal();
+ }
+ }
+
+ public KernelResult InitializeThread(
+ KThread Thread,
+ ulong Entrypoint,
+ ulong ArgsPtr,
+ ulong StackTop,
+ int Priority,
+ int CpuCore)
+ {
+ lock (ProcessLock)
+ {
+ return Thread.Initialize(Entrypoint, ArgsPtr, StackTop, Priority, CpuCore, this);
+ }
+ }
+
+ public void SubscribeThreadEventHandlers(CpuThread Context)
+ {
+ Context.ThreadState.Interrupt += InterruptHandler;
+ Context.ThreadState.SvcCall += SvcHandler.SvcCall;
+ }
+
+ private void InterruptHandler(object sender, EventArgs e)
+ {
+ System.Scheduler.ContextSwitch();
+ }
+
+ public void IncrementThreadCount()
+ {
+ Interlocked.Increment(ref ThreadCount);
+
+ System.ThreadCounter.AddCount();
+ }
+
+ public void DecrementThreadCountAndTerminateIfZero()
+ {
+ System.ThreadCounter.Signal();
+
+ if (Interlocked.Decrement(ref ThreadCount) == 0)
+ {
+ Terminate();
+ }
+ }
+
+ public ulong GetMemoryCapacity()
+ {
+ ulong TotalCapacity = (ulong)ResourceLimit.GetRemainingValue(LimitableResource.Memory);
+
+ TotalCapacity += MemoryManager.GetTotalHeapSize();
+
+ TotalCapacity += GetPersonalMmHeapSize();
+
+ TotalCapacity += ImageSize + MainThreadStackSize;
+
+ if (TotalCapacity <= MemoryUsageCapacity)
+ {
+ return TotalCapacity;
+ }
+
+ return MemoryUsageCapacity;
+ }
+
+ public ulong GetMemoryUsage()
+ {
+ return ImageSize + MainThreadStackSize + MemoryManager.GetTotalHeapSize() + GetPersonalMmHeapSize();
+ }
+
+ public ulong GetMemoryCapacityWithoutPersonalMmHeap()
+ {
+ return GetMemoryCapacity() - GetPersonalMmHeapSize();
+ }
+
+ public ulong GetMemoryUsageWithoutPersonalMmHeap()
+ {
+ return GetMemoryUsage() - GetPersonalMmHeapSize();
+ }
+
+ private ulong GetPersonalMmHeapSize()
+ {
+ return GetPersonalMmHeapSize(PersonalMmHeapPagesCount, MemRegion);
+ }
+
+ private static ulong GetPersonalMmHeapSize(ulong PersonalMmHeapPagesCount, MemoryRegion MemRegion)
+ {
+ if (MemRegion == MemoryRegion.Applet)
+ {
+ return 0;
+ }
+
+ return PersonalMmHeapPagesCount * KMemoryManager.PageSize;
+ }
+
+ public void AddThread(KThread Thread)
+ {
+ lock (ThreadingLock)
+ {
+ Thread.ProcessListNode = Threads.AddLast(Thread);
+ }
+ }
+
+ public void RemoveThread(KThread Thread)
+ {
+ lock (ThreadingLock)
+ {
+ Threads.Remove(Thread.ProcessListNode);
+ }
+ }
+
+ public bool IsCpuCoreAllowed(int Core)
+ {
+ return (Capabilities.AllowedCpuCoresMask & (1L << Core)) != 0;
+ }
+
+ public bool IsPriorityAllowed(int Priority)
+ {
+ return (Capabilities.AllowedThreadPriosMask & (1L << Priority)) != 0;
+ }
+
+ public override bool IsSignaled()
+ {
+ return Signaled;
+ }
+
+ public KernelResult Terminate()
+ {
+ KernelResult Result;
+
+ bool ShallTerminate = false;
+
+ System.CriticalSection.Enter();
+
+ lock (ProcessLock)
+ {
+ if (State >= ProcessState.Started)
+ {
+ if (State == ProcessState.Started ||
+ State == ProcessState.Crashed ||
+ State == ProcessState.Attached ||
+ State == ProcessState.DebugSuspended)
+ {
+ SetState(ProcessState.Exiting);
+
+ ShallTerminate = true;
+ }
+
+ Result = KernelResult.Success;
+ }
+ else
+ {
+ Result = KernelResult.InvalidState;
+ }
+ }
+
+ System.CriticalSection.Leave();
+
+ if (ShallTerminate)
+ {
+ //UnpauseAndTerminateAllThreadsExcept(System.Scheduler.GetCurrentThread());
+
+ HandleTable.Destroy();
+
+ SignalExitForDebugEvent();
+ SignalExit();
+ }
+
+ return Result;
+ }
+
+ private void UnpauseAndTerminateAllThreadsExcept(KThread Thread)
+ {
+ //TODO.
+ }
+
+ private void SignalExitForDebugEvent()
+ {
+ //TODO: Debug events.
+ }
+
+ private void SignalExit()
+ {
+ if (ResourceLimit != null)
+ {
+ ResourceLimit.Release(LimitableResource.Memory, GetMemoryUsage());
+ }
+
+ System.CriticalSection.Enter();
+
+ SetState(ProcessState.Exited);
+
+ System.CriticalSection.Leave();
+ }
+
+ public KernelResult ClearIfNotExited()
+ {
+ KernelResult Result;
+
+ System.CriticalSection.Enter();
+
+ lock (ProcessLock)
+ {
+ if (State != ProcessState.Exited && Signaled)
+ {
+ Signaled = false;
+
+ Result = KernelResult.Success;
+ }
+ else
+ {
+ Result = KernelResult.InvalidState;
+ }
+ }
+
+ System.CriticalSection.Leave();
+
+ return Result;
+ }
+
+ public void StopAllThreads()
+ {
+ lock (ThreadingLock)
+ {
+ foreach (KThread Thread in Threads)
+ {
+ Thread.Context.StopExecution();
+
+ System.Scheduler.CoreManager.Set(Thread.Context.Work);
+ }
+ }
+ }
+
+ private void InvalidAccessHandler(object sender, InvalidAccessEventArgs e)
+ {
+ PrintCurrentThreadStackTrace();
+ }
+
+ public void PrintCurrentThreadStackTrace()
+ {
+ System.Scheduler.GetCurrentThread().PrintGuestStackTrace();
+ }
+
+ private void CpuTraceHandler(object sender, CpuTraceEventArgs e)
+ {
+ Logger.PrintInfo(LogClass.Cpu, $"Executing at 0x{e.Position:X16}.");
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs b/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs
new file mode 100644
index 00000000..dfbe1f36
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs
@@ -0,0 +1,311 @@
+using Ryujinx.Common;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KProcessCapabilities
+ {
+ public byte[] SvcAccessMask { get; private set; }
+ public byte[] IrqAccessMask { get; private set; }
+
+ public long AllowedCpuCoresMask { get; private set; }
+ public long AllowedThreadPriosMask { get; private set; }
+
+ public int DebuggingFlags { get; private set; }
+ public int HandleTableSize { get; private set; }
+ public int KernelReleaseVersion { get; private set; }
+ public int ApplicationType { get; private set; }
+
+ public KProcessCapabilities()
+ {
+ SvcAccessMask = new byte[0x10];
+ IrqAccessMask = new byte[0x80];
+ }
+
+ public KernelResult InitializeForKernel(int[] Caps, KMemoryManager MemoryManager)
+ {
+ AllowedCpuCoresMask = 0xf;
+ AllowedThreadPriosMask = -1;
+ DebuggingFlags &= ~3;
+ KernelReleaseVersion = KProcess.KernelVersionPacked;
+
+ return Parse(Caps, MemoryManager);
+ }
+
+ public KernelResult InitializeForUser(int[] Caps, KMemoryManager MemoryManager)
+ {
+ return Parse(Caps, MemoryManager);
+ }
+
+ private KernelResult Parse(int[] Caps, KMemoryManager MemoryManager)
+ {
+ int Mask0 = 0;
+ int Mask1 = 0;
+
+ for (int Index = 0; Index < Caps.Length; Index++)
+ {
+ int Cap = Caps[Index];
+
+ if (((Cap + 1) & ~Cap) != 0x40)
+ {
+ KernelResult Result = ParseCapability(Cap, ref Mask0, ref Mask1, MemoryManager);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+ }
+ else
+ {
+ if ((uint)Index + 1 >= Caps.Length)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ int PrevCap = Cap;
+
+ Cap = Caps[++Index];
+
+ if (((Cap + 1) & ~Cap) != 0x40)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ if ((Cap & 0x78000000) != 0)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ if ((Cap & 0x7ffff80) == 0)
+ {
+ return KernelResult.InvalidSize;
+ }
+
+ long Address = ((long)(uint)PrevCap << 5) & 0xffffff000;
+ long Size = ((long)(uint)Cap << 5) & 0xfffff000;
+
+ if (((ulong)(Address + Size - 1) >> 36) != 0)
+ {
+ return KernelResult.InvalidAddress;
+ }
+
+ MemoryPermission Perm = (PrevCap >> 31) != 0
+ ? MemoryPermission.Read
+ : MemoryPermission.ReadAndWrite;
+
+ KernelResult Result;
+
+ if ((Cap >> 31) != 0)
+ {
+ Result = MemoryManager.MapNormalMemory(Address, Size, Perm);
+ }
+ else
+ {
+ Result = MemoryManager.MapIoMemory(Address, Size, Perm);
+ }
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+ }
+ }
+
+ return KernelResult.Success;
+ }
+
+ private KernelResult ParseCapability(int Cap, ref int Mask0, ref int Mask1, KMemoryManager MemoryManager)
+ {
+ int Code = (Cap + 1) & ~Cap;
+
+ if (Code == 1)
+ {
+ return KernelResult.InvalidCapability;
+ }
+ else if (Code == 0)
+ {
+ return KernelResult.Success;
+ }
+
+ int CodeMask = 1 << (32 - BitUtils.CountLeadingZeros32(Code + 1));
+
+ //Check if the property was already set.
+ if (((Mask0 & CodeMask) & 0x1e008) != 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ Mask0 |= CodeMask;
+
+ switch (Code)
+ {
+ case 8:
+ {
+ if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0)
+ {
+ return KernelResult.InvalidCapability;
+ }
+
+ int LowestCpuCore = (Cap >> 16) & 0xff;
+ int HighestCpuCore = (Cap >> 24) & 0xff;
+
+ if (LowestCpuCore > HighestCpuCore)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ int HighestThreadPrio = (Cap >> 4) & 0x3f;
+ int LowestThreadPrio = (Cap >> 10) & 0x3f;
+
+ if (LowestThreadPrio > HighestThreadPrio)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ if (HighestCpuCore >= KScheduler.CpuCoresCount)
+ {
+ return KernelResult.InvalidCpuCore;
+ }
+
+ AllowedCpuCoresMask = GetMaskFromMinMax(LowestCpuCore, HighestCpuCore);
+ AllowedThreadPriosMask = GetMaskFromMinMax(LowestThreadPrio, HighestThreadPrio);
+
+ break;
+ }
+
+ case 0x10:
+ {
+ int Slot = (Cap >> 29) & 7;
+
+ int SvcSlotMask = 1 << Slot;
+
+ if ((Mask1 & SvcSlotMask) != 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ Mask1 |= SvcSlotMask;
+
+ int SvcMask = (Cap >> 5) & 0xffffff;
+
+ int BaseSvc = Slot * 24;
+
+ for (int Index = 0; Index < 24; Index++)
+ {
+ if (((SvcMask >> Index) & 1) == 0)
+ {
+ continue;
+ }
+
+ int SvcId = BaseSvc + Index;
+
+ if (SvcId > 0x7f)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ SvcAccessMask[SvcId / 8] |= (byte)(1 << (SvcId & 7));
+ }
+
+ break;
+ }
+
+ case 0x80:
+ {
+ long Address = ((long)(uint)Cap << 4) & 0xffffff000;
+
+ MemoryManager.MapIoMemory(Address, KMemoryManager.PageSize, MemoryPermission.ReadAndWrite);
+
+ break;
+ }
+
+ case 0x800:
+ {
+ //TODO: GIC distributor check.
+ int Irq0 = (Cap >> 12) & 0x3ff;
+ int Irq1 = (Cap >> 22) & 0x3ff;
+
+ if (Irq0 != 0x3ff)
+ {
+ IrqAccessMask[Irq0 / 8] |= (byte)(1 << (Irq0 & 7));
+ }
+
+ if (Irq1 != 0x3ff)
+ {
+ IrqAccessMask[Irq1 / 8] |= (byte)(1 << (Irq1 & 7));
+ }
+
+ break;
+ }
+
+ case 0x2000:
+ {
+ int ApplicationType = Cap >> 14;
+
+ if ((uint)ApplicationType > 7)
+ {
+ return KernelResult.ReservedValue;
+ }
+
+ this.ApplicationType = ApplicationType;
+
+ break;
+ }
+
+ case 0x4000:
+ {
+ //Note: This check is bugged on kernel too, we are just replicating the bug here.
+ if ((KernelReleaseVersion >> 17) != 0 || Cap < 0x80000)
+ {
+ return KernelResult.ReservedValue;
+ }
+
+ KernelReleaseVersion = Cap;
+
+ break;
+ }
+
+ case 0x8000:
+ {
+ int HandleTableSize = Cap >> 26;
+
+ if ((uint)HandleTableSize > 0x3ff)
+ {
+ return KernelResult.ReservedValue;
+ }
+
+ this.HandleTableSize = HandleTableSize;
+
+ break;
+ }
+
+ case 0x10000:
+ {
+ int DebuggingFlags = Cap >> 19;
+
+ if ((uint)DebuggingFlags > 3)
+ {
+ return KernelResult.ReservedValue;
+ }
+
+ this.DebuggingFlags &= ~3;
+ this.DebuggingFlags |= DebuggingFlags;
+
+ break;
+ }
+
+ default: return KernelResult.InvalidCapability;
+ }
+
+ return KernelResult.Success;
+ }
+
+ private static long GetMaskFromMinMax(int Min, int Max)
+ {
+ int Range = Max - Min + 1;
+
+ long Mask = (1L << Range) - 1;
+
+ return Mask << Min;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs b/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs
index d43fe824..bfb8e7e2 100644
--- a/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Kernel
public override void Signal()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (!Signaled)
{
@@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Kernel
base.Signal();
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public KernelResult Clear()
@@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
KernelResult Result;
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (Signaled)
{
@@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Kernel
Result = KernelResult.InvalidState;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return Result;
}
diff --git a/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs b/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs
new file mode 100644
index 00000000..6fd70d0c
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs
@@ -0,0 +1,146 @@
+using Ryujinx.Common;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KResourceLimit
+ {
+ private const int Time10SecondsMs = 10000;
+
+ private long[] Current;
+ private long[] Limit;
+ private long[] Available;
+
+ private object LockObj;
+
+ private LinkedList<KThread> WaitingThreads;
+
+ private int WaitingThreadsCount;
+
+ private Horizon System;
+
+ public KResourceLimit(Horizon System)
+ {
+ Current = new long[(int)LimitableResource.Count];
+ Limit = new long[(int)LimitableResource.Count];
+ Available = new long[(int)LimitableResource.Count];
+
+ LockObj = new object();
+
+ WaitingThreads = new LinkedList<KThread>();
+
+ this.System = System;
+ }
+
+ public bool Reserve(LimitableResource Resource, ulong Amount)
+ {
+ return Reserve(Resource, (long)Amount);
+ }
+
+ public bool Reserve(LimitableResource Resource, long Amount)
+ {
+ return Reserve(Resource, Amount, KTimeManager.ConvertMillisecondsToNanoseconds(Time10SecondsMs));
+ }
+
+ public bool Reserve(LimitableResource Resource, long Amount, long Timeout)
+ {
+ long EndTimePoint = KTimeManager.ConvertNanosecondsToMilliseconds(Timeout);
+
+ EndTimePoint += PerformanceCounter.ElapsedMilliseconds;
+
+ bool Success = false;
+
+ int Index = GetIndex(Resource);
+
+ lock (LockObj)
+ {
+ long NewCurrent = Current[Index] + Amount;
+
+ while (NewCurrent > Limit[Index] && Available[Index] + Amount <= Limit[Index])
+ {
+ WaitingThreadsCount++;
+
+ KConditionVariable.Wait(System, WaitingThreads, LockObj, Timeout);
+
+ WaitingThreadsCount--;
+
+ NewCurrent = Current[Index] + Amount;
+
+ if (Timeout >= 0 && PerformanceCounter.ElapsedMilliseconds > EndTimePoint)
+ {
+ break;
+ }
+ }
+
+ if (NewCurrent <= Limit[Index])
+ {
+ Current[Index] = NewCurrent;
+
+ Success = true;
+ }
+ }
+
+ return Success;
+ }
+
+ public void Release(LimitableResource Resource, ulong Amount)
+ {
+ Release(Resource, (long)Amount);
+ }
+
+ public void Release(LimitableResource Resource, long Amount)
+ {
+ Release(Resource, Amount, Amount);
+ }
+
+ private void Release(LimitableResource Resource, long UsedAmount, long AvailableAmount)
+ {
+ int Index = GetIndex(Resource);
+
+ lock (LockObj)
+ {
+ Current [Index] -= UsedAmount;
+ Available[Index] -= AvailableAmount;
+
+ if (WaitingThreadsCount > 0)
+ {
+ KConditionVariable.NotifyAll(System, WaitingThreads);
+ }
+ }
+ }
+
+ public long GetRemainingValue(LimitableResource Resource)
+ {
+ int Index = GetIndex(Resource);
+
+ lock (LockObj)
+ {
+ return Limit[Index] - Current[Index];
+ }
+ }
+
+ public KernelResult SetLimitValue(LimitableResource Resource, long Limit)
+ {
+ int Index = GetIndex(Resource);
+
+ lock (LockObj)
+ {
+ if (Current[Index] <= Limit)
+ {
+ this.Limit[Index] = Limit;
+
+ return KernelResult.Success;
+ }
+ else
+ {
+ return KernelResult.InvalidState;
+ }
+ }
+ }
+
+ private static int GetIndex(LimitableResource Resource)
+ {
+ return (int)Resource;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/KScheduler.cs
index 3cfda419..3342f4a6 100644
--- a/Ryujinx.HLE/HOS/Kernel/KScheduler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KScheduler.cs
@@ -38,14 +38,14 @@ namespace Ryujinx.HLE.HOS.Kernel
private void PreemptThreads()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
PreemptThread(PreemptionPriorityCores012, 0);
PreemptThread(PreemptionPriorityCores012, 1);
PreemptThread(PreemptionPriorityCores012, 2);
PreemptThread(PreemptionPriorityCore3, 3);
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
private void PreemptThread(int Prio, int Core)
@@ -82,7 +82,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
//If the candidate was scheduled after the current thread, then it's not worth it.
- if (SelectedThread == null || SelectedThread.LastScheduledTicks >= Thread.LastScheduledTicks)
+ if (SelectedThread == null || SelectedThread.LastScheduledTime >= Thread.LastScheduledTime)
{
yield return Thread;
}
@@ -212,6 +212,11 @@ namespace Ryujinx.HLE.HOS.Kernel
throw new InvalidOperationException("Current thread is not scheduled!");
}
+ public KProcess GetCurrentProcess()
+ {
+ return GetCurrentThread().Owner;
+ }
+
public void Dispose()
{
Dispose(true);
diff --git a/Ryujinx.HLE/HOS/Kernel/KServerPort.cs b/Ryujinx.HLE/HOS/Kernel/KServerPort.cs
new file mode 100644
index 00000000..42135cd8
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KServerPort.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KServerPort : KSynchronizationObject
+ {
+ private KPort Parent;
+
+ public KServerPort(Horizon System) : base(System) { }
+
+ public void Initialize(KPort Parent)
+ {
+ this.Parent = Parent;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs b/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs
index cdd31667..a440438b 100644
--- a/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KSharedMemory.cs
@@ -1,14 +1,68 @@
+using Ryujinx.Common;
+
namespace Ryujinx.HLE.HOS.Kernel
{
class KSharedMemory
{
- public long PA { get; private set; }
- public long Size { get; private set; }
+ private KPageList PageList;
+
+ private long OwnerPid;
+
+ private MemoryPermission OwnerPermission;
+ private MemoryPermission UserPermission;
+
+ public KSharedMemory(
+ KPageList PageList,
+ long OwnerPid,
+ MemoryPermission OwnerPermission,
+ MemoryPermission UserPermission)
+ {
+ this.PageList = PageList;
+ this.OwnerPid = OwnerPid;
+ this.OwnerPermission = OwnerPermission;
+ this.UserPermission = UserPermission;
+ }
- public KSharedMemory(long PA, long Size)
+ public KernelResult MapIntoProcess(
+ KMemoryManager MemoryManager,
+ ulong Address,
+ ulong Size,
+ KProcess Process,
+ MemoryPermission Permission)
{
- this.PA = PA;
- this.Size = Size;
+ ulong PagesCountRounded = BitUtils.DivRoundUp(Size, KMemoryManager.PageSize);
+
+ if (PageList.GetPagesCount() != PagesCountRounded)
+ {
+ return KernelResult.InvalidSize;
+ }
+
+ MemoryPermission ExpectedPermission = Process.Pid == OwnerPid
+ ? OwnerPermission
+ : UserPermission;
+
+ if (Permission != ExpectedPermission)
+ {
+ return KernelResult.InvalidPermission;
+ }
+
+ return MemoryManager.MapPages(Address, PageList, MemoryState.SharedMemory, Permission);
+ }
+
+ public KernelResult UnmapFromProcess(
+ KMemoryManager MemoryManager,
+ ulong Address,
+ ulong Size,
+ KProcess Process)
+ {
+ ulong PagesCountRounded = BitUtils.DivRoundUp(Size, KMemoryManager.PageSize);
+
+ if (PageList.GetPagesCount() != PagesCountRounded)
+ {
+ return KernelResult.InvalidSize;
+ }
+
+ return MemoryManager.UnmapPages(Address, PageList, MemoryState.SharedMemory);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs b/Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs
new file mode 100644
index 00000000..2d6b3ca0
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs
@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KSlabHeap
+ {
+ private LinkedList<ulong> Items;
+
+ public KSlabHeap(ulong Pa, ulong ItemSize, ulong Size)
+ {
+ Items = new LinkedList<ulong>();
+
+ int ItemsCount = (int)(Size / ItemSize);
+
+ for (int Index = 0; Index < ItemsCount; Index++)
+ {
+ Items.AddLast(Pa);
+
+ Pa += ItemSize;
+ }
+ }
+
+ public bool TryGetItem(out ulong Pa)
+ {
+ lock (Items)
+ {
+ if (Items.First != null)
+ {
+ Pa = Items.First.Value;
+
+ Items.RemoveFirst();
+
+ return true;
+ }
+ }
+
+ Pa = 0;
+
+ return false;
+ }
+
+ public void Free(ulong Pa)
+ {
+ lock (Items)
+ {
+ Items.AddFirst(Pa);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs
index 57a6296c..19e700f4 100644
--- a/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KSynchronization.cs
@@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
long Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
//Check if objects are already signaled before waiting.
for (int Index = 0; Index < SyncObjs.Length; Index++)
@@ -29,14 +29,14 @@ namespace Ryujinx.HLE.HOS.Kernel
HndIndex = Index;
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return 0;
}
if (Timeout == 0)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return Result;
}
@@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Kernel
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
CurrentThread.WaitingSync = false;
@@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Kernel
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
}
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
Result = (uint)CurrentThread.ObjSyncResult;
@@ -100,14 +100,14 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return Result;
}
public void SignalObject(KSynchronizationObject SyncObj)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (SyncObj.IsSignaled())
{
@@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{
KThread Thread = Node.Value;
- if ((Thread.SchedFlags & ThreadSchedState.LowNibbleMask) == ThreadSchedState.Paused)
+ if ((Thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
{
Thread.SignaledObj = SyncObj;
Thread.ObjSyncResult = 0;
@@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs b/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs
index 28eac330..5ba7784f 100644
--- a/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KSynchronizationObject.cs
@@ -2,16 +2,12 @@ using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel
{
- class KSynchronizationObject
+ class KSynchronizationObject : KAutoObject
{
public LinkedList<KThread> WaitingThreads;
- protected Horizon System;
-
- public KSynchronizationObject(Horizon System)
+ public KSynchronizationObject(Horizon System) : base(System)
{
- this.System = System;
-
WaitingThreads = new LinkedList<KThread>();
}
diff --git a/Ryujinx.HLE/HOS/Kernel/KThread.cs b/Ryujinx.HLE/HOS/Kernel/KThread.cs
index 73ee2322..88f144c8 100644
--- a/Ryujinx.HLE/HOS/Kernel/KThread.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KThread.cs
@@ -1,4 +1,5 @@
using ChocolArm64;
+using ChocolArm64.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -13,20 +14,30 @@ namespace Ryujinx.HLE.HOS.Kernel
public long AffinityMask { get; set; }
- public int ThreadId { get; private set; }
+ public long ThreadUid { get; private set; }
- public KSynchronizationObject SignaledObj;
+ public long TotalTimeRunning { get; set; }
+
+ public KSynchronizationObject SignaledObj { get; set; }
public long CondVarAddress { get; set; }
- public long MutexAddress { get; set; }
- public Process Owner { get; private set; }
+ private ulong Entrypoint;
+
+ public long MutexAddress { get; set; }
+
+ public KProcess Owner { get; private set; }
- public long LastScheduledTicks { get; set; }
+ private ulong TlsAddress;
+
+ public long LastScheduledTime { get; set; }
public LinkedListNode<KThread>[] SiblingsPerCore { get; private set; }
- private LinkedListNode<KThread> WithholderNode;
+ public LinkedList<KThread> Withholder { get; set; }
+ public LinkedListNode<KThread> WithholderNode { get; set; }
+
+ public LinkedListNode<KThread> ProcessListNode { get; set; }
private LinkedList<KThread> MutexWaiters;
private LinkedListNode<KThread> MutexWaiterNode;
@@ -65,38 +76,131 @@ namespace Ryujinx.HLE.HOS.Kernel
public long LastPc { get; set; }
- public KThread(
- CpuThread Thread,
- Process Process,
- Horizon System,
- int ProcessorId,
- int Priority,
- int ThreadId) : base(System)
+ public KThread(Horizon System) : base(System)
{
- this.ThreadId = ThreadId;
-
- Context = Thread;
- Owner = Process;
- PreferredCore = ProcessorId;
Scheduler = System.Scheduler;
SchedulingData = System.Scheduler.SchedulingData;
SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount];
MutexWaiters = new LinkedList<KThread>();
+ }
+
+ public KernelResult Initialize(
+ ulong Entrypoint,
+ ulong ArgsPtr,
+ ulong StackTop,
+ int Priority,
+ int DefaultCpuCore,
+ KProcess Owner,
+ ThreadType Type = ThreadType.User)
+ {
+ if ((uint)Type > 3)
+ {
+ throw new ArgumentException($"Invalid thread type \"{Type}\".");
+ }
- AffinityMask = 1 << ProcessorId;
+ PreferredCore = DefaultCpuCore;
- DynamicPriority = BasePriority = Priority;
+ AffinityMask |= 1L << DefaultCpuCore;
+
+ SchedFlags = Type == ThreadType.Dummy
+ ? ThreadSchedState.Running
+ : ThreadSchedState.None;
CurrentCore = PreferredCore;
+
+ DynamicPriority = Priority;
+ BasePriority = Priority;
+
+ ObjSyncResult = 0x7201;
+
+ this.Entrypoint = Entrypoint;
+
+ if (Type == ThreadType.User)
+ {
+ if (Owner.AllocateThreadLocalStorage(out TlsAddress) != KernelResult.Success)
+ {
+ return KernelResult.OutOfMemory;
+ }
+
+ MemoryHelper.FillWithZeros(Owner.CpuMemory, (long)TlsAddress, KTlsPageInfo.TlsEntrySize);
+ }
+
+ bool Is64Bits;
+
+ if (Owner != null)
+ {
+ this.Owner = Owner;
+
+ Owner.IncrementThreadCount();
+
+ Is64Bits = (Owner.MmuFlags & 1) != 0;
+ }
+ else
+ {
+ Is64Bits = true;
+ }
+
+ Context = new CpuThread(Owner.Translator, Owner.CpuMemory, (long)Entrypoint);
+
+ Context.ThreadState.X0 = ArgsPtr;
+ Context.ThreadState.X31 = StackTop;
+
+ Context.ThreadState.CntfrqEl0 = 19200000;
+ Context.ThreadState.Tpidr = (long)TlsAddress;
+
+ Owner.SubscribeThreadEventHandlers(Context);
+
+ Context.WorkFinished += ThreadFinishedHandler;
+
+ ThreadUid = System.GetThreadUid();
+
+ if (Owner != null)
+ {
+ Owner.AddThread(this);
+
+ if (Owner.IsPaused)
+ {
+ System.CriticalSection.Enter();
+
+ if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
+ {
+ System.CriticalSection.Leave();
+
+ return KernelResult.Success;
+ }
+
+ ForcePauseFlags |= ThreadSchedState.ProcessPauseFlag;
+
+ CombineForcePauseFlags();
+
+ System.CriticalSection.Leave();
+ }
+ }
+
+ return KernelResult.Success;
}
- public long Start()
+ public KernelResult Start()
{
- long Result = MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
+ if (!System.KernelInitialized)
+ {
+ System.CriticalSection.Enter();
+
+ if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
+ {
+ ForcePauseFlags |= ThreadSchedState.KernelInitPauseFlag;
+
+ CombineForcePauseFlags();
+ }
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Leave();
+ }
+
+ KernelResult Result = KernelResult.ThreadTerminating;
+
+ System.CriticalSection.Enter();
if (!ShallBeTerminated)
{
@@ -106,9 +210,9 @@ namespace Ryujinx.HLE.HOS.Kernel
CurrentThread.SchedFlags != ThreadSchedState.TerminationPending &&
!CurrentThread.ShallBeTerminated)
{
- if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.None)
+ if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.None)
{
- Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
+ Result = KernelResult.InvalidState;
break;
}
@@ -122,7 +226,7 @@ namespace Ryujinx.HLE.HOS.Kernel
SetNewSchedFlags(ThreadSchedState.Running);
- Result = 0;
+ Result = KernelResult.Success;
break;
}
@@ -130,8 +234,8 @@ namespace Ryujinx.HLE.HOS.Kernel
{
CurrentThread.CombineForcePauseFlags();
- System.CriticalSectionLock.Unlock();
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Leave();
+ System.CriticalSection.Enter();
if (CurrentThread.ShallBeTerminated)
{
@@ -141,25 +245,25 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return Result;
}
public void Exit()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
- ForcePauseFlags &= ~ThreadSchedState.ExceptionalMask;
+ ForcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
ExitImpl();
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
private void ExitImpl()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
SetNewSchedFlags(ThreadSchedState.TerminationPending);
@@ -167,16 +271,16 @@ namespace Ryujinx.HLE.HOS.Kernel
Signal();
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public long Sleep(long Timeout)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
}
@@ -188,7 +292,7 @@ namespace Ryujinx.HLE.HOS.Kernel
System.TimeManager.ScheduleFutureInvocation(this, Timeout);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
if (Timeout > 0)
{
@@ -200,11 +304,11 @@ namespace Ryujinx.HLE.HOS.Kernel
public void Yield()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (SchedFlags != ThreadSchedState.Running)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
@@ -219,27 +323,27 @@ namespace Ryujinx.HLE.HOS.Kernel
Scheduler.ThreadReselectionRequested = true;
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
}
public void YieldWithLoadBalancing()
{
- System.CriticalSectionLock.Lock();
-
- int Prio = DynamicPriority;
- int Core = CurrentCore;
+ System.CriticalSection.Enter();
if (SchedFlags != ThreadSchedState.Running)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
return;
}
+ int Prio = DynamicPriority;
+ int Core = CurrentCore;
+
KThread NextThreadOnCurrentQueue = null;
if (DynamicPriority < KScheduler.PrioritiesCount)
@@ -270,7 +374,7 @@ namespace Ryujinx.HLE.HOS.Kernel
//If the candidate was scheduled after the current thread, then it's not worth it,
//unless the priority is higher than the current one.
- if (NextThreadOnCurrentQueue.LastScheduledTicks >= Thread.LastScheduledTicks ||
+ if (NextThreadOnCurrentQueue.LastScheduledTime >= Thread.LastScheduledTime ||
NextThreadOnCurrentQueue.DynamicPriority < Thread.DynamicPriority)
{
yield return Thread;
@@ -292,18 +396,18 @@ namespace Ryujinx.HLE.HOS.Kernel
Scheduler.ThreadReselectionRequested = true;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
}
public void YieldAndWaitForLoadBalancing()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (SchedFlags != ThreadSchedState.Running)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
@@ -348,47 +452,47 @@ namespace Ryujinx.HLE.HOS.Kernel
Scheduler.ThreadReselectionRequested = true;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
System.Scheduler.ContextSwitch();
}
public void SetPriority(int Priority)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
BasePriority = Priority;
UpdatePriorityInheritance();
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public long SetActivity(bool Pause)
{
long Result = 0;
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
- ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask;
+ ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowMask;
if (LowNibble != ThreadSchedState.Paused && LowNibble != ThreadSchedState.Running)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
}
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
{
if (Pause)
{
//Pause, the force pause flag should be clear (thread is NOT paused).
- if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) == 0)
+ if ((ForcePauseFlags & ThreadSchedState.ThreadPauseFlag) == 0)
{
- ForcePauseFlags |= ThreadSchedState.ForcePauseFlag;
+ ForcePauseFlags |= ThreadSchedState.ThreadPauseFlag;
CombineForcePauseFlags();
}
@@ -400,17 +504,17 @@ namespace Ryujinx.HLE.HOS.Kernel
else
{
//Unpause, the force pause flag should be set (thread is paused).
- if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) != 0)
+ if ((ForcePauseFlags & ThreadSchedState.ThreadPauseFlag) != 0)
{
ThreadSchedState OldForcePauseFlags = ForcePauseFlags;
- ForcePauseFlags &= ~ThreadSchedState.ForcePauseFlag;
+ ForcePauseFlags &= ~ThreadSchedState.ThreadPauseFlag;
- if ((OldForcePauseFlags & ~ThreadSchedState.ForcePauseFlag) == ThreadSchedState.None)
+ if ((OldForcePauseFlags & ~ThreadSchedState.ThreadPauseFlag) == ThreadSchedState.None)
{
ThreadSchedState OldSchedFlags = SchedFlags;
- SchedFlags &= ThreadSchedState.LowNibbleMask;
+ SchedFlags &= ThreadSchedState.LowMask;
AdjustScheduling(OldSchedFlags);
}
@@ -422,27 +526,27 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
+ System.CriticalSection.Leave();
return Result;
}
public void CancelSynchronization()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
- if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.Paused || !WaitingSync)
+ if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.Paused || !WaitingSync)
{
SyncCancelled = true;
}
- else if (WithholderNode != null)
+ else if (Withholder != null)
{
- System.Withholders.Remove(WithholderNode);
+ Withholder.Remove(WithholderNode);
SetNewSchedFlags(ThreadSchedState.Running);
- WithholderNode = null;
+ Withholder = null;
SyncCancelled = true;
}
@@ -456,12 +560,12 @@ namespace Ryujinx.HLE.HOS.Kernel
SyncCancelled = false;
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
- public long SetCoreAndAffinityMask(int NewCore, long NewAffinityMask)
+ public KernelResult SetCoreAndAffinityMask(int NewCore, long NewAffinityMask)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
bool UseOverride = AffinityOverrideCount != 0;
@@ -472,9 +576,9 @@ namespace Ryujinx.HLE.HOS.Kernel
if ((NewAffinityMask & (1 << NewCore)) == 0)
{
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
- return MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
+ return KernelResult.InvalidCombination;
}
}
@@ -510,9 +614,9 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
- return 0;
+ return KernelResult.Success;
}
private static int HighestSetCore(long Mask)
@@ -531,7 +635,7 @@ namespace Ryujinx.HLE.HOS.Kernel
private void CombineForcePauseFlags()
{
ThreadSchedState OldFlags = SchedFlags;
- ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask;
+ ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowMask;
SchedFlags = LowNibble | ForcePauseFlags;
@@ -540,33 +644,33 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SetNewSchedFlags(ThreadSchedState NewFlags)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
ThreadSchedState OldFlags = SchedFlags;
- SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) | NewFlags;
+ SchedFlags = (OldFlags & ThreadSchedState.HighMask) | NewFlags;
- if ((OldFlags & ThreadSchedState.LowNibbleMask) != NewFlags)
+ if ((OldFlags & ThreadSchedState.LowMask) != NewFlags)
{
AdjustScheduling(OldFlags);
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public void ReleaseAndResume()
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
- if ((SchedFlags & ThreadSchedState.LowNibbleMask) == ThreadSchedState.Paused)
+ if ((SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
{
- if (WithholderNode != null)
+ if (Withholder != null)
{
- System.Withholders.Remove(WithholderNode);
+ Withholder.Remove(WithholderNode);
SetNewSchedFlags(ThreadSchedState.Running);
- WithholderNode = null;
+ Withholder = null;
}
else
{
@@ -574,21 +678,21 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public void Reschedule(ThreadSchedState NewFlags)
{
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
ThreadSchedState OldFlags = SchedFlags;
- SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) |
- (NewFlags & ThreadSchedState.LowNibbleMask);
+ SchedFlags = (OldFlags & ThreadSchedState.HighMask) |
+ (NewFlags & ThreadSchedState.LowMask);
AdjustScheduling(OldFlags);
- System.CriticalSectionLock.Unlock();
+ System.CriticalSection.Leave();
}
public void AddMutexWaiter(KThread Requester)
@@ -866,18 +970,61 @@ namespace Ryujinx.HLE.HOS.Kernel
return HasExited;
}
+ public void SetEntryArguments(long ArgsPtr, int ThreadHandle)
+ {
+ Context.ThreadState.X0 = (ulong)ArgsPtr;
+ Context.ThreadState.X1 = (ulong)ThreadHandle;
+ }
+
public void ClearExclusive()
{
- Owner.Memory.ClearExclusive(CurrentCore);
+ Owner.CpuMemory.ClearExclusive(CurrentCore);
}
public void TimeUp()
{
- System.CriticalSectionLock.Lock();
+ ReleaseAndResume();
+ }
+
+ public void PrintGuestStackTrace()
+ {
+ Owner.Debugger.PrintGuestStackTrace(Context.ThreadState);
+ }
+
+ private void ThreadFinishedHandler(object sender, EventArgs e)
+ {
+ System.Scheduler.ExitThread(this);
+
+ Terminate();
+
+ System.Scheduler.RemoveThread(this);
+ }
+
+ public void Terminate()
+ {
+ Owner?.RemoveThread(this);
+
+ if (TlsAddress != 0 && Owner.FreeThreadLocalStorage(TlsAddress) != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Unexpected failure freeing thread local storage.");
+ }
+
+ System.CriticalSection.Enter();
+
+ //Wake up all threads that may be waiting for a mutex being held
+ //by this thread.
+ foreach (KThread Thread in MutexWaiters)
+ {
+ Thread.MutexOwner = null;
+ Thread.PreferredCoreOverride = 0;
+ Thread.ObjSyncResult = 0xfa01;
+
+ Thread.ReleaseAndResume();
+ }
- SetNewSchedFlags(ThreadSchedState.Running);
+ System.CriticalSection.Leave();
- System.CriticalSectionLock.Unlock();
+ Owner?.DecrementThreadCountAndTerminateIfZero();
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs
index 47a3c86c..375789f0 100644
--- a/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KTimeManager.cs
@@ -1,6 +1,6 @@
+using Ryujinx.Common;
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Linq;
using System.Threading;
@@ -25,18 +25,12 @@ namespace Ryujinx.HLE.HOS.Kernel
private AutoResetEvent WaitEvent;
- private Stopwatch Counter;
-
private bool KeepRunning;
public KTimeManager()
{
WaitingObjects = new List<WaitingObject>();
- Counter = new Stopwatch();
-
- Counter.Start();
-
KeepRunning = true;
Thread Work = new Thread(WaitAndCheckScheduledObjects);
@@ -46,26 +40,36 @@ namespace Ryujinx.HLE.HOS.Kernel
public void ScheduleFutureInvocation(IKFutureSchedulerObject Object, long Timeout)
{
+ long TimePoint = PerformanceCounter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(Timeout);
+
lock (WaitingObjects)
{
- long TimePoint = Counter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(Timeout);
-
WaitingObjects.Add(new WaitingObject(Object, TimePoint));
}
WaitEvent.Set();
}
- private long ConvertNanosecondsToMilliseconds(long Timeout)
+ public static long ConvertNanosecondsToMilliseconds(long Time)
{
- Timeout /= 1000000;
+ Time /= 1000000;
- if ((ulong)Timeout > int.MaxValue)
+ if ((ulong)Time > int.MaxValue)
{
return int.MaxValue;
}
- return Timeout;
+ return Time;
+ }
+
+ public static long ConvertMillisecondsToNanoseconds(long Time)
+ {
+ return Time * 1000000;
+ }
+
+ public static long ConvertMillisecondsToTicks(long Time)
+ {
+ return Time * 19200;
}
public void UnscheduleFutureInvocation(IKFutureSchedulerObject Object)
@@ -82,26 +86,31 @@ namespace Ryujinx.HLE.HOS.Kernel
{
while (KeepRunning)
{
- Monitor.Enter(WaitingObjects);
-
- WaitingObject Next = WaitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
+ WaitingObject Next;
- Monitor.Exit(WaitingObjects);
+ lock (WaitingObjects)
+ {
+ Next = WaitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
+ }
if (Next != null)
{
- long TimePoint = Counter.ElapsedMilliseconds;
+ long TimePoint = PerformanceCounter.ElapsedMilliseconds;
if (Next.TimePoint > TimePoint)
{
WaitEvent.WaitOne((int)(Next.TimePoint - TimePoint));
}
- Monitor.Enter(WaitingObjects);
+ bool TimeUp = PerformanceCounter.ElapsedMilliseconds >= Next.TimePoint;
- bool TimeUp = Counter.ElapsedMilliseconds >= Next.TimePoint && WaitingObjects.Remove(Next);
-
- Monitor.Exit(WaitingObjects);
+ if (TimeUp)
+ {
+ lock (WaitingObjects)
+ {
+ TimeUp = WaitingObjects.Remove(Next);
+ }
+ }
if (TimeUp)
{
diff --git a/Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs b/Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs
new file mode 100644
index 00000000..18dc2dec
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs
@@ -0,0 +1,73 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class KTlsPageInfo
+ {
+ public const int TlsEntrySize = 0x200;
+
+ public ulong PageAddr { get; private set; }
+
+ private bool[] IsSlotFree;
+
+ public KTlsPageInfo(ulong PageAddress)
+ {
+ this.PageAddr = PageAddress;
+
+ IsSlotFree = new bool[KMemoryManager.PageSize / TlsEntrySize];
+
+ for (int Index = 0; Index < IsSlotFree.Length; Index++)
+ {
+ IsSlotFree[Index] = true;
+ }
+ }
+
+ public bool TryGetFreePage(out ulong Address)
+ {
+ Address = PageAddr;
+
+ for (int Index = 0; Index < IsSlotFree.Length; Index++)
+ {
+ if (IsSlotFree[Index])
+ {
+ IsSlotFree[Index] = false;
+
+ return true;
+ }
+
+ Address += TlsEntrySize;
+ }
+
+ Address = 0;
+
+ return false;
+ }
+
+ public bool IsFull()
+ {
+ bool HasFree = false;
+
+ for (int Index = 0; Index < IsSlotFree.Length; Index++)
+ {
+ HasFree |= IsSlotFree[Index];
+ }
+
+ return !HasFree;
+ }
+
+ public bool IsEmpty()
+ {
+ bool AllFree = true;
+
+ for (int Index = 0; Index < IsSlotFree.Length; Index++)
+ {
+ AllFree &= IsSlotFree[Index];
+ }
+
+ return AllFree;
+ }
+
+ public void FreeTlsSlot(ulong Address)
+ {
+ IsSlotFree[(Address - PageAddr) / TlsEntrySize] = true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KTransferMemory.cs b/Ryujinx.HLE/HOS/Kernel/KTransferMemory.cs
index 6ebffa7e..5598f78d 100644
--- a/Ryujinx.HLE/HOS/Kernel/KTransferMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KTransferMemory.cs
@@ -2,13 +2,13 @@ namespace Ryujinx.HLE.HOS.Kernel
{
class KTransferMemory
{
- public long Position { get; private set; }
- public long Size { get; private set; }
+ public ulong Address { get; private set; }
+ public ulong Size { get; private set; }
- public KTransferMemory(long Position, long Size)
+ public KTransferMemory(ulong Address, ulong Size)
{
- this.Position = Position;
- this.Size = Size;
+ this.Address = Address;
+ this.Size = Size;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KernelInit.cs b/Ryujinx.HLE/HOS/Kernel/KernelInit.cs
new file mode 100644
index 00000000..efb514c1
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KernelInit.cs
@@ -0,0 +1,136 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ static class KernelInit
+ {
+ public static void InitializeResourceLimit(KResourceLimit ResourceLimit)
+ {
+ void EnsureSuccess(KernelResult Result)
+ {
+ if (Result != KernelResult.Success)
+ {
+ throw new InvalidOperationException($"Unexpected result \"{Result}\".");
+ }
+ }
+
+ int KernelMemoryCfg = 0;
+
+ long RamSize = GetRamSize(KernelMemoryCfg);
+
+ EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Memory, RamSize));
+ EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Thread, 800));
+ EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Event, 700));
+ EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.TransferMemory, 200));
+ EnsureSuccess(ResourceLimit.SetLimitValue(LimitableResource.Session, 900));
+
+ if (!ResourceLimit.Reserve(LimitableResource.Memory, 0) ||
+ !ResourceLimit.Reserve(LimitableResource.Memory, 0x60000))
+ {
+ throw new InvalidOperationException("Unexpected failure reserving memory on resource limit.");
+ }
+ }
+
+ public static KMemoryRegionManager[] GetMemoryRegions()
+ {
+ KMemoryArrange Arrange = GetMemoryArrange();
+
+ return new KMemoryRegionManager[]
+ {
+ GetMemoryRegion(Arrange.Application),
+ GetMemoryRegion(Arrange.Applet),
+ GetMemoryRegion(Arrange.Service),
+ GetMemoryRegion(Arrange.NvServices)
+ };
+ }
+
+ private static KMemoryRegionManager GetMemoryRegion(KMemoryArrangeRegion Region)
+ {
+ return new KMemoryRegionManager(Region.Address, Region.Size, Region.EndAddr);
+ }
+
+ private static KMemoryArrange GetMemoryArrange()
+ {
+ int McEmemCfg = 0x1000;
+
+ ulong EmemApertureSize = (ulong)(McEmemCfg & 0x3fff) << 20;
+
+ int KernelMemoryCfg = 0;
+
+ ulong RamSize = (ulong)GetRamSize(KernelMemoryCfg);
+
+ ulong RamPart0;
+ ulong RamPart1;
+
+ if (RamSize * 2 > EmemApertureSize)
+ {
+ RamPart0 = EmemApertureSize / 2;
+ RamPart1 = EmemApertureSize / 2;
+ }
+ else
+ {
+ RamPart0 = EmemApertureSize;
+ RamPart1 = 0;
+ }
+
+ int MemoryArrange = 1;
+
+ ulong ApplicationRgSize;
+
+ switch (MemoryArrange)
+ {
+ case 2: ApplicationRgSize = 0x80000000; break;
+ case 0x11:
+ case 0x21: ApplicationRgSize = 0x133400000; break;
+ default: ApplicationRgSize = 0xcd500000; break;
+ }
+
+ ulong AppletRgSize;
+
+ switch (MemoryArrange)
+ {
+ case 2: AppletRgSize = 0x61200000; break;
+ case 3: AppletRgSize = 0x1c000000; break;
+ case 0x11: AppletRgSize = 0x23200000; break;
+ case 0x12:
+ case 0x21: AppletRgSize = 0x89100000; break;
+ default: AppletRgSize = 0x1fb00000; break;
+ }
+
+ KMemoryArrangeRegion ServiceRg;
+ KMemoryArrangeRegion NvServicesRg;
+ KMemoryArrangeRegion AppletRg;
+ KMemoryArrangeRegion ApplicationRg;
+
+ const ulong NvServicesRgSize = 0x29ba000;
+
+ ulong ApplicationRgEnd = DramMemoryMap.DramEnd; //- RamPart0;
+
+ ApplicationRg = new KMemoryArrangeRegion(ApplicationRgEnd - ApplicationRgSize, ApplicationRgSize);
+
+ ulong NvServicesRgEnd = ApplicationRg.Address - AppletRgSize;
+
+ NvServicesRg = new KMemoryArrangeRegion(NvServicesRgEnd - NvServicesRgSize, NvServicesRgSize);
+ AppletRg = new KMemoryArrangeRegion(NvServicesRgEnd, AppletRgSize);
+
+ //Note: There is an extra region used by the kernel, however
+ //since we are doing HLE we are not going to use that memory, so give all
+ //the remaining memory space to services.
+ ulong ServiceRgSize = NvServicesRg.Address - DramMemoryMap.SlabHeapEnd;
+
+ ServiceRg = new KMemoryArrangeRegion(DramMemoryMap.SlabHeapEnd, ServiceRgSize);
+
+ return new KMemoryArrange(ServiceRg, NvServicesRg, AppletRg, ApplicationRg);
+ }
+
+ private static long GetRamSize(int KernelMemoryCfg)
+ {
+ switch ((KernelMemoryCfg >> 16) & 3)
+ {
+ case 1: return 0x180000000;
+ case 2: return 0x200000000;
+ default: return 0x100000000;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KernelResult.cs b/Ryujinx.HLE/HOS/Kernel/KernelResult.cs
index d9cbfc67..9870d175 100644
--- a/Ryujinx.HLE/HOS/Kernel/KernelResult.cs
+++ b/Ryujinx.HLE/HOS/Kernel/KernelResult.cs
@@ -2,9 +2,30 @@ namespace Ryujinx.HLE.HOS.Kernel
{
enum KernelResult
{
- Success = 0,
- HandleTableFull = 0xd201,
- InvalidHandle = 0xe401,
- InvalidState = 0xfa01
+ Success = 0,
+ InvalidCapability = 0x1c01,
+ ThreadTerminating = 0x7601,
+ InvalidSize = 0xca01,
+ InvalidAddress = 0xcc01,
+ OutOfResource = 0xce01,
+ OutOfMemory = 0xd001,
+ HandleTableFull = 0xd201,
+ InvalidMemState = 0xd401,
+ InvalidPermission = 0xd801,
+ InvalidMemRange = 0xdc01,
+ InvalidPriority = 0xe001,
+ InvalidCpuCore = 0xe201,
+ InvalidHandle = 0xe401,
+ UserCopyFailed = 0xe601,
+ InvalidCombination = 0xe801,
+ TimedOut = 0xea01,
+ Cancelled = 0xec01,
+ MaximumExceeded = 0xee01,
+ InvalidEnumValue = 0xf001,
+ NotFound = 0xf201,
+ InvalidThread = 0xf401,
+ InvalidState = 0xfa01,
+ ReservedValue = 0xfc01,
+ ResLimitExceeded = 0x10801
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs b/Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs
new file mode 100644
index 00000000..a3fabeae
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs
@@ -0,0 +1,71 @@
+using ChocolArm64.Memory;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ static class KernelTransfer
+ {
+ public static bool UserToKernelInt32(Horizon System, long Address, out int Value)
+ {
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (CurrentProcess.CpuMemory.IsMapped(Address) &&
+ CurrentProcess.CpuMemory.IsMapped(Address + 3))
+ {
+ Value = CurrentProcess.CpuMemory.ReadInt32(Address);
+
+ return true;
+ }
+
+ Value = 0;
+
+ return false;
+ }
+
+ public static bool UserToKernelString(Horizon System, long Address, int Size, out string Value)
+ {
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (CurrentProcess.CpuMemory.IsMapped(Address) &&
+ CurrentProcess.CpuMemory.IsMapped(Address + Size - 1))
+ {
+ Value = MemoryHelper.ReadAsciiString(CurrentProcess.CpuMemory, Address, Size);
+
+ return true;
+ }
+
+ Value = null;
+
+ return false;
+ }
+
+ public static bool KernelToUserInt32(Horizon System, long Address, int Value)
+ {
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (CurrentProcess.CpuMemory.IsMapped(Address) &&
+ CurrentProcess.CpuMemory.IsMapped(Address + 3))
+ {
+ CurrentProcess.CpuMemory.WriteInt32ToSharedAddr(Address, Value);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public static bool KernelToUserInt64(Horizon System, long Address, long Value)
+ {
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (CurrentProcess.CpuMemory.IsMapped(Address) &&
+ CurrentProcess.CpuMemory.IsMapped(Address + 7))
+ {
+ CurrentProcess.CpuMemory.WriteInt64(Address, Value);
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs b/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs
new file mode 100644
index 00000000..baab4222
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/LimitableResource.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ enum LimitableResource : byte
+ {
+ Memory = 0,
+ Thread = 1,
+ Event = 2,
+ TransferMemory = 3,
+ Session = 4,
+
+ Count = 5
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs b/Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs
new file mode 100644
index 00000000..b9350121
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ enum MemoryOperation
+ {
+ MapPa,
+ MapVa,
+ Allocate,
+ Unmap,
+ ChangePermRw,
+ ChangePermsAndAttributes
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs b/Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs
new file mode 100644
index 00000000..ea4f33c9
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ enum MemoryRegion
+ {
+ Application = 0,
+ Applet = 1,
+ Service = 2,
+ NvServices = 3
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/MemoryState.cs b/Ryujinx.HLE/HOS/Kernel/MemoryState.cs
index 2c37723c..e2ce27ef 100644
--- a/Ryujinx.HLE/HOS/Kernel/MemoryState.cs
+++ b/Ryujinx.HLE/HOS/Kernel/MemoryState.cs
@@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel
ModCodeStatic = 0x00DD7E08,
ModCodeMutable = 0x03FFBD09,
IpcBuffer0 = 0x005C3C0A,
- MappedMemory = 0x005C3C0B,
+ Stack = 0x005C3C0B,
ThreadLocal = 0x0040200C,
TransferMemoryIsolated = 0x015C3C0D,
TransferMemory = 0x005C380E,
diff --git a/Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs b/Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs
new file mode 100644
index 00000000..b90d54d2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs
@@ -0,0 +1,128 @@
+using Ryujinx.Common;
+
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ class MersenneTwister
+ {
+ private int Index;
+ private uint[] Mt;
+
+ public MersenneTwister(uint Seed)
+ {
+ Mt = new uint[624];
+
+ Mt[0] = Seed;
+
+ for (int MtIdx = 1; MtIdx < Mt.Length; MtIdx++)
+ {
+ uint Prev = Mt[MtIdx - 1];
+
+ Mt[MtIdx] = (uint)(0x6c078965 * (Prev ^ (Prev >> 30)) + MtIdx);
+ }
+
+ Index = Mt.Length;
+ }
+
+ public long GenRandomNumber(long Min, long Max)
+ {
+ long Range = Max - Min;
+
+ if (Min == Max)
+ {
+ return Min;
+ }
+
+ if (Range == -1)
+ {
+ //Increment would cause a overflow, special case.
+ return GenRandomNumber(2, 2, 32, 0xffffffffu, 0xffffffffu);
+ }
+
+ Range++;
+
+ //This is log2(Range) plus one.
+ int NextRangeLog2 = 64 - BitUtils.CountLeadingZeros64(Range);
+
+ //If Range is already power of 2, subtract one to use log2(Range) directly.
+ int RangeLog2 = NextRangeLog2 - (BitUtils.IsPowerOfTwo64(Range) ? 1 : 0);
+
+ int Parts = RangeLog2 > 32 ? 2 : 1;
+ int BitsPerPart = RangeLog2 / Parts;
+
+ int FullParts = Parts - (RangeLog2 - Parts * BitsPerPart);
+
+ uint Mask = 0xffffffffu >> (32 - BitsPerPart);
+ uint MaskPlus1 = 0xffffffffu >> (31 - BitsPerPart);
+
+ long RandomNumber;
+
+ do
+ {
+ RandomNumber = GenRandomNumber(Parts, FullParts, BitsPerPart, Mask, MaskPlus1);
+ }
+ while ((ulong)RandomNumber >= (ulong)Range);
+
+ return Min + RandomNumber;
+ }
+
+ private long GenRandomNumber(
+ int Parts,
+ int FullParts,
+ int BitsPerPart,
+ uint Mask,
+ uint MaskPlus1)
+ {
+ long RandomNumber = 0;
+
+ int Part = 0;
+
+ for (; Part < FullParts; Part++)
+ {
+ RandomNumber <<= BitsPerPart;
+ RandomNumber |= GenRandomNumber() & Mask;
+ }
+
+ for (; Part < Parts; Part++)
+ {
+ RandomNumber <<= BitsPerPart + 1;
+ RandomNumber |= GenRandomNumber() & MaskPlus1;
+ }
+
+ return RandomNumber;
+ }
+
+ private uint GenRandomNumber()
+ {
+ if (Index >= Mt.Length)
+ {
+ Twist();
+ }
+
+ uint Value = Mt[Index++];
+
+ Value ^= Value >> 11;
+ Value ^= (Value << 7) & 0x9d2c5680;
+ Value ^= (Value << 15) & 0xefc60000;
+ Value ^= Value >> 18;
+
+ return Value;
+ }
+
+ private void Twist()
+ {
+ for (int MtIdx = 0; MtIdx < Mt.Length; MtIdx++)
+ {
+ uint Value = (Mt[MtIdx] & 0x80000000) + (Mt[(MtIdx + 1) % Mt.Length] & 0x7fffffff);
+
+ Mt[MtIdx] = Mt[(MtIdx + 397) % Mt.Length] ^ (Value >> 1);
+
+ if ((Value & 1) != 0)
+ {
+ Mt[MtIdx] ^= 0x9908b0df;
+ }
+ }
+
+ Index = 0;
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs b/Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs
new file mode 100644
index 00000000..dae1345a
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs
@@ -0,0 +1,37 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ struct ProcessCreationInfo
+ {
+ public string Name { get; private set; }
+
+ public int Category { get; private set; }
+ public long TitleId { get; private set; }
+
+ public ulong CodeAddress { get; private set; }
+ public int CodePagesCount { get; private set; }
+
+ public int MmuFlags { get; private set; }
+ public int ResourceLimitHandle { get; private set; }
+ public int PersonalMmHeapPagesCount { get; private set; }
+
+ public ProcessCreationInfo(
+ string Name,
+ int Category,
+ long TitleId,
+ ulong CodeAddress,
+ int CodePagesCount,
+ int MmuFlags,
+ int ResourceLimitHandle,
+ int PersonalMmHeapPagesCount)
+ {
+ this.Name = Name;
+ this.Category = Category;
+ this.TitleId = TitleId;
+ this.CodeAddress = CodeAddress;
+ this.CodePagesCount = CodePagesCount;
+ this.MmuFlags = MmuFlags;
+ this.ResourceLimitHandle = ResourceLimitHandle;
+ this.PersonalMmHeapPagesCount = PersonalMmHeapPagesCount;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/ProcessState.cs b/Ryujinx.HLE/HOS/Kernel/ProcessState.cs
new file mode 100644
index 00000000..98ff4207
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/ProcessState.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ enum ProcessState : byte
+ {
+ Created = 0,
+ CreatedAttached = 1,
+ Started = 2,
+ Crashed = 3,
+ Attached = 4,
+ Exiting = 5,
+ Exited = 6,
+ DebugSuspended = 7
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs
index 9b475d4e..cbc5e31c 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs
@@ -1,8 +1,8 @@
using ChocolArm64.Events;
using ChocolArm64.Memory;
using ChocolArm64.State;
-using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.Common.Logging;
using System;
using System.Collections.Generic;
@@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel
private Dictionary<int, SvcFunc> SvcFuncs;
private Switch Device;
- private Process Process;
+ private KProcess Process;
private Horizon System;
private MemoryManager Memory;
@@ -39,9 +39,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- private static Random Rng;
-
- public SvcHandler(Switch Device, Process Process)
+ public SvcHandler(Switch Device, KProcess Process)
{
SvcFuncs = new Dictionary<int, SvcFunc>()
{
@@ -51,14 +49,14 @@ namespace Ryujinx.HLE.HOS.Kernel
{ 0x05, SvcUnmapMemory },
{ 0x06, SvcQueryMemory },
{ 0x07, SvcExitProcess },
- { 0x08, SvcCreateThread },
+ { 0x08, CreateThread64 },
{ 0x09, SvcStartThread },
{ 0x0a, SvcExitThread },
{ 0x0b, SvcSleepThread },
{ 0x0c, SvcGetThreadPriority },
{ 0x0d, SvcSetThreadPriority },
{ 0x0e, SvcGetThreadCoreMask },
- { 0x0f, SvcSetThreadCoreMask },
+ { 0x0f, SetThreadCoreMask64 },
{ 0x10, SvcGetCurrentProcessorNumber },
{ 0x11, SignalEvent64 },
{ 0x12, ClearEvent64 },
@@ -77,36 +75,34 @@ namespace Ryujinx.HLE.HOS.Kernel
{ 0x1f, SvcConnectToNamedPort },
{ 0x21, SvcSendSyncRequest },
{ 0x22, SvcSendSyncRequestWithUserBuffer },
+ { 0x24, GetProcessId64 },
{ 0x25, SvcGetThreadId },
{ 0x26, SvcBreak },
{ 0x27, SvcOutputDebugString },
- { 0x29, SvcGetInfo },
+ { 0x29, GetInfo64 },
{ 0x2c, SvcMapPhysicalMemory },
{ 0x2d, SvcUnmapPhysicalMemory },
{ 0x32, SvcSetThreadActivity },
{ 0x33, SvcGetThreadContext3 },
{ 0x34, SvcWaitForAddress },
{ 0x35, SvcSignalToAddress },
- { 0x45, CreateEvent64 }
+ { 0x45, CreateEvent64 },
+ { 0x65, GetProcessList64 },
+ { 0x6f, GetSystemInfo64 },
+ { 0x70, CreatePort64 },
+ { 0x71, ManageNamedPort64 }
};
this.Device = Device;
this.Process = Process;
- this.System = Process.Device.System;
- this.Memory = Process.Memory;
- }
-
- static SvcHandler()
- {
- Rng = new Random();
+ this.System = Device.System;
+ this.Memory = Process.CpuMemory;
}
public void SvcCall(object sender, InstExceptionEventArgs e)
{
CpuThreadState ThreadState = (CpuThreadState)sender;
- Process.GetThread(ThreadState.Tpidr).LastPc = e.Position;
-
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
{
Logger.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called.");
@@ -117,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Kernel
}
else
{
- Process.PrintStackTrace(ThreadState);
+ //Process.PrintStackTrace(ThreadState);
throw new NotImplementedException($"0x{e.Id:x4}");
}
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs
index 560ad4b3..b5845f0b 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs
@@ -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)
- {
- 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);
+ KernelResult Result = SharedMemory.MapIntoProcess(
+ CurrentProcess.MemoryManager,
+ Address,
+ Size,
+ CurrentProcess,
+ Permission);
- 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;
@@ -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, $"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 {Position:x16}!");
+ 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;
- }
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs
index 54aef5d7..1c1d76f1 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs
@@ -1,5 +1,6 @@
using ChocolArm64.Memory;
using ChocolArm64.State;
+using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc;
@@ -13,13 +14,9 @@ namespace Ryujinx.HLE.HOS.Kernel
{
partial class SvcHandler
{
- private const int AllowedCpuIdBitmask = 0b1111;
-
- private const bool EnableProcessDebugging = false;
-
private void SvcExitProcess(CpuThreadState ThreadState)
{
- Device.System.ExitProcess(Process.ProcessId);
+ System.Scheduler.GetCurrentProcess().Terminate();
}
private void SignalEvent64(CpuThreadState ThreadState)
@@ -106,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Kernel
else if (Obj is KTransferMemory TransferMemory)
{
Process.MemoryManager.ResetTransferMemory(
- TransferMemory.Position,
+ TransferMemory.Address,
TransferMemory.Size);
}
@@ -120,18 +117,28 @@ namespace Ryujinx.HLE.HOS.Kernel
private KernelResult ResetSignal(int Handle)
{
- KReadableEvent ReadableEvent = Process.HandleTable.GetObject<KReadableEvent>(Handle);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ KReadableEvent ReadableEvent = CurrentProcess.HandleTable.GetObject<KReadableEvent>(Handle);
KernelResult Result;
- //TODO: KProcess support.
if (ReadableEvent != null)
{
Result = ReadableEvent.ClearIfSignaled();
}
else
{
- Result = KernelResult.InvalidHandle;
+ KProcess Process = CurrentProcess.HandleTable.GetKProcess(Handle);
+
+ if (Process != null)
+ {
+ Result = Process.ClearIfNotExited();
+ }
+ else
+ {
+ Result = KernelResult.InvalidHandle;
+ }
}
if (Result == KernelResult.InvalidState)
@@ -187,17 +194,13 @@ namespace Ryujinx.HLE.HOS.Kernel
private void SendSyncRequest(CpuThreadState ThreadState, long MessagePtr, long Size, int Handle)
{
- KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
-
byte[] MessageData = Memory.ReadBytes(MessagePtr, Size);
KSession Session = Process.HandleTable.GetObject<KSession>(Handle);
if (Session != null)
{
- //Process.Scheduler.Suspend(CurrThread);
-
- System.CriticalSectionLock.Lock();
+ System.CriticalSection.Enter();
KThread CurrentThread = System.Scheduler.GetCurrentThread();
@@ -214,7 +217,9 @@ namespace Ryujinx.HLE.HOS.Kernel
Message,
MessagePtr));
- System.CriticalSectionLock.Unlock();
+ System.ThreadCounter.AddCount();
+
+ System.CriticalSection.Leave();
ThreadState.X0 = (ulong)CurrentThread.ObjSyncResult;
}
@@ -238,25 +243,65 @@ namespace Ryujinx.HLE.HOS.Kernel
IpcMessage.Message,
IpcMessage.MessagePtr);
+ System.ThreadCounter.Signal();
+
IpcMessage.Thread.Reschedule(ThreadSchedState.Running);
}
+ private void GetProcessId64(CpuThreadState ThreadState)
+ {
+ int Handle = (int)ThreadState.X1;
+
+ KernelResult Result = GetProcessId(Handle, out long Pid);
+
+ ThreadState.X0 = (ulong)Result;
+ ThreadState.X1 = (ulong)Pid;
+ }
+
+ private KernelResult GetProcessId(int Handle, out long Pid)
+ {
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ KProcess Process = CurrentProcess.HandleTable.GetKProcess(Handle);
+
+ if (Process == null)
+ {
+ KThread Thread = CurrentProcess.HandleTable.GetKThread(Handle);
+
+ if (Thread != null)
+ {
+ Process = Thread.Owner;
+ }
+
+ //TODO: KDebugEvent.
+ }
+
+ Pid = Process?.Pid ?? 0;
+
+ return Process != null
+ ? KernelResult.Success
+ : KernelResult.InvalidHandle;
+ }
+
private void SvcBreak(CpuThreadState ThreadState)
{
long Reason = (long)ThreadState.X0;
long Unknown = (long)ThreadState.X1;
long Info = (long)ThreadState.X2;
+ KThread CurrentThread = System.Scheduler.GetCurrentThread();
+
if ((Reason & (1 << 31)) == 0)
{
- Process.PrintStackTrace(ThreadState);
+ CurrentThread.PrintGuestStackTrace();
throw new GuestBrokeExecutionException();
}
else
{
- Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered");
- Process.PrintStackTrace(ThreadState);
+ Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered.");
+
+ CurrentThread.PrintGuestStackTrace();
}
}
@@ -272,98 +317,243 @@ namespace Ryujinx.HLE.HOS.Kernel
ThreadState.X0 = 0;
}
- private void SvcGetInfo(CpuThreadState ThreadState)
+ private void GetInfo64(CpuThreadState ThreadState)
{
long StackPtr = (long)ThreadState.X0;
- int InfoType = (int)ThreadState.X1;
- long Handle = (long)ThreadState.X2;
- int InfoId = (int)ThreadState.X3;
+ uint Id = (uint)ThreadState.X1;
+ int Handle = (int)ThreadState.X2;
+ long SubId = (long)ThreadState.X3;
- //Fail for info not available on older Kernel versions.
- if (InfoType == 18 ||
- InfoType == 19 ||
- InfoType == 20 ||
- InfoType == 21 ||
- InfoType == 22)
- {
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
+ KernelResult Result = GetInfo(Id, Handle, SubId, out long Value);
- return;
- }
+ ThreadState.X0 = (ulong)Result;
+ ThreadState.X1 = (ulong)Value;
+ }
+
+ private KernelResult GetInfo(uint Id, int Handle, long SubId, out long Value)
+ {
+ Value = 0;
- switch (InfoType)
+ switch (Id)
{
case 0:
- ThreadState.X1 = AllowedCpuIdBitmask;
- break;
-
+ case 1:
case 2:
- ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionStart;
- break;
-
case 3:
- ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionEnd -
- (ulong)Process.MemoryManager.MapRegionStart;
- break;
-
case 4:
- ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionStart;
- break;
-
case 5:
- ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionEnd -
- (ulong)Process.MemoryManager.HeapRegionStart;
- break;
-
case 6:
- ThreadState.X1 = (ulong)Process.Device.Memory.Allocator.TotalAvailableSize;
- break;
-
case 7:
- ThreadState.X1 = (ulong)Process.Device.Memory.Allocator.TotalUsedSize;
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 20:
+ case 21:
+ case 22:
+ {
+ if (SubId != 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ KProcess Process = CurrentProcess.HandleTable.GetKProcess(Handle);
+
+ if (Process == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ switch (Id)
+ {
+ case 0: Value = Process.Capabilities.AllowedCpuCoresMask; break;
+ case 1: Value = Process.Capabilities.AllowedThreadPriosMask; break;
+
+ case 2: Value = (long)Process.MemoryManager.AliasRegionStart; break;
+ case 3: Value = (long)(Process.MemoryManager.AliasRegionEnd -
+ Process.MemoryManager.AliasRegionStart); break;
+
+ case 4: Value = (long)Process.MemoryManager.HeapRegionStart; break;
+ case 5: Value = (long)(Process.MemoryManager.HeapRegionEnd -
+ Process.MemoryManager.HeapRegionStart); break;
+
+ case 6: Value = (long)Process.GetMemoryCapacity(); break;
+
+ case 7: Value = (long)Process.GetMemoryUsage(); break;
+
+ case 12: Value = (long)Process.MemoryManager.GetAddrSpaceBaseAddr(); break;
+
+ case 13: Value = (long)Process.MemoryManager.GetAddrSpaceSize(); break;
+
+ case 14: Value = (long)Process.MemoryManager.StackRegionStart; break;
+ case 15: Value = (long)(Process.MemoryManager.StackRegionEnd -
+ Process.MemoryManager.StackRegionStart); break;
+
+ case 16: Value = (long)Process.PersonalMmHeapPagesCount * KMemoryManager.PageSize; break;
+
+ case 17:
+ if (Process.PersonalMmHeapPagesCount != 0)
+ {
+ Value = Process.MemoryManager.GetMmUsedPages() * KMemoryManager.PageSize;
+ }
+
+ break;
+
+ case 18: Value = Process.TitleId; break;
+
+ case 20: Value = (long)Process.UserExceptionContextAddress; break;
+
+ case 21: Value = (long)Process.GetMemoryCapacityWithoutPersonalMmHeap(); break;
+
+ case 22: Value = (long)Process.GetMemoryUsageWithoutPersonalMmHeap(); break;
+ }
+
break;
+ }
case 8:
- ThreadState.X1 = EnableProcessDebugging ? 1 : 0;
- break;
+ {
+ if (Handle != 0)
+ {
+ return KernelResult.InvalidHandle;
+ }
- case 11:
- ThreadState.X1 = (ulong)Rng.Next() + ((ulong)Rng.Next() << 32);
- break;
+ if (SubId != 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
- case 12:
- ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceStart;
- break;
+ Value = System.Scheduler.GetCurrentProcess().Debug ? 1 : 0;
- case 13:
- ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceEnd -
- (ulong)Process.MemoryManager.AddrSpaceStart;
break;
+ }
- case 14:
- ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionStart;
- break;
+ case 9:
+ {
+ if (Handle != 0)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ if (SubId != 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (CurrentProcess.ResourceLimit != null)
+ {
+ KHandleTable HandleTable = CurrentProcess.HandleTable;
+ KResourceLimit ResourceLimit = CurrentProcess.ResourceLimit;
+
+ KernelResult Result = HandleTable.GenerateHandle(ResourceLimit, out int ResLimHandle);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ Value = (uint)ResLimHandle;
+ }
- case 15:
- ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionEnd -
- (ulong)Process.MemoryManager.NewMapRegionStart;
break;
+ }
+
+ case 10:
+ {
+ if (Handle != 0)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ int CurrentCore = System.Scheduler.GetCurrentThread().CurrentCore;
+
+ if (SubId != -1 && SubId != CurrentCore)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ Value = System.Scheduler.CoreContexts[CurrentCore].TotalIdleTimeTicks;
- case 16:
- ThreadState.X1 = (ulong)(Process.MetaData?.SystemResourceSize ?? 0);
break;
+ }
+
+ case 11:
+ {
+ if (Handle != 0)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ if ((ulong)SubId > 3)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+
+ Value = CurrentProcess.RandomEntropy[SubId];
- case 17:
- ThreadState.X1 = (ulong)Process.MemoryManager.PersonalMmHeapUsage;
break;
+ }
+
+ case 0xf0000002u:
+ {
+ if (SubId < -1 || SubId > 3)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ KThread Thread = System.Scheduler.GetCurrentProcess().HandleTable.GetKThread(Handle);
+
+ if (Thread == null)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ KThread CurrentThread = System.Scheduler.GetCurrentThread();
+
+ int CurrentCore = CurrentThread.CurrentCore;
+
+ if (SubId != -1 && SubId != CurrentCore)
+ {
+ return KernelResult.Success;
+ }
- default:
- Process.PrintStackTrace(ThreadState);
+ KCoreContext CoreContext = System.Scheduler.CoreContexts[CurrentCore];
+
+ long TimeDelta = PerformanceCounter.ElapsedMilliseconds - CoreContext.LastContextSwitchTime;
+
+ if (SubId != -1)
+ {
+ Value = KTimeManager.ConvertMillisecondsToTicks(TimeDelta);
+ }
+ else
+ {
+ long TotalTimeRunning = Thread.TotalTimeRunning;
+
+ if (Thread == CurrentThread)
+ {
+ TotalTimeRunning += TimeDelta;
+ }
+
+ Value = KTimeManager.ConvertMillisecondsToTicks(TotalTimeRunning);
+ }
+
+ break;
+ }
- throw new NotImplementedException($"SvcGetInfo: {InfoType} 0x{Handle:x8} {InfoId}");
+ default: return KernelResult.InvalidEnumValue;
}
- ThreadState.X0 = 0;
+ return KernelResult.Success;
}
private void CreateEvent64(CpuThreadState State)
@@ -397,5 +587,241 @@ namespace Ryujinx.HLE.HOS.Kernel
return Result;
}
+
+ private void GetProcessList64(CpuThreadState State)
+ {
+ ulong Address = State.X1;
+ int MaxOut = (int)State.X2;
+
+ KernelResult Result = GetProcessList(Address, MaxOut, out int Count);
+
+ State.X0 = (ulong)Result;
+ State.X1 = (ulong)Count;
+ }
+
+ private KernelResult GetProcessList(ulong Address, int MaxCount, out int Count)
+ {
+ Count = 0;
+
+ if ((MaxCount >> 28) != 0)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ if (MaxCount != 0)
+ {
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ ulong CopySize = (ulong)MaxCount * 8;
+
+ if (Address + CopySize <= Address)
+ {
+ return KernelResult.InvalidMemState;
+ }
+
+ if (CurrentProcess.MemoryManager.OutsideAddrSpace(Address, CopySize))
+ {
+ return KernelResult.InvalidMemState;
+ }
+ }
+
+ int CopyCount = 0;
+
+ lock (System.Processes)
+ {
+ foreach (KProcess Process in System.Processes.Values)
+ {
+ if (CopyCount < MaxCount)
+ {
+ if (!KernelTransfer.KernelToUserInt64(System, (long)Address + CopyCount * 8, Process.Pid))
+ {
+ return KernelResult.UserCopyFailed;
+ }
+ }
+
+ CopyCount++;
+ }
+ }
+
+ Count = CopyCount;
+
+ return KernelResult.Success;
+ }
+
+ private void GetSystemInfo64(CpuThreadState State)
+ {
+ uint Id = (uint)State.X1;
+ int Handle = (int)State.X2;
+ long SubId = (long)State.X3;
+
+ KernelResult Result = GetSystemInfo(Id, Handle, SubId, out long Value);
+
+ State.X0 = (ulong)Result;
+ State.X1 = (ulong)Value;
+ }
+
+ private KernelResult GetSystemInfo(uint Id, int Handle, long SubId, out long Value)
+ {
+ Value = 0;
+
+ if (Id > 2)
+ {
+ return KernelResult.InvalidEnumValue;
+ }
+
+ if (Handle != 0)
+ {
+ return KernelResult.InvalidHandle;
+ }
+
+ if (Id < 2)
+ {
+ if ((ulong)SubId > 3)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ KMemoryRegionManager Region = System.MemoryRegions[SubId];
+
+ switch (Id)
+ {
+ //Memory region capacity.
+ case 0: Value = (long)Region.Size; break;
+
+ //Memory region free space.
+ case 1:
+ {
+ ulong FreePagesCount = Region.GetFreePages();
+
+ Value = (long)(FreePagesCount * KMemoryManager.PageSize);
+
+ break;
+ }
+ }
+ }
+ else /* if (Id == 2) */
+ {
+ if ((ulong)SubId > 1)
+ {
+ return KernelResult.InvalidCombination;
+ }
+
+ switch (SubId)
+ {
+ case 0: Value = System.PrivilegedProcessLowestId; break;
+ case 1: Value = System.PrivilegedProcessHighestId; break;
+ }
+ }
+
+ return KernelResult.Success;
+ }
+
+ private void CreatePort64(CpuThreadState State)
+ {
+ int MaxSessions = (int)State.X2;
+ bool IsLight = (State.X3 & 1) != 0;
+ long NameAddress = (long)State.X4;
+
+ KernelResult Result = CreatePort(
+ MaxSessions,
+ IsLight,
+ NameAddress,
+ out int ServerPortHandle,
+ out int ClientPortHandle);
+
+ State.X0 = (ulong)Result;
+ State.X1 = (ulong)ServerPortHandle;
+ State.X2 = (ulong)ClientPortHandle;
+ }
+
+ private KernelResult CreatePort(
+ int MaxSessions,
+ bool IsLight,
+ long NameAddress,
+ out int ServerPortHandle,
+ out int ClientPortHandle)
+ {
+ ServerPortHandle = ClientPortHandle = 0;
+
+ if (MaxSessions < 1)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ KPort Port = new KPort(System);
+
+ Port.Initialize(MaxSessions, IsLight, NameAddress);
+
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ KernelResult Result = CurrentProcess.HandleTable.GenerateHandle(Port.ClientPort, out ClientPortHandle);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ Result = CurrentProcess.HandleTable.GenerateHandle(Port.ServerPort, out ServerPortHandle);
+
+ if (Result != KernelResult.Success)
+ {
+ CurrentProcess.HandleTable.CloseHandle(ClientPortHandle);
+ }
+
+ return Result;
+ }
+
+ private void ManageNamedPort64(CpuThreadState State)
+ {
+ long NameAddress = (long)State.X1;
+ int MaxSessions = (int)State.X2;
+
+ KernelResult Result = ManageNamedPort(NameAddress, MaxSessions, out int Handle);
+
+ State.X0 = (ulong)Result;
+ State.X1 = (ulong)Handle;
+ }
+
+ private KernelResult ManageNamedPort(long NameAddress, int MaxSessions, out int Handle)
+ {
+ Handle = 0;
+
+ if (!KernelTransfer.UserToKernelString(System, NameAddress, 12, out string Name))
+ {
+ return KernelResult.UserCopyFailed;
+ }
+
+ if (MaxSessions < 0 || Name.Length > 11)
+ {
+ return KernelResult.MaximumExceeded;
+ }
+
+ if (MaxSessions == 0)
+ {
+ return KClientPort.RemoveName(System, Name);
+ }
+
+ KPort Port = new KPort(System);
+
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ KernelResult Result = CurrentProcess.HandleTable.GenerateHandle(Port.ServerPort, out Handle);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ Port.Initialize(MaxSessions, false, 0);
+
+ Result = Port.SetName(Name);
+
+ if (Result != KernelResult.Success)
+ {
+ CurrentProcess.HandleTable.CloseHandle(Handle);
+ }
+
+ return Result;
+ }
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs
index 53a557de..ded8f8dc 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs
@@ -7,46 +7,82 @@ namespace Ryujinx.HLE.HOS.Kernel
{
partial class SvcHandler
{
- private void SvcCreateThread(CpuThreadState ThreadState)
+ private void CreateThread64(CpuThreadState ThreadState)
{
- long EntryPoint = (long)ThreadState.X1;
- long ArgsPtr = (long)ThreadState.X2;
- long StackTop = (long)ThreadState.X3;
- int Priority = (int)ThreadState.X4;
- int ProcessorId = (int)ThreadState.X5;
+ ulong Entrypoint = ThreadState.X1;
+ ulong ArgsPtr = ThreadState.X2;
+ ulong StackTop = ThreadState.X3;
+ int Priority = (int)ThreadState.X4;
+ int CpuCore = (int)ThreadState.X5;
- if ((uint)Priority > 0x3f)
- {
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid priority 0x{Priority:x8}!");
+ KernelResult Result = CreateThread(Entrypoint, ArgsPtr, StackTop, Priority, CpuCore, out int Handle);
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPriority);
+ ThreadState.X0 = (ulong)Result;
+ ThreadState.X1 = (ulong)Handle;
+ }
- return;
+ private KernelResult CreateThread(
+ ulong Entrypoint,
+ ulong ArgsPtr,
+ ulong StackTop,
+ int Priority,
+ int CpuCore,
+ out int Handle)
+ {
+ Handle = 0;
+
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (CpuCore == -2)
+ {
+ CpuCore = CurrentProcess.DefaultCpuCore;
}
- if (ProcessorId == -2)
+ if ((uint)CpuCore >= KScheduler.CpuCoresCount || !CurrentProcess.IsCpuCoreAllowed(CpuCore))
{
- //TODO: Get this value from the NPDM file.
- ProcessorId = 0;
+ return KernelResult.InvalidCpuCore;
}
- else if ((uint)ProcessorId > 3)
+
+ if ((uint)Priority >= KScheduler.PrioritiesCount || !CurrentProcess.IsPriorityAllowed(Priority))
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{ProcessorId:x8}!");
+ return KernelResult.InvalidPriority;
+ }
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
+ long Timeout = KTimeManager.ConvertMillisecondsToNanoseconds(100);
- return;
+ if (CurrentProcess.ResourceLimit != null &&
+ !CurrentProcess.ResourceLimit.Reserve(LimitableResource.Thread, 1, Timeout))
+ {
+ return KernelResult.ResLimitExceeded;
}
- int Handle = Process.MakeThread(
- EntryPoint,
- StackTop,
+ KThread Thread = new KThread(System);
+
+ KernelResult Result = CurrentProcess.InitializeThread(
+ Thread,
+ Entrypoint,
ArgsPtr,
+ StackTop,
Priority,
- ProcessorId);
+ CpuCore);
- ThreadState.X0 = 0;
- ThreadState.X1 = (ulong)Handle;
+ if (Result != KernelResult.Success)
+ {
+ CurrentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
+
+ return Result;
+ }
+
+ Result = Process.HandleTable.GenerateHandle(Thread, out Handle);
+
+ if (Result != KernelResult.Success)
+ {
+ Thread.Terminate();
+
+ CurrentProcess.ResourceLimit?.Release(LimitableResource.Thread, 1);
+ }
+
+ return Result;
}
private void SvcStartThread(CpuThreadState ThreadState)
@@ -57,11 +93,11 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Thread != null)
{
- long Result = Thread.Start();
+ KernelResult Result = Thread.Start();
- if (Result != 0)
+ if (Result != KernelResult.Success)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
}
ThreadState.X0 = (ulong)Result;
@@ -78,9 +114,9 @@ namespace Ryujinx.HLE.HOS.Kernel
{
KThread CurrentThread = System.Scheduler.GetCurrentThread();
- CurrentThread.Exit();
+ System.Scheduler.ExitThread(CurrentThread);
- System.Scheduler.StopThread(CurrentThread);
+ CurrentThread.Exit();
}
private void SvcSleepThread(CpuThreadState ThreadState)
@@ -176,46 +212,60 @@ namespace Ryujinx.HLE.HOS.Kernel
}
}
- private void SvcSetThreadCoreMask(CpuThreadState ThreadState)
+ private void SetThreadCoreMask64(CpuThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
- int PrefferedCore = (int)ThreadState.X1;
+ int PreferredCore = (int)ThreadState.X1;
long AffinityMask = (long)ThreadState.X2;
Logger.PrintDebug(LogClass.KernelSvc,
"Handle = 0x" + Handle .ToString("x8") + ", " +
- "PrefferedCore = 0x" + PrefferedCore.ToString("x8") + ", " +
+ "PreferredCore = 0x" + PreferredCore.ToString("x8") + ", " +
"AffinityMask = 0x" + AffinityMask .ToString("x16"));
- if (PrefferedCore == -2)
+ KernelResult Result = SetThreadCoreMask(Handle, PreferredCore, AffinityMask);
+
+ if (Result != KernelResult.Success)
{
- //TODO: Get this value from the NPDM file.
- PrefferedCore = 0;
+ Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
+ }
- AffinityMask = 1 << PrefferedCore;
+ ThreadState.X0 = (ulong)Result;
+ }
+
+ private KernelResult SetThreadCoreMask(int Handle, int PreferredCore, long AffinityMask)
+ {
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ if (PreferredCore == -2)
+ {
+ PreferredCore = CurrentProcess.DefaultCpuCore;
+
+ AffinityMask = 1 << PreferredCore;
}
else
{
- //TODO: Check allowed cores from NPDM file.
-
- if ((uint)PrefferedCore > 3)
+ if ((CurrentProcess.Capabilities.AllowedCpuCoresMask | AffinityMask) !=
+ CurrentProcess.Capabilities.AllowedCpuCoresMask)
{
- if ((PrefferedCore | 2) != -1)
- {
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{PrefferedCore:x8}!");
+ return KernelResult.InvalidCpuCore;
+ }
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
+ if (AffinityMask == 0)
+ {
+ return KernelResult.InvalidCombination;
+ }
- return;
+ if ((uint)PreferredCore > 3)
+ {
+ if ((PreferredCore | 2) != -1)
+ {
+ return KernelResult.InvalidCpuCore;
}
}
- else if ((AffinityMask & (1 << PrefferedCore)) == 0)
+ else if ((AffinityMask & (1 << PreferredCore)) == 0)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{AffinityMask:x8}!");
-
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
-
- return;
+ return KernelResult.InvalidCombination;
}
}
@@ -223,26 +273,15 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Thread == null)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
-
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
-
- return;
- }
-
- long Result = Thread.SetCoreAndAffinityMask(PrefferedCore, AffinityMask);
-
- if (Result != 0)
- {
- Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ return KernelResult.InvalidHandle;
}
- ThreadState.X0 = (ulong)Result;
+ return Thread.SetCoreAndAffinityMask(PreferredCore, AffinityMask);
}
private void SvcGetCurrentProcessorNumber(CpuThreadState ThreadState)
{
- ThreadState.X0 = (ulong)Process.GetThread(ThreadState.Tpidr).CurrentCore;
+ ThreadState.X0 = (ulong)System.Scheduler.GetCurrentThread().CurrentCore;
}
private void SvcGetThreadId(CpuThreadState ThreadState)
@@ -254,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Kernel
if (Thread != null)
{
ThreadState.X0 = 0;
- ThreadState.X1 = (ulong)Thread.ThreadId;
+ ThreadState.X1 = (ulong)Thread.ThreadUid;
}
else
{
@@ -280,15 +319,24 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if (Thread.Owner != Process)
+ if (Thread.Owner != System.Scheduler.GetCurrentProcess())
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread owner process!");
+ Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread, it belongs to another process.");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
return;
}
+ if (Thread == System.Scheduler.GetCurrentThread())
+ {
+ Logger.PrintWarning(LogClass.KernelSvc, "Invalid thread, current thread is not accepted.");
+
+ ThreadState.X0 = (ulong)KernelResult.InvalidThread;
+
+ return;
+ }
+
long Result = Thread.SetActivity(Pause);
if (Result != 0)
@@ -304,6 +352,9 @@ namespace Ryujinx.HLE.HOS.Kernel
long Position = (long)ThreadState.X0;
int Handle = (int)ThreadState.X1;
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+ KThread CurrentThread = System.Scheduler.GetCurrentThread();
+
KThread Thread = Process.HandleTable.GetObject<KThread>(Handle);
if (Thread == null)
@@ -315,9 +366,18 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- if (Process.GetThread(ThreadState.Tpidr) == Thread)
+ if (Thread.Owner != CurrentProcess)
+ {
+ Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread, it belongs to another process.");
+
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+
+ return;
+ }
+
+ if (CurrentThread == Thread)
{
- Logger.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is current thread!");
+ Logger.PrintWarning(LogClass.KernelSvc, "Invalid thread, current thread is not accepted.");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidThread);
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs
index 318bd290..3935df5d 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs
@@ -32,6 +32,8 @@ namespace Ryujinx.HLE.HOS.Kernel
{
int Handle = Memory.ReadInt32(HandlesPtr + Index * 4);
+ Logger.PrintDebug(LogClass.KernelSvc, $"Sync handle 0x{Handle:x8}");
+
KSynchronizationObject SyncObj = Process.HandleTable.GetObject<KSynchronizationObject>(Handle);
if (SyncObj == null)
@@ -116,12 +118,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- long Result = System.AddressArbiter.ArbitrateLock(
- Process,
- Memory,
- OwnerHandle,
- MutexAddress,
- RequesterHandle);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ long Result = CurrentProcess.AddressArbiter.ArbitrateLock(OwnerHandle, MutexAddress, RequesterHandle);
if (Result != 0)
{
@@ -155,7 +154,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- long Result = System.AddressArbiter.ArbitrateUnlock(Memory, MutexAddress);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ long Result = CurrentProcess.AddressArbiter.ArbitrateUnlock(MutexAddress);
if (Result != 0)
{
@@ -196,8 +197,9 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- long Result = System.AddressArbiter.WaitProcessWideKeyAtomic(
- Memory,
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ long Result = CurrentProcess.AddressArbiter.WaitProcessWideKeyAtomic(
MutexAddress,
CondVarAddress,
ThreadHandle,
@@ -227,7 +229,9 @@ namespace Ryujinx.HLE.HOS.Kernel
"Address = 0x" + Address.ToString("x16") + ", " +
"Count = 0x" + Count .ToString("x8"));
- System.AddressArbiter.SignalProcessWideKey(Process, Memory, Address, Count);
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
+ CurrentProcess.AddressArbiter.SignalProcessWideKey(Address, Count);
ThreadState.X0 = 0;
}
@@ -263,20 +267,22 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
long Result;
switch (Type)
{
case ArbitrationType.WaitIfLessThan:
- Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, false, Timeout);
+ Result = CurrentProcess.AddressArbiter.WaitForAddressIfLessThan(Address, Value, false, Timeout);
break;
case ArbitrationType.DecrementAndWaitIfLessThan:
- Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, true, Timeout);
+ Result = CurrentProcess.AddressArbiter.WaitForAddressIfLessThan(Address, Value, true, Timeout);
break;
case ArbitrationType.WaitIfEqual:
- Result = System.AddressArbiter.WaitForAddressIfEqual(Memory, Address, Value, Timeout);
+ Result = CurrentProcess.AddressArbiter.WaitForAddressIfEqual(Address, Value, Timeout);
break;
default:
@@ -323,20 +329,22 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
+ KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
+
long Result;
switch (Type)
{
case SignalType.Signal:
- Result = System.AddressArbiter.Signal(Address, Count);
+ Result = CurrentProcess.AddressArbiter.Signal(Address, Count);
break;
case SignalType.SignalAndIncrementIfEqual:
- Result = System.AddressArbiter.SignalAndIncrementIfEqual(Memory, Address, Value, Count);
+ Result = CurrentProcess.AddressArbiter.SignalAndIncrementIfEqual(Address, Value, Count);
break;
case SignalType.SignalAndModifyIfEqual:
- Result = System.AddressArbiter.SignalAndModifyIfEqual(Memory, Address, Value, Count);
+ Result = CurrentProcess.AddressArbiter.SignalAndModifyIfEqual(Address, Value, Count);
break;
default:
diff --git a/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs b/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs
index 603446f3..37e5908a 100644
--- a/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs
+++ b/Ryujinx.HLE/HOS/Kernel/ThreadSchedState.cs
@@ -1,11 +1,15 @@
namespace Ryujinx.HLE.HOS.Kernel
{
- enum ThreadSchedState : byte
+ enum ThreadSchedState : ushort
{
- LowNibbleMask = 0xf,
- HighNibbleMask = 0xf0,
- ExceptionalMask = 0x70,
- ForcePauseFlag = 0x20,
+ LowMask = 0xf,
+ HighMask = 0xfff0,
+ ForcePauseMask = 0x70,
+
+ ProcessPauseFlag = 1 << 4,
+ ThreadPauseFlag = 1 << 5,
+ ProcessDebugPauseFlag = 1 << 6,
+ KernelInitPauseFlag = 1 << 8,
None = 0,
Paused = 1,
diff --git a/Ryujinx.HLE/HOS/Kernel/ThreadType.cs b/Ryujinx.HLE/HOS/Kernel/ThreadType.cs
new file mode 100644
index 00000000..0fe83423
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Kernel/ThreadType.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.HLE.HOS.Kernel
+{
+ enum ThreadType
+ {
+ Dummy,
+ Kernel,
+ Kernel2,
+ User
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Process.cs b/Ryujinx.HLE/HOS/Process.cs
deleted file mode 100644
index 93b2d68d..00000000
--- a/Ryujinx.HLE/HOS/Process.cs
+++ /dev/null
@@ -1,528 +0,0 @@
-using ChocolArm64;
-using ChocolArm64.Events;
-using ChocolArm64.Memory;
-using ChocolArm64.State;
-using LibHac;
-using Ryujinx.Common.Logging;
-using Ryujinx.HLE.Exceptions;
-using Ryujinx.HLE.HOS.Diagnostics.Demangler;
-using Ryujinx.HLE.HOS.Kernel;
-using Ryujinx.HLE.HOS.Services.Nv;
-using Ryujinx.HLE.HOS.SystemState;
-using Ryujinx.HLE.Loaders;
-using Ryujinx.HLE.Loaders.Executables;
-using Ryujinx.HLE.Loaders.Npdm;
-using Ryujinx.HLE.Utilities;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-
-namespace Ryujinx.HLE.HOS
-{
- class Process : IDisposable
- {
- private const int TickFreq = 19_200_000;
-
- public Switch Device { get; private set; }
-
- public bool NeedsHbAbi { get; private set; }
-
- public long HbAbiDataPosition { get; private set; }
-
- public int ProcessId { get; private set; }
-
- private Translator Translator;
-
- public MemoryManager Memory { get; private set; }
-
- public KMemoryManager MemoryManager { get; private set; }
-
- private List<KTlsPageManager> TlsPages;
-
- public Npdm MetaData { get; private set; }
-
- public Nacp ControlData { get; set; }
-
- public KProcessHandleTable HandleTable { get; private set; }
-
- public AppletStateMgr AppletState { get; private set; }
-
- private SvcHandler SvcHandler;
-
- private ConcurrentDictionary<long, KThread> Threads;
-
- private List<Executable> Executables;
-
- private long ImageBase;
-
- private bool Disposed;
-
- public Process(Switch Device, int ProcessId, Npdm MetaData)
- {
- this.Device = Device;
- this.MetaData = MetaData;
- this.ProcessId = ProcessId;
-
- Memory = new MemoryManager(Device.Memory.RamPointer);
-
- Memory.InvalidAccess += CpuInvalidAccessHandler;
-
- MemoryManager = new KMemoryManager(this);
-
- TlsPages = new List<KTlsPageManager>();
-
- int HandleTableSize = 1024;
-
- if (MetaData != null)
- {
- foreach (KernelAccessControlItem Item in MetaData.ACI0.KernelAccessControl.Items)
- {
- if (Item.HasHandleTableSize)
- {
- HandleTableSize = Item.HandleTableSize;
-
- break;
- }
- }
- }
-
- HandleTable = new KProcessHandleTable(Device.System, HandleTableSize);
-
- AppletState = new AppletStateMgr(Device.System);
-
- SvcHandler = new SvcHandler(Device, this);
-
- Threads = new ConcurrentDictionary<long, KThread>();
-
- Executables = new List<Executable>();
-
- ImageBase = MemoryManager.CodeRegionStart;
- }
-
- public void LoadProgram(IExecutable Program)
- {
- if (Disposed)
- {
- throw new ObjectDisposedException(nameof(Process));
- }
-
- long ImageEnd = LoadProgram(Program, ImageBase);
-
- ImageBase = IntUtils.AlignUp(ImageEnd, KMemoryManager.PageSize);
- }
-
- public long LoadProgram(IExecutable Program, long ExecutableBase)
- {
- if (Disposed)
- {
- throw new ObjectDisposedException(nameof(Process));
- }
-
- Logger.PrintInfo(LogClass.Loader, $"Image base at 0x{ExecutableBase:x16}.");
-
- Executable Executable = new Executable(Program, MemoryManager, Memory, ExecutableBase);
-
- Executables.Add(Executable);
-
- return Executable.ImageEnd;
- }
-
- public void RemoveProgram(long ExecutableBase)
- {
- foreach (Executable Executable in Executables)
- {
- if (Executable.ImageBase == ExecutableBase)
- {
- Executables.Remove(Executable);
- break;
- }
- }
- }
-
- public void SetEmptyArgs()
- {
- //TODO: This should be part of Run.
- ImageBase += KMemoryManager.PageSize;
- }
-
- public bool Run(bool NeedsHbAbi = false)
- {
- if (Disposed)
- {
- throw new ObjectDisposedException(nameof(Process));
- }
-
- this.NeedsHbAbi = NeedsHbAbi;
-
- if (Executables.Count == 0)
- {
- return false;
- }
-
- long MainStackTop = MemoryManager.CodeRegionEnd - KMemoryManager.PageSize;
-
- long MainStackSize = 1 * 1024 * 1024;
-
- long MainStackBottom = MainStackTop - MainStackSize;
-
- MemoryManager.HleMapCustom(
- MainStackBottom,
- MainStackSize,
- MemoryState.MappedMemory,
- MemoryPermission.ReadAndWrite);
-
- int Handle = MakeThread(Executables[0].ImageBase, MainStackTop, 0, 44, 0);
-
- if (Handle == -1)
- {
- return false;
- }
-
- KThread MainThread = HandleTable.GetKThread(Handle);
-
- if (NeedsHbAbi)
- {
- HbAbiDataPosition = IntUtils.AlignUp(Executables[0].ImageEnd, KMemoryManager.PageSize);
-
- const long HbAbiDataSize = KMemoryManager.PageSize;
-
- MemoryManager.HleMapCustom(
- HbAbiDataPosition,
- HbAbiDataSize,
- MemoryState.MappedMemory,
- MemoryPermission.ReadAndWrite);
-
- string SwitchPath = Device.FileSystem.SystemPathToSwitchPath(Executables[0].FilePath);
-
- Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle, SwitchPath);
-
- MainThread.Context.ThreadState.X0 = (ulong)HbAbiDataPosition;
- MainThread.Context.ThreadState.X1 = ulong.MaxValue;
- }
-
- MainThread.TimeUp();
-
- return true;
- }
-
- private int ThreadIdCtr = 1;
-
- public int MakeThread(
- long EntryPoint,
- long StackTop,
- long ArgsPtr,
- int Priority,
- int ProcessorId)
- {
- if (Disposed)
- {
- throw new ObjectDisposedException(nameof(Process));
- }
-
- CpuThread CpuThread = new CpuThread(GetTranslator(), Memory, EntryPoint);
-
- long Tpidr = GetFreeTls();
-
- int ThreadId = ThreadIdCtr++; //(int)((Tpidr - MemoryManager.TlsIoRegionStart) / 0x200) + 1;
-
- KThread Thread = new KThread(CpuThread, this, Device.System, ProcessorId, Priority, ThreadId);
-
- Thread.LastPc = EntryPoint;
-
- HandleTable.GenerateHandle(Thread, out int Handle);
-
- CpuThread.ThreadState.CntfrqEl0 = TickFreq;
- CpuThread.ThreadState.Tpidr = Tpidr;
-
- CpuThread.ThreadState.X0 = (ulong)ArgsPtr;
- CpuThread.ThreadState.X1 = (ulong)Handle;
- CpuThread.ThreadState.X31 = (ulong)StackTop;
-
- CpuThread.ThreadState.Interrupt += InterruptHandler;
- CpuThread.ThreadState.Break += BreakHandler;
- CpuThread.ThreadState.SvcCall += SvcHandler.SvcCall;
- CpuThread.ThreadState.Undefined += UndefinedHandler;
-
- CpuThread.WorkFinished += ThreadFinished;
-
- Threads.TryAdd(CpuThread.ThreadState.Tpidr, Thread);
-
- return Handle;
- }
-
- private long GetFreeTls()
- {
- long Position;
-
- lock (TlsPages)
- {
- for (int Index = 0; Index < TlsPages.Count; Index++)
- {
- if (TlsPages[Index].TryGetFreeTlsAddr(out Position))
- {
- return Position;
- }
- }
-
- long PagePosition = MemoryManager.HleMapTlsPage();
-
- KTlsPageManager TlsPage = new KTlsPageManager(PagePosition);
-
- TlsPages.Add(TlsPage);
-
- TlsPage.TryGetFreeTlsAddr(out Position);
- }
-
- return Position;
- }
-
- private void InterruptHandler(object sender, EventArgs e)
- {
- Device.System.Scheduler.ContextSwitch();
- }
-
- private void BreakHandler(object sender, InstExceptionEventArgs e)
- {
- PrintStackTraceForCurrentThread();
-
- throw new GuestBrokeExecutionException();
- }
-
- private void UndefinedHandler(object sender, InstUndefinedEventArgs e)
- {
- PrintStackTraceForCurrentThread();
-
- throw new UndefinedInstructionException(e.Position, e.RawOpCode);
- }
-
- public void EnableCpuTracing()
- {
- Translator.EnableCpuTrace = true;
- }
-
- public void DisableCpuTracing()
- {
- Translator.EnableCpuTrace = false;
- }
-
- private void CpuTraceHandler(object sender, CpuTraceEventArgs e)
- {
- Executable Exe = GetExecutable(e.Position);
-
- if (Exe == null)
- {
- return;
- }
-
- if (!TryGetSubName(Exe, e.Position, out string SubName))
- {
- SubName = string.Empty;
- }
-
- long Offset = e.Position - Exe.ImageBase;
-
- string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}";
-
- Logger.PrintDebug(LogClass.Cpu, ExeNameWithAddr + " " + SubName);
- }
-
- private Translator GetTranslator()
- {
- if (Translator == null)
- {
- Translator = new Translator();
-
- Translator.CpuTrace += CpuTraceHandler;
- }
-
- return Translator;
- }
-
- private void CpuInvalidAccessHandler(object sender, InvalidAccessEventArgs e)
- {
- PrintStackTraceForCurrentThread();
- }
-
- private void PrintStackTraceForCurrentThread()
- {
- foreach (KThread Thread in Threads.Values)
- {
- if (Thread.Context.IsCurrentThread())
- {
- PrintStackTrace(Thread.Context.ThreadState);
-
- break;
- }
- }
- }
-
- public void PrintStackTrace(CpuThreadState ThreadState)
- {
- StringBuilder Trace = new StringBuilder();
-
- Trace.AppendLine("Guest stack trace:");
-
- void AppendTrace(long Position)
- {
- Executable Exe = GetExecutable(Position);
-
- if (Exe == null)
- {
- return;
- }
-
- if (!TryGetSubName(Exe, Position, out string SubName))
- {
- SubName = $"Sub{Position:x16}";
- }
- else if (SubName.StartsWith("_Z"))
- {
- SubName = Demangler.Parse(SubName);
- }
-
- long Offset = Position - Exe.ImageBase;
-
- string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}";
-
- Trace.AppendLine(" " + ExeNameWithAddr + " " + SubName);
- }
-
- long FramePointer = (long)ThreadState.X29;
-
- while (FramePointer != 0)
- {
- AppendTrace(Memory.ReadInt64(FramePointer + 8));
-
- FramePointer = Memory.ReadInt64(FramePointer);
- }
-
- Logger.PrintInfo(LogClass.Cpu, Trace.ToString());
- }
-
- private bool TryGetSubName(Executable Exe, long Position, out string Name)
- {
- Position -= Exe.ImageBase;
-
- int Left = 0;
- int Right = Exe.SymbolTable.Count - 1;
-
- while (Left <= Right)
- {
- int Size = Right - Left;
-
- int Middle = Left + (Size >> 1);
-
- ElfSym Symbol = Exe.SymbolTable[Middle];
-
- long EndPosition = Symbol.Value + Symbol.Size;
-
- if ((ulong)Position >= (ulong)Symbol.Value && (ulong)Position < (ulong)EndPosition)
- {
- Name = Symbol.Name;
-
- return true;
- }
-
- if ((ulong)Position < (ulong)Symbol.Value)
- {
- Right = Middle - 1;
- }
- else
- {
- Left = Middle + 1;
- }
- }
-
- Name = null;
-
- return false;
- }
-
- private Executable GetExecutable(long Position)
- {
- string Name = string.Empty;
-
- for (int Index = Executables.Count - 1; Index >= 0; Index--)
- {
- if ((ulong)Position >= (ulong)Executables[Index].ImageBase)
- {
- return Executables[Index];
- }
- }
-
- return null;
- }
-
- private void ThreadFinished(object sender, EventArgs e)
- {
- if (sender is CpuThread Thread)
- {
- if (Threads.TryRemove(Thread.ThreadState.Tpidr, out KThread KernelThread))
- {
- Device.System.Scheduler.RemoveThread(KernelThread);
- }
- }
-
- if (Threads.Count == 0)
- {
- Device.System.ExitProcess(ProcessId);
- }
- }
-
- public KThread GetThread(long Tpidr)
- {
- if (!Threads.TryGetValue(Tpidr, out KThread Thread))
- {
- throw new InvalidOperationException();
- }
-
- return Thread;
- }
-
- private void Unload()
- {
- if (Disposed || Threads.Count > 0)
- {
- return;
- }
-
- Disposed = true;
-
- HandleTable.Destroy();
-
- INvDrvServices.UnloadProcess(this);
-
- if (NeedsHbAbi && Executables.Count > 0 && Executables[0].FilePath.EndsWith(Homebrew.TemporaryNroSuffix))
- {
- File.Delete(Executables[0].FilePath);
- }
-
- Logger.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting...");
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool Disposing)
- {
- if (Disposing)
- {
- if (Threads.Count > 0)
- {
- foreach (KThread Thread in Threads.Values)
- {
- Device.System.Scheduler.StopThread(Thread);
- }
- }
- else
- {
- Unload();
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs
new file mode 100644
index 00000000..ddacd3fd
--- /dev/null
+++ b/Ryujinx.HLE/HOS/ProgramLoader.cs
@@ -0,0 +1,292 @@
+using ChocolArm64.Memory;
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Kernel;
+using Ryujinx.HLE.Loaders.Executables;
+using Ryujinx.HLE.Loaders.Npdm;
+
+namespace Ryujinx.HLE.HOS
+{
+ class ProgramLoader
+ {
+ private const bool AslrEnabled = true;
+
+ private const int ArgsHeaderSize = 8;
+ private const int ArgsDataSize = 0x9000;
+ private const int ArgsTotalSize = ArgsHeaderSize + ArgsDataSize;
+
+ public static bool LoadKernelInitalProcess(Horizon System, KernelInitialProcess Kip)
+ {
+ int EndOffset = Kip.DataOffset + Kip.Data.Length;
+
+ if (Kip.BssSize != 0)
+ {
+ EndOffset = Kip.BssOffset + Kip.BssSize;
+ }
+
+ int CodeSize = BitUtils.AlignUp(Kip.TextOffset + EndOffset, KMemoryManager.PageSize);
+
+ int CodePagesCount = CodeSize / KMemoryManager.PageSize;
+
+ ulong CodeBaseAddress = Kip.Addr39Bits ? 0x8000000UL : 0x200000UL;
+
+ ulong CodeAddress = CodeBaseAddress + (ulong)Kip.TextOffset;
+
+ int MmuFlags = 0;
+
+ if (AslrEnabled)
+ {
+ //TODO: Randomization.
+
+ MmuFlags |= 0x20;
+ }
+
+ if (Kip.Addr39Bits)
+ {
+ MmuFlags |= (int)AddressSpaceType.Addr39Bits << 1;
+ }
+
+ if (Kip.Is64Bits)
+ {
+ MmuFlags |= 1;
+ }
+
+ ProcessCreationInfo CreationInfo = new ProcessCreationInfo(
+ Kip.Name,
+ Kip.ProcessCategory,
+ Kip.TitleId,
+ CodeAddress,
+ CodePagesCount,
+ MmuFlags,
+ 0,
+ 0);
+
+ MemoryRegion MemRegion = Kip.IsService
+ ? MemoryRegion.Service
+ : MemoryRegion.Application;
+
+ KMemoryRegionManager Region = System.MemoryRegions[(int)MemRegion];
+
+ KernelResult Result = Region.AllocatePages((ulong)CodePagesCount, false, out KPageList PageList);
+
+ if (Result != KernelResult.Success)
+ {
+ Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
+
+ return false;
+ }
+
+ KProcess Process = new KProcess(System);
+
+ Result = Process.InitializeKip(
+ CreationInfo,
+ Kip.Capabilities,
+ PageList,
+ System.ResourceLimit,
+ MemRegion);
+
+ if (Result != KernelResult.Success)
+ {
+ Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
+
+ return false;
+ }
+
+ Result = LoadIntoMemory(Process, Kip, CodeBaseAddress);
+
+ if (Result != KernelResult.Success)
+ {
+ Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
+
+ return false;
+ }
+
+ Result = Process.Start(Kip.MainThreadPriority, (ulong)Kip.MainThreadStackSize);
+
+ if (Result != KernelResult.Success)
+ {
+ Logger.PrintError(LogClass.Loader, $"Process start returned error \"{Result}\".");
+
+ return false;
+ }
+
+ System.Processes.Add(Process.Pid, Process);
+
+ return true;
+ }
+
+ public static bool LoadStaticObjects(
+ Horizon System,
+ Npdm MetaData,
+ IExecutable[] StaticObjects,
+ byte[] Arguments = null)
+ {
+ ulong ArgsStart = 0;
+ int ArgsSize = 0;
+ ulong CodeStart = 0x8000000;
+ int CodeSize = 0;
+
+ ulong[] NsoBase = new ulong[StaticObjects.Length];
+
+ for (int Index = 0; Index < StaticObjects.Length; Index++)
+ {
+ IExecutable StaticObject = StaticObjects[Index];
+
+ int TextEnd = StaticObject.TextOffset + StaticObject.Text.Length;
+ int ROEnd = StaticObject.ROOffset + StaticObject.RO.Length;
+ int DataEnd = StaticObject.DataOffset + StaticObject.Data.Length + StaticObject.BssSize;
+
+ int NsoSize = TextEnd;
+
+ if ((uint)NsoSize < (uint)ROEnd)
+ {
+ NsoSize = ROEnd;
+ }
+
+ if ((uint)NsoSize < (uint)DataEnd)
+ {
+ NsoSize = DataEnd;
+ }
+
+ NsoSize = BitUtils.AlignUp(NsoSize, KMemoryManager.PageSize);
+
+ NsoBase[Index] = CodeStart + (ulong)CodeSize;
+
+ CodeSize += NsoSize;
+
+ if (Arguments != null && ArgsSize == 0)
+ {
+ ArgsStart = (ulong)CodeSize;
+
+ ArgsSize = BitUtils.AlignDown(Arguments.Length * 2 + ArgsTotalSize - 1, KMemoryManager.PageSize);
+
+ CodeSize += ArgsSize;
+ }
+ }
+
+ int CodePagesCount = CodeSize / KMemoryManager.PageSize;
+
+ int PersonalMmHeapPagesCount = MetaData.PersonalMmHeapSize / KMemoryManager.PageSize;
+
+ ProcessCreationInfo CreationInfo = new ProcessCreationInfo(
+ MetaData.TitleName,
+ MetaData.ProcessCategory,
+ MetaData.ACI0.TitleId,
+ CodeStart,
+ CodePagesCount,
+ MetaData.MmuFlags,
+ 0,
+ PersonalMmHeapPagesCount);
+
+ KernelResult Result;
+
+ KResourceLimit ResourceLimit = new KResourceLimit(System);
+
+ long ApplicationRgSize = (long)System.MemoryRegions[(int)MemoryRegion.Application].Size;
+
+ Result = ResourceLimit.SetLimitValue(LimitableResource.Memory, ApplicationRgSize);
+ Result |= ResourceLimit.SetLimitValue(LimitableResource.Thread, 608);
+ Result |= ResourceLimit.SetLimitValue(LimitableResource.Event, 700);
+ Result |= ResourceLimit.SetLimitValue(LimitableResource.TransferMemory, 128);
+ Result |= ResourceLimit.SetLimitValue(LimitableResource.Session, 894);
+
+ if (Result != KernelResult.Success)
+ {
+ Logger.PrintError(LogClass.Loader, $"Process initialization failed setting resource limit values.");
+
+ return false;
+ }
+
+ KProcess Process = new KProcess(System);
+
+ Result = Process.Initialize(
+ CreationInfo,
+ MetaData.ACI0.KernelAccessControl.Capabilities,
+ ResourceLimit,
+ MemoryRegion.Application);
+
+ if (Result != KernelResult.Success)
+ {
+ Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
+
+ return false;
+ }
+
+ for (int Index = 0; Index < StaticObjects.Length; Index++)
+ {
+ Logger.PrintInfo(LogClass.Loader, $"Loading image {Index} at 0x{NsoBase[Index]:x16}...");
+
+ Result = LoadIntoMemory(Process, StaticObjects[Index], NsoBase[Index]);
+
+ if (Result != KernelResult.Success)
+ {
+ Logger.PrintError(LogClass.Loader, $"Process initialization returned error \"{Result}\".");
+
+ return false;
+ }
+ }
+
+ Result = Process.Start(MetaData.MainThreadPriority, (ulong)MetaData.MainThreadStackSize);
+
+ if (Result != KernelResult.Success)
+ {
+ Logger.PrintError(LogClass.Loader, $"Process start returned error \"{Result}\".");
+
+ return false;
+ }
+
+ System.Processes.Add(Process.Pid, Process);
+
+ return true;
+ }
+
+ private static KernelResult LoadIntoMemory(KProcess Process, IExecutable Image, ulong BaseAddress)
+ {
+ ulong TextStart = BaseAddress + (ulong)Image.TextOffset;
+ ulong ROStart = BaseAddress + (ulong)Image.ROOffset;
+ ulong DataStart = BaseAddress + (ulong)Image.DataOffset;
+ ulong BssStart = BaseAddress + (ulong)Image.BssOffset;
+
+ ulong End = DataStart + (ulong)Image.Data.Length;
+
+ if (Image.BssSize != 0)
+ {
+ End = BssStart + (ulong)Image.BssSize;
+ }
+
+ Process.CpuMemory.WriteBytes((long)TextStart, Image.Text);
+ Process.CpuMemory.WriteBytes((long)ROStart, Image.RO);
+ Process.CpuMemory.WriteBytes((long)DataStart, Image.Data);
+
+ MemoryHelper.FillWithZeros(Process.CpuMemory, (long)BssStart, Image.BssSize);
+
+ KernelResult SetProcessMemoryPermission(ulong Address, ulong Size, MemoryPermission Permission)
+ {
+ if (Size == 0)
+ {
+ return KernelResult.Success;
+ }
+
+ Size = BitUtils.AlignUp(Size, KMemoryManager.PageSize);
+
+ return Process.MemoryManager.SetProcessMemoryPermission(Address, Size, Permission);
+ }
+
+ KernelResult Result = SetProcessMemoryPermission(TextStart, (ulong)Image.Text.Length, MemoryPermission.ReadAndExecute);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ Result = SetProcessMemoryPermission(ROStart, (ulong)Image.RO.Length, MemoryPermission.Read);
+
+ if (Result != KernelResult.Success)
+ {
+ return Result;
+ }
+
+ return SetProcessMemoryPermission(DataStart, End - DataStart, MemoryPermission.ReadAndWrite);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/ServiceCtx.cs b/Ryujinx.HLE/HOS/ServiceCtx.cs
index a591673e..76c426bc 100644
--- a/Ryujinx.HLE/HOS/ServiceCtx.cs
+++ b/Ryujinx.HLE/HOS/ServiceCtx.cs
@@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS
class ServiceCtx
{
public Switch Device { get; private set; }
- public Process Process { get; private set; }
+ public KProcess Process { get; private set; }
public MemoryManager Memory { get; private set; }
public KSession Session { get; private set; }
public IpcMessage Request { get; private set; }
@@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS
public ServiceCtx(
Switch Device,
- Process Process,
+ KProcess Process,
MemoryManager Memory,
KSession Session,
IpcMessage Request,
diff --git a/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs b/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs
index 6b012689..2feaf8fc 100644
--- a/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs
@@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
public long GetEventHandle(ServiceCtx Context)
{
- KEvent Event = Context.Process.AppletState.MessageEvent;
+ KEvent Event = Context.Device.System.AppletState.MessageEvent;
if (Context.Process.HandleTable.GenerateHandle(Event.ReadableEvent, out int Handle) != KernelResult.Success)
{
@@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
public long ReceiveMessage(ServiceCtx Context)
{
- if (!Context.Process.AppletState.TryDequeueMessage(out MessageInfo Message))
+ if (!Context.Device.System.AppletState.TryDequeueMessage(out MessageInfo Message))
{
return MakeError(ErrorModule.Am, AmErr.NoMessages);
}
@@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
public long GetCurrentFocusState(ServiceCtx Context)
{
- Context.ResponseData.Write((byte)Context.Process.AppletState.FocusState);
+ Context.ResponseData.Write((byte)Context.Device.System.AppletState.FocusState);
return 0;
}
diff --git a/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs
index 4d595fde..f0899bd4 100644
--- a/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs
+++ b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs
@@ -1,4 +1,7 @@
-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;
using System;
@@ -62,17 +65,32 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
class NroInfo
{
- public Nro Executable { get; private set; }
- public byte[] Hash { get; private set; }
- public long NroAddress { get; private set; }
- public long TotalSize { get; private set; }
- public long NroMappedAddress { get; set; }
+ public NxRelocatableObject Executable { get; private set; }
- public NroInfo(Nro Executable, byte[] Hash, long TotalSize)
+ 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 NroAddress,
+ ulong NroSize,
+ ulong BssAddress,
+ ulong BssSize,
+ ulong TotalSize)
{
this.Executable = Executable;
this.Hash = Hash;
- this.TotalSize = TotalSize;
+ this.NroAddress = NroAddress;
+ this.NroSize = NroSize;
+ this.BssAddress = BssAddress;
+ this.BssSize = BssSize;
+ this.TotalSize = TotalSize;
}
}
@@ -174,7 +192,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
return false;
}
- public long ParseNro(out NroInfo Res, ServiceCtx Context, long NroHeapAddress, long NroSize, long BssHeapAddress, long BssSize)
+ public long ParseNro(out NroInfo Res, ServiceCtx Context, ulong NroAddress, ulong NroSize, ulong BssAddress, ulong BssSize)
{
Res = null;
@@ -182,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(NroHeapAddress + 0x10);
- uint NroFileSize = Context.Memory.ReadUInt32(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(NroHeapAddress, NroSize);
+ byte[] NroData = Context.Memory.ReadBytes((long)NroAddress, (long)NroSize);
byte[] NroHash = null;
MemoryStream Stream = new MemoryStream(NroData);
@@ -225,67 +243,106 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
Stream.Position = 0;
- Nro Executable = new Nro(Stream, "memory", 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
- || (Executable.Data.Length & 0xFFF) != 0 || (Executable.BssSize & 0xFFF) != 0)
+ if ((Executable.Text.Length & 0xFFF) != 0 || (Executable.RO.Length & 0xFFF) != 0 ||
+ (Executable.Data.Length & 0xFFF) != 0 || (Executable.BssSize & 0xFFF) != 0)
{
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
}
// check if everything is contiguous.
- if (Executable.ROOffset != Executable.TextOffset + Executable.Text.Length
- || Executable.DataOffset != Executable.ROOffset + Executable.RO.Length
- || NroFileSize != Executable.DataOffset + Executable.Data.Length)
+ if (Executable.ROOffset != Executable.TextOffset + Executable.Text.Length ||
+ Executable.DataOffset != Executable.ROOffset + Executable.RO.Length ||
+ NroFileSize != Executable.DataOffset + Executable.Data.Length)
{
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
}
// finally check the bss size match.
- if (Executable.BssSize != BssSize)
+ if ((ulong)Executable.BssSize != BssSize)
{
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
}
- Res = new NroInfo(Executable, NroHash, 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,
+ NroAddress,
+ NroSize,
+ BssAddress,
+ BssSize,
+ (ulong)TotalSize);
return 0;
}
- private long MapNro(ServiceCtx Context, NroInfo Info, out long NroMappedAddress)
+ private long MapNro(ServiceCtx Context, NroInfo Info, out ulong NroMappedAddress)
{
NroMappedAddress = 0;
- long TargetAddress = Context.Process.MemoryManager.AddrSpaceStart;
- long HeapRegionStart = Context.Process.MemoryManager.HeapRegionStart;
- long HeapRegionEnd = Context.Process.MemoryManager.HeapRegionEnd;
+ KMemoryManager MemMgr = Context.Process.MemoryManager;
- long MapRegionStart = Context.Process.MemoryManager.MapRegionStart;
- long MapRegionEnd = Context.Process.MemoryManager.MapRegionEnd;
+ ulong TargetAddress = MemMgr.GetAddrSpaceBaseAddr();
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;
+ }
+
+ KernelResult Result = MemMgr.MapProcessCodeMemory(TargetAddress, Info.NroAddress, Info.NroSize);
+
+ if (Result != KernelResult.Success)
+ {
+ return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState);
+ }
+
+ ulong BssTargetAddress = TargetAddress + Info.NroSize;
+
+ if (Info.BssSize != 0)
+ {
+ 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);
+ }
}
- Context.Process.LoadProgram(Info.Executable, TargetAddress);
+ Result = LoadNroIntoMemory(Context.Process, Info.Executable, TargetAddress);
+
+ if (Result != KernelResult.Success)
+ {
+ MemMgr.UnmapProcessCodeMemory(TargetAddress, Info.NroAddress, Info.NroSize);
+
+ if (Info.BssSize != 0)
+ {
+ MemMgr.UnmapProcessCodeMemory(BssTargetAddress, Info.BssAddress, Info.BssSize);
+ }
+
+ return 0;
+ }
Info.NroMappedAddress = TargetAddress;
NroMappedAddress = TargetAddress;
@@ -293,6 +350,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)
@@ -308,24 +400,46 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
return MakeError(ErrorModule.Loader, LoaderErr.BadNrrAddress);
}
- private long RemoveNroInfo(ServiceCtx Context, long NroMappedAddress, long NroHeapAddress)
+ private long RemoveNroInfo(ServiceCtx Context, ulong NroMappedAddress)
{
foreach (NroInfo Info in NroInfos)
{
- if (Info.NroMappedAddress == NroMappedAddress && Info.Executable.SourceAddress == NroHeapAddress)
+ if (Info.NroMappedAddress == NroMappedAddress)
{
NroInfos.Remove(Info);
- Context.Process.RemoveProgram(Info.NroMappedAddress);
+ 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;
- long Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress, Info.Executable.SourceAddress, Info.TotalSize - Info.Executable.BssSize);
+ KernelResult Result = KernelResult.Success;
- if (Result == 0 && Info.Executable.BssSize != 0)
+ if (Info.Executable.BssSize != 0)
{
- Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress + Info.TotalSize - Info.Executable.BssSize, Info.Executable.BssAddress, Info.Executable.BssSize);
+ Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(
+ Info.NroMappedAddress + TextSize + ROSize + DataSize,
+ Info.Executable.BssAddress,
+ BssSize);
}
- return Result;
+ 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 (long)Result;
}
}
@@ -340,12 +454,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
// Zero
Context.RequestData.ReadUInt64();
- long NroHeapAddress = Context.RequestData.ReadInt64();
- long NroSize = Context.RequestData.ReadInt64();
- long BssHeapAddress = Context.RequestData.ReadInt64();
- long BssSize = Context.RequestData.ReadInt64();
+ ulong NroHeapAddress = Context.RequestData.ReadUInt64();
+ ulong NroSize = Context.RequestData.ReadUInt64();
+ ulong BssHeapAddress = Context.RequestData.ReadUInt64();
+ ulong BssSize = Context.RequestData.ReadUInt64();
- long NroMappedAddress = 0;
+ ulong NroMappedAddress = 0;
if (IsInitialized)
{
@@ -374,17 +488,19 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
{
long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization);
- long NroMappedAddress = Context.RequestData.ReadInt64();
- long NroHeapAddress = Context.RequestData.ReadInt64();
+ // Zero
+ Context.RequestData.ReadUInt64();
+
+ ulong NroMappedAddress = Context.RequestData.ReadUInt64();
if (IsInitialized)
{
- if ((NroMappedAddress & 0xFFF) != 0 || (NroHeapAddress & 0xFFF) != 0)
+ if ((NroMappedAddress & 0xFFF) != 0)
{
return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress);
}
- Result = RemoveNroInfo(Context, NroMappedAddress, NroHeapAddress);
+ Result = RemoveNroInfo(Context, NroMappedAddress);
}
return Result;
diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
index b8ae11ce..6786d0e2 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
@@ -205,7 +205,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
return ((Cmd >> 31) & 1) != 0;
}
- public static void UnloadProcess(Process Process)
+ public static void UnloadProcess(KProcess Process)
{
Fds.DeleteProcess(Process);
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
index fed41042..7fe3bbed 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs
@@ -1,6 +1,7 @@
using ChocolArm64.Memory;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Memory;
+using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Services.Nv.NvMap;
using System;
using System.Collections.Concurrent;
@@ -13,11 +14,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS
private const int FlagRemapSubRange = 0x100;
- private static ConcurrentDictionary<Process, NvGpuASCtx> ASCtxs;
+ private static ConcurrentDictionary<KProcess, NvGpuASCtx> ASCtxs;
static NvGpuASIoctl()
{
- ASCtxs = new ConcurrentDictionary<Process, NvGpuASCtx>();
+ ASCtxs = new ConcurrentDictionary<KProcess, NvGpuASCtx>();
}
public static int ProcessIoctl(ServiceCtx Context, int Cmd)
@@ -321,7 +322,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS
return ASCtxs.GetOrAdd(Context.Process, (Key) => new NvGpuASCtx(Context));
}
- public static void UnloadProcess(Process Process)
+ public static void UnloadProcess(KProcess Process)
{
ASCtxs.TryRemove(Process, out _);
}
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
index 39f39d45..d9f5602b 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
@@ -1,6 +1,7 @@
using ChocolArm64.Memory;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Memory;
+using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS;
using System;
using System.Collections.Concurrent;
@@ -21,11 +22,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
}
}
- private static ConcurrentDictionary<Process, ChannelsPerProcess> Channels;
+ private static ConcurrentDictionary<KProcess, ChannelsPerProcess> Channels;
static NvHostChannelIoctl()
{
- Channels = new ConcurrentDictionary<Process, ChannelsPerProcess>();
+ Channels = new ConcurrentDictionary<KProcess, ChannelsPerProcess>();
}
public static int ProcessIoctlGpu(ServiceCtx Context, int Cmd)
@@ -194,7 +195,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
return Cpp.Channels[Channel];
}
- public static void UnloadProcess(Process Process)
+ public static void UnloadProcess(KProcess Process)
{
Channels.TryRemove(Process, out _);
}
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs
index 6cb14741..bf92afb4 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs
@@ -1,5 +1,6 @@
using ChocolArm64.Memory;
using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Kernel;
using System;
using System.Collections.Concurrent;
using System.Text;
@@ -9,13 +10,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl
{
class NvHostCtrlIoctl
{
- private static ConcurrentDictionary<Process, NvHostCtrlUserCtx> UserCtxs;
+ private static ConcurrentDictionary<KProcess, NvHostCtrlUserCtx> UserCtxs;
private static bool IsProductionMode = true;
static NvHostCtrlIoctl()
{
- UserCtxs = new ConcurrentDictionary<Process, NvHostCtrlUserCtx>();
+ UserCtxs = new ConcurrentDictionary<KProcess, NvHostCtrlUserCtx>();
if (Set.NxSettings.Settings.TryGetValue("nv!rmos_set_production_mode", out object ProductionModeSetting))
{
@@ -390,7 +391,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl
return UserCtxs.GetOrAdd(Context.Process, (Key) => new NvHostCtrlUserCtx());
}
- public static void UnloadProcess(Process Process)
+ public static void UnloadProcess(KProcess Process)
{
UserCtxs.TryRemove(Process, out _);
}
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs
index f5378ef7..adc523e5 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs
@@ -1,6 +1,7 @@
using ChocolArm64.Memory;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Memory;
+using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.Utilities;
using System.Collections.Concurrent;
@@ -10,11 +11,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap
{
private const int FlagNotFreedYet = 1;
- private static ConcurrentDictionary<Process, IdDictionary> Maps;
+ private static ConcurrentDictionary<KProcess, IdDictionary> Maps;
static NvMapIoctl()
{
- Maps = new ConcurrentDictionary<Process, IdDictionary>();
+ Maps = new ConcurrentDictionary<KProcess, IdDictionary>();
}
public static int ProcessIoctl(ServiceCtx Context, int Cmd)
@@ -130,10 +131,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap
//When the address is zero, we need to allocate
//our own backing memory for the NvMap.
//TODO: Is this allocation inside the transfer memory?
- if (!Context.Device.Memory.Allocator.TryAllocate((uint)Size, out Address))
- {
- Result = NvResult.OutOfMemory;
- }
+ Result = NvResult.OutOfMemory;
}
if (Result == NvResult.Success)
@@ -294,7 +292,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap
return null;
}
- public static void UnloadProcess(Process Process)
+ public static void UnloadProcess(KProcess Process)
{
Maps.TryRemove(Process, out _);
}
diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs
index 64e0b4a9..facfbe60 100644
--- a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs
+++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs
@@ -275,8 +275,7 @@ namespace Ryujinx.HLE.HOS.Services.Android
private long MakeReplyParcel(ServiceCtx Context, byte[] Data)
{
- long ReplyPos = Context.Request.ReceiveBuff[0].Position;
- long ReplySize = Context.Request.ReceiveBuff[0].Size;
+ (long ReplyPos, long ReplySize) = Context.Request.GetBufferType0x22();
byte[] Reply = MakeParcel(Data, new byte[0]);
diff --git a/Ryujinx.HLE/Homebrew.npdm b/Ryujinx.HLE/Homebrew.npdm
new file mode 100644
index 00000000..81411614
--- /dev/null
+++ b/Ryujinx.HLE/Homebrew.npdm
Binary files differ
diff --git a/Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs b/Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs
new file mode 100644
index 00000000..43cc601f
--- /dev/null
+++ b/Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs
@@ -0,0 +1,105 @@
+using System;
+using System.IO;
+
+namespace Ryujinx.HLE.Loaders.Compression
+{
+ static class BackwardsLz
+ {
+ private class BackwardsReader
+ {
+ private Stream BaseStream;
+
+ public BackwardsReader(Stream BaseStream)
+ {
+ this.BaseStream = BaseStream;
+ }
+
+ public byte ReadByte()
+ {
+ BaseStream.Seek(-1, SeekOrigin.Current);
+
+ byte Value = (byte)BaseStream.ReadByte();
+
+ BaseStream.Seek(-1, SeekOrigin.Current);
+
+ return Value;
+ }
+
+ public short ReadInt16()
+ {
+ return (short)((ReadByte() << 8) | (ReadByte() << 0));
+ }
+
+ public int ReadInt32()
+ {
+ return ((ReadByte() << 24) |
+ (ReadByte() << 16) |
+ (ReadByte() << 8) |
+ (ReadByte() << 0));
+ }
+ }
+
+ public static byte[] Decompress(Stream Input, int DecompressedLength)
+ {
+ long End = Input.Position;
+
+ BackwardsReader Reader = new BackwardsReader(Input);
+
+ int AdditionalDecLength = Reader.ReadInt32();
+ int StartOffset = Reader.ReadInt32();
+ int CompressedLength = Reader.ReadInt32();
+
+ Input.Seek(12 - StartOffset, SeekOrigin.Current);
+
+ byte[] Dec = new byte[DecompressedLength];
+
+ int DecompressedLengthUnpadded = CompressedLength + AdditionalDecLength;
+
+ int DecompressionStart = DecompressedLength - DecompressedLengthUnpadded;
+
+ int DecPos = Dec.Length;
+
+ byte Mask = 0;
+ byte Header = 0;
+
+ while (DecPos > DecompressionStart)
+ {
+ if ((Mask >>= 1) == 0)
+ {
+ Header = Reader.ReadByte();
+ Mask = 0x80;
+ }
+
+ if ((Header & Mask) == 0)
+ {
+ Dec[--DecPos] = Reader.ReadByte();
+ }
+ else
+ {
+ ushort Pair = (ushort)Reader.ReadInt16();
+
+ int Length = (Pair >> 12) + 3;
+ int Position = (Pair & 0xfff) + 3;
+
+ DecPos -= Length;
+
+ if (Length <= Position)
+ {
+ int SrcPos = DecPos + Position;
+
+ Buffer.BlockCopy(Dec, SrcPos, Dec, DecPos, Length);
+ }
+ else
+ {
+ for (int Offset = 0; Offset < Length; Offset++)
+ {
+ Dec[DecPos + Offset] = Dec[DecPos + Position + Offset];
+ }
+ }
+ }
+ }
+
+ return Dec;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs b/Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs
new file mode 100644
index 00000000..fb0ea53e
--- /dev/null
+++ b/Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.HLE.Loaders.Elf
+{
+ struct ElfDynamic
+ {
+ public ElfDynamicTag Tag { get; private set; }
+
+ public long Value { get; private set; }
+
+ public ElfDynamic(ElfDynamicTag Tag, long Value)
+ {
+ this.Tag = Tag;
+ this.Value = Value;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/ElfDynTag.cs b/Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs
index 5915d4d1..9d7ad72e 100644
--- a/Ryujinx.HLE/Loaders/ElfDynTag.cs
+++ b/Ryujinx.HLE/Loaders/Elf/ElfDynamicTag.cs
@@ -1,6 +1,6 @@
-namespace Ryujinx.HLE.Loaders
+namespace Ryujinx.HLE.Loaders.Elf
{
- enum ElfDynTag
+ enum ElfDynamicTag
{
DT_NULL = 0,
DT_NEEDED = 1,
diff --git a/Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs b/Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs
new file mode 100644
index 00000000..3f3a2a79
--- /dev/null
+++ b/Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs
@@ -0,0 +1,40 @@
+namespace Ryujinx.HLE.Loaders.Elf
+{
+ struct ElfSymbol
+ {
+ public string Name { get; private set; }
+
+ public ElfSymbolType Type { get; private set; }
+ public ElfSymbolBinding Binding { get; private set; }
+ public ElfSymbolVisibility Visibility { get; private set; }
+
+ public bool IsFuncOrObject =>
+ Type == ElfSymbolType.STT_FUNC ||
+ Type == ElfSymbolType.STT_OBJECT;
+
+ public bool IsGlobalOrWeak =>
+ Binding == ElfSymbolBinding.STB_GLOBAL ||
+ Binding == ElfSymbolBinding.STB_WEAK;
+
+ public int SHIdx { get; private set; }
+ public long Value { get; private set; }
+ public long Size { get; private set; }
+
+ public ElfSymbol(
+ string Name,
+ int Info,
+ int Other,
+ int SHIdx,
+ long Value,
+ long Size)
+ {
+ this.Name = Name;
+ this.Type = (ElfSymbolType)(Info & 0xf);
+ this.Binding = (ElfSymbolBinding)(Info >> 4);
+ this.Visibility = (ElfSymbolVisibility)Other;
+ this.SHIdx = SHIdx;
+ this.Value = Value;
+ this.Size = Size;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/ElfSymBinding.cs b/Ryujinx.HLE/Loaders/Elf/ElfSymbolBinding.cs
index f1d249ec..3c915311 100644
--- a/Ryujinx.HLE/Loaders/ElfSymBinding.cs
+++ b/Ryujinx.HLE/Loaders/Elf/ElfSymbolBinding.cs
@@ -1,6 +1,6 @@
-namespace Ryujinx.HLE.Loaders
+namespace Ryujinx.HLE.Loaders.Elf
{
- enum ElfSymBinding
+ enum ElfSymbolBinding
{
STB_LOCAL = 0,
STB_GLOBAL = 1,
diff --git a/Ryujinx.HLE/Loaders/ElfSymType.cs b/Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs
index 478064bc..f22e6c45 100644
--- a/Ryujinx.HLE/Loaders/ElfSymType.cs
+++ b/Ryujinx.HLE/Loaders/Elf/ElfSymbolType.cs
@@ -1,6 +1,6 @@
-namespace Ryujinx.HLE.Loaders
+namespace Ryujinx.HLE.Loaders.Elf
{
- enum ElfSymType
+ enum ElfSymbolType
{
STT_NOTYPE = 0,
STT_OBJECT = 1,
diff --git a/Ryujinx.HLE/Loaders/ElfSymVisibility.cs b/Ryujinx.HLE/Loaders/Elf/ElfSymbolVisibility.cs
index fe7243a7..4bec50a3 100644
--- a/Ryujinx.HLE/Loaders/ElfSymVisibility.cs
+++ b/Ryujinx.HLE/Loaders/Elf/ElfSymbolVisibility.cs
@@ -1,6 +1,6 @@
-namespace Ryujinx.HLE.Loaders
+namespace Ryujinx.HLE.Loaders.Elf
{
- enum ElfSymVisibility
+ enum ElfSymbolVisibility
{
STV_DEFAULT = 0,
STV_INTERNAL = 1,
diff --git a/Ryujinx.HLE/Loaders/ElfDyn.cs b/Ryujinx.HLE/Loaders/ElfDyn.cs
deleted file mode 100644
index 3508e6e4..00000000
--- a/Ryujinx.HLE/Loaders/ElfDyn.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Ryujinx.HLE.Loaders
-{
- struct ElfDyn
- {
- public ElfDynTag Tag { get; private set; }
-
- public long Value { get; private set; }
-
- public ElfDyn(ElfDynTag Tag, long Value)
- {
- this.Tag = Tag;
- this.Value = Value;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/ElfRel.cs b/Ryujinx.HLE/Loaders/ElfRel.cs
deleted file mode 100644
index cfc31d89..00000000
--- a/Ryujinx.HLE/Loaders/ElfRel.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace Ryujinx.HLE.Loaders
-{
- struct ElfRel
- {
- public long Offset { get; private set; }
- public long Addend { get; private set; }
-
- public ElfSym Symbol { get; private set; }
- public ElfRelType Type { get; private set; }
-
- public ElfRel(long Offset, long Addend, ElfSym Symbol, ElfRelType Type)
- {
- this.Offset = Offset;
- this.Addend = Addend;
- this.Symbol = Symbol;
- this.Type = Type;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/ElfRelType.cs b/Ryujinx.HLE/Loaders/ElfRelType.cs
deleted file mode 100644
index 7da5eec3..00000000
--- a/Ryujinx.HLE/Loaders/ElfRelType.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-namespace Ryujinx.HLE.Loaders
-{
- enum ElfRelType
- {
- R_AARCH64_NONE = 0,
- R_AARCH64_ABS64 = 257,
- R_AARCH64_ABS32 = 258,
- R_AARCH64_ABS16 = 259,
- R_AARCH64_PREL64 = 260,
- R_AARCH64_PREL32 = 261,
- R_AARCH64_PREL16 = 262,
- R_AARCH64_MOVW_UABS_G0 = 263,
- R_AARCH64_MOVW_UABS_G0_NC = 264,
- R_AARCH64_MOVW_UABS_G1 = 265,
- R_AARCH64_MOVW_UABS_G1_NC = 266,
- R_AARCH64_MOVW_UABS_G2 = 267,
- R_AARCH64_MOVW_UABS_G2_NC = 268,
- R_AARCH64_MOVW_UABS_G3 = 269,
- R_AARCH64_MOVW_SABS_G0 = 270,
- R_AARCH64_MOVW_SABS_G1 = 271,
- R_AARCH64_MOVW_SABS_G2 = 272,
- R_AARCH64_LD_PREL_LO19 = 273,
- R_AARCH64_ADR_PREL_LO21 = 274,
- R_AARCH64_ADR_PREL_PG_HI21 = 275,
- R_AARCH64_ADR_PREL_PG_HI21_NC = 276,
- R_AARCH64_ADD_ABS_LO12_NC = 277,
- R_AARCH64_LDST8_ABS_LO12_NC = 278,
- R_AARCH64_TSTBR14 = 279,
- R_AARCH64_CONDBR19 = 280,
- R_AARCH64_JUMP26 = 282,
- R_AARCH64_CALL26 = 283,
- R_AARCH64_LDST16_ABS_LO12_NC = 284,
- R_AARCH64_LDST32_ABS_LO12_NC = 285,
- R_AARCH64_LDST64_ABS_LO12_NC = 286,
- R_AARCH64_MOVW_PREL_G0 = 287,
- R_AARCH64_MOVW_PREL_G0_NC = 288,
- R_AARCH64_MOVW_PREL_G1 = 289,
- R_AARCH64_MOVW_PREL_G1_NC = 290,
- R_AARCH64_MOVW_PREL_G2 = 291,
- R_AARCH64_MOVW_PREL_G2_NC = 292,
- R_AARCH64_MOVW_PREL_G3 = 293,
- R_AARCH64_LDST128_ABS_LO12_NC = 299,
- R_AARCH64_MOVW_GOTOFF_G0 = 300,
- R_AARCH64_MOVW_GOTOFF_G0_NC = 301,
- R_AARCH64_MOVW_GOTOFF_G1 = 302,
- R_AARCH64_MOVW_GOTOFF_G1_NC = 303,
- R_AARCH64_MOVW_GOTOFF_G2 = 304,
- R_AARCH64_MOVW_GOTOFF_G2_NC = 305,
- R_AARCH64_MOVW_GOTOFF_G3 = 306,
- R_AARCH64_GOTREL64 = 307,
- R_AARCH64_GOTREL32 = 308,
- R_AARCH64_GOT_LD_PREL19 = 309,
- R_AARCH64_LD64_GOTOFF_LO15 = 310,
- R_AARCH64_ADR_GOT_PAGE = 311,
- R_AARCH64_LD64_GOT_LO12_NC = 312,
- R_AARCH64_LD64_GOTPAGE_LO15 = 313,
- R_AARCH64_TLSGD_ADR_PREL21 = 512,
- R_AARCH64_TLSGD_ADR_PAGE21 = 513,
- R_AARCH64_TLSGD_ADD_LO12_NC = 514,
- R_AARCH64_TLSGD_MOVW_G1 = 515,
- R_AARCH64_TLSGD_MOVW_G0_NC = 516,
- R_AARCH64_TLSLD_ADR_PREL21 = 517,
- R_AARCH64_TLSLD_ADR_PAGE21 = 518,
- R_AARCH64_TLSLD_ADD_LO12_NC = 519,
- R_AARCH64_TLSLD_MOVW_G1 = 520,
- R_AARCH64_TLSLD_MOVW_G0_NC = 521,
- R_AARCH64_TLSLD_LD_PREL19 = 522,
- R_AARCH64_TLSLD_MOVW_DTPREL_G2 = 523,
- R_AARCH64_TLSLD_MOVW_DTPREL_G1 = 524,
- R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC = 525,
- R_AARCH64_TLSLD_MOVW_DTPREL_G0 = 526,
- R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC = 527,
- R_AARCH64_TLSLD_ADD_DTPREL_HI12 = 528,
- R_AARCH64_TLSLD_ADD_DTPREL_LO12 = 529,
- R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC = 530,
- R_AARCH64_TLSLD_LDST8_DTPREL_LO12 = 531,
- R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC = 532,
- R_AARCH64_TLSLD_LDST16_DTPREL_LO12 = 533,
- R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC = 534,
- R_AARCH64_TLSLD_LDST32_DTPREL_LO12 = 535,
- R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC = 536,
- R_AARCH64_TLSLD_LDST64_DTPREL_LO12 = 537,
- R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC = 538,
- R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 = 539,
- R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC = 540,
- R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 = 541,
- R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC = 542,
- R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 = 543,
- R_AARCH64_TLSLE_MOVW_TPREL_G2 = 544,
- R_AARCH64_TLSLE_MOVW_TPREL_G1 = 545,
- R_AARCH64_TLSLE_MOVW_TPREL_G1_NC = 546,
- R_AARCH64_TLSLE_MOVW_TPREL_G0 = 547,
- R_AARCH64_TLSLE_MOVW_TPREL_G0_NC = 548,
- R_AARCH64_TLSLE_ADD_TPREL_HI12 = 549,
- R_AARCH64_TLSLE_ADD_TPREL_LO12 = 550,
- R_AARCH64_TLSLE_ADD_TPREL_LO12_NC = 551,
- R_AARCH64_TLSLE_LDST8_TPREL_LO12 = 552,
- R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC = 553,
- R_AARCH64_TLSLE_LDST16_TPREL_LO12 = 554,
- R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC = 555,
- R_AARCH64_TLSLE_LDST32_TPREL_LO12 = 556,
- R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC = 557,
- R_AARCH64_TLSLE_LDST64_TPREL_LO12 = 558,
- R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC = 559,
- R_AARCH64_TLSDESC_LD_PREL19 = 560,
- R_AARCH64_TLSDESC_ADR_PREL21 = 561,
- R_AARCH64_TLSDESC_ADR_PAGE21 = 562,
- R_AARCH64_TLSDESC_LD64_LO12 = 563,
- R_AARCH64_TLSDESC_ADD_LO12 = 564,
- R_AARCH64_TLSDESC_OFF_G1 = 565,
- R_AARCH64_TLSDESC_OFF_G0_NC = 566,
- R_AARCH64_TLSDESC_LDR = 567,
- R_AARCH64_TLSDESC_ADD = 568,
- R_AARCH64_TLSDESC_CALL = 569,
- R_AARCH64_TLSLE_LDST128_TPREL_LO12 = 570,
- R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC = 571,
- R_AARCH64_TLSLD_LDST128_DTPREL_LO12 = 572,
- R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC = 573,
- R_AARCH64_COPY = 1024,
- R_AARCH64_GLOB_DAT = 1025,
- R_AARCH64_JUMP_SLOT = 1026,
- R_AARCH64_RELATIVE = 1027,
- R_AARCH64_TLS_DTPMOD64 = 1028,
- R_AARCH64_TLS_DTPREL64 = 1029,
- R_AARCH64_TLS_TPREL64 = 1030,
- R_AARCH64_TLSDESC = 1031
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/ElfSym.cs b/Ryujinx.HLE/Loaders/ElfSym.cs
deleted file mode 100644
index 869938d3..00000000
--- a/Ryujinx.HLE/Loaders/ElfSym.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-namespace Ryujinx.HLE.Loaders
-{
- struct ElfSym
- {
- public string Name { get; private set; }
-
- public ElfSymType Type { get; private set; }
- public ElfSymBinding Binding { get; private set; }
- public ElfSymVisibility Visibility { get; private set; }
-
- public bool IsFuncOrObject =>
- Type == ElfSymType.STT_FUNC ||
- Type == ElfSymType.STT_OBJECT;
-
- public bool IsGlobalOrWeak =>
- Binding == ElfSymBinding.STB_GLOBAL ||
- Binding == ElfSymBinding.STB_WEAK;
-
- public int SHIdx { get; private set; }
- public long Value { get; private set; }
- public long Size { get; private set; }
-
- public ElfSym(
- string Name,
- int Info,
- int Other,
- int SHIdx,
- long Value,
- long Size)
- {
- this.Name = Name;
- this.Type = (ElfSymType)(Info & 0xf);
- this.Binding = (ElfSymBinding)(Info >> 4);
- this.Visibility = (ElfSymVisibility)Other;
- this.SHIdx = SHIdx;
- this.Value = Value;
- this.Size = Size;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Executable.cs b/Ryujinx.HLE/Loaders/Executable.cs
deleted file mode 100644
index d4d79073..00000000
--- a/Ryujinx.HLE/Loaders/Executable.cs
+++ /dev/null
@@ -1,205 +0,0 @@
-using ChocolArm64.Memory;
-using Ryujinx.HLE.HOS;
-using Ryujinx.HLE.HOS.Kernel;
-using Ryujinx.HLE.Loaders.Executables;
-using Ryujinx.HLE.Utilities;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.IO;
-using System.Linq;
-
-namespace Ryujinx.HLE.Loaders
-{
- class Executable
- {
- private MemoryManager Memory;
-
- private List<ElfDyn> Dynamic;
-
- public ReadOnlyCollection<ElfSym> SymbolTable;
-
- public string Name { get; private set; }
-
- public string FilePath { get; private set; }
-
- public long ImageBase { get; private set; }
- public long ImageEnd { get; private set; }
-
- private KMemoryManager MemoryManager;
-
- public Executable(IExecutable Exe, KMemoryManager MemoryManager, MemoryManager Memory, long ImageBase)
- {
- Dynamic = new List<ElfDyn>();
-
- FilePath = Exe.FilePath;
-
- if (FilePath != null)
- {
- Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, ""));
- }
-
- this.Memory = Memory;
- this.MemoryManager = MemoryManager;
- this.ImageBase = ImageBase;
- this.ImageEnd = ImageBase;
-
- long TextPosition = ImageBase + (uint)Exe.TextOffset;
- long ROPosition = ImageBase + (uint)Exe.ROOffset;
- long DataPosition = ImageBase + (uint)Exe.DataOffset;
-
- long TextSize = (uint)IntUtils.AlignUp(Exe.Text.Length, KMemoryManager.PageSize);
- long ROSize = (uint)IntUtils.AlignUp(Exe.RO.Length, KMemoryManager.PageSize);
- long DataSize = (uint)IntUtils.AlignUp(Exe.Data.Length, KMemoryManager.PageSize);
- long BssSize = (uint)IntUtils.AlignUp(Exe.BssSize, KMemoryManager.PageSize);
-
- long DataAndBssSize = BssSize + DataSize;
-
- ImageEnd = DataPosition + DataAndBssSize;
-
- if (Exe.SourceAddress == 0)
- {
- MemoryManager.HleMapProcessCode(TextPosition, TextSize + ROSize + DataAndBssSize);
-
- MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read);
- MemoryManager.SetProcessMemoryPermission(DataPosition, DataAndBssSize, MemoryPermission.ReadAndWrite);
-
- Memory.WriteBytes(TextPosition, Exe.Text);
- Memory.WriteBytes(ROPosition, Exe.RO);
- Memory.WriteBytes(DataPosition, Exe.Data);
- }
- else
- {
- long Result = MemoryManager.MapProcessCodeMemory(TextPosition, Exe.SourceAddress, TextSize + ROSize + DataSize);
-
- if (Result != 0)
- {
- throw new InvalidOperationException();
- }
-
- MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read);
- MemoryManager.SetProcessMemoryPermission(DataPosition, DataSize, MemoryPermission.ReadAndWrite);
-
- if (Exe.BssAddress != 0 && Exe.BssSize != 0)
- {
- Result = MemoryManager.MapProcessCodeMemory(DataPosition + DataSize, Exe.BssAddress, BssSize);
-
- if (Result != 0)
- {
- throw new InvalidOperationException();
- }
-
- MemoryManager.SetProcessMemoryPermission(DataPosition + DataSize, BssSize, MemoryPermission.ReadAndWrite);
- }
- }
-
- if (Exe.Mod0Offset == 0)
- {
- return;
- }
-
- long Mod0Offset = ImageBase + Exe.Mod0Offset;
-
- int Mod0Magic = Memory.ReadInt32(Mod0Offset + 0x0);
- long DynamicOffset = Memory.ReadInt32(Mod0Offset + 0x4) + Mod0Offset;
- long BssStartOffset = Memory.ReadInt32(Mod0Offset + 0x8) + Mod0Offset;
- long BssEndOffset = Memory.ReadInt32(Mod0Offset + 0xc) + Mod0Offset;
- long EhHdrStartOffset = Memory.ReadInt32(Mod0Offset + 0x10) + Mod0Offset;
- long EhHdrEndOffset = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset;
- long ModObjOffset = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset;
-
- while (true)
- {
- long TagVal = Memory.ReadInt64(DynamicOffset + 0);
- long Value = Memory.ReadInt64(DynamicOffset + 8);
-
- DynamicOffset += 0x10;
-
- ElfDynTag Tag = (ElfDynTag)TagVal;
-
- if (Tag == ElfDynTag.DT_NULL)
- {
- break;
- }
-
- Dynamic.Add(new ElfDyn(Tag, Value));
- }
-
- long StrTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_STRTAB);
- long SymTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_SYMTAB);
-
- long SymEntSize = GetFirstValue(ElfDynTag.DT_SYMENT);
-
- List<ElfSym> Symbols = new List<ElfSym>();
-
- while ((ulong)SymTblAddr < (ulong)StrTblAddr)
- {
- ElfSym Sym = GetSymbol(SymTblAddr, StrTblAddr);
-
- Symbols.Add(Sym);
-
- SymTblAddr += SymEntSize;
- }
-
- SymbolTable = Array.AsReadOnly(Symbols.OrderBy(x => x.Value).ToArray());
- }
-
- private ElfRel GetRelocation(long Position)
- {
- long Offset = Memory.ReadInt64(Position + 0);
- long Info = Memory.ReadInt64(Position + 8);
- long Addend = Memory.ReadInt64(Position + 16);
-
- int RelType = (int)(Info >> 0);
- int SymIdx = (int)(Info >> 32);
-
- ElfSym Symbol = GetSymbol(SymIdx);
-
- return new ElfRel(Offset, Addend, Symbol, (ElfRelType)RelType);
- }
-
- private ElfSym GetSymbol(int Index)
- {
- long StrTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_STRTAB);
- long SymTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_SYMTAB);
-
- long SymEntSize = GetFirstValue(ElfDynTag.DT_SYMENT);
-
- long Position = SymTblAddr + Index * SymEntSize;
-
- return GetSymbol(Position, StrTblAddr);
- }
-
- private ElfSym GetSymbol(long Position, long StrTblAddr)
- {
- int NameIndex = Memory.ReadInt32(Position + 0);
- int Info = Memory.ReadByte(Position + 4);
- int Other = Memory.ReadByte(Position + 5);
- int SHIdx = Memory.ReadInt16(Position + 6);
- long Value = Memory.ReadInt64(Position + 8);
- long Size = Memory.ReadInt64(Position + 16);
-
- string Name = string.Empty;
-
- for (int Chr; (Chr = Memory.ReadByte(StrTblAddr + NameIndex++)) != 0;)
- {
- Name += (char)Chr;
- }
-
- return new ElfSym(Name, Info, Other, SHIdx, Value, Size);
- }
-
- private long GetFirstValue(ElfDynTag Tag)
- {
- foreach (ElfDyn Entry in Dynamic)
- {
- if (Entry.Tag == Tag)
- {
- return Entry.Value;
- }
- }
-
- return 0;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs
index 6f0952ab..d3eefde6 100644
--- a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs
+++ b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs
@@ -1,20 +1,15 @@
namespace Ryujinx.HLE.Loaders.Executables
{
- public interface IExecutable
+ interface IExecutable
{
- string FilePath { get; }
-
byte[] Text { get; }
byte[] RO { get; }
byte[] Data { get; }
- long SourceAddress { get; }
- long BssAddress { get; }
-
- int Mod0Offset { get; }
int TextOffset { get; }
int ROOffset { get; }
int DataOffset { get; }
+ int BssOffset { get; }
int BssSize { get; }
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs b/Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs
new file mode 100644
index 00000000..1395d56f
--- /dev/null
+++ b/Ryujinx.HLE/Loaders/Executables/KernelInitialProcess.cs
@@ -0,0 +1,149 @@
+using Ryujinx.HLE.Loaders.Compression;
+using System.IO;
+
+namespace Ryujinx.HLE.Loaders.Executables
+{
+ class KernelInitialProcess : IExecutable
+ {
+ public string Name { get; private set; }
+
+ public long TitleId { get; private set; }
+
+ public int ProcessCategory { get; private set; }
+
+ public byte MainThreadPriority { get; private set; }
+ public byte DefaultProcessorId { get; private set; }
+
+ public bool Is64Bits { get; private set; }
+ public bool Addr39Bits { get; private set; }
+ public bool IsService { get; private set; }
+
+ public byte[] Text { get; private set; }
+ public byte[] RO { get; private set; }
+ public byte[] Data { get; private set; }
+
+ public int TextOffset { get; private set; }
+ public int ROOffset { get; private set; }
+ public int DataOffset { get; private set; }
+ public int BssOffset { get; private set; }
+ public int BssSize { get; private set; }
+
+ public int MainThreadStackSize { get; private set; }
+
+ public int[] Capabilities { get; private set; }
+
+ private struct SegmentHeader
+ {
+ public int Offset { get; private set; }
+ public int DecompressedSize { get; private set; }
+ public int CompressedSize { get; private set; }
+ public int Attribute { get; private set; }
+
+ public SegmentHeader(
+ int Offset,
+ int DecompressedSize,
+ int CompressedSize,
+ int Attribute)
+ {
+ this.Offset = Offset;
+ this.DecompressedSize = DecompressedSize;
+ this.CompressedSize = CompressedSize;
+ this.Attribute = Attribute;
+ }
+ }
+
+ public KernelInitialProcess(Stream Input)
+ {
+ BinaryReader Reader = new BinaryReader(Input);
+
+ string Magic = ReadString(Reader, 4);
+
+ if (Magic != "KIP1")
+ {
+
+ }
+
+ Name = ReadString(Reader, 12);
+
+ TitleId = Reader.ReadInt64();
+
+ ProcessCategory = Reader.ReadInt32();
+
+ MainThreadPriority = Reader.ReadByte();
+ DefaultProcessorId = Reader.ReadByte();
+
+ byte Reserved = Reader.ReadByte();
+ byte Flags = Reader.ReadByte();
+
+ Is64Bits = (Flags & 0x08) != 0;
+ Addr39Bits = (Flags & 0x10) != 0;
+ IsService = (Flags & 0x20) != 0;
+
+ SegmentHeader[] Segments = new SegmentHeader[6];
+
+ for (int Index = 0; Index < Segments.Length; Index++)
+ {
+ Segments[Index] = new SegmentHeader(
+ Reader.ReadInt32(),
+ Reader.ReadInt32(),
+ Reader.ReadInt32(),
+ Reader.ReadInt32());
+ }
+
+ TextOffset = Segments[0].Offset;
+ ROOffset = Segments[1].Offset;
+ DataOffset = Segments[2].Offset;
+ BssOffset = Segments[3].Offset;
+ BssSize = Segments[3].DecompressedSize;
+
+ MainThreadStackSize = Segments[1].Attribute;
+
+ Capabilities = new int[8];
+
+ for (int Index = 0; Index < Capabilities.Length; Index++)
+ {
+ Capabilities[Index] = Reader.ReadInt32();
+ }
+
+ Input.Seek(0x100, SeekOrigin.Begin);
+
+ Text = ReadSegment(Segments[0], Input);
+ RO = ReadSegment(Segments[1], Input);
+ Data = ReadSegment(Segments[2], Input);
+ }
+
+ private byte[] ReadSegment(SegmentHeader Header, Stream Input)
+ {
+ long End = Input.Position + Header.CompressedSize;
+
+ Input.Seek(End, SeekOrigin.Begin);
+
+ byte[] Data = BackwardsLz.Decompress(Input, Header.DecompressedSize);
+
+ Input.Seek(End, SeekOrigin.Begin);
+
+ return Data;
+ }
+
+ private static string ReadString(BinaryReader Reader, int MaxSize)
+ {
+ string Value = string.Empty;
+
+ for (int Index = 0; Index < MaxSize; Index++)
+ {
+ char Chr = (char)Reader.ReadByte();
+
+ if (Chr == '\0')
+ {
+ Reader.BaseStream.Seek(MaxSize - Index - 1, SeekOrigin.Current);
+
+ break;
+ }
+
+ Value += Chr;
+ }
+
+ return Value;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Executables/Nro.cs b/Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs
index 6015da21..20de5b5d 100644
--- a/Ryujinx.HLE/Loaders/Executables/Nro.cs
+++ b/Ryujinx.HLE/Loaders/Executables/NxRelocatableObject.cs
@@ -2,10 +2,8 @@ using System.IO;
namespace Ryujinx.HLE.Loaders.Executables
{
- class Nro : IExecutable
+ class NxRelocatableObject : IExecutable
{
- public string FilePath { get; private set; }
-
public byte[] Text { get; private set; }
public byte[] RO { get; private set; }
public byte[] Data { get; private set; }
@@ -16,12 +14,13 @@ namespace Ryujinx.HLE.Loaders.Executables
public int DataOffset { get; private set; }
public int BssSize { get; private set; }
- public long SourceAddress { get; private set; }
- public long BssAddress { get; private set; }
+ public int BssOffset => DataOffset + Data.Length;
+
+ public ulong SourceAddress { get; private set; }
+ public ulong BssAddress { get; private set; }
- public Nro(Stream Input, string FilePath, long SourceAddress = 0, long BssAddress = 0)
+ public NxRelocatableObject(Stream Input, ulong SourceAddress = 0, ulong BssAddress = 0)
{
- this.FilePath = FilePath;
this.SourceAddress = SourceAddress;
this.BssAddress = BssAddress;
diff --git a/Ryujinx.HLE/Loaders/Executables/Nso.cs b/Ryujinx.HLE/Loaders/Executables/NxStaticObject.cs
index c7b48a5f..9fecb650 100644
--- a/Ryujinx.HLE/Loaders/Executables/Nso.cs
+++ b/Ryujinx.HLE/Loaders/Executables/NxStaticObject.cs
@@ -4,22 +4,18 @@ using System.IO;
namespace Ryujinx.HLE.Loaders.Executables
{
- class Nso : IExecutable
+ class NxStaticObject : IExecutable
{
- public string FilePath { get; private set; }
-
public byte[] Text { get; private set; }
public byte[] RO { get; private set; }
public byte[] Data { get; private set; }
- public int Mod0Offset { get; private set; }
public int TextOffset { get; private set; }
public int ROOffset { get; private set; }
public int DataOffset { get; private set; }
public int BssSize { get; private set; }
- public long SourceAddress { get; private set; }
- public long BssAddress { get; private set; }
+ public int BssOffset => DataOffset + Data.Length;
[Flags]
private enum NsoFlags
@@ -32,13 +28,8 @@ namespace Ryujinx.HLE.Loaders.Executables
HasDataHash = 1 << 5
}
- public Nso(Stream Input, string FilePath)
+ public NxStaticObject(Stream Input)
{
- this.FilePath = FilePath;
-
- SourceAddress = 0;
- BssAddress = 0;
-
BinaryReader Reader = new BinaryReader(Input);
Input.Seek(0, SeekOrigin.Begin);
@@ -89,7 +80,7 @@ namespace Ryujinx.HLE.Loaders.Executables
Text = Reader.ReadBytes(TextSize);
- if (Flags.HasFlag(NsoFlags.IsTextCompressed) || true)
+ if (Flags.HasFlag(NsoFlags.IsTextCompressed))
{
Text = Lz4.Decompress(Text, TextDecSize);
}
@@ -99,7 +90,7 @@ namespace Ryujinx.HLE.Loaders.Executables
RO = Reader.ReadBytes(ROSize);
- if (Flags.HasFlag(NsoFlags.IsROCompressed) || true)
+ if (Flags.HasFlag(NsoFlags.IsROCompressed))
{
RO = Lz4.Decompress(RO, RODecSize);
}
@@ -109,19 +100,10 @@ namespace Ryujinx.HLE.Loaders.Executables
Data = Reader.ReadBytes(DataSize);
- if (Flags.HasFlag(NsoFlags.IsDataCompressed) || true)
+ if (Flags.HasFlag(NsoFlags.IsDataCompressed))
{
Data = Lz4.Decompress(Data, DataDecSize);
}
-
- using (MemoryStream TextMS = new MemoryStream(Text))
- {
- BinaryReader TextReader = new BinaryReader(TextMS);
-
- TextMS.Seek(4, SeekOrigin.Begin);
-
- Mod0Offset = TextReader.ReadInt32();
- }
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Npdm/ApplicationType.cs b/Ryujinx.HLE/Loaders/Npdm/ApplicationType.cs
deleted file mode 100644
index ad279032..00000000
--- a/Ryujinx.HLE/Loaders/Npdm/ApplicationType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Ryujinx.HLE.Loaders.Npdm
-{
- enum ApplicationType
- {
- SystemModule,
- Application,
- Applet
- }
-}
diff --git a/Ryujinx.HLE/Loaders/Npdm/FsPermissionBool.cs b/Ryujinx.HLE/Loaders/Npdm/FsPermissionBool.cs
deleted file mode 100644
index 571b7b5a..00000000
--- a/Ryujinx.HLE/Loaders/Npdm/FsPermissionBool.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-namespace Ryujinx.HLE.Loaders.Npdm
-{
- enum FsPermissionBool : ulong
- {
- BisCache = 0x8000000000000080,
- EraseMmc = 0x8000000000000080,
- GameCardCertificate = 0x8000000000000010,
- GameCardIdSet = 0x8000000000000010,
- GameCardDriver = 0x8000000000000200,
- GameCardAsic = 0x8000000000000200,
- SaveDataCreate = 0x8000000000002020,
- SaveDataDelete0 = 0x8000000000000060,
- SystemSaveDataCreate0 = 0x8000000000000028,
- SystemSaveDataCreate1 = 0x8000000000000020,
- SaveDataDelete1 = 0x8000000000004028,
- SaveDataIterators0 = 0x8000000000000060,
- SaveDataIterators1 = 0x8000000000004020,
- SaveThumbnails = 0x8000000000020000,
- PosixTime = 0x8000000000000400,
- SaveDataExtraData = 0x8000000000004060,
- GlobalMode = 0x8000000000080000,
- SpeedEmulation = 0x8000000000080000,
- NULL = 0,
- PaddingFiles = 0xC000000000800000,
- SaveData_Debug = 0xC000000001000000,
- SaveData_SystemManagement = 0xC000000002000000,
- Unknown0x16 = 0x8000000004000000,
- Unknown0x17 = 0x8000000008000000,
- Unknown0x18 = 0x8000000010000000,
- Unknown0x19 = 0x8000000000000800,
- Unknown0x1A = 0x8000000000004020
- }
-}
diff --git a/Ryujinx.HLE/Loaders/Npdm/FsPermissionRw.cs b/Ryujinx.HLE/Loaders/Npdm/FsPermissionRw.cs
deleted file mode 100644
index ca21279b..00000000
--- a/Ryujinx.HLE/Loaders/Npdm/FsPermissionRw.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-namespace Ryujinx.HLE.Loaders.Npdm
-{
- enum FsPermissionRw : ulong
- {
- MountContentType2 = 0x8000000000000801,
- MountContentType5 = 0x8000000000000801,
- MountContentType3 = 0x8000000000000801,
- MountContentType4 = 0x8000000000000801,
- MountContentType6 = 0x8000000000000801,
- MountContentType7 = 0x8000000000000801,
- Unknown0x6 = 0x8000000000000000,
- ContentStorageAccess = 0x8000000000000800,
- ImageDirectoryAccess = 0x8000000000001000,
- MountBisType28 = 0x8000000000000084,
- MountBisType29 = 0x8000000000000080,
- MountBisType30 = 0x8000000000008080,
- MountBisType31 = 0x8000000000008080,
- Unknown0xD = 0x8000000000000080,
- SdCardAccess = 0xC000000000200000,
- GameCardUser = 0x8000000000000010,
- SaveDataAccess0 = 0x8000000000040020,
- SystemSaveDataAccess0 = 0x8000000000000028,
- SaveDataAccess1 = 0x8000000000000020,
- SystemSaveDataAccess1 = 0x8000000000000020,
- BisPartition0 = 0x8000000000010082,
- BisPartition10 = 0x8000000000010080,
- BisPartition20 = 0x8000000000010080,
- BisPartition21 = 0x8000000000010080,
- BisPartition22 = 0x8000000000010080,
- BisPartition23 = 0x8000000000010080,
- BisPartition24 = 0x8000000000010080,
- BisPartition25 = 0x8000000000010080,
- BisPartition26 = 0x8000000000000080,
- BisPartition27 = 0x8000000000000084,
- BisPartition28 = 0x8000000000000084,
- BisPartition29 = 0x8000000000000080,
- BisPartition30 = 0x8000000000000080,
- BisPartition31 = 0x8000000000000080,
- BisPartition32 = 0x8000000000000080,
- Unknown0x23 = 0xC000000000200000,
- GameCard_System = 0x8000000000000100,
- MountContent_System = 0x8000000000100008,
- HostAccess = 0xC000000000400000
- }
-}
diff --git a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs b/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs
index 0b45ebfb..cd3d3252 100644
--- a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs
+++ b/Ryujinx.HLE/Loaders/Npdm/KernelAccessControl.cs
@@ -1,173 +1,23 @@
-using Ryujinx.HLE.Exceptions;
-using System;
-using System.Collections.ObjectModel;
-using System.IO;
+using System.IO;
namespace Ryujinx.HLE.Loaders.Npdm
{
class KernelAccessControl
{
- public ReadOnlyCollection<KernelAccessControlItem> Items;
+ public int[] Capabilities { get; private set; }
public KernelAccessControl(Stream Stream, int Offset, int Size)
{
Stream.Seek(Offset, SeekOrigin.Begin);
- BinaryReader Reader = new BinaryReader(Stream);
+ Capabilities = new int[Size / 4];
- KernelAccessControlItem[] Items = new KernelAccessControlItem[Size / 4];
+ BinaryReader Reader = new BinaryReader(Stream);
- for (int Index = 0; Index < Size / 4; Index++)
+ for (int Index = 0; Index < Capabilities.Length; Index++)
{
- uint Descriptor = Reader.ReadUInt32();
-
- //Ignore the descriptor.
- if (Descriptor == 0xffffffff)
- {
- continue;
- }
-
- Items[Index] = new KernelAccessControlItem();
-
- int LowBits = 0;
-
- while ((Descriptor & 1) != 0)
- {
- Descriptor >>= 1;
-
- LowBits++;
- }
-
- Descriptor >>= 1;
-
- switch (LowBits)
- {
- //Kernel flags.
- case 3:
- {
- Items[Index].HasKernelFlags = true;
-
- Items[Index].HighestThreadPriority = (Descriptor >> 0) & 0x3f;
- Items[Index].LowestThreadPriority = (Descriptor >> 6) & 0x3f;
- Items[Index].LowestCpuId = (Descriptor >> 12) & 0xff;
- Items[Index].HighestCpuId = (Descriptor >> 20) & 0xff;
-
- break;
- }
-
- //Syscall mask.
- case 4:
- {
- Items[Index].HasSvcFlags = true;
-
- Items[Index].AllowedSvcs = new bool[0x80];
-
- int SysCallBase = (int)(Descriptor >> 24) * 0x18;
-
- for (int SysCall = 0; SysCall < 0x18 && SysCallBase + SysCall < 0x80; SysCall++)
- {
- Items[Index].AllowedSvcs[SysCallBase + SysCall] = (Descriptor & 1) != 0;
-
- Descriptor >>= 1;
- }
-
- break;
- }
-
- //Map IO/Normal.
- case 6:
- {
- ulong Address = (Descriptor & 0xffffff) << 12;
- bool IsRo = (Descriptor >> 24) != 0;
-
- if (Index == Size / 4 - 1)
- {
- throw new InvalidNpdmException("Invalid Kernel Access Control Descriptors!");
- }
-
- Descriptor = Reader.ReadUInt32();
-
- if ((Descriptor & 0x7f) != 0x3f)
- {
- throw new InvalidNpdmException("Invalid Kernel Access Control Descriptors!");
- }
-
- Descriptor >>= 7;
-
- ulong MmioSize = (Descriptor & 0xffffff) << 12;
- bool IsNormal = (Descriptor >> 24) != 0;
-
- Items[Index].NormalMmio.Add(new KernelAccessControlMmio(Address, MmioSize, IsRo, IsNormal));
-
- Index++;
-
- break;
- }
-
- //Map Normal Page.
- case 7:
- {
- ulong Address = Descriptor << 12;
-
- Items[Index].PageMmio.Add(new KernelAccessControlMmio(Address, 0x1000, false, false));
-
- break;
- }
-
- //IRQ Pair.
- case 11:
- {
- Items[Index].Irq.Add(new KernelAccessControlIrq(
- (Descriptor >> 0) & 0x3ff,
- (Descriptor >> 10) & 0x3ff));
-
- break;
- }
-
- //Application Type.
- case 13:
- {
- Items[Index].HasApplicationType = true;
-
- Items[Index].ApplicationType = (int)Descriptor & 7;
-
- break;
- }
-
- //Kernel Release Version.
- case 14:
- {
- Items[Index].HasKernelVersion = true;
-
- Items[Index].KernelVersionRelease = (int)Descriptor;
-
- break;
- }
-
- //Handle Table Size.
- case 15:
- {
- Items[Index].HasHandleTableSize = true;
-
- Items[Index].HandleTableSize = (int)Descriptor;
-
- break;
- }
-
- //Debug Flags.
- case 16:
- {
- Items[Index].HasDebugFlags = true;
-
- Items[Index].AllowDebug = ((Descriptor >> 0) & 1) != 0;
- Items[Index].ForceDebug = ((Descriptor >> 1) & 1) != 0;
-
- break;
- }
- }
+ Capabilities[Index] = Reader.ReadInt32();
}
-
- this.Items = Array.AsReadOnly(Items);
}
}
}
diff --git a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlIrq.cs b/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlIrq.cs
deleted file mode 100644
index 63671331..00000000
--- a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlIrq.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.HLE.Loaders.Npdm
-{
- struct KernelAccessControlIrq
- {
- public uint Irq0 { get; private set; }
- public uint Irq1 { get; private set; }
-
- public KernelAccessControlIrq(uint Irq0, uint Irq1)
- {
- this.Irq0 = Irq0;
- this.Irq1 = Irq1;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlMmio.cs b/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlMmio.cs
deleted file mode 100644
index 1ec79c88..00000000
--- a/Ryujinx.HLE/Loaders/Npdm/KernelAccessControlMmio.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace Ryujinx.HLE.Loaders.Npdm
-{
- struct KernelAccessControlMmio
- {
- public ulong Address { get; private set; }
- public ulong Size { get; private set; }
- public bool IsRo { get; private set; }
- public bool IsNormal { get; private set; }
-
- public KernelAccessControlMmio(
- ulong Address,
- ulong Size,
- bool IsRo,
- bool IsNormal)
- {
- this.Address = Address;
- this.Size = Size;
- this.IsRo = IsRo;
- this.IsNormal = IsNormal;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Npdm/KernelAccessItem.cs b/Ryujinx.HLE/Loaders/Npdm/KernelAccessItem.cs
deleted file mode 100644
index 42015c3e..00000000
--- a/Ryujinx.HLE/Loaders/Npdm/KernelAccessItem.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.Collections.Generic;
-
-namespace Ryujinx.HLE.Loaders.Npdm
-{
- struct KernelAccessControlItem
- {
- public bool HasKernelFlags { get; set; }
- public uint LowestThreadPriority { get; set; }
- public uint HighestThreadPriority { get; set; }
- public uint LowestCpuId { get; set; }
- public uint HighestCpuId { get; set; }
-
- public bool HasSvcFlags { get; set; }
- public bool[] AllowedSvcs { get; set; }
-
- public List<KernelAccessControlMmio> NormalMmio { get; set; }
- public List<KernelAccessControlMmio> PageMmio { get; set; }
- public List<KernelAccessControlIrq> Irq { get; set; }
-
- public bool HasApplicationType { get; set; }
- public int ApplicationType { get; set; }
-
- public bool HasKernelVersion { get; set; }
- public int KernelVersionRelease { get; set; }
-
- public bool HasHandleTableSize { get; set; }
- public int HandleTableSize { get; set; }
-
- public bool HasDebugFlags { get; set; }
- public bool AllowDebug { get; set; }
- public bool ForceDebug { get; set; }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs b/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
index 8aacfd99..9c2fdb38 100644
--- a/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
+++ b/Ryujinx.HLE/Loaders/Npdm/Npdm.cs
@@ -1,5 +1,4 @@
using Ryujinx.HLE.Exceptions;
-using Ryujinx.HLE.Utilities;
using System.IO;
using System.Text;
@@ -12,15 +11,15 @@ namespace Ryujinx.HLE.Loaders.Npdm
{
private const int MetaMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24;
- public bool Is64Bits { get; private set; }
- public int AddressSpaceWidth { get; private set; }
- public byte MainThreadPriority { get; private set; }
- public byte DefaultCpuId { get; private set; }
- public int SystemResourceSize { get; private set; }
- public int ProcessCategory { get; private set; }
- public int MainEntrypointStackSize { get; private set; }
- public string TitleName { get; private set; }
- public byte[] ProductCode { get; private set; }
+ public byte MmuFlags { get; private set; }
+ public bool Is64Bits { get; private set; }
+ public byte MainThreadPriority { get; private set; }
+ public byte DefaultCpuId { get; private set; }
+ public int PersonalMmHeapSize { get; private set; }
+ public int ProcessCategory { get; private set; }
+ public int MainThreadStackSize { get; private set; }
+ public string TitleName { get; private set; }
+ public byte[] ProductCode { get; private set; }
public ACI0 ACI0 { get; private set; }
public ACID ACID { get; private set; }
@@ -36,27 +35,22 @@ namespace Ryujinx.HLE.Loaders.Npdm
Reader.ReadInt64();
- //MmuFlags, bit0: 64-bit instructions, bits1-3: address space width (1=64-bit, 2=32-bit). Needs to be <= 0xF.
- byte MmuFlags = Reader.ReadByte();
+ MmuFlags = Reader.ReadByte();
- Is64Bits = (MmuFlags & 1) != 0;
- AddressSpaceWidth = (MmuFlags >> 1) & 7;
+ Is64Bits = (MmuFlags & 1) != 0;
Reader.ReadByte();
- MainThreadPriority = Reader.ReadByte(); //(0-63).
+ MainThreadPriority = Reader.ReadByte();
DefaultCpuId = Reader.ReadByte();
Reader.ReadInt32();
- //System resource size (max size as of 5.x: 534773760).
- SystemResourceSize = EndianSwap.Swap32(Reader.ReadInt32());
+ PersonalMmHeapSize = Reader.ReadInt32();
- //ProcessCategory (0: regular title, 1: kernel built-in). Should be 0 here.
- ProcessCategory = EndianSwap.Swap32(Reader.ReadInt32());
+ ProcessCategory = Reader.ReadInt32();
- //Main entrypoint stack size.
- MainEntrypointStackSize = Reader.ReadInt32();
+ MainThreadStackSize = Reader.ReadInt32();
byte[] TempTitleName = Reader.ReadBytes(0x10);
diff --git a/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs b/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs
index 910eacb3..b18538e5 100644
--- a/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs
+++ b/Ryujinx.HLE/Loaders/Npdm/ServiceAccessControl.cs
@@ -28,8 +28,8 @@ namespace Ryujinx.HLE.Loaders.Npdm
break;
}
- int Length = ((ControlByte & 0x07)) + 1;
- bool RegisterAllowed = ((ControlByte & 0x80) != 0);
+ int Length = (ControlByte & 0x07) + 1;
+ bool RegisterAllowed = (ControlByte & 0x80) != 0;
Services.Add(Encoding.ASCII.GetString(Reader.ReadBytes(Length), 0, Length), RegisterAllowed);
diff --git a/Ryujinx.HLE/Loaders/Npdm/SvcName.cs b/Ryujinx.HLE/Loaders/Npdm/SvcName.cs
deleted file mode 100644
index e519e05e..00000000
--- a/Ryujinx.HLE/Loaders/Npdm/SvcName.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-namespace Ryujinx.HLE.Loaders.Npdm
-{
- enum SvcName
- {
- Reserved0,
- SetHeapSize,
- SetMemoryPermission,
- SetMemoryAttribute,
- MapMemory,
- UnmapMemory,
- QueryMemory,
- ExitProcess,
- CreateThread,
- StartThread,
- ExitThread,
- SleepThread,
- GetThreadPriority,
- SetThreadPriority,
- GetThreadCoreMask,
- SetThreadCoreMask,
- GetCurrentProcessorNumber,
- SignalEvent,
- ClearEvent,
- MapSharedMemory,
- UnmapSharedMemory,
- CreateTransferMemory,
- CloseHandle,
- ResetSignal,
- WaitSynchronization,
- CancelSynchronization,
- ArbitrateLock,
- ArbitrateUnlock,
- WaitProcessWideKeyAtomic,
- SignalProcessWideKey,
- GetSystemTick,
- ConnectToNamedPort,
- SendSyncRequestLight,
- SendSyncRequest,
- SendSyncRequestWithUserBuffer,
- SendAsyncRequestWithUserBuffer,
- GetProcessId,
- GetThreadId,
- Break,
- OutputDebugString,
- ReturnFromException,
- GetInfo,
- FlushEntireDataCache,
- FlushDataCache,
- MapPhysicalMemory,
- UnmapPhysicalMemory,
- GetFutureThreadInfo,
- GetLastThreadInfo,
- GetResourceLimitLimitValue,
- GetResourceLimitCurrentValue,
- SetThreadActivity,
- GetThreadContext3,
- WaitForAddress,
- SignalToAddress,
- Reserved1,
- Reserved2,
- Reserved3,
- Reserved4,
- Reserved5,
- Reserved6,
- DumpInfo,
- DumpInfoNew,
- Reserved7,
- Reserved8,
- CreateSession,
- AcceptSession,
- ReplyAndReceiveLight,
- ReplyAndReceive,
- ReplyAndReceiveWithUserBuffer,
- CreateEvent,
- Reserved9,
- Reserved10,
- MapPhysicalMemoryUnsafe,
- UnmapPhysicalMemoryUnsafe,
- SetUnsafeLimit,
- CreateCodeMemory,
- ControlCodeMemory,
- SleepSystem,
- ReadWriteRegister,
- SetProcessActivity,
- CreateSharedMemory,
- MapTransferMemory,
- UnmapTransferMemory,
- CreateInterruptEvent,
- QueryPhysicalAddress,
- QueryIoMapping,
- CreateDeviceAddressSpace,
- AttachDeviceAddressSpace,
- DetachDeviceAddressSpace,
- MapDeviceAddressSpaceByForce,
- MapDeviceAddressSpaceAligned,
- MapDeviceAddressSpace,
- UnmapDeviceAddressSpace,
- InvalidateProcessDataCache,
- StoreProcessDataCache,
- FlushProcessDataCache,
- DebugActiveProcess,
- BreakDebugProcess,
- TerminateDebugProcess,
- GetDebugEvent,
- ContinueDebugEvent,
- GetProcessList,
- GetThreadList,
- GetDebugThreadContext,
- SetDebugThreadContext,
- QueryDebugProcessMemory,
- ReadDebugProcessMemory,
- WriteDebugProcessMemory,
- SetHardwareBreakPoint,
- GetDebugThreadParam,
- Reserved11,
- GetSystemInfo,
- CreatePort,
- ManageNamedPort,
- ConnectToPort,
- SetProcessMemoryPermission,
- MapProcessMemory,
- UnmapProcessMemory,
- QueryProcessMemory,
- MapProcessCodeMemory,
- UnmapProcessCodeMemory,
- CreateProcess,
- StartProcess,
- TerminateProcess,
- GetProcessInfo,
- CreateResourceLimit,
- SetResourceLimitLimitValue,
- CallSecureMonitor
- }
-}
diff --git a/Ryujinx.HLE/Memory/ArenaAllocator.cs b/Ryujinx.HLE/Memory/ArenaAllocator.cs
deleted file mode 100644
index 9bcb7873..00000000
--- a/Ryujinx.HLE/Memory/ArenaAllocator.cs
+++ /dev/null
@@ -1,150 +0,0 @@
-using System.Collections.Generic;
-
-namespace Ryujinx.HLE.Memory
-{
- class ArenaAllocator
- {
- private class Region
- {
- public long Position { get; set; }
- public long Size { get; set; }
-
- public Region(long Position, long Size)
- {
- this.Position = Position;
- this.Size = Size;
- }
- }
-
- private LinkedList<Region> FreeRegions;
-
- public long TotalAvailableSize { get; private set; }
- public long TotalUsedSize { get; private set; }
-
- public ArenaAllocator(long ArenaSize)
- {
- TotalAvailableSize = ArenaSize;
-
- FreeRegions = new LinkedList<Region>();
-
- FreeRegions.AddFirst(new Region(0, ArenaSize));
- }
-
- public bool TryAllocate(long Size, out long Position)
- {
- LinkedListNode<Region> Node = FreeRegions.First;
-
- while (Node != null)
- {
- Region Rg = Node.Value;
-
- if ((ulong)Rg.Size >= (ulong)Size)
- {
- Position = Rg.Position;
-
- Rg.Position += Size;
- Rg.Size -= Size;
-
- if (Rg.Size == 0)
- {
- //Region is empty, just remove it.
- FreeRegions.Remove(Node);
- }
- else if (Node.Previous != null)
- {
- //Re-sort based on size (smaller first).
- Node = Node.Previous;
-
- FreeRegions.Remove(Node.Next);
-
- while (Node != null && (ulong)Node.Value.Size > (ulong)Rg.Size)
- {
- Node = Node.Previous;
- }
-
- if (Node != null)
- {
- FreeRegions.AddAfter(Node, Rg);
- }
- else
- {
- FreeRegions.AddFirst(Rg);
- }
- }
-
- TotalUsedSize += Size;
-
- return true;
- }
-
- Node = Node.Next;
- }
-
- Position = 0;
-
- return false;
- }
-
- public void Free(long Position, long Size)
- {
- long End = Position + Size;
-
- Region NewRg = new Region(Position, Size);
-
- LinkedListNode<Region> Node = FreeRegions.First;
- LinkedListNode<Region> PrevSz = null;
-
- while (Node != null)
- {
- LinkedListNode<Region> NextNode = Node.Next;
-
- Region Rg = Node.Value;
-
- long RgEnd = Rg.Position + Rg.Size;
-
- if (Rg.Position == End)
- {
- //Current region position matches the end of the freed region,
- //just merge the two and remove the current region from the list.
- NewRg.Size += Rg.Size;
-
- FreeRegions.Remove(Node);
- }
- else if (RgEnd == Position)
- {
- //End of the current region matches the position of the freed region,
- //just merge the two and remove the current region from the list.
- NewRg.Position = Rg.Position;
- NewRg.Size += Rg.Size;
-
- FreeRegions.Remove(Node);
- }
- else
- {
- if (PrevSz == null)
- {
- PrevSz = Node;
- }
- else if ((ulong)Rg.Size < (ulong)NewRg.Size &&
- (ulong)Rg.Size > (ulong)PrevSz.Value.Size)
- {
- PrevSz = Node;
- }
- }
-
- Node = NextNode;
- }
-
- if (PrevSz != null && (ulong)PrevSz.Value.Size < (ulong)Size)
- {
- FreeRegions.AddAfter(PrevSz, NewRg);
- }
- else
- {
- FreeRegions.AddFirst(NewRg);
- }
-
- TotalUsedSize -= Size;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj
index 71a0cf1c..6285825a 100644
--- a/Ryujinx.HLE/Ryujinx.HLE.csproj
+++ b/Ryujinx.HLE/Ryujinx.HLE.csproj
@@ -14,10 +14,12 @@
</PropertyGroup>
<ItemGroup>
+ <None Remove="Homebrew.npdm" />
<None Remove="RyujinxProfileImage.jpg" />
</ItemGroup>
<ItemGroup>
+ <EmbeddedResource Include="Homebrew.npdm" />
<EmbeddedResource Include="RyujinxProfileImage.jpg" />
</ItemGroup>
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index 8de49ca4..5b3b36d0 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -4,7 +4,6 @@ using Ryujinx.Graphics.Gal;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.Input;
-using Ryujinx.HLE.Memory;
using System;
using System.Threading;
@@ -56,7 +55,7 @@ namespace Ryujinx.HLE
Statistics = new PerformanceStatistics();
- Hid = new Hid(this, System.HidSharedMem.PA);
+ Hid = new Hid(this, System.HidBaseAddress);
VsyncEvent = new AutoResetEvent(true);
}
diff --git a/Ryujinx.HLE/Utilities/WSAError.cs b/Ryujinx.HLE/Utilities/WSAError.cs
index 55c04f22..ff0896d4 100644
--- a/Ryujinx.HLE/Utilities/WSAError.cs
+++ b/Ryujinx.HLE/Utilities/WSAError.cs
@@ -1,17 +1,13 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Ryujinx.HLE.Utilities
+namespace Ryujinx.HLE.Utilities
{
- public enum WSAError
+ enum WSAError
{
/*
* All Windows Sockets error constants are biased by WSABASEERR from
* the "normal"
*/
WSABASEERR = 10000,
-
+
/*
* Windows Sockets definitions of regular Microsoft C error constants
*/
diff --git a/Ryujinx/Ui/ConsoleLog.cs b/Ryujinx/Ui/ConsoleLog.cs
index 1ecd4cde..67eaa0e3 100644
--- a/Ryujinx/Ui/ConsoleLog.cs
+++ b/Ryujinx/Ui/ConsoleLog.cs
@@ -26,7 +26,7 @@ namespace Ryujinx
{ LogLevel.Error, ConsoleColor.Red }
};
- _messageQueue = new BlockingCollection<LogEventArgs>();
+ _messageQueue = new BlockingCollection<LogEventArgs>(10);
_consoleLock = new object();
@@ -58,7 +58,7 @@ namespace Ryujinx
string formattedTime = e.Time.ToString(@"hh\:mm\:ss\.fff");
string currentThread = Thread.CurrentThread.ManagedThreadId.ToString("d4");
-
+
string message = formattedTime + " | " + currentThread + " " + e.Message;
if (_logColors.TryGetValue(e.Level, out ConsoleColor color))