Better process implementation (#491)
* Initial implementation of KProcess * Some improvements to the memory manager, implement back guest stack trace printing * Better GetInfo implementation, improve checking in some places with information from process capabilities * Allow the cpu to read/write from the correct memory locations for accesses crossing a page boundary * Change long -> ulong for address/size on memory related methods to avoid unnecessary casts * Attempt at implementing ldr:ro with new KProcess * Allow BSS with size 0 on ldr:ro * Add checking for memory block slab heap usage, return errors if full, exit gracefully * Use KMemoryBlockSize const from KMemoryManager * Allow all methods to read from non-contiguous locations * Fix for TransactParcelAuto * Address PR feedback, additionally fix some small issues related to the KIP loader and implement SVCs GetProcessId, GetProcessList, GetSystemInfo, CreatePort and ManageNamedPort * Fix wrong check for source pages count from page list on MapPhysicalMemory * Fix some issues with UnloadNro on ldr:ro
This commit is contained in:
parent
e7fe7d7247
commit
00579927e4
119 changed files with 7998 additions and 3232 deletions
|
@ -18,7 +18,7 @@ namespace ChocolArm64
|
||||||
|
|
||||||
private int _isExecuting;
|
private int _isExecuting;
|
||||||
|
|
||||||
public CpuThread(Translator translator, MemoryManager memory, long entryPoint)
|
public CpuThread(Translator translator, MemoryManager memory, long entrypoint)
|
||||||
{
|
{
|
||||||
_translator = translator;
|
_translator = translator;
|
||||||
Memory = memory;
|
Memory = memory;
|
||||||
|
@ -31,7 +31,7 @@ namespace ChocolArm64
|
||||||
|
|
||||||
Work = new Thread(delegate()
|
Work = new Thread(delegate()
|
||||||
{
|
{
|
||||||
translator.ExecuteSubroutine(this, entryPoint);
|
translator.ExecuteSubroutine(this, entrypoint);
|
||||||
|
|
||||||
memory.RemoveMonitor(ThreadState.Core);
|
memory.RemoveMonitor(ThreadState.Core);
|
||||||
|
|
||||||
|
|
|
@ -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)) { }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -26,22 +26,26 @@ namespace ChocolArm64.Memory
|
||||||
{
|
{
|
||||||
long size = Marshal.SizeOf<T>();
|
long size = Marshal.SizeOf<T>();
|
||||||
|
|
||||||
memory.EnsureRangeIsValid(position, size);
|
byte[] data = memory.ReadBytes(position, size);
|
||||||
|
|
||||||
IntPtr ptr = (IntPtr)memory.Translate(position);
|
fixed (byte* ptr = data)
|
||||||
|
{
|
||||||
return Marshal.PtrToStructure<T>(ptr);
|
return Marshal.PtrToStructure<T>((IntPtr)ptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe static void Write<T>(MemoryManager memory, long position, T value) where T : struct
|
public unsafe static void Write<T>(MemoryManager memory, long position, T value) where T : struct
|
||||||
{
|
{
|
||||||
long size = Marshal.SizeOf<T>();
|
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)
|
public static string ReadAsciiString(MemoryManager memory, long position, long maxSize = -1)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using ChocolArm64.Events;
|
using ChocolArm64.Events;
|
||||||
using ChocolArm64.Exceptions;
|
using ChocolArm64.Exceptions;
|
||||||
|
using ChocolArm64.Instructions;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
@ -196,19 +197,43 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
|
|
||||||
public ushort ReadUInt16(long position)
|
public ushort ReadUInt16(long position)
|
||||||
|
{
|
||||||
|
if ((position & 1) == 0)
|
||||||
{
|
{
|
||||||
return *((ushort*)Translate(position));
|
return *((ushort*)Translate(position));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (ushort)(ReadByte(position + 0) << 0 |
|
||||||
|
ReadByte(position + 1) << 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public uint ReadUInt32(long position)
|
public uint ReadUInt32(long position)
|
||||||
|
{
|
||||||
|
if ((position & 3) == 0)
|
||||||
{
|
{
|
||||||
return *((uint*)Translate(position));
|
return *((uint*)Translate(position));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (uint)(ReadUInt16(position + 0) << 0 |
|
||||||
|
ReadUInt16(position + 2) << 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ulong ReadUInt64(long position)
|
public ulong ReadUInt64(long position)
|
||||||
|
{
|
||||||
|
if ((position & 7) == 0)
|
||||||
{
|
{
|
||||||
return *((ulong*)Translate(position));
|
return *((ulong*)Translate(position));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (ulong)ReadUInt32(position + 0) << 0 |
|
||||||
|
(ulong)ReadUInt32(position + 4) << 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Vector128<float> ReadVector8(long position)
|
public Vector128<float> ReadVector8(long position)
|
||||||
{
|
{
|
||||||
|
@ -218,74 +243,117 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new PlatformNotSupportedException();
|
Vector128<float> value = VectorHelper.VectorSingleZero();
|
||||||
|
|
||||||
|
value = VectorHelper.VectorInsertInt(ReadByte(position), value, 0, 0);
|
||||||
|
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public Vector128<float> ReadVector16(long position)
|
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));
|
return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse2.SetZeroVector128<ushort>(), ReadUInt16(position), 0));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new PlatformNotSupportedException();
|
Vector128<float> value = VectorHelper.VectorSingleZero();
|
||||||
|
|
||||||
|
value = VectorHelper.VectorInsertInt(ReadUInt16(position), value, 0, 1);
|
||||||
|
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public Vector128<float> ReadVector32(long position)
|
public Vector128<float> ReadVector32(long position)
|
||||||
{
|
{
|
||||||
if (Sse.IsSupported)
|
if (Sse.IsSupported && (position & 3) == 0)
|
||||||
{
|
{
|
||||||
return Sse.LoadScalarVector128((float*)Translate(position));
|
return Sse.LoadScalarVector128((float*)Translate(position));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new PlatformNotSupportedException();
|
Vector128<float> value = VectorHelper.VectorSingleZero();
|
||||||
|
|
||||||
|
value = VectorHelper.VectorInsertInt(ReadUInt32(position), value, 0, 2);
|
||||||
|
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public Vector128<float> ReadVector64(long position)
|
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)));
|
return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)Translate(position)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new PlatformNotSupportedException();
|
Vector128<float> value = VectorHelper.VectorSingleZero();
|
||||||
|
|
||||||
|
value = VectorHelper.VectorInsertInt(ReadUInt64(position), value, 0, 3);
|
||||||
|
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public Vector128<float> ReadVector128(long position)
|
public Vector128<float> ReadVector128(long position)
|
||||||
{
|
{
|
||||||
if (Sse.IsSupported)
|
if (Sse.IsSupported && (position & 15) == 0)
|
||||||
{
|
{
|
||||||
return Sse.LoadVector128((float*)Translate(position));
|
return Sse.LoadVector128((float*)Translate(position));
|
||||||
}
|
}
|
||||||
else
|
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)
|
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));
|
throw new ArgumentOutOfRangeException(nameof(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureRangeIsValid(position, size);
|
if ((ulong)endAddr < (ulong)position)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(position));
|
||||||
|
}
|
||||||
|
|
||||||
byte[] data = new byte[size];
|
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;
|
return data;
|
||||||
}
|
}
|
||||||
|
@ -293,9 +361,36 @@ namespace ChocolArm64.Memory
|
||||||
public void ReadBytes(long position, byte[] data, int startIndex, int size)
|
public void ReadBytes(long position, byte[] data, int startIndex, int size)
|
||||||
{
|
{
|
||||||
//Note: This will be moved later.
|
//Note: This will be moved later.
|
||||||
EnsureRangeIsValid(position, (uint)size);
|
long endAddr = position + size;
|
||||||
|
|
||||||
Marshal.Copy((IntPtr)Translate(position), data, startIndex, size);
|
if ((ulong)size > int.MaxValue)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(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)
|
public void WriteSByte(long position, sbyte value)
|
||||||
|
@ -324,19 +419,43 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteUInt16(long position, ushort value)
|
public void WriteUInt16(long position, ushort value)
|
||||||
|
{
|
||||||
|
if ((position & 1) == 0)
|
||||||
{
|
{
|
||||||
*((ushort*)TranslateWrite(position)) = value;
|
*((ushort*)TranslateWrite(position)) = value;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteByte(position + 0, (byte)(value >> 0));
|
||||||
|
WriteByte(position + 1, (byte)(value >> 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void WriteUInt32(long position, uint value)
|
public void WriteUInt32(long position, uint value)
|
||||||
|
{
|
||||||
|
if ((position & 3) == 0)
|
||||||
{
|
{
|
||||||
*((uint*)TranslateWrite(position)) = value;
|
*((uint*)TranslateWrite(position)) = value;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteUInt16(position + 0, (ushort)(value >> 0));
|
||||||
|
WriteUInt16(position + 2, (ushort)(value >> 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void WriteUInt64(long position, ulong value)
|
public void WriteUInt64(long position, ulong value)
|
||||||
|
{
|
||||||
|
if ((position & 7) == 0)
|
||||||
{
|
{
|
||||||
*((ulong*)TranslateWrite(position)) = value;
|
*((ulong*)TranslateWrite(position)) = value;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteUInt32(position + 0, (uint)(value >> 0));
|
||||||
|
WriteUInt32(position + 4, (uint)(value >> 32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteVector8(long position, Vector128<float> value)
|
public void WriteVector8(long position, Vector128<float> value)
|
||||||
|
@ -351,7 +470,7 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new PlatformNotSupportedException();
|
WriteByte(position, (byte)VectorHelper.VectorExtractIntZx(value, 0, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,46 +483,47 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new PlatformNotSupportedException();
|
WriteUInt16(position, (ushort)VectorHelper.VectorExtractIntZx(value, 0, 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteVector32(long position, Vector128<float> value)
|
public void WriteVector32(long position, Vector128<float> value)
|
||||||
{
|
{
|
||||||
if (Sse.IsSupported)
|
if (Sse.IsSupported && (position & 3) == 0)
|
||||||
{
|
{
|
||||||
Sse.StoreScalar((float*)TranslateWrite(position), value);
|
Sse.StoreScalar((float*)TranslateWrite(position), value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new PlatformNotSupportedException();
|
WriteUInt32(position, (uint)VectorHelper.VectorExtractIntZx(value, 0, 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteVector64(long position, Vector128<float> value)
|
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));
|
Sse2.StoreScalar((double*)TranslateWrite(position), Sse.StaticCast<float, double>(value));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new PlatformNotSupportedException();
|
WriteUInt64(position, VectorHelper.VectorExtractIntZx(value, 0, 3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteVector128(long position, Vector128<float> value)
|
public void WriteVector128(long position, Vector128<float> value)
|
||||||
{
|
{
|
||||||
if (Sse.IsSupported)
|
if (Sse.IsSupported && (position & 15) == 0)
|
||||||
{
|
{
|
||||||
Sse.Store((float*)TranslateWrite(position), value);
|
Sse.Store((float*)TranslateWrite(position), value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new PlatformNotSupportedException();
|
WriteUInt64(position + 0, VectorHelper.VectorExtractIntZx(value, 0, 3));
|
||||||
|
WriteUInt64(position + 8, VectorHelper.VectorExtractIntZx(value, 1, 3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,23 +559,49 @@ namespace ChocolArm64.Memory
|
||||||
public void WriteBytes(long position, byte[] data, int startIndex, int size)
|
public void WriteBytes(long position, byte[] data, int startIndex, int size)
|
||||||
{
|
{
|
||||||
//Note: This will be moved later.
|
//Note: This will be moved later.
|
||||||
//Using Translate instead of TranslateWrite is on purpose.
|
long endAddr = position + size;
|
||||||
EnsureRangeIsValid(position, (uint)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)
|
public void CopyBytes(long src, long dst, long size)
|
||||||
{
|
{
|
||||||
//Note: This will be moved later.
|
//Note: This will be moved later.
|
||||||
EnsureRangeIsValid(src, size);
|
if (IsContiguous(src, size) &&
|
||||||
EnsureRangeIsValid(dst, size);
|
IsContiguous(dst, size))
|
||||||
|
{
|
||||||
byte* srcPtr = Translate(src);
|
byte* srcPtr = Translate(src);
|
||||||
byte* dstPtr = TranslateWrite(dst);
|
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)
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void EnsureRangeIsValid(long position, long size)
|
ptr = IntPtr.Zero;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsContiguous(long position, long size)
|
||||||
{
|
{
|
||||||
long endPos = position + size;
|
long endPos = position + size;
|
||||||
|
|
||||||
|
@ -724,12 +877,14 @@ Unmapped:
|
||||||
|
|
||||||
if (pa != expectedPa)
|
if (pa != expectedPa)
|
||||||
{
|
{
|
||||||
throw new VmmAccessException(position, size);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
position += PageSize;
|
position += PageSize;
|
||||||
expectedPa += PageSize;
|
expectedPa += PageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsValidPosition(long position)
|
public bool IsValidPosition(long position)
|
||||||
|
|
104
Ryujinx.Common/BitUtils.cs
Normal file
104
Ryujinx.Common/BitUtils.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gal
|
namespace Ryujinx.Graphics.Gal
|
||||||
{
|
{
|
||||||
public struct GalVertexAttrib
|
public struct GalVertexAttrib
|
||||||
|
@ -7,7 +5,7 @@ namespace Ryujinx.Graphics.Gal
|
||||||
public int Index { get; private set; }
|
public int Index { get; private set; }
|
||||||
public bool IsConst { get; private set; }
|
public bool IsConst { get; private set; }
|
||||||
public int Offset { 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 GalVertexAttribSize Size { get; private set; }
|
||||||
public GalVertexAttribType Type { get; private set; }
|
public GalVertexAttribType Type { get; private set; }
|
||||||
|
@ -18,14 +16,14 @@ namespace Ryujinx.Graphics.Gal
|
||||||
int Index,
|
int Index,
|
||||||
bool IsConst,
|
bool IsConst,
|
||||||
int Offset,
|
int Offset,
|
||||||
IntPtr Pointer,
|
byte[] Data,
|
||||||
GalVertexAttribSize Size,
|
GalVertexAttribSize Size,
|
||||||
GalVertexAttribType Type,
|
GalVertexAttribType Type,
|
||||||
bool IsBgra)
|
bool IsBgra)
|
||||||
{
|
{
|
||||||
this.Index = Index;
|
this.Index = Index;
|
||||||
this.IsConst = IsConst;
|
this.IsConst = IsConst;
|
||||||
this.Pointer = Pointer;
|
this.Data = Data;
|
||||||
this.Offset = Offset;
|
this.Offset = Offset;
|
||||||
this.Size = Size;
|
this.Size = Size;
|
||||||
this.Type = Type;
|
this.Type = Type;
|
||||||
|
|
|
@ -12,5 +12,6 @@ namespace Ryujinx.Graphics.Gal
|
||||||
bool IsCached(long Key, long Size);
|
bool IsCached(long Key, long Size);
|
||||||
|
|
||||||
void SetData(long Key, long Size, IntPtr HostAddress);
|
void SetData(long Key, long Size, IntPtr HostAddress);
|
||||||
|
void SetData(long Key, byte[] Data);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -22,6 +22,7 @@ namespace Ryujinx.Graphics.Gal
|
||||||
bool IsIboCached(long Key, long DataSize);
|
bool IsIboCached(long Key, long DataSize);
|
||||||
|
|
||||||
void CreateVbo(long Key, int DataSize, IntPtr HostAddress);
|
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, IntPtr HostAddress);
|
||||||
void CreateIbo(long Key, int DataSize, byte[] Buffer);
|
void CreateIbo(long Key, int DataSize, byte[] Buffer);
|
||||||
|
|
|
@ -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)
|
public bool TryGetUbo(long Key, out int UboHandle)
|
||||||
{
|
{
|
||||||
if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
|
if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
|
||||||
|
|
|
@ -600,6 +600,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
ThrowUnsupportedAttrib(Attrib);
|
ThrowUnsupportedAttrib(Attrib);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fixed (byte* Ptr = Attrib.Data)
|
||||||
|
{
|
||||||
if (Attrib.Type == GalVertexAttribType.Unorm)
|
if (Attrib.Type == GalVertexAttribType.Unorm)
|
||||||
{
|
{
|
||||||
switch (Attrib.Size)
|
switch (Attrib.Size)
|
||||||
|
@ -608,21 +610,21 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
case GalVertexAttribSize._8_8:
|
case GalVertexAttribSize._8_8:
|
||||||
case GalVertexAttribSize._8_8_8:
|
case GalVertexAttribSize._8_8_8:
|
||||||
case GalVertexAttribSize._8_8_8_8:
|
case GalVertexAttribSize._8_8_8_8:
|
||||||
GL.VertexAttrib4N((uint)Attrib.Index, (byte*)Attrib.Pointer);
|
GL.VertexAttrib4N((uint)Attrib.Index, Ptr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GalVertexAttribSize._16:
|
case GalVertexAttribSize._16:
|
||||||
case GalVertexAttribSize._16_16:
|
case GalVertexAttribSize._16_16:
|
||||||
case GalVertexAttribSize._16_16_16:
|
case GalVertexAttribSize._16_16_16:
|
||||||
case GalVertexAttribSize._16_16_16_16:
|
case GalVertexAttribSize._16_16_16_16:
|
||||||
GL.VertexAttrib4N((uint)Attrib.Index, (ushort*)Attrib.Pointer);
|
GL.VertexAttrib4N((uint)Attrib.Index, (ushort*)Ptr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GalVertexAttribSize._32:
|
case GalVertexAttribSize._32:
|
||||||
case GalVertexAttribSize._32_32:
|
case GalVertexAttribSize._32_32:
|
||||||
case GalVertexAttribSize._32_32_32:
|
case GalVertexAttribSize._32_32_32:
|
||||||
case GalVertexAttribSize._32_32_32_32:
|
case GalVertexAttribSize._32_32_32_32:
|
||||||
GL.VertexAttrib4N((uint)Attrib.Index, (uint*)Attrib.Pointer);
|
GL.VertexAttrib4N((uint)Attrib.Index, (uint*)Ptr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -634,21 +636,21 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
case GalVertexAttribSize._8_8:
|
case GalVertexAttribSize._8_8:
|
||||||
case GalVertexAttribSize._8_8_8:
|
case GalVertexAttribSize._8_8_8:
|
||||||
case GalVertexAttribSize._8_8_8_8:
|
case GalVertexAttribSize._8_8_8_8:
|
||||||
GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Attrib.Pointer);
|
GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Ptr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GalVertexAttribSize._16:
|
case GalVertexAttribSize._16:
|
||||||
case GalVertexAttribSize._16_16:
|
case GalVertexAttribSize._16_16:
|
||||||
case GalVertexAttribSize._16_16_16:
|
case GalVertexAttribSize._16_16_16:
|
||||||
case GalVertexAttribSize._16_16_16_16:
|
case GalVertexAttribSize._16_16_16_16:
|
||||||
GL.VertexAttrib4N((uint)Attrib.Index, (short*)Attrib.Pointer);
|
GL.VertexAttrib4N((uint)Attrib.Index, (short*)Ptr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GalVertexAttribSize._32:
|
case GalVertexAttribSize._32:
|
||||||
case GalVertexAttribSize._32_32:
|
case GalVertexAttribSize._32_32:
|
||||||
case GalVertexAttribSize._32_32_32:
|
case GalVertexAttribSize._32_32_32:
|
||||||
case GalVertexAttribSize._32_32_32_32:
|
case GalVertexAttribSize._32_32_32_32:
|
||||||
GL.VertexAttrib4N((uint)Attrib.Index, (int*)Attrib.Pointer);
|
GL.VertexAttrib4N((uint)Attrib.Index, (int*)Ptr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -660,21 +662,21 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
case GalVertexAttribSize._8_8:
|
case GalVertexAttribSize._8_8:
|
||||||
case GalVertexAttribSize._8_8_8:
|
case GalVertexAttribSize._8_8_8:
|
||||||
case GalVertexAttribSize._8_8_8_8:
|
case GalVertexAttribSize._8_8_8_8:
|
||||||
GL.VertexAttribI4((uint)Attrib.Index, (byte*)Attrib.Pointer);
|
GL.VertexAttribI4((uint)Attrib.Index, Ptr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GalVertexAttribSize._16:
|
case GalVertexAttribSize._16:
|
||||||
case GalVertexAttribSize._16_16:
|
case GalVertexAttribSize._16_16:
|
||||||
case GalVertexAttribSize._16_16_16:
|
case GalVertexAttribSize._16_16_16:
|
||||||
case GalVertexAttribSize._16_16_16_16:
|
case GalVertexAttribSize._16_16_16_16:
|
||||||
GL.VertexAttribI4((uint)Attrib.Index, (ushort*)Attrib.Pointer);
|
GL.VertexAttribI4((uint)Attrib.Index, (ushort*)Ptr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GalVertexAttribSize._32:
|
case GalVertexAttribSize._32:
|
||||||
case GalVertexAttribSize._32_32:
|
case GalVertexAttribSize._32_32:
|
||||||
case GalVertexAttribSize._32_32_32:
|
case GalVertexAttribSize._32_32_32:
|
||||||
case GalVertexAttribSize._32_32_32_32:
|
case GalVertexAttribSize._32_32_32_32:
|
||||||
GL.VertexAttribI4((uint)Attrib.Index, (uint*)Attrib.Pointer);
|
GL.VertexAttribI4((uint)Attrib.Index, (uint*)Ptr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -686,21 +688,21 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
case GalVertexAttribSize._8_8:
|
case GalVertexAttribSize._8_8:
|
||||||
case GalVertexAttribSize._8_8_8:
|
case GalVertexAttribSize._8_8_8:
|
||||||
case GalVertexAttribSize._8_8_8_8:
|
case GalVertexAttribSize._8_8_8_8:
|
||||||
GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Attrib.Pointer);
|
GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Ptr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GalVertexAttribSize._16:
|
case GalVertexAttribSize._16:
|
||||||
case GalVertexAttribSize._16_16:
|
case GalVertexAttribSize._16_16:
|
||||||
case GalVertexAttribSize._16_16_16:
|
case GalVertexAttribSize._16_16_16:
|
||||||
case GalVertexAttribSize._16_16_16_16:
|
case GalVertexAttribSize._16_16_16_16:
|
||||||
GL.VertexAttribI4((uint)Attrib.Index, (short*)Attrib.Pointer);
|
GL.VertexAttribI4((uint)Attrib.Index, (short*)Ptr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GalVertexAttribSize._32:
|
case GalVertexAttribSize._32:
|
||||||
case GalVertexAttribSize._32_32:
|
case GalVertexAttribSize._32_32:
|
||||||
case GalVertexAttribSize._32_32_32:
|
case GalVertexAttribSize._32_32_32:
|
||||||
case GalVertexAttribSize._32_32_32_32:
|
case GalVertexAttribSize._32_32_32_32:
|
||||||
GL.VertexAttribI4((uint)Attrib.Index, (int*)Attrib.Pointer);
|
GL.VertexAttribI4((uint)Attrib.Index, (int*)Ptr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -712,13 +714,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
case GalVertexAttribSize._32_32:
|
case GalVertexAttribSize._32_32:
|
||||||
case GalVertexAttribSize._32_32_32:
|
case GalVertexAttribSize._32_32_32:
|
||||||
case GalVertexAttribSize._32_32_32_32:
|
case GalVertexAttribSize._32_32_32_32:
|
||||||
GL.VertexAttrib4(Attrib.Index, (float*)Attrib.Pointer);
|
GL.VertexAttrib4(Attrib.Index, (float*)Ptr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: ThrowUnsupportedAttrib(Attrib); break;
|
default: ThrowUnsupportedAttrib(Attrib); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void ThrowUnsupportedAttrib(GalVertexAttrib Attrib)
|
private static void ThrowUnsupportedAttrib(GalVertexAttrib Attrib)
|
||||||
{
|
{
|
||||||
|
|
|
@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
int Handle = GL.GenBuffer();
|
int Handle = GL.GenBuffer();
|
||||||
|
|
||||||
VboCache.AddOrUpdate(Key, Handle, (uint)DataSize);
|
VboCache.AddOrUpdate(Key, Handle, DataSize);
|
||||||
|
|
||||||
IntPtr Length = new IntPtr(DataSize);
|
IntPtr Length = new IntPtr(DataSize);
|
||||||
|
|
||||||
|
@ -100,6 +100,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
GL.BufferData(BufferTarget.ArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw);
|
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)
|
public void CreateIbo(long Key, int DataSize, IntPtr HostAddress)
|
||||||
{
|
{
|
||||||
int Handle = GL.GenBuffer();
|
int Handle = GL.GenBuffer();
|
||||||
|
@ -116,7 +128,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
int Handle = GL.GenBuffer();
|
int Handle = GL.GenBuffer();
|
||||||
|
|
||||||
IboCache.AddOrUpdate(Key, Handle, (uint)DataSize);
|
IboCache.AddOrUpdate(Key, Handle, DataSize);
|
||||||
|
|
||||||
IntPtr Length = new IntPtr(Buffer.Length);
|
IntPtr Length = new IntPtr(Buffer.Length);
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Size, HostAddress);
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
|
|
|
@ -243,9 +243,9 @@ namespace Ryujinx.Graphics.Memory
|
||||||
return Cache.IsRegionModified(Memory, BufferType, PA, Size);
|
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)
|
public byte ReadByte(long Position)
|
||||||
|
|
|
@ -615,9 +615,14 @@ namespace Ryujinx.Graphics
|
||||||
|
|
||||||
if (Gpu.ResourceManager.MemoryRegionModified(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer))
|
if (Gpu.ResourceManager.MemoryRegionModified(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer))
|
||||||
{
|
{
|
||||||
IntPtr Source = Vmm.GetHostAddress(Cb.Position, Cb.Size);
|
if (Vmm.TryGetHostAddress(Cb.Position, Cb.Size, out IntPtr CbPtr))
|
||||||
|
{
|
||||||
Gpu.Renderer.Buffer.SetData(Key, Cb.Size, Source);
|
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;
|
State.ConstBufferKeys[Stage][DeclInfo.Cbuf] = Key;
|
||||||
|
@ -660,9 +665,14 @@ namespace Ryujinx.Graphics
|
||||||
{
|
{
|
||||||
if (!UsesLegacyQuads)
|
if (!UsesLegacyQuads)
|
||||||
{
|
{
|
||||||
IntPtr DataAddress = Vmm.GetHostAddress(IbPosition, IbSize);
|
if (Vmm.TryGetHostAddress(IbPosition, IbSize, out IntPtr IbPtr))
|
||||||
|
{
|
||||||
Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress);
|
Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, IbPtr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, Vmm.ReadBytes(IbPosition, IbSize));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -711,22 +721,22 @@ namespace Ryujinx.Graphics
|
||||||
Attribs[ArrayIndex] = new List<GalVertexAttrib>();
|
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;
|
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,
|
//Note: 16 is the maximum size of an attribute,
|
||||||
//having a component size of 32-bits with 4 elements (a vec4).
|
//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(
|
Attribs[ArrayIndex].Add(new GalVertexAttrib(Attr, IsConst, Offset, Data, Size, Type, IsRgba));
|
||||||
Attr,
|
|
||||||
((Packed >> 6) & 0x1) != 0,
|
|
||||||
Offset,
|
|
||||||
Pointer,
|
|
||||||
(GalVertexAttribSize)((Packed >> 21) & 0x3f),
|
|
||||||
(GalVertexAttribType)((Packed >> 27) & 0x7),
|
|
||||||
((Packed >> 31) & 0x1) != 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
State.VertexBindings = new GalVertexBinding[32];
|
State.VertexBindings = new GalVertexBinding[32];
|
||||||
|
@ -747,8 +757,8 @@ namespace Ryujinx.Graphics
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
|
long VbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
|
||||||
long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2);
|
long VbEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2);
|
||||||
|
|
||||||
int VertexDivisor = ReadRegister(NvGpuEngine3dReg.VertexArrayNDivisor + Index * 4);
|
int VertexDivisor = ReadRegister(NvGpuEngine3dReg.VertexArrayNDivisor + Index * 4);
|
||||||
|
|
||||||
|
@ -758,26 +768,31 @@ namespace Ryujinx.Graphics
|
||||||
|
|
||||||
if (Instanced && VertexDivisor != 0)
|
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
|
//Instance is invalid, ignore the draw call
|
||||||
continue;
|
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);
|
bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize);
|
||||||
|
|
||||||
if (!VboCached || Gpu.ResourceManager.MemoryRegionModified(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex))
|
if (!VboCached || Gpu.ResourceManager.MemoryRegionModified(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex))
|
||||||
{
|
{
|
||||||
IntPtr DataAddress = Vmm.GetHostAddress(VertexPosition, VbSize);
|
if (Vmm.TryGetHostAddress(VbPosition, VbSize, out IntPtr VbPtr))
|
||||||
|
{
|
||||||
Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, DataAddress);
|
Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, VbPtr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Gpu.Renderer.Rasterizer.CreateVbo(VboKey, Vmm.ReadBytes(VbPosition, VbSize));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
State.VertexBindings[Index].Enabled = true;
|
State.VertexBindings[Index].Enabled = true;
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Memory
|
namespace Ryujinx.HLE
|
||||||
{
|
{
|
||||||
class DeviceMemory : IDisposable
|
class DeviceMemory : IDisposable
|
||||||
{
|
{
|
||||||
public const long RamSize = 4L * 1024 * 1024 * 1024;
|
public const long RamSize = 4L * 1024 * 1024 * 1024;
|
||||||
|
|
||||||
public ArenaAllocator Allocator { get; private set; }
|
|
||||||
|
|
||||||
public IntPtr RamPointer { get; private set; }
|
public IntPtr RamPointer { get; private set; }
|
||||||
|
|
||||||
private unsafe byte* RamPtr;
|
private unsafe byte* RamPtr;
|
||||||
|
|
||||||
public unsafe DeviceMemory()
|
public unsafe DeviceMemory()
|
||||||
{
|
{
|
||||||
Allocator = new ArenaAllocator(RamSize);
|
|
||||||
|
|
||||||
RamPointer = Marshal.AllocHGlobal(new IntPtr(RamSize));
|
RamPointer = Marshal.AllocHGlobal(new IntPtr(RamSize));
|
||||||
|
|
||||||
RamPtr = (byte*)RamPointer;
|
RamPtr = (byte*)RamPointer;
|
|
@ -29,10 +29,7 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
|
|
||||||
if (SaveMetaData.TitleId == 0 && SaveMetaData.SaveDataType == SaveDataType.SaveData)
|
if (SaveMetaData.TitleId == 0 && SaveMetaData.SaveDataType == SaveDataType.SaveData)
|
||||||
{
|
{
|
||||||
if (Context.Process.MetaData != null)
|
CurrentTitleId = Context.Process.TitleId;
|
||||||
{
|
|
||||||
CurrentTitleId = Context.Process.MetaData.ACI0.TitleId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string SaveAccount = SaveMetaData.UserId.IsZero() ? "savecommon" : SaveMetaData.UserId.ToString();
|
string SaveAccount = SaveMetaData.UserId.IsZero() ? "savecommon" : SaveMetaData.UserId.ToString();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using LibHac;
|
using LibHac;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
using Ryujinx.HLE.FileSystem.Content;
|
||||||
using Ryujinx.HLE.Resource;
|
using Ryujinx.HLE.Resource;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
@ -5,28 +6,28 @@ namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
class GlobalStateTable
|
class GlobalStateTable
|
||||||
{
|
{
|
||||||
private ConcurrentDictionary<Process, IdDictionary> DictByProcess;
|
private ConcurrentDictionary<KProcess, IdDictionary> DictByProcess;
|
||||||
|
|
||||||
public GlobalStateTable()
|
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());
|
IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary());
|
||||||
|
|
||||||
return Dict.Add(Id, Data);
|
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());
|
IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary());
|
||||||
|
|
||||||
return Dict.Add(Data);
|
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))
|
if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
|
||||||
{
|
{
|
||||||
|
@ -36,7 +37,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
return null;
|
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))
|
if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
|
||||||
{
|
{
|
||||||
|
@ -46,7 +47,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
return default(T);
|
return default(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Delete(Process Process, int Id)
|
public object Delete(KProcess Process, int Id)
|
||||||
{
|
{
|
||||||
if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
|
if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
|
||||||
{
|
{
|
||||||
|
@ -56,7 +57,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICollection<object> DeleteProcess(Process Process)
|
public ICollection<object> DeleteProcess(KProcess Process)
|
||||||
{
|
{
|
||||||
if (DictByProcess.TryRemove(Process, out IdDictionary Dict))
|
if (DictByProcess.TryRemove(Process, out IdDictionary Dict))
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,32 +11,68 @@ using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
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
|
namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
public class Horizon : IDisposable
|
public class Horizon : IDisposable
|
||||||
{
|
{
|
||||||
|
internal const int InitialKipId = 1;
|
||||||
|
internal const int InitialProcessId = 0x51;
|
||||||
|
|
||||||
internal const int HidSize = 0x40000;
|
internal const int HidSize = 0x40000;
|
||||||
internal const int FontSize = 0x1100000;
|
internal const int FontSize = 0x1100000;
|
||||||
|
|
||||||
private Switch Device;
|
private const int MemoryBlockAllocatorSize = 0x2710;
|
||||||
|
|
||||||
private ConcurrentDictionary<int, Process> Processes;
|
private const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;
|
||||||
|
private const ulong UserSlabHeapItemSize = KMemoryManager.PageSize;
|
||||||
|
private const ulong UserSlabHeapSize = 0x3de000;
|
||||||
|
|
||||||
|
internal long PrivilegedProcessLowestId { get; set; } = 1;
|
||||||
|
internal long PrivilegedProcessHighestId { get; set; } = 8;
|
||||||
|
|
||||||
|
internal Switch Device { get; private set; }
|
||||||
|
|
||||||
public SystemStateMgr State { 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 KScheduler Scheduler { get; private set; }
|
||||||
|
|
||||||
internal KTimeManager TimeManager { get; private set; }
|
internal KTimeManager TimeManager { get; private set; }
|
||||||
|
|
||||||
internal KAddressArbiter AddressArbiter { get; private set; }
|
|
||||||
|
|
||||||
internal KSynchronization Synchronization { 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 HidSharedMem { get; private set; }
|
||||||
internal KSharedMemory FontSharedMem { get; private set; }
|
internal KSharedMemory FontSharedMem { get; private set; }
|
||||||
|
@ -57,38 +93,74 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; }
|
public IntegrityCheckLevel FsIntegrityCheckLevel { get; set; }
|
||||||
|
|
||||||
|
internal long HidBaseAddress { get; private set; }
|
||||||
|
|
||||||
public Horizon(Switch Device)
|
public Horizon(Switch Device)
|
||||||
{
|
{
|
||||||
this.Device = Device;
|
this.Device = Device;
|
||||||
|
|
||||||
Processes = new ConcurrentDictionary<int, Process>();
|
|
||||||
|
|
||||||
State = new SystemStateMgr();
|
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);
|
Scheduler = new KScheduler(this);
|
||||||
|
|
||||||
TimeManager = new KTimeManager();
|
TimeManager = new KTimeManager();
|
||||||
|
|
||||||
AddressArbiter = new KAddressArbiter(this);
|
|
||||||
|
|
||||||
Synchronization = new KSynchronization(this);
|
Synchronization = new KSynchronization(this);
|
||||||
|
|
||||||
Withholders = new LinkedList<KThread>();
|
ContextIdManager = new KContextIdManager();
|
||||||
|
|
||||||
|
KipId = InitialKipId;
|
||||||
|
ProcessId = InitialProcessId;
|
||||||
|
|
||||||
Scheduler.StartAutoPreemptionThread();
|
Scheduler.StartAutoPreemptionThread();
|
||||||
|
|
||||||
if (!Device.Memory.Allocator.TryAllocate(HidSize, out long HidPA) ||
|
KernelInitialized = true;
|
||||||
!Device.Memory.Allocator.TryAllocate(FontSize, out long FontPA))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
HidSharedMem = new KSharedMemory(HidPA, HidSize);
|
ThreadCounter = new CountdownEvent(1);
|
||||||
FontSharedMem = new KSharedMemory(FontPA, FontSize);
|
|
||||||
|
|
||||||
Font = new SharedFontManager(Device, FontSharedMem.PA);
|
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);
|
||||||
|
|
||||||
|
KPageList HidPageList = new KPageList();
|
||||||
|
KPageList FontPageList = new KPageList();
|
||||||
|
|
||||||
|
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);
|
VsyncEvent = new KEvent(this);
|
||||||
|
|
||||||
|
@ -120,13 +192,15 @@ namespace Ryujinx.HLE.HOS
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
|
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)
|
if (Path.GetExtension(File) != string.Empty)
|
||||||
{
|
{
|
||||||
|
@ -137,33 +211,28 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
using (FileStream Input = new FileStream(File, FileMode.Open))
|
using (FileStream Input = new FileStream(File, FileMode.Open))
|
||||||
{
|
{
|
||||||
string Name = Path.GetFileNameWithoutExtension(File);
|
NxStaticObject StaticObject = new NxStaticObject(Input);
|
||||||
|
|
||||||
Nso Program = new Nso(Input, Name);
|
StaticObjects.Add(StaticObject);
|
||||||
|
|
||||||
MainProcess.LoadProgram(Program);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(MainProcess.MetaData?.Is64Bits ?? true))
|
if (!MetaData.Is64Bits)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException("32-bit titles are unsupported!");
|
throw new NotImplementedException("32-bit titles are unsupported!");
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentTitle = MainProcess.MetaData.ACI0.TitleId.ToString("x16");
|
CurrentTitle = MetaData.ACI0.TitleId.ToString("x16");
|
||||||
|
|
||||||
LoadNso("rtld");
|
LoadNso("rtld");
|
||||||
|
|
||||||
MainProcess.SetEmptyArgs();
|
|
||||||
|
|
||||||
LoadNso("main");
|
LoadNso("main");
|
||||||
LoadNso("subsdk*");
|
LoadNso("subsdk*");
|
||||||
LoadNso("sdk");
|
LoadNso("sdk");
|
||||||
|
|
||||||
ContentManager.LoadEntries();
|
ContentManager.LoadEntries();
|
||||||
|
|
||||||
MainProcess.Run();
|
ProgramLoader.LoadStaticObjects(this, MetaData, StaticObjects.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadXci(string XciFile)
|
public void LoadXci(string XciFile)
|
||||||
|
@ -356,9 +425,11 @@ namespace Ryujinx.HLE.HOS
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
|
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 Filename)
|
||||||
{
|
{
|
||||||
|
@ -371,11 +442,9 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
Logger.PrintInfo(LogClass.Loader, $"Loading {Filename}...");
|
Logger.PrintInfo(LogClass.Loader, $"Loading {Filename}...");
|
||||||
|
|
||||||
string Name = Path.GetFileNameWithoutExtension(File.Name);
|
NxStaticObject StaticObject = new NxStaticObject(Exefs.OpenFile(File));
|
||||||
|
|
||||||
Nso Program = new Nso(Exefs.OpenFile(File), Name);
|
StaticObjects.Add(StaticObject);
|
||||||
|
|
||||||
MainProcess.LoadProgram(Program);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,69 +470,52 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
if (ControlNca != null)
|
if (ControlNca != null)
|
||||||
{
|
{
|
||||||
MainProcess.ControlData = ReadControlData();
|
ReadControlData();
|
||||||
}
|
}
|
||||||
else
|
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");
|
LoadNso("rtld");
|
||||||
|
|
||||||
MainProcess.SetEmptyArgs();
|
|
||||||
|
|
||||||
LoadNso("main");
|
LoadNso("main");
|
||||||
LoadNso("subsdk");
|
LoadNso("subsdk");
|
||||||
LoadNso("sdk");
|
LoadNso("sdk");
|
||||||
|
|
||||||
ContentManager.LoadEntries();
|
ContentManager.LoadEntries();
|
||||||
|
|
||||||
MainProcess.Run();
|
ProgramLoader.LoadStaticObjects(this, MetaData, StaticObjects.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadProgram(string FilePath)
|
public void LoadProgram(string FilePath)
|
||||||
{
|
{
|
||||||
|
Npdm MetaData = GetDefaultNpdm();
|
||||||
|
|
||||||
bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro";
|
bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro";
|
||||||
|
|
||||||
string Name = Path.GetFileNameWithoutExtension(FilePath);
|
|
||||||
string SwitchFilePath = Device.FileSystem.SystemPathToSwitchPath(FilePath);
|
|
||||||
|
|
||||||
if (IsNro && (SwitchFilePath == null || !SwitchFilePath.StartsWith("sdmc:/")))
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
FilePath = TempPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
Process MainProcess = MakeProcess();
|
|
||||||
|
|
||||||
using (FileStream Input = new FileStream(FilePath, FileMode.Open))
|
using (FileStream Input = new FileStream(FilePath, FileMode.Open))
|
||||||
{
|
{
|
||||||
MainProcess.LoadProgram(IsNro
|
IExecutable StaticObject = IsNro
|
||||||
? (IExecutable)new Nro(Input, FilePath)
|
? (IExecutable)new NxRelocatableObject(Input)
|
||||||
: (IExecutable)new Nso(Input, FilePath));
|
: (IExecutable)new NxStaticObject(Input);
|
||||||
|
|
||||||
|
ProgramLoader.LoadStaticObjects(this, MetaData, new IExecutable[] { StaticObject });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MainProcess.SetEmptyArgs();
|
private Npdm GetDefaultNpdm()
|
||||||
|
{
|
||||||
|
Assembly Asm = Assembly.GetCallingAssembly();
|
||||||
|
|
||||||
ContentManager.LoadEntries();
|
using (Stream NpdmStream = Asm.GetManifestResourceStream("Ryujinx.HLE.Homebrew.npdm"))
|
||||||
|
{
|
||||||
MainProcess.Run(IsNro);
|
return new Npdm(NpdmStream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadKeySet()
|
public void LoadKeySet()
|
||||||
|
@ -507,51 +559,19 @@ namespace Ryujinx.HLE.HOS
|
||||||
VsyncEvent.ReadableEvent.Signal();
|
VsyncEvent.ReadableEvent.Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Process MakeProcess(Npdm MetaData = null)
|
internal long GetThreadUid()
|
||||||
{
|
{
|
||||||
HasStarted = true;
|
return Interlocked.Increment(ref ThreadUid) - 1;
|
||||||
|
|
||||||
Process Process;
|
|
||||||
|
|
||||||
lock (Processes)
|
|
||||||
{
|
|
||||||
int ProcessId = 0;
|
|
||||||
|
|
||||||
while (Processes.ContainsKey(ProcessId))
|
|
||||||
{
|
|
||||||
ProcessId++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Process = new Process(Device, ProcessId, MetaData);
|
internal long GetKipId()
|
||||||
|
|
||||||
Processes.TryAdd(ProcessId, Process);
|
|
||||||
}
|
|
||||||
|
|
||||||
InitializeProcess(Process);
|
|
||||||
|
|
||||||
return Process;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeProcess(Process Process)
|
|
||||||
{
|
{
|
||||||
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))
|
return Interlocked.Increment(ref ProcessId) - 1;
|
||||||
{
|
|
||||||
Process.Dispose();
|
|
||||||
|
|
||||||
if (Processes.Count == 0)
|
|
||||||
{
|
|
||||||
Scheduler.Dispose();
|
|
||||||
|
|
||||||
TimeManager.Dispose();
|
|
||||||
|
|
||||||
Device.Unload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnableMultiCoreScheduling()
|
public void EnableMultiCoreScheduling()
|
||||||
|
@ -579,11 +599,26 @@ namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
if (Disposing)
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
static class IpcHandler
|
static class IpcHandler
|
||||||
{
|
{
|
||||||
public static long IpcCall(
|
public static long IpcCall(
|
||||||
Switch Ns,
|
Switch Device,
|
||||||
Process Process,
|
KProcess Process,
|
||||||
MemoryManager Memory,
|
MemoryManager Memory,
|
||||||
KSession Session,
|
KSession Session,
|
||||||
IpcMessage Request,
|
IpcMessage Request,
|
||||||
|
@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
BinaryWriter ResWriter = new BinaryWriter(ResMS);
|
BinaryWriter ResWriter = new BinaryWriter(ResMS);
|
||||||
|
|
||||||
ServiceCtx Context = new ServiceCtx(
|
ServiceCtx Context = new ServiceCtx(
|
||||||
Ns,
|
Device,
|
||||||
Process,
|
Process,
|
||||||
Memory,
|
Memory,
|
||||||
Session,
|
Session,
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
Addr32Bits = 0,
|
Addr32Bits = 0,
|
||||||
Addr36Bits = 1,
|
Addr36Bits = 1,
|
||||||
Addr36BitsNoMap = 2,
|
Addr32BitsNoMap = 2,
|
||||||
Addr39Bits = 3
|
Addr39Bits = 3
|
||||||
}
|
}
|
||||||
}
|
}
|
15
Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs
Normal file
15
Ryujinx.HLE/HOS/Kernel/DramMemoryMap.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,24 +5,61 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class HleCoreManager
|
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()
|
public HleCoreManager()
|
||||||
{
|
{
|
||||||
Threads = new ConcurrentDictionary<Thread, ManualResetEvent>();
|
Threads = new ConcurrentDictionary<Thread, PausableThread>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ManualResetEvent GetThread(Thread Thread)
|
public void Set(Thread Thread)
|
||||||
{
|
{
|
||||||
return Threads.GetOrAdd(Thread, (Key) => new ManualResetEvent(false));
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PausableThread GetThread(Thread Thread)
|
||||||
|
{
|
||||||
|
return Threads.GetOrAdd(Thread, (Key) => new PausableThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveThread(Thread Thread)
|
public void RemoveThread(Thread Thread)
|
||||||
{
|
{
|
||||||
if (Threads.TryRemove(Thread, out ManualResetEvent Event))
|
if (Threads.TryRemove(Thread, out PausableThread PausableThread))
|
||||||
{
|
{
|
||||||
Event.Set();
|
PausableThread.Event.Set();
|
||||||
Event.Dispose();
|
PausableThread.Event.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
310
Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs
Normal file
310
Ryujinx.HLE/HOS/Kernel/HleProcessDebugger.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public bool MultiCoreScheduling { get; set; }
|
public bool MultiCoreScheduling { get; set; }
|
||||||
|
|
||||||
private HleCoreManager CoreManager;
|
public HleCoreManager CoreManager { get; private set; }
|
||||||
|
|
||||||
private bool KeepPreempting;
|
private bool KeepPreempting;
|
||||||
|
|
||||||
|
@ -49,11 +49,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
if (SelectedCount == 0)
|
if (SelectedCount == 0)
|
||||||
{
|
{
|
||||||
CoreManager.GetThread(Thread.CurrentThread).Reset();
|
CoreManager.Reset(Thread.CurrentThread);
|
||||||
}
|
}
|
||||||
else if (SelectedCount == 1)
|
else if (SelectedCount == 1)
|
||||||
{
|
{
|
||||||
CoreManager.GetThread(Thread.CurrentThread).Set();
|
CoreManager.Set(Thread.CurrentThread);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -77,7 +77,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CoreManager.GetThread(CurrentThread.Context.Work).Reset();
|
CoreManager.Reset(CurrentThread.Context.Work);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Advance current core and try picking a thread,
|
//Advance current core and try picking a thread,
|
||||||
|
@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
CoreContext.CurrentThread.ClearExclusive();
|
CoreContext.CurrentThread.ClearExclusive();
|
||||||
|
|
||||||
CoreManager.GetThread(CoreContext.CurrentThread.Context.Work).Set();
|
CoreManager.Set(CoreContext.CurrentThread.Context.Work);
|
||||||
|
|
||||||
CoreContext.CurrentThread.Context.Execute();
|
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()
|
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();
|
Thread.Context.StopExecution();
|
||||||
|
|
||||||
CoreManager.GetThread(Thread.Context.Work).Set();
|
CoreManager.Exit(Thread.Context.Work);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveThread(KThread Thread)
|
public void RemoveThread(KThread Thread)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using ChocolArm64.Memory;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
|
@ -23,39 +22,36 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
ArbiterThreads = new List<KThread>();
|
ArbiterThreads = new List<KThread>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long ArbitrateLock(
|
public long ArbitrateLock(int OwnerHandle, long MutexAddress, int RequesterHandle)
|
||||||
Process Process,
|
|
||||||
MemoryManager Memory,
|
|
||||||
int OwnerHandle,
|
|
||||||
long MutexAddress,
|
|
||||||
int RequesterHandle)
|
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
|
||||||
|
|
||||||
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
CurrentThread.SignaledObj = null;
|
CurrentThread.SignaledObj = null;
|
||||||
CurrentThread.ObjSyncResult = 0;
|
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);;
|
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MutexValue != (OwnerHandle | HasListenersMask))
|
if (MutexValue != (OwnerHandle | HasListenersMask))
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
KThread MutexOwner = Process.HandleTable.GetObject<KThread>(OwnerHandle);
|
KThread MutexOwner = CurrentProcess.HandleTable.GetObject<KThread>(OwnerHandle);
|
||||||
|
|
||||||
if (MutexOwner == null)
|
if (MutexOwner == null)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
return MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||||
}
|
}
|
||||||
|
@ -67,26 +63,26 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
CurrentThread.Reschedule(ThreadSchedState.Paused);
|
CurrentThread.Reschedule(ThreadSchedState.Paused);
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (CurrentThread.MutexOwner != null)
|
if (CurrentThread.MutexOwner != null)
|
||||||
{
|
{
|
||||||
CurrentThread.MutexOwner.RemoveMutexWaiter(CurrentThread);
|
CurrentThread.MutexOwner.RemoveMutexWaiter(CurrentThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return (uint)CurrentThread.ObjSyncResult;
|
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();
|
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)
|
if (Result != 0 && NewOwnerThread != null)
|
||||||
{
|
{
|
||||||
|
@ -94,19 +90,18 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
NewOwnerThread.ObjSyncResult = (int)Result;
|
NewOwnerThread.ObjSyncResult = (int)Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long WaitProcessWideKeyAtomic(
|
public long WaitProcessWideKeyAtomic(
|
||||||
MemoryManager Memory,
|
|
||||||
long MutexAddress,
|
long MutexAddress,
|
||||||
long CondVarAddress,
|
long CondVarAddress,
|
||||||
int ThreadHandle,
|
int ThreadHandle,
|
||||||
long Timeout)
|
long Timeout)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
|
@ -116,16 +111,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
if (CurrentThread.ShallBeTerminated ||
|
if (CurrentThread.ShallBeTerminated ||
|
||||||
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
||||||
}
|
}
|
||||||
|
|
||||||
(long Result, _) = MutexUnlock(Memory, CurrentThread, MutexAddress);
|
(long Result, _) = MutexUnlock(CurrentThread, MutexAddress);
|
||||||
|
|
||||||
if (Result != 0)
|
if (Result != 0)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
@ -146,14 +141,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
if (Timeout > 0)
|
if (Timeout > 0)
|
||||||
{
|
{
|
||||||
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (CurrentThread.MutexOwner != null)
|
if (CurrentThread.MutexOwner != null)
|
||||||
{
|
{
|
||||||
|
@ -162,12 +157,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
CondVarThreads.Remove(CurrentThread);
|
CondVarThreads.Remove(CurrentThread);
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return (uint)CurrentThread.ObjSyncResult;
|
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);
|
KThread NewOwnerThread = CurrentThread.RelinquishMutex(MutexAddress, out int Count);
|
||||||
|
|
||||||
|
@ -190,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
long Result = 0;
|
long Result = 0;
|
||||||
|
|
||||||
if (!KernelToUserInt32(Memory, MutexAddress, MutexValue))
|
if (!KernelTransfer.KernelToUserInt32(System, MutexAddress, MutexValue))
|
||||||
{
|
{
|
||||||
Result = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
Result = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
}
|
}
|
||||||
|
@ -198,17 +193,17 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return (Result, NewOwnerThread);
|
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>();
|
Queue<KThread> SignaledThreads = new Queue<KThread>();
|
||||||
|
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
IOrderedEnumerable<KThread> SortedThreads = CondVarThreads.OrderBy(x => x.DynamicPriority);
|
IOrderedEnumerable<KThread> SortedThreads = CondVarThreads.OrderBy(x => x.DynamicPriority);
|
||||||
|
|
||||||
foreach (KThread Thread in SortedThreads.Where(x => x.CondVarAddress == Address))
|
foreach (KThread Thread in SortedThreads.Where(x => x.CondVarAddress == Address))
|
||||||
{
|
{
|
||||||
TryAcquireMutex(Process, Memory, Thread);
|
TryAcquireMutex(Thread);
|
||||||
|
|
||||||
SignaledThreads.Enqueue(Thread);
|
SignaledThreads.Enqueue(Thread);
|
||||||
|
|
||||||
|
@ -224,19 +219,21 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
CondVarThreads.Remove(Thread);
|
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;
|
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.
|
//Invalid address.
|
||||||
Memory.ClearExclusive(0);
|
CurrentProcess.CpuMemory.ClearExclusive(0);
|
||||||
|
|
||||||
Requester.SignaledObj = null;
|
Requester.SignaledObj = null;
|
||||||
Requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
Requester.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
@ -246,27 +243,27 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (Memory.TestExclusive(0, Address))
|
if (CurrentProcess.CpuMemory.TestExclusive(0, Address))
|
||||||
{
|
{
|
||||||
if (MutexValue != 0)
|
if (MutexValue != 0)
|
||||||
{
|
{
|
||||||
//Update value to indicate there is a mutex waiter now.
|
//Update value to indicate there is a mutex waiter now.
|
||||||
Memory.WriteInt32(Address, MutexValue | HasListenersMask);
|
CurrentProcess.CpuMemory.WriteInt32(Address, MutexValue | HasListenersMask);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//No thread owning the mutex, assign to requesting thread.
|
//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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Memory.SetExclusive(0, Address);
|
CurrentProcess.CpuMemory.SetExclusive(0, Address);
|
||||||
|
|
||||||
MutexValue = Memory.ReadInt32(Address);
|
MutexValue = CurrentProcess.CpuMemory.ReadInt32(Address);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MutexValue == 0)
|
if (MutexValue == 0)
|
||||||
|
@ -282,7 +279,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
MutexValue &= ~HasListenersMask;
|
MutexValue &= ~HasListenersMask;
|
||||||
|
|
||||||
KThread MutexOwner = Process.HandleTable.GetObject<KThread>(MutexValue);
|
KThread MutexOwner = CurrentProcess.HandleTable.GetObject<KThread>(MutexValue);
|
||||||
|
|
||||||
if (MutexOwner != null)
|
if (MutexOwner != null)
|
||||||
{
|
{
|
||||||
|
@ -301,16 +298,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return MutexOwner;
|
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();
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (CurrentThread.ShallBeTerminated ||
|
if (CurrentThread.ShallBeTerminated ||
|
||||||
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
||||||
}
|
}
|
||||||
|
@ -318,9 +315,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
CurrentThread.SignaledObj = null;
|
CurrentThread.SignaledObj = null;
|
||||||
CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
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);
|
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
}
|
}
|
||||||
|
@ -329,7 +326,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
if (Timeout == 0)
|
if (Timeout == 0)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||||
}
|
}
|
||||||
|
@ -346,14 +343,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
|
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
if (Timeout > 0)
|
if (Timeout > 0)
|
||||||
{
|
{
|
||||||
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (CurrentThread.WaitingInArbitration)
|
if (CurrentThread.WaitingInArbitration)
|
||||||
{
|
{
|
||||||
|
@ -362,31 +359,26 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
CurrentThread.WaitingInArbitration = false;
|
CurrentThread.WaitingInArbitration = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return CurrentThread.ObjSyncResult;
|
return CurrentThread.ObjSyncResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long WaitForAddressIfLessThan(
|
public long WaitForAddressIfLessThan(long Address, int Value, bool ShouldDecrement, long Timeout)
|
||||||
MemoryManager Memory,
|
|
||||||
long Address,
|
|
||||||
int Value,
|
|
||||||
bool ShouldDecrement,
|
|
||||||
long Timeout)
|
|
||||||
{
|
{
|
||||||
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (CurrentThread.ShallBeTerminated ||
|
if (CurrentThread.ShallBeTerminated ||
|
||||||
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
CurrentThread.SchedFlags == ThreadSchedState.TerminationPending)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
||||||
}
|
}
|
||||||
|
@ -394,12 +386,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
CurrentThread.SignaledObj = null;
|
CurrentThread.SignaledObj = null;
|
||||||
CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
CurrentThread.ObjSyncResult = (int)MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||||
|
|
||||||
//If ShouldDecrement is true, do atomic decrement of the value at Address.
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
Memory.SetExclusive(0, Address);
|
|
||||||
|
|
||||||
if (!UserToKernelInt32(Memory, Address, out int CurrentValue))
|
//If ShouldDecrement is true, do atomic decrement of the value at Address.
|
||||||
|
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);
|
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
}
|
}
|
||||||
|
@ -408,28 +402,28 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
while (CurrentValue < Value)
|
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;
|
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 (CurrentValue < Value)
|
||||||
{
|
{
|
||||||
if (Timeout == 0)
|
if (Timeout == 0)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
return MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||||
}
|
}
|
||||||
|
@ -446,14 +440,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
|
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
if (Timeout > 0)
|
if (Timeout > 0)
|
||||||
{
|
{
|
||||||
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (CurrentThread.WaitingInArbitration)
|
if (CurrentThread.WaitingInArbitration)
|
||||||
{
|
{
|
||||||
|
@ -462,12 +456,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
CurrentThread.WaitingInArbitration = false;
|
CurrentThread.WaitingInArbitration = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return CurrentThread.ObjSyncResult;
|
return CurrentThread.ObjSyncResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||||
}
|
}
|
||||||
|
@ -498,63 +492,65 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public long Signal(long Address, int Count)
|
public long Signal(long Address, int Count)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
WakeArbiterThreads(Address, Count);
|
WakeArbiterThreads(Address, Count);
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return 0;
|
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();
|
||||||
|
|
||||||
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);
|
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (CurrentValue == Value)
|
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;
|
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 (CurrentValue != Value)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||||
}
|
}
|
||||||
|
|
||||||
WakeArbiterThreads(Address, Count);
|
WakeArbiterThreads(Address, Count);
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return 0;
|
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;
|
int Offset;
|
||||||
|
|
||||||
|
@ -580,43 +576,45 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
Offset = 1;
|
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);
|
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (CurrentValue == Value)
|
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;
|
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 (CurrentValue != Value)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||||
}
|
}
|
||||||
|
|
||||||
WakeArbiterThreads(Address, Count);
|
WakeArbiterThreads(Address, Count);
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -648,31 +646,5 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
ArbiterThreads.Remove(Thread);
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
42
Ryujinx.HLE/HOS/Kernel/KAutoObject.cs
Normal file
42
Ryujinx.HLE/HOS/Kernel/KAutoObject.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
Ryujinx.HLE/HOS/Kernel/KClientPort.cs
Normal file
31
Ryujinx.HLE/HOS/Kernel/KClientPort.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
71
Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs
Normal file
71
Ryujinx.HLE/HOS/Kernel/KConditionVariable.cs
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
83
Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs
Normal file
83
Ryujinx.HLE/HOS/Kernel/KContextIdManager.cs
Normal file
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
|
@ -11,6 +10,10 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public bool ContextSwitchNeeded { get; private set; }
|
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 CurrentThread { get; private set; }
|
||||||
public KThread SelectedThread { get; private set; }
|
public KThread SelectedThread { get; private set; }
|
||||||
|
|
||||||
|
@ -24,11 +27,6 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
SelectedThread = Thread;
|
SelectedThread = Thread;
|
||||||
|
|
||||||
if (Thread != null)
|
|
||||||
{
|
|
||||||
Thread.LastScheduledTicks = PerformanceCounter.ElapsedMilliseconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SelectedThread != CurrentThread)
|
if (SelectedThread != CurrentThread)
|
||||||
{
|
{
|
||||||
ContextSwitchNeeded = true;
|
ContextSwitchNeeded = true;
|
||||||
|
@ -39,25 +37,42 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
ContextSwitchNeeded = false;
|
ContextSwitchNeeded = false;
|
||||||
|
|
||||||
|
LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
|
||||||
|
|
||||||
CurrentThread = SelectedThread;
|
CurrentThread = SelectedThread;
|
||||||
|
|
||||||
|
if (CurrentThread != null)
|
||||||
|
{
|
||||||
|
long CurrentTime = PerformanceCounter.ElapsedMilliseconds;
|
||||||
|
|
||||||
|
CurrentThread.TotalTimeRunning += CurrentTime - CurrentThread.LastScheduledTime;
|
||||||
|
CurrentThread.LastScheduledTime = CurrentTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ContextSwitch()
|
public void ContextSwitch()
|
||||||
{
|
{
|
||||||
ContextSwitchNeeded = false;
|
ContextSwitchNeeded = false;
|
||||||
|
|
||||||
|
LastContextSwitchTime = PerformanceCounter.ElapsedMilliseconds;
|
||||||
|
|
||||||
if (CurrentThread != null)
|
if (CurrentThread != null)
|
||||||
{
|
{
|
||||||
CoreManager.GetThread(CurrentThread.Context.Work).Reset();
|
CoreManager.Reset(CurrentThread.Context.Work);
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentThread = SelectedThread;
|
CurrentThread = SelectedThread;
|
||||||
|
|
||||||
if (CurrentThread != null)
|
if (CurrentThread != null)
|
||||||
{
|
{
|
||||||
|
long CurrentTime = PerformanceCounter.ElapsedMilliseconds;
|
||||||
|
|
||||||
|
CurrentThread.TotalTimeRunning += CurrentTime - CurrentThread.LastScheduledTime;
|
||||||
|
CurrentThread.LastScheduledTime = CurrentTime;
|
||||||
|
|
||||||
CurrentThread.ClearExclusive();
|
CurrentThread.ClearExclusive();
|
||||||
|
|
||||||
CoreManager.GetThread(CurrentThread.Context.Work).Set();
|
CoreManager.Set(CurrentThread.Context.Work);
|
||||||
|
|
||||||
CurrentThread.Context.Execute();
|
CurrentThread.Context.Execute();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KRecursiveLock
|
class KCriticalSection
|
||||||
{
|
{
|
||||||
private Horizon System;
|
private Horizon System;
|
||||||
|
|
||||||
|
@ -11,21 +11,21 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private int RecursionCount;
|
private int RecursionCount;
|
||||||
|
|
||||||
public KRecursiveLock(Horizon System)
|
public KCriticalSection(Horizon System)
|
||||||
{
|
{
|
||||||
this.System = System;
|
this.System = System;
|
||||||
|
|
||||||
LockObj = new object();
|
LockObj = new object();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Lock()
|
public void Enter()
|
||||||
{
|
{
|
||||||
Monitor.Enter(LockObj);
|
Monitor.Enter(LockObj);
|
||||||
|
|
||||||
RecursionCount++;
|
RecursionCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unlock()
|
public void Leave()
|
||||||
{
|
{
|
||||||
if (RecursionCount == 0)
|
if (RecursionCount == 0)
|
||||||
{
|
{
|
|
@ -2,7 +2,7 @@ using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KProcessHandleTable
|
class KHandleTable
|
||||||
{
|
{
|
||||||
private const int SelfThreadHandle = (0x1ffff << 15) | 0;
|
private const int SelfThreadHandle = (0x1ffff << 15) | 0;
|
||||||
private const int SelfProcessHandle = (0x1ffff << 15) | 1;
|
private const int SelfProcessHandle = (0x1ffff << 15) | 1;
|
||||||
|
@ -20,11 +20,23 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private ushort IdCounter;
|
private ushort IdCounter;
|
||||||
|
|
||||||
private object LockObj;
|
public KHandleTable(Horizon System)
|
||||||
|
|
||||||
public KProcessHandleTable(Horizon System, int Size = 1024)
|
|
||||||
{
|
{
|
||||||
this.System = System;
|
this.System = System;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult Initialize(int Size)
|
||||||
|
{
|
||||||
|
if ((uint)Size > 1024)
|
||||||
|
{
|
||||||
|
return KernelResult.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Size < 1)
|
||||||
|
{
|
||||||
|
Size = 1024;
|
||||||
|
}
|
||||||
|
|
||||||
this.Size = Size;
|
this.Size = Size;
|
||||||
|
|
||||||
IdCounter = 1;
|
IdCounter = 1;
|
||||||
|
@ -48,14 +60,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
NextFreeEntry = TableHead;
|
NextFreeEntry = TableHead;
|
||||||
|
|
||||||
LockObj = new object();
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult GenerateHandle(object Obj, out int Handle)
|
public KernelResult GenerateHandle(object Obj, out int Handle)
|
||||||
{
|
{
|
||||||
Handle = 0;
|
Handle = 0;
|
||||||
|
|
||||||
lock (LockObj)
|
lock (Table)
|
||||||
{
|
{
|
||||||
if (ActiveSlotsCount >= Size)
|
if (ActiveSlotsCount >= Size)
|
||||||
{
|
{
|
||||||
|
@ -100,7 +112,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
bool Result = false;
|
bool Result = false;
|
||||||
|
|
||||||
lock (LockObj)
|
lock (Table)
|
||||||
{
|
{
|
||||||
if (HandleId != 0 && Index < Size)
|
if (HandleId != 0 && Index < Size)
|
||||||
{
|
{
|
||||||
|
@ -128,7 +140,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
int Index = (Handle >> 0) & 0x7fff;
|
int Index = (Handle >> 0) & 0x7fff;
|
||||||
int HandleId = (Handle >> 15);
|
int HandleId = (Handle >> 15);
|
||||||
|
|
||||||
lock (LockObj)
|
lock (Table)
|
||||||
{
|
{
|
||||||
if ((Handle >> 30) == 0 && HandleId != 0)
|
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()
|
public void Destroy()
|
||||||
{
|
{
|
||||||
lock (LockObj)
|
lock (Table)
|
||||||
{
|
{
|
||||||
for (int Index = 0; Index < Size; Index++)
|
for (int Index = 0; Index < Size; Index++)
|
||||||
{
|
{
|
22
Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs
Normal file
22
Ryujinx.HLE/HOS/Kernel/KMemoryArrange.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs
Normal file
16
Ryujinx.HLE/HOS/Kernel/KMemoryArrangeRegion.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KMemoryBlock
|
class KMemoryBlock
|
||||||
{
|
{
|
||||||
public long BasePosition { get; set; }
|
public ulong BaseAddress { get; set; }
|
||||||
public long PagesCount { get; set; }
|
public ulong PagesCount { get; set; }
|
||||||
|
|
||||||
public MemoryState State { get; set; }
|
public MemoryState State { get; set; }
|
||||||
public MemoryPermission Permission { get; set; }
|
public MemoryPermission Permission { get; set; }
|
||||||
|
@ -13,13 +13,13 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
public int DeviceRefCount { get; set; }
|
public int DeviceRefCount { get; set; }
|
||||||
|
|
||||||
public KMemoryBlock(
|
public KMemoryBlock(
|
||||||
long BasePosition,
|
ulong BaseAddress,
|
||||||
long PagesCount,
|
ulong PagesCount,
|
||||||
MemoryState State,
|
MemoryState State,
|
||||||
MemoryPermission Permission,
|
MemoryPermission Permission,
|
||||||
MemoryAttribute Attribute)
|
MemoryAttribute Attribute)
|
||||||
{
|
{
|
||||||
this.BasePosition = BasePosition;
|
this.BaseAddress = BaseAddress;
|
||||||
this.PagesCount = PagesCount;
|
this.PagesCount = PagesCount;
|
||||||
this.State = State;
|
this.State = State;
|
||||||
this.Attribute = Attribute;
|
this.Attribute = Attribute;
|
||||||
|
@ -28,10 +28,10 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public KMemoryInfo GetInfo()
|
public KMemoryInfo GetInfo()
|
||||||
{
|
{
|
||||||
long Size = PagesCount * KMemoryManager.PageSize;
|
ulong Size = PagesCount * KMemoryManager.PageSize;
|
||||||
|
|
||||||
return new KMemoryInfo(
|
return new KMemoryInfo(
|
||||||
BasePosition,
|
BaseAddress,
|
||||||
Size,
|
Size,
|
||||||
State,
|
State,
|
||||||
Permission,
|
Permission,
|
||||||
|
|
19
Ryujinx.HLE/HOS/Kernel/KMemoryBlockAllocator.cs
Normal file
19
Ryujinx.HLE/HOS/Kernel/KMemoryBlockAllocator.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KMemoryInfo
|
class KMemoryInfo
|
||||||
{
|
{
|
||||||
public long Position { get; private set; }
|
public ulong Address { get; private set; }
|
||||||
public long Size { get; private set; }
|
public ulong Size { get; private set; }
|
||||||
|
|
||||||
public MemoryState State { get; private set; }
|
public MemoryState State { get; private set; }
|
||||||
public MemoryPermission Permission { 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 int DeviceRefCount { get; private set; }
|
||||||
|
|
||||||
public KMemoryInfo(
|
public KMemoryInfo(
|
||||||
long Position,
|
ulong Address,
|
||||||
long Size,
|
ulong Size,
|
||||||
MemoryState State,
|
MemoryState State,
|
||||||
MemoryPermission Permission,
|
MemoryPermission Permission,
|
||||||
MemoryAttribute Attribute,
|
MemoryAttribute Attribute,
|
||||||
int IpcRefCount,
|
int IpcRefCount,
|
||||||
int DeviceRefCount)
|
int DeviceRefCount)
|
||||||
{
|
{
|
||||||
this.Position = Position;
|
this.Address = Address;
|
||||||
this.Size = Size;
|
this.Size = Size;
|
||||||
this.State = State;
|
this.State = State;
|
||||||
this.Attribute = Attribute;
|
this.Attribute = Attribute;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
43
Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs
Normal file
43
Ryujinx.HLE/HOS/Kernel/KMemoryRegionBlock.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
428
Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs
Normal file
428
Ryujinx.HLE/HOS/Kernel/KMemoryRegionManager.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
80
Ryujinx.HLE/HOS/Kernel/KPageList.cs
Normal file
80
Ryujinx.HLE/HOS/Kernel/KPageList.cs
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.HLE/HOS/Kernel/KPageNode.cs
Normal file
14
Ryujinx.HLE/HOS/Kernel/KPageNode.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
Ryujinx.HLE/HOS/Kernel/KPort.cs
Normal file
26
Ryujinx.HLE/HOS/Kernel/KPort.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1013
Ryujinx.HLE/HOS/Kernel/KProcess.cs
Normal file
1013
Ryujinx.HLE/HOS/Kernel/KProcess.cs
Normal file
File diff suppressed because it is too large
Load diff
311
Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs
Normal file
311
Ryujinx.HLE/HOS/Kernel/KProcessCapabilities.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public override void Signal()
|
public override void Signal()
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (!Signaled)
|
if (!Signaled)
|
||||||
{
|
{
|
||||||
|
@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
base.Signal();
|
base.Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KernelResult Clear()
|
public KernelResult Clear()
|
||||||
|
@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
KernelResult Result;
|
KernelResult Result;
|
||||||
|
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (Signaled)
|
if (Signaled)
|
||||||
{
|
{
|
||||||
|
@ -49,7 +49,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
Result = KernelResult.InvalidState;
|
Result = KernelResult.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
146
Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs
Normal file
146
Ryujinx.HLE/HOS/Kernel/KResourceLimit.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,14 +38,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private void PreemptThreads()
|
private void PreemptThreads()
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
PreemptThread(PreemptionPriorityCores012, 0);
|
PreemptThread(PreemptionPriorityCores012, 0);
|
||||||
PreemptThread(PreemptionPriorityCores012, 1);
|
PreemptThread(PreemptionPriorityCores012, 1);
|
||||||
PreemptThread(PreemptionPriorityCores012, 2);
|
PreemptThread(PreemptionPriorityCores012, 2);
|
||||||
PreemptThread(PreemptionPriorityCore3, 3);
|
PreemptThread(PreemptionPriorityCore3, 3);
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PreemptThread(int Prio, int Core)
|
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 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;
|
yield return Thread;
|
||||||
}
|
}
|
||||||
|
@ -212,6 +212,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
throw new InvalidOperationException("Current thread is not scheduled!");
|
throw new InvalidOperationException("Current thread is not scheduled!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KProcess GetCurrentProcess()
|
||||||
|
{
|
||||||
|
return GetCurrentThread().Owner;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
|
|
14
Ryujinx.HLE/HOS/Kernel/KServerPort.cs
Normal file
14
Ryujinx.HLE/HOS/Kernel/KServerPort.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,68 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KSharedMemory
|
class KSharedMemory
|
||||||
{
|
{
|
||||||
public long PA { get; private set; }
|
private KPageList PageList;
|
||||||
public long Size { get; private set; }
|
|
||||||
|
|
||||||
public KSharedMemory(long PA, long Size)
|
private long OwnerPid;
|
||||||
|
|
||||||
|
private MemoryPermission OwnerPermission;
|
||||||
|
private MemoryPermission UserPermission;
|
||||||
|
|
||||||
|
public KSharedMemory(
|
||||||
|
KPageList PageList,
|
||||||
|
long OwnerPid,
|
||||||
|
MemoryPermission OwnerPermission,
|
||||||
|
MemoryPermission UserPermission)
|
||||||
{
|
{
|
||||||
this.PA = PA;
|
this.PageList = PageList;
|
||||||
this.Size = Size;
|
this.OwnerPid = OwnerPid;
|
||||||
|
this.OwnerPermission = OwnerPermission;
|
||||||
|
this.UserPermission = UserPermission;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KernelResult MapIntoProcess(
|
||||||
|
KMemoryManager MemoryManager,
|
||||||
|
ulong Address,
|
||||||
|
ulong Size,
|
||||||
|
KProcess Process,
|
||||||
|
MemoryPermission Permission)
|
||||||
|
{
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
50
Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs
Normal file
50
Ryujinx.HLE/HOS/Kernel/KSlabHeap.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
long Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
long Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||||
|
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
//Check if objects are already signaled before waiting.
|
//Check if objects are already signaled before waiting.
|
||||||
for (int Index = 0; Index < SyncObjs.Length; Index++)
|
for (int Index = 0; Index < SyncObjs.Length; Index++)
|
||||||
|
@ -29,14 +29,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
HndIndex = Index;
|
HndIndex = Index;
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Timeout == 0)
|
if (Timeout == 0)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
|
System.TimeManager.ScheduleFutureInvocation(CurrentThread, Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
CurrentThread.WaitingSync = false;
|
CurrentThread.WaitingSync = false;
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
System.TimeManager.UnscheduleFutureInvocation(CurrentThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
Result = (uint)CurrentThread.ObjSyncResult;
|
Result = (uint)CurrentThread.ObjSyncResult;
|
||||||
|
|
||||||
|
@ -100,14 +100,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SignalObject(KSynchronizationObject SyncObj)
|
public void SignalObject(KSynchronizationObject SyncObj)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (SyncObj.IsSignaled())
|
if (SyncObj.IsSignaled())
|
||||||
{
|
{
|
||||||
|
@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
KThread Thread = Node.Value;
|
KThread Thread = Node.Value;
|
||||||
|
|
||||||
if ((Thread.SchedFlags & ThreadSchedState.LowNibbleMask) == ThreadSchedState.Paused)
|
if ((Thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
|
||||||
{
|
{
|
||||||
Thread.SignaledObj = SyncObj;
|
Thread.SignaledObj = SyncObj;
|
||||||
Thread.ObjSyncResult = 0;
|
Thread.ObjSyncResult = 0;
|
||||||
|
@ -129,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,16 +2,12 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KSynchronizationObject
|
class KSynchronizationObject : KAutoObject
|
||||||
{
|
{
|
||||||
public LinkedList<KThread> WaitingThreads;
|
public LinkedList<KThread> WaitingThreads;
|
||||||
|
|
||||||
protected Horizon System;
|
public KSynchronizationObject(Horizon System) : base(System)
|
||||||
|
|
||||||
public KSynchronizationObject(Horizon System)
|
|
||||||
{
|
{
|
||||||
this.System = System;
|
|
||||||
|
|
||||||
WaitingThreads = new LinkedList<KThread>();
|
WaitingThreads = new LinkedList<KThread>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using ChocolArm64;
|
using ChocolArm64;
|
||||||
|
using ChocolArm64.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -13,20 +14,30 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public long AffinityMask { get; set; }
|
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 CondVarAddress { get; set; }
|
||||||
|
|
||||||
|
private ulong Entrypoint;
|
||||||
|
|
||||||
public long MutexAddress { get; set; }
|
public long MutexAddress { get; set; }
|
||||||
|
|
||||||
public Process Owner { get; private 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; }
|
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 LinkedList<KThread> MutexWaiters;
|
||||||
private LinkedListNode<KThread> MutexWaiterNode;
|
private LinkedListNode<KThread> MutexWaiterNode;
|
||||||
|
@ -65,38 +76,131 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public long LastPc { get; set; }
|
public long LastPc { get; set; }
|
||||||
|
|
||||||
public KThread(
|
public KThread(Horizon System) : base(System)
|
||||||
CpuThread Thread,
|
|
||||||
Process Process,
|
|
||||||
Horizon System,
|
|
||||||
int ProcessorId,
|
|
||||||
int Priority,
|
|
||||||
int ThreadId) : base(System)
|
|
||||||
{
|
{
|
||||||
this.ThreadId = ThreadId;
|
|
||||||
|
|
||||||
Context = Thread;
|
|
||||||
Owner = Process;
|
|
||||||
PreferredCore = ProcessorId;
|
|
||||||
Scheduler = System.Scheduler;
|
Scheduler = System.Scheduler;
|
||||||
SchedulingData = System.Scheduler.SchedulingData;
|
SchedulingData = System.Scheduler.SchedulingData;
|
||||||
|
|
||||||
SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount];
|
SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount];
|
||||||
|
|
||||||
MutexWaiters = new LinkedList<KThread>();
|
MutexWaiters = new LinkedList<KThread>();
|
||||||
|
|
||||||
AffinityMask = 1 << ProcessorId;
|
|
||||||
|
|
||||||
DynamicPriority = BasePriority = Priority;
|
|
||||||
|
|
||||||
CurrentCore = PreferredCore;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long Start()
|
public KernelResult Initialize(
|
||||||
|
ulong Entrypoint,
|
||||||
|
ulong ArgsPtr,
|
||||||
|
ulong StackTop,
|
||||||
|
int Priority,
|
||||||
|
int DefaultCpuCore,
|
||||||
|
KProcess Owner,
|
||||||
|
ThreadType Type = ThreadType.User)
|
||||||
{
|
{
|
||||||
long Result = MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
if ((uint)Type > 3)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Invalid thread type \"{Type}\".");
|
||||||
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Lock();
|
PreferredCore = DefaultCpuCore;
|
||||||
|
|
||||||
|
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 KernelResult Start()
|
||||||
|
{
|
||||||
|
if (!System.KernelInitialized)
|
||||||
|
{
|
||||||
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
|
if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
|
||||||
|
{
|
||||||
|
ForcePauseFlags |= ThreadSchedState.KernelInitPauseFlag;
|
||||||
|
|
||||||
|
CombineForcePauseFlags();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.CriticalSection.Leave();
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelResult Result = KernelResult.ThreadTerminating;
|
||||||
|
|
||||||
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (!ShallBeTerminated)
|
if (!ShallBeTerminated)
|
||||||
{
|
{
|
||||||
|
@ -106,9 +210,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
CurrentThread.SchedFlags != ThreadSchedState.TerminationPending &&
|
CurrentThread.SchedFlags != ThreadSchedState.TerminationPending &&
|
||||||
!CurrentThread.ShallBeTerminated)
|
!CurrentThread.ShallBeTerminated)
|
||||||
{
|
{
|
||||||
if ((SchedFlags & ThreadSchedState.LowNibbleMask) != ThreadSchedState.None)
|
if ((SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.None)
|
||||||
{
|
{
|
||||||
Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
Result = KernelResult.InvalidState;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -122,7 +226,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
SetNewSchedFlags(ThreadSchedState.Running);
|
SetNewSchedFlags(ThreadSchedState.Running);
|
||||||
|
|
||||||
Result = 0;
|
Result = KernelResult.Success;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -130,8 +234,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
CurrentThread.CombineForcePauseFlags();
|
CurrentThread.CombineForcePauseFlags();
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (CurrentThread.ShallBeTerminated)
|
if (CurrentThread.ShallBeTerminated)
|
||||||
{
|
{
|
||||||
|
@ -141,25 +245,25 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Exit()
|
public void Exit()
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
ForcePauseFlags &= ~ThreadSchedState.ExceptionalMask;
|
ForcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
|
||||||
|
|
||||||
ExitImpl();
|
ExitImpl();
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExitImpl()
|
private void ExitImpl()
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
SetNewSchedFlags(ThreadSchedState.TerminationPending);
|
SetNewSchedFlags(ThreadSchedState.TerminationPending);
|
||||||
|
|
||||||
|
@ -167,16 +271,16 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
Signal();
|
Signal();
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long Sleep(long Timeout)
|
public long Sleep(long Timeout)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
|
if (ShallBeTerminated || SchedFlags == ThreadSchedState.TerminationPending)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
return MakeError(ErrorModule.Kernel, KernelErr.ThreadTerminating);
|
||||||
}
|
}
|
||||||
|
@ -188,7 +292,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
System.TimeManager.ScheduleFutureInvocation(this, Timeout);
|
System.TimeManager.ScheduleFutureInvocation(this, Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
if (Timeout > 0)
|
if (Timeout > 0)
|
||||||
{
|
{
|
||||||
|
@ -200,11 +304,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public void Yield()
|
public void Yield()
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (SchedFlags != ThreadSchedState.Running)
|
if (SchedFlags != ThreadSchedState.Running)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
System.Scheduler.ContextSwitch();
|
System.Scheduler.ContextSwitch();
|
||||||
|
|
||||||
|
@ -219,27 +323,27 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
Scheduler.ThreadReselectionRequested = true;
|
Scheduler.ThreadReselectionRequested = true;
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
System.Scheduler.ContextSwitch();
|
System.Scheduler.ContextSwitch();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void YieldWithLoadBalancing()
|
public void YieldWithLoadBalancing()
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
int Prio = DynamicPriority;
|
|
||||||
int Core = CurrentCore;
|
|
||||||
|
|
||||||
if (SchedFlags != ThreadSchedState.Running)
|
if (SchedFlags != ThreadSchedState.Running)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
System.Scheduler.ContextSwitch();
|
System.Scheduler.ContextSwitch();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Prio = DynamicPriority;
|
||||||
|
int Core = CurrentCore;
|
||||||
|
|
||||||
KThread NextThreadOnCurrentQueue = null;
|
KThread NextThreadOnCurrentQueue = null;
|
||||||
|
|
||||||
if (DynamicPriority < KScheduler.PrioritiesCount)
|
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,
|
//If the candidate was scheduled after the current thread, then it's not worth it,
|
||||||
//unless the priority is higher than the current one.
|
//unless the priority is higher than the current one.
|
||||||
if (NextThreadOnCurrentQueue.LastScheduledTicks >= Thread.LastScheduledTicks ||
|
if (NextThreadOnCurrentQueue.LastScheduledTime >= Thread.LastScheduledTime ||
|
||||||
NextThreadOnCurrentQueue.DynamicPriority < Thread.DynamicPriority)
|
NextThreadOnCurrentQueue.DynamicPriority < Thread.DynamicPriority)
|
||||||
{
|
{
|
||||||
yield return Thread;
|
yield return Thread;
|
||||||
|
@ -292,18 +396,18 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
Scheduler.ThreadReselectionRequested = true;
|
Scheduler.ThreadReselectionRequested = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
System.Scheduler.ContextSwitch();
|
System.Scheduler.ContextSwitch();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void YieldAndWaitForLoadBalancing()
|
public void YieldAndWaitForLoadBalancing()
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (SchedFlags != ThreadSchedState.Running)
|
if (SchedFlags != ThreadSchedState.Running)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
System.Scheduler.ContextSwitch();
|
System.Scheduler.ContextSwitch();
|
||||||
|
|
||||||
|
@ -348,47 +452,47 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
Scheduler.ThreadReselectionRequested = true;
|
Scheduler.ThreadReselectionRequested = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
System.Scheduler.ContextSwitch();
|
System.Scheduler.ContextSwitch();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetPriority(int Priority)
|
public void SetPriority(int Priority)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
BasePriority = Priority;
|
BasePriority = Priority;
|
||||||
|
|
||||||
UpdatePriorityInheritance();
|
UpdatePriorityInheritance();
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long SetActivity(bool Pause)
|
public long SetActivity(bool Pause)
|
||||||
{
|
{
|
||||||
long Result = 0;
|
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)
|
if (LowNibble != ThreadSchedState.Paused && LowNibble != ThreadSchedState.Running)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
return MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
|
if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
|
||||||
{
|
{
|
||||||
if (Pause)
|
if (Pause)
|
||||||
{
|
{
|
||||||
//Pause, the force pause flag should be clear (thread is NOT paused).
|
//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();
|
CombineForcePauseFlags();
|
||||||
}
|
}
|
||||||
|
@ -400,17 +504,17 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Unpause, the force pause flag should be set (thread is paused).
|
//Unpause, the force pause flag should be set (thread is paused).
|
||||||
if ((ForcePauseFlags & ThreadSchedState.ForcePauseFlag) != 0)
|
if ((ForcePauseFlags & ThreadSchedState.ThreadPauseFlag) != 0)
|
||||||
{
|
{
|
||||||
ThreadSchedState OldForcePauseFlags = ForcePauseFlags;
|
ThreadSchedState OldForcePauseFlags = ForcePauseFlags;
|
||||||
|
|
||||||
ForcePauseFlags &= ~ThreadSchedState.ForcePauseFlag;
|
ForcePauseFlags &= ~ThreadSchedState.ThreadPauseFlag;
|
||||||
|
|
||||||
if ((OldForcePauseFlags & ~ThreadSchedState.ForcePauseFlag) == ThreadSchedState.None)
|
if ((OldForcePauseFlags & ~ThreadSchedState.ThreadPauseFlag) == ThreadSchedState.None)
|
||||||
{
|
{
|
||||||
ThreadSchedState OldSchedFlags = SchedFlags;
|
ThreadSchedState OldSchedFlags = SchedFlags;
|
||||||
|
|
||||||
SchedFlags &= ThreadSchedState.LowNibbleMask;
|
SchedFlags &= ThreadSchedState.LowMask;
|
||||||
|
|
||||||
AdjustScheduling(OldSchedFlags);
|
AdjustScheduling(OldSchedFlags);
|
||||||
}
|
}
|
||||||
|
@ -422,27 +526,27 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CancelSynchronization()
|
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;
|
SyncCancelled = true;
|
||||||
}
|
}
|
||||||
else if (WithholderNode != null)
|
else if (Withholder != null)
|
||||||
{
|
{
|
||||||
System.Withholders.Remove(WithholderNode);
|
Withholder.Remove(WithholderNode);
|
||||||
|
|
||||||
SetNewSchedFlags(ThreadSchedState.Running);
|
SetNewSchedFlags(ThreadSchedState.Running);
|
||||||
|
|
||||||
WithholderNode = null;
|
Withholder = null;
|
||||||
|
|
||||||
SyncCancelled = true;
|
SyncCancelled = true;
|
||||||
}
|
}
|
||||||
|
@ -456,12 +560,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
SyncCancelled = false;
|
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;
|
bool UseOverride = AffinityOverrideCount != 0;
|
||||||
|
|
||||||
|
@ -472,9 +576,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
if ((NewAffinityMask & (1 << NewCore)) == 0)
|
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)
|
private static int HighestSetCore(long Mask)
|
||||||
|
@ -531,7 +635,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
private void CombineForcePauseFlags()
|
private void CombineForcePauseFlags()
|
||||||
{
|
{
|
||||||
ThreadSchedState OldFlags = SchedFlags;
|
ThreadSchedState OldFlags = SchedFlags;
|
||||||
ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowNibbleMask;
|
ThreadSchedState LowNibble = SchedFlags & ThreadSchedState.LowMask;
|
||||||
|
|
||||||
SchedFlags = LowNibble | ForcePauseFlags;
|
SchedFlags = LowNibble | ForcePauseFlags;
|
||||||
|
|
||||||
|
@ -540,33 +644,33 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private void SetNewSchedFlags(ThreadSchedState NewFlags)
|
private void SetNewSchedFlags(ThreadSchedState NewFlags)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
ThreadSchedState OldFlags = SchedFlags;
|
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);
|
AdjustScheduling(OldFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReleaseAndResume()
|
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);
|
SetNewSchedFlags(ThreadSchedState.Running);
|
||||||
|
|
||||||
WithholderNode = null;
|
Withholder = null;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -574,21 +678,21 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reschedule(ThreadSchedState NewFlags)
|
public void Reschedule(ThreadSchedState NewFlags)
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
ThreadSchedState OldFlags = SchedFlags;
|
ThreadSchedState OldFlags = SchedFlags;
|
||||||
|
|
||||||
SchedFlags = (OldFlags & ThreadSchedState.HighNibbleMask) |
|
SchedFlags = (OldFlags & ThreadSchedState.HighMask) |
|
||||||
(NewFlags & ThreadSchedState.LowNibbleMask);
|
(NewFlags & ThreadSchedState.LowMask);
|
||||||
|
|
||||||
AdjustScheduling(OldFlags);
|
AdjustScheduling(OldFlags);
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.CriticalSection.Leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddMutexWaiter(KThread Requester)
|
public void AddMutexWaiter(KThread Requester)
|
||||||
|
@ -866,18 +970,61 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return HasExited;
|
return HasExited;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetEntryArguments(long ArgsPtr, int ThreadHandle)
|
||||||
|
{
|
||||||
|
Context.ThreadState.X0 = (ulong)ArgsPtr;
|
||||||
|
Context.ThreadState.X1 = (ulong)ThreadHandle;
|
||||||
|
}
|
||||||
|
|
||||||
public void ClearExclusive()
|
public void ClearExclusive()
|
||||||
{
|
{
|
||||||
Owner.Memory.ClearExclusive(CurrentCore);
|
Owner.CpuMemory.ClearExclusive(CurrentCore);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TimeUp()
|
public void TimeUp()
|
||||||
{
|
{
|
||||||
System.CriticalSectionLock.Lock();
|
ReleaseAndResume();
|
||||||
|
}
|
||||||
|
|
||||||
SetNewSchedFlags(ThreadSchedState.Running);
|
public void PrintGuestStackTrace()
|
||||||
|
{
|
||||||
|
Owner.Debugger.PrintGuestStackTrace(Context.ThreadState);
|
||||||
|
}
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
|
Owner?.DecrementThreadCountAndTerminateIfZero();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
@ -25,18 +25,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private AutoResetEvent WaitEvent;
|
private AutoResetEvent WaitEvent;
|
||||||
|
|
||||||
private Stopwatch Counter;
|
|
||||||
|
|
||||||
private bool KeepRunning;
|
private bool KeepRunning;
|
||||||
|
|
||||||
public KTimeManager()
|
public KTimeManager()
|
||||||
{
|
{
|
||||||
WaitingObjects = new List<WaitingObject>();
|
WaitingObjects = new List<WaitingObject>();
|
||||||
|
|
||||||
Counter = new Stopwatch();
|
|
||||||
|
|
||||||
Counter.Start();
|
|
||||||
|
|
||||||
KeepRunning = true;
|
KeepRunning = true;
|
||||||
|
|
||||||
Thread Work = new Thread(WaitAndCheckScheduledObjects);
|
Thread Work = new Thread(WaitAndCheckScheduledObjects);
|
||||||
|
@ -46,26 +40,36 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
public void ScheduleFutureInvocation(IKFutureSchedulerObject Object, long Timeout)
|
public void ScheduleFutureInvocation(IKFutureSchedulerObject Object, long Timeout)
|
||||||
{
|
{
|
||||||
|
long TimePoint = PerformanceCounter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(Timeout);
|
||||||
|
|
||||||
lock (WaitingObjects)
|
lock (WaitingObjects)
|
||||||
{
|
{
|
||||||
long TimePoint = Counter.ElapsedMilliseconds + ConvertNanosecondsToMilliseconds(Timeout);
|
|
||||||
|
|
||||||
WaitingObjects.Add(new WaitingObject(Object, TimePoint));
|
WaitingObjects.Add(new WaitingObject(Object, TimePoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
WaitEvent.Set();
|
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 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)
|
public void UnscheduleFutureInvocation(IKFutureSchedulerObject Object)
|
||||||
|
@ -82,26 +86,31 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
while (KeepRunning)
|
while (KeepRunning)
|
||||||
{
|
{
|
||||||
Monitor.Enter(WaitingObjects);
|
WaitingObject Next;
|
||||||
|
|
||||||
WaitingObject Next = WaitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
|
lock (WaitingObjects)
|
||||||
|
{
|
||||||
Monitor.Exit(WaitingObjects);
|
Next = WaitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
if (Next != null)
|
if (Next != null)
|
||||||
{
|
{
|
||||||
long TimePoint = Counter.ElapsedMilliseconds;
|
long TimePoint = PerformanceCounter.ElapsedMilliseconds;
|
||||||
|
|
||||||
if (Next.TimePoint > TimePoint)
|
if (Next.TimePoint > TimePoint)
|
||||||
{
|
{
|
||||||
WaitEvent.WaitOne((int)(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);
|
if (TimeUp)
|
||||||
|
{
|
||||||
Monitor.Exit(WaitingObjects);
|
lock (WaitingObjects)
|
||||||
|
{
|
||||||
|
TimeUp = WaitingObjects.Remove(Next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (TimeUp)
|
if (TimeUp)
|
||||||
{
|
{
|
||||||
|
|
73
Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs
Normal file
73
Ryujinx.HLE/HOS/Kernel/KTlsPageInfo.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,12 +2,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
class KTransferMemory
|
class KTransferMemory
|
||||||
{
|
{
|
||||||
public long Position { get; private set; }
|
public ulong Address { get; private set; }
|
||||||
public long Size { 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.Address = Address;
|
||||||
this.Size = Size;
|
this.Size = Size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
136
Ryujinx.HLE/HOS/Kernel/KernelInit.cs
Normal file
136
Ryujinx.HLE/HOS/Kernel/KernelInit.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,8 +3,29 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
enum KernelResult
|
enum KernelResult
|
||||||
{
|
{
|
||||||
Success = 0,
|
Success = 0,
|
||||||
|
InvalidCapability = 0x1c01,
|
||||||
|
ThreadTerminating = 0x7601,
|
||||||
|
InvalidSize = 0xca01,
|
||||||
|
InvalidAddress = 0xcc01,
|
||||||
|
OutOfResource = 0xce01,
|
||||||
|
OutOfMemory = 0xd001,
|
||||||
HandleTableFull = 0xd201,
|
HandleTableFull = 0xd201,
|
||||||
|
InvalidMemState = 0xd401,
|
||||||
|
InvalidPermission = 0xd801,
|
||||||
|
InvalidMemRange = 0xdc01,
|
||||||
|
InvalidPriority = 0xe001,
|
||||||
|
InvalidCpuCore = 0xe201,
|
||||||
InvalidHandle = 0xe401,
|
InvalidHandle = 0xe401,
|
||||||
InvalidState = 0xfa01
|
UserCopyFailed = 0xe601,
|
||||||
|
InvalidCombination = 0xe801,
|
||||||
|
TimedOut = 0xea01,
|
||||||
|
Cancelled = 0xec01,
|
||||||
|
MaximumExceeded = 0xee01,
|
||||||
|
InvalidEnumValue = 0xf001,
|
||||||
|
NotFound = 0xf201,
|
||||||
|
InvalidThread = 0xf401,
|
||||||
|
InvalidState = 0xfa01,
|
||||||
|
ReservedValue = 0xfc01,
|
||||||
|
ResLimitExceeded = 0x10801
|
||||||
}
|
}
|
||||||
}
|
}
|
71
Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs
Normal file
71
Ryujinx.HLE/HOS/Kernel/KernelTransfer.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
Ryujinx.HLE/HOS/Kernel/LimitableResource.cs
Normal file
13
Ryujinx.HLE/HOS/Kernel/LimitableResource.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
{
|
||||||
|
enum LimitableResource : byte
|
||||||
|
{
|
||||||
|
Memory = 0,
|
||||||
|
Thread = 1,
|
||||||
|
Event = 2,
|
||||||
|
TransferMemory = 3,
|
||||||
|
Session = 4,
|
||||||
|
|
||||||
|
Count = 5
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs
Normal file
12
Ryujinx.HLE/HOS/Kernel/MemoryOperation.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
{
|
||||||
|
enum MemoryOperation
|
||||||
|
{
|
||||||
|
MapPa,
|
||||||
|
MapVa,
|
||||||
|
Allocate,
|
||||||
|
Unmap,
|
||||||
|
ChangePermRw,
|
||||||
|
ChangePermsAndAttributes
|
||||||
|
}
|
||||||
|
}
|
10
Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs
Normal file
10
Ryujinx.HLE/HOS/Kernel/MemoryRegion.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
{
|
||||||
|
enum MemoryRegion
|
||||||
|
{
|
||||||
|
Application = 0,
|
||||||
|
Applet = 1,
|
||||||
|
Service = 2,
|
||||||
|
NvServices = 3
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
ModCodeStatic = 0x00DD7E08,
|
ModCodeStatic = 0x00DD7E08,
|
||||||
ModCodeMutable = 0x03FFBD09,
|
ModCodeMutable = 0x03FFBD09,
|
||||||
IpcBuffer0 = 0x005C3C0A,
|
IpcBuffer0 = 0x005C3C0A,
|
||||||
MappedMemory = 0x005C3C0B,
|
Stack = 0x005C3C0B,
|
||||||
ThreadLocal = 0x0040200C,
|
ThreadLocal = 0x0040200C,
|
||||||
TransferMemoryIsolated = 0x015C3C0D,
|
TransferMemoryIsolated = 0x015C3C0D,
|
||||||
TransferMemory = 0x005C380E,
|
TransferMemory = 0x005C380E,
|
||||||
|
|
128
Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs
Normal file
128
Ryujinx.HLE/HOS/Kernel/MersenneTwister.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs
Normal file
37
Ryujinx.HLE/HOS/Kernel/ProcessCreationInfo.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.HLE/HOS/Kernel/ProcessState.cs
Normal file
14
Ryujinx.HLE/HOS/Kernel/ProcessState.cs
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
using ChocolArm64.Events;
|
using ChocolArm64.Events;
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
private Dictionary<int, SvcFunc> SvcFuncs;
|
private Dictionary<int, SvcFunc> SvcFuncs;
|
||||||
|
|
||||||
private Switch Device;
|
private Switch Device;
|
||||||
private Process Process;
|
private KProcess Process;
|
||||||
private Horizon System;
|
private Horizon System;
|
||||||
private MemoryManager Memory;
|
private MemoryManager Memory;
|
||||||
|
|
||||||
|
@ -39,9 +39,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Random Rng;
|
public SvcHandler(Switch Device, KProcess Process)
|
||||||
|
|
||||||
public SvcHandler(Switch Device, Process Process)
|
|
||||||
{
|
{
|
||||||
SvcFuncs = new Dictionary<int, SvcFunc>()
|
SvcFuncs = new Dictionary<int, SvcFunc>()
|
||||||
{
|
{
|
||||||
|
@ -51,14 +49,14 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{ 0x05, SvcUnmapMemory },
|
{ 0x05, SvcUnmapMemory },
|
||||||
{ 0x06, SvcQueryMemory },
|
{ 0x06, SvcQueryMemory },
|
||||||
{ 0x07, SvcExitProcess },
|
{ 0x07, SvcExitProcess },
|
||||||
{ 0x08, SvcCreateThread },
|
{ 0x08, CreateThread64 },
|
||||||
{ 0x09, SvcStartThread },
|
{ 0x09, SvcStartThread },
|
||||||
{ 0x0a, SvcExitThread },
|
{ 0x0a, SvcExitThread },
|
||||||
{ 0x0b, SvcSleepThread },
|
{ 0x0b, SvcSleepThread },
|
||||||
{ 0x0c, SvcGetThreadPriority },
|
{ 0x0c, SvcGetThreadPriority },
|
||||||
{ 0x0d, SvcSetThreadPriority },
|
{ 0x0d, SvcSetThreadPriority },
|
||||||
{ 0x0e, SvcGetThreadCoreMask },
|
{ 0x0e, SvcGetThreadCoreMask },
|
||||||
{ 0x0f, SvcSetThreadCoreMask },
|
{ 0x0f, SetThreadCoreMask64 },
|
||||||
{ 0x10, SvcGetCurrentProcessorNumber },
|
{ 0x10, SvcGetCurrentProcessorNumber },
|
||||||
{ 0x11, SignalEvent64 },
|
{ 0x11, SignalEvent64 },
|
||||||
{ 0x12, ClearEvent64 },
|
{ 0x12, ClearEvent64 },
|
||||||
|
@ -77,36 +75,34 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{ 0x1f, SvcConnectToNamedPort },
|
{ 0x1f, SvcConnectToNamedPort },
|
||||||
{ 0x21, SvcSendSyncRequest },
|
{ 0x21, SvcSendSyncRequest },
|
||||||
{ 0x22, SvcSendSyncRequestWithUserBuffer },
|
{ 0x22, SvcSendSyncRequestWithUserBuffer },
|
||||||
|
{ 0x24, GetProcessId64 },
|
||||||
{ 0x25, SvcGetThreadId },
|
{ 0x25, SvcGetThreadId },
|
||||||
{ 0x26, SvcBreak },
|
{ 0x26, SvcBreak },
|
||||||
{ 0x27, SvcOutputDebugString },
|
{ 0x27, SvcOutputDebugString },
|
||||||
{ 0x29, SvcGetInfo },
|
{ 0x29, GetInfo64 },
|
||||||
{ 0x2c, SvcMapPhysicalMemory },
|
{ 0x2c, SvcMapPhysicalMemory },
|
||||||
{ 0x2d, SvcUnmapPhysicalMemory },
|
{ 0x2d, SvcUnmapPhysicalMemory },
|
||||||
{ 0x32, SvcSetThreadActivity },
|
{ 0x32, SvcSetThreadActivity },
|
||||||
{ 0x33, SvcGetThreadContext3 },
|
{ 0x33, SvcGetThreadContext3 },
|
||||||
{ 0x34, SvcWaitForAddress },
|
{ 0x34, SvcWaitForAddress },
|
||||||
{ 0x35, SvcSignalToAddress },
|
{ 0x35, SvcSignalToAddress },
|
||||||
{ 0x45, CreateEvent64 }
|
{ 0x45, CreateEvent64 },
|
||||||
|
{ 0x65, GetProcessList64 },
|
||||||
|
{ 0x6f, GetSystemInfo64 },
|
||||||
|
{ 0x70, CreatePort64 },
|
||||||
|
{ 0x71, ManageNamedPort64 }
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Device = Device;
|
this.Device = Device;
|
||||||
this.Process = Process;
|
this.Process = Process;
|
||||||
this.System = Process.Device.System;
|
this.System = Device.System;
|
||||||
this.Memory = Process.Memory;
|
this.Memory = Process.CpuMemory;
|
||||||
}
|
|
||||||
|
|
||||||
static SvcHandler()
|
|
||||||
{
|
|
||||||
Rng = new Random();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SvcCall(object sender, InstExceptionEventArgs e)
|
public void SvcCall(object sender, InstExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
CpuThreadState ThreadState = (CpuThreadState)sender;
|
CpuThreadState ThreadState = (CpuThreadState)sender;
|
||||||
|
|
||||||
Process.GetThread(ThreadState.Tpidr).LastPc = e.Position;
|
|
||||||
|
|
||||||
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
|
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
|
||||||
{
|
{
|
||||||
Logger.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called.");
|
Logger.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called.");
|
||||||
|
@ -117,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Process.PrintStackTrace(ThreadState);
|
//Process.PrintStackTrace(ThreadState);
|
||||||
|
|
||||||
throw new NotImplementedException($"0x{e.Id:x4}");
|
throw new NotImplementedException($"0x{e.Id:x4}");
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
ulong Size = ThreadState.X1;
|
ulong Size = ThreadState.X1;
|
||||||
|
|
||||||
if ((Size & 0xFFFFFFFE001FFFFF) != 0)
|
if ((Size & 0xfffffffe001fffff) != 0)
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!");
|
Logger.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!");
|
||||||
|
|
||||||
|
@ -20,24 +20,24 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long Result = Process.MemoryManager.TrySetHeapSize((long)Size, out long Position);
|
KernelResult Result = Process.MemoryManager.SetHeapSize(Size, out ulong Position);
|
||||||
|
|
||||||
ThreadState.X0 = (ulong)Result;
|
ThreadState.X0 = (ulong)Result;
|
||||||
|
|
||||||
if (Result == 0)
|
if (Result == KernelResult.Success)
|
||||||
{
|
{
|
||||||
ThreadState.X1 = (ulong)Position;
|
ThreadState.X1 = Position;
|
||||||
}
|
}
|
||||||
else
|
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)
|
private void SvcSetMemoryAttribute(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Position = (long)ThreadState.X0;
|
ulong Position = ThreadState.X0;
|
||||||
long Size = (long)ThreadState.X1;
|
ulong Size = ThreadState.X1;
|
||||||
|
|
||||||
if (!PageAligned(Position))
|
if (!PageAligned(Position))
|
||||||
{
|
{
|
||||||
|
@ -72,19 +72,19 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long Result = Process.MemoryManager.SetMemoryAttribute(
|
KernelResult Result = Process.MemoryManager.SetMemoryAttribute(
|
||||||
Position,
|
Position,
|
||||||
Size,
|
Size,
|
||||||
AttributeMask,
|
AttributeMask,
|
||||||
AttributeValue);
|
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
|
else
|
||||||
{
|
{
|
||||||
Memory.StopObservingRegion(Position, Size);
|
Memory.StopObservingRegion((long)Position, (long)Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadState.X0 = (ulong)Result;
|
ThreadState.X0 = (ulong)Result;
|
||||||
|
@ -92,9 +92,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private void SvcMapMemory(CpuThreadState ThreadState)
|
private void SvcMapMemory(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Dst = (long)ThreadState.X0;
|
ulong Dst = ThreadState.X0;
|
||||||
long Src = (long)ThreadState.X1;
|
ulong Src = ThreadState.X1;
|
||||||
long Size = (long)ThreadState.X2;
|
ulong Size = ThreadState.X2;
|
||||||
|
|
||||||
if (!PageAligned(Src | Dst))
|
if (!PageAligned(Src | Dst))
|
||||||
{
|
{
|
||||||
|
@ -114,7 +114,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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!");
|
Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
|
||||||
|
|
||||||
|
@ -123,7 +123,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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!");
|
Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
|
||||||
|
|
||||||
|
@ -132,7 +134,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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!");
|
Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
|
||||||
|
|
||||||
|
@ -141,9 +145,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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}!");
|
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)
|
private void SvcUnmapMemory(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Dst = (long)ThreadState.X0;
|
ulong Dst = ThreadState.X0;
|
||||||
long Src = (long)ThreadState.X1;
|
ulong Src = ThreadState.X1;
|
||||||
long Size = (long)ThreadState.X2;
|
ulong Size = ThreadState.X2;
|
||||||
|
|
||||||
if (!PageAligned(Src | Dst))
|
if (!PageAligned(Src | Dst))
|
||||||
{
|
{
|
||||||
|
@ -175,7 +179,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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!");
|
Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
|
||||||
|
|
||||||
|
@ -184,7 +188,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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!");
|
Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
|
||||||
|
|
||||||
|
@ -193,7 +199,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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!");
|
Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
|
||||||
|
|
||||||
|
@ -202,9 +210,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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}!");
|
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
||||||
}
|
}
|
||||||
|
@ -215,18 +223,18 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
private void SvcQueryMemory(CpuThreadState ThreadState)
|
private void SvcQueryMemory(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long InfoPtr = (long)ThreadState.X0;
|
long InfoPtr = (long)ThreadState.X0;
|
||||||
long Position = (long)ThreadState.X2;
|
ulong Position = ThreadState.X2;
|
||||||
|
|
||||||
KMemoryInfo BlkInfo = Process.MemoryManager.QueryMemory(Position);
|
KMemoryInfo BlkInfo = Process.MemoryManager.QueryMemory(Position);
|
||||||
|
|
||||||
Memory.WriteInt64(InfoPtr + 0x00, BlkInfo.Position);
|
Memory.WriteUInt64(InfoPtr + 0x00, BlkInfo.Address);
|
||||||
Memory.WriteInt64(InfoPtr + 0x08, BlkInfo.Size);
|
Memory.WriteUInt64(InfoPtr + 0x08, BlkInfo.Size);
|
||||||
Memory.WriteInt32(InfoPtr + 0x10, (int)BlkInfo.State & 0xff);
|
Memory.WriteInt32 (InfoPtr + 0x10, (int)BlkInfo.State & 0xff);
|
||||||
Memory.WriteInt32(InfoPtr + 0x14, (int)BlkInfo.Attribute);
|
Memory.WriteInt32 (InfoPtr + 0x14, (int)BlkInfo.Attribute);
|
||||||
Memory.WriteInt32(InfoPtr + 0x18, (int)BlkInfo.Permission);
|
Memory.WriteInt32 (InfoPtr + 0x18, (int)BlkInfo.Permission);
|
||||||
Memory.WriteInt32(InfoPtr + 0x1c, BlkInfo.IpcRefCount);
|
Memory.WriteInt32 (InfoPtr + 0x1c, BlkInfo.IpcRefCount);
|
||||||
Memory.WriteInt32(InfoPtr + 0x20, BlkInfo.DeviceRefCount);
|
Memory.WriteInt32 (InfoPtr + 0x20, BlkInfo.DeviceRefCount);
|
||||||
Memory.WriteInt32(InfoPtr + 0x24, 0);
|
Memory.WriteInt32 (InfoPtr + 0x24, 0);
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = 0;
|
||||||
ThreadState.X1 = 0;
|
ThreadState.X1 = 0;
|
||||||
|
@ -235,12 +243,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
private void SvcMapSharedMemory(CpuThreadState ThreadState)
|
private void SvcMapSharedMemory(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
long Position = (long)ThreadState.X1;
|
ulong Address = ThreadState.X1;
|
||||||
long Size = (long)ThreadState.X2;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
|
@ -256,9 +264,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
@ -276,7 +284,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
KSharedMemory SharedMemory = Process.HandleTable.GetObject<KSharedMemory>(Handle);
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KSharedMemory SharedMemory = CurrentProcess.HandleTable.GetObject<KSharedMemory>(Handle);
|
||||||
|
|
||||||
if (SharedMemory == null)
|
if (SharedMemory == null)
|
||||||
{
|
{
|
||||||
|
@ -287,29 +297,27 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SharedMemory.Size != Size)
|
KernelResult Result = SharedMemory.MapIntoProcess(
|
||||||
|
CurrentProcess.MemoryManager,
|
||||||
|
Address,
|
||||||
|
Size,
|
||||||
|
CurrentProcess,
|
||||||
|
Permission);
|
||||||
|
|
||||||
|
if (Result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} does not match shared memory size 0x{SharedMemory.Size:16}!");
|
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long Result = Process.MemoryManager.MapSharedMemory(SharedMemory, Permission, Position);
|
|
||||||
|
|
||||||
if (Result != 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadState.X0 = (ulong)Result;
|
ThreadState.X0 = (ulong)Result;
|
||||||
|
@ -318,12 +326,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
private void SvcUnmapSharedMemory(CpuThreadState ThreadState)
|
private void SvcUnmapSharedMemory(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
long Position = (long)ThreadState.X1;
|
ulong Address = ThreadState.X1;
|
||||||
long Size = (long)ThreadState.X2;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
|
@ -339,16 +347,18 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
KSharedMemory SharedMemory = Process.HandleTable.GetObject<KSharedMemory>(Handle);
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
KSharedMemory SharedMemory = CurrentProcess.HandleTable.GetObject<KSharedMemory>(Handle);
|
||||||
|
|
||||||
if (SharedMemory == null)
|
if (SharedMemory == null)
|
||||||
{
|
{
|
||||||
|
@ -359,20 +369,26 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
return;
|
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;
|
ThreadState.X0 = (ulong)Result;
|
||||||
|
@ -380,12 +396,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private void SvcCreateTransferMemory(CpuThreadState ThreadState)
|
private void SvcCreateTransferMemory(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Position = (long)ThreadState.X1;
|
ulong Address = ThreadState.X1;
|
||||||
long Size = (long)ThreadState.X2;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
|
@ -401,9 +417,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
@ -421,9 +437,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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);
|
KernelResult Result = Process.HandleTable.GenerateHandle(TransferMemory, out int Handle);
|
||||||
|
|
||||||
|
@ -433,12 +449,12 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private void SvcMapPhysicalMemory(CpuThreadState ThreadState)
|
private void SvcMapPhysicalMemory(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Position = (long)ThreadState.X0;
|
ulong Address = ThreadState.X0;
|
||||||
long Size = (long)ThreadState.X1;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
|
@ -454,27 +470,39 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
return;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
return;
|
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}!");
|
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)
|
private void SvcUnmapPhysicalMemory(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Position = (long)ThreadState.X0;
|
ulong Address = ThreadState.X0;
|
||||||
long Size = (long)ThreadState.X1;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
|
@ -505,27 +533,39 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
return;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
return;
|
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}!");
|
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
||||||
}
|
}
|
||||||
|
@ -533,45 +573,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
ThreadState.X0 = (ulong)Result;
|
ThreadState.X0 = (ulong)Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool PageAligned(long Position)
|
private static bool PageAligned(ulong Position)
|
||||||
{
|
{
|
||||||
return (Position & (KMemoryManager.PageSize - 1)) == 0;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
@ -13,13 +14,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
partial class SvcHandler
|
||||||
{
|
{
|
||||||
private const int AllowedCpuIdBitmask = 0b1111;
|
|
||||||
|
|
||||||
private const bool EnableProcessDebugging = false;
|
|
||||||
|
|
||||||
private void SvcExitProcess(CpuThreadState ThreadState)
|
private void SvcExitProcess(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
Device.System.ExitProcess(Process.ProcessId);
|
System.Scheduler.GetCurrentProcess().Terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SignalEvent64(CpuThreadState ThreadState)
|
private void SignalEvent64(CpuThreadState ThreadState)
|
||||||
|
@ -106,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
else if (Obj is KTransferMemory TransferMemory)
|
else if (Obj is KTransferMemory TransferMemory)
|
||||||
{
|
{
|
||||||
Process.MemoryManager.ResetTransferMemory(
|
Process.MemoryManager.ResetTransferMemory(
|
||||||
TransferMemory.Position,
|
TransferMemory.Address,
|
||||||
TransferMemory.Size);
|
TransferMemory.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,19 +117,29 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private KernelResult ResetSignal(int Handle)
|
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;
|
KernelResult Result;
|
||||||
|
|
||||||
//TODO: KProcess support.
|
|
||||||
if (ReadableEvent != null)
|
if (ReadableEvent != null)
|
||||||
{
|
{
|
||||||
Result = ReadableEvent.ClearIfSignaled();
|
Result = ReadableEvent.ClearIfSignaled();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
KProcess Process = CurrentProcess.HandleTable.GetKProcess(Handle);
|
||||||
|
|
||||||
|
if (Process != null)
|
||||||
|
{
|
||||||
|
Result = Process.ClearIfNotExited();
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Result = KernelResult.InvalidHandle;
|
Result = KernelResult.InvalidHandle;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Result == KernelResult.InvalidState)
|
if (Result == KernelResult.InvalidState)
|
||||||
{
|
{
|
||||||
|
@ -187,17 +194,13 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
private void SendSyncRequest(CpuThreadState ThreadState, long MessagePtr, long Size, int Handle)
|
private void SendSyncRequest(CpuThreadState ThreadState, long MessagePtr, long Size, int Handle)
|
||||||
{
|
{
|
||||||
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
|
||||||
|
|
||||||
byte[] MessageData = Memory.ReadBytes(MessagePtr, Size);
|
byte[] MessageData = Memory.ReadBytes(MessagePtr, Size);
|
||||||
|
|
||||||
KSession Session = Process.HandleTable.GetObject<KSession>(Handle);
|
KSession Session = Process.HandleTable.GetObject<KSession>(Handle);
|
||||||
|
|
||||||
if (Session != null)
|
if (Session != null)
|
||||||
{
|
{
|
||||||
//Process.Scheduler.Suspend(CurrThread);
|
System.CriticalSection.Enter();
|
||||||
|
|
||||||
System.CriticalSectionLock.Lock();
|
|
||||||
|
|
||||||
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
|
@ -214,7 +217,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
Message,
|
Message,
|
||||||
MessagePtr));
|
MessagePtr));
|
||||||
|
|
||||||
System.CriticalSectionLock.Unlock();
|
System.ThreadCounter.AddCount();
|
||||||
|
|
||||||
|
System.CriticalSection.Leave();
|
||||||
|
|
||||||
ThreadState.X0 = (ulong)CurrentThread.ObjSyncResult;
|
ThreadState.X0 = (ulong)CurrentThread.ObjSyncResult;
|
||||||
}
|
}
|
||||||
|
@ -238,25 +243,65 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
IpcMessage.Message,
|
IpcMessage.Message,
|
||||||
IpcMessage.MessagePtr);
|
IpcMessage.MessagePtr);
|
||||||
|
|
||||||
|
System.ThreadCounter.Signal();
|
||||||
|
|
||||||
IpcMessage.Thread.Reschedule(ThreadSchedState.Running);
|
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)
|
private void SvcBreak(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Reason = (long)ThreadState.X0;
|
long Reason = (long)ThreadState.X0;
|
||||||
long Unknown = (long)ThreadState.X1;
|
long Unknown = (long)ThreadState.X1;
|
||||||
long Info = (long)ThreadState.X2;
|
long Info = (long)ThreadState.X2;
|
||||||
|
|
||||||
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
if ((Reason & (1 << 31)) == 0)
|
if ((Reason & (1 << 31)) == 0)
|
||||||
{
|
{
|
||||||
Process.PrintStackTrace(ThreadState);
|
CurrentThread.PrintGuestStackTrace();
|
||||||
|
|
||||||
throw new GuestBrokeExecutionException();
|
throw new GuestBrokeExecutionException();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered");
|
Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered.");
|
||||||
Process.PrintStackTrace(ThreadState);
|
|
||||||
|
CurrentThread.PrintGuestStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,98 +317,243 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcGetInfo(CpuThreadState ThreadState)
|
private void GetInfo64(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long StackPtr = (long)ThreadState.X0;
|
long StackPtr = (long)ThreadState.X0;
|
||||||
int InfoType = (int)ThreadState.X1;
|
uint Id = (uint)ThreadState.X1;
|
||||||
long Handle = (long)ThreadState.X2;
|
int Handle = (int)ThreadState.X2;
|
||||||
int InfoId = (int)ThreadState.X3;
|
long SubId = (long)ThreadState.X3;
|
||||||
|
|
||||||
//Fail for info not available on older Kernel versions.
|
KernelResult Result = GetInfo(Id, Handle, SubId, out long Value);
|
||||||
if (InfoType == 18 ||
|
|
||||||
InfoType == 19 ||
|
|
||||||
InfoType == 20 ||
|
|
||||||
InfoType == 21 ||
|
|
||||||
InfoType == 22)
|
|
||||||
{
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
|
|
||||||
|
|
||||||
return;
|
ThreadState.X0 = (ulong)Result;
|
||||||
|
ThreadState.X1 = (ulong)Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (InfoType)
|
private KernelResult GetInfo(uint Id, int Handle, long SubId, out long Value)
|
||||||
|
{
|
||||||
|
Value = 0;
|
||||||
|
|
||||||
|
switch (Id)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
ThreadState.X1 = AllowedCpuIdBitmask;
|
case 1:
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionEnd -
|
|
||||||
(ulong)Process.MemoryManager.MapRegionStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionEnd -
|
|
||||||
(ulong)Process.MemoryManager.HeapRegionStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
ThreadState.X1 = (ulong)Process.Device.Memory.Allocator.TotalAvailableSize;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 7:
|
case 7:
|
||||||
ThreadState.X1 = (ulong)Process.Device.Memory.Allocator.TotalUsedSize;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 8:
|
|
||||||
ThreadState.X1 = EnableProcessDebugging ? 1 : 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 11:
|
|
||||||
ThreadState.X1 = (ulong)Rng.Next() + ((ulong)Rng.Next() << 32);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 12:
|
case 12:
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 13:
|
case 13:
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceEnd -
|
|
||||||
(ulong)Process.MemoryManager.AddrSpaceStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 14:
|
case 14:
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 15:
|
case 15:
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionEnd -
|
|
||||||
(ulong)Process.MemoryManager.NewMapRegionStart;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 16:
|
case 16:
|
||||||
ThreadState.X1 = (ulong)(Process.MetaData?.SystemResourceSize ?? 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 17:
|
case 17:
|
||||||
ThreadState.X1 = (ulong)Process.MemoryManager.PersonalMmHeapUsage;
|
case 18:
|
||||||
break;
|
case 20:
|
||||||
|
case 21:
|
||||||
default:
|
case 22:
|
||||||
Process.PrintStackTrace(ThreadState);
|
{
|
||||||
|
if (SubId != 0)
|
||||||
throw new NotImplementedException($"SvcGetInfo: {InfoType} 0x{Handle:x8} {InfoId}");
|
{
|
||||||
|
return KernelResult.InvalidCombination;
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
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:
|
||||||
|
{
|
||||||
|
if (Handle != 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SubId != 0)
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidCombination;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value = System.Scheduler.GetCurrentProcess().Debug ? 1 : 0;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: return KernelResult.InvalidEnumValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KernelResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateEvent64(CpuThreadState State)
|
private void CreateEvent64(CpuThreadState State)
|
||||||
|
@ -397,5 +587,241 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
return Result;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,48 +7,84 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
partial class SvcHandler
|
||||||
{
|
{
|
||||||
private void SvcCreateThread(CpuThreadState ThreadState)
|
private void CreateThread64(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long EntryPoint = (long)ThreadState.X1;
|
ulong Entrypoint = ThreadState.X1;
|
||||||
long ArgsPtr = (long)ThreadState.X2;
|
ulong ArgsPtr = ThreadState.X2;
|
||||||
long StackTop = (long)ThreadState.X3;
|
ulong StackTop = ThreadState.X3;
|
||||||
int Priority = (int)ThreadState.X4;
|
int Priority = (int)ThreadState.X4;
|
||||||
int ProcessorId = (int)ThreadState.X5;
|
int CpuCore = (int)ThreadState.X5;
|
||||||
|
|
||||||
if ((uint)Priority > 0x3f)
|
KernelResult Result = CreateThread(Entrypoint, ArgsPtr, StackTop, Priority, CpuCore, out int Handle);
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid priority 0x{Priority:x8}!");
|
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPriority);
|
ThreadState.X0 = (ulong)Result;
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ProcessorId == -2)
|
|
||||||
{
|
|
||||||
//TODO: Get this value from the NPDM file.
|
|
||||||
ProcessorId = 0;
|
|
||||||
}
|
|
||||||
else if ((uint)ProcessorId > 3)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{ProcessorId:x8}!");
|
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Handle = Process.MakeThread(
|
|
||||||
EntryPoint,
|
|
||||||
StackTop,
|
|
||||||
ArgsPtr,
|
|
||||||
Priority,
|
|
||||||
ProcessorId);
|
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
|
||||||
ThreadState.X1 = (ulong)Handle;
|
ThreadState.X1 = (ulong)Handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 ((uint)CpuCore >= KScheduler.CpuCoresCount || !CurrentProcess.IsCpuCoreAllowed(CpuCore))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidCpuCore;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((uint)Priority >= KScheduler.PrioritiesCount || !CurrentProcess.IsPriorityAllowed(Priority))
|
||||||
|
{
|
||||||
|
return KernelResult.InvalidPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
long Timeout = KTimeManager.ConvertMillisecondsToNanoseconds(100);
|
||||||
|
|
||||||
|
if (CurrentProcess.ResourceLimit != null &&
|
||||||
|
!CurrentProcess.ResourceLimit.Reserve(LimitableResource.Thread, 1, Timeout))
|
||||||
|
{
|
||||||
|
return KernelResult.ResLimitExceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
KThread Thread = new KThread(System);
|
||||||
|
|
||||||
|
KernelResult Result = CurrentProcess.InitializeThread(
|
||||||
|
Thread,
|
||||||
|
Entrypoint,
|
||||||
|
ArgsPtr,
|
||||||
|
StackTop,
|
||||||
|
Priority,
|
||||||
|
CpuCore);
|
||||||
|
|
||||||
|
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)
|
private void SvcStartThread(CpuThreadState ThreadState)
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
|
@ -57,11 +93,11 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
if (Thread != null)
|
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;
|
ThreadState.X0 = (ulong)Result;
|
||||||
|
@ -78,9 +114,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
CurrentThread.Exit();
|
System.Scheduler.ExitThread(CurrentThread);
|
||||||
|
|
||||||
System.Scheduler.StopThread(CurrentThread);
|
CurrentThread.Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcSleepThread(CpuThreadState ThreadState)
|
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 Handle = (int)ThreadState.X0;
|
||||||
int PrefferedCore = (int)ThreadState.X1;
|
int PreferredCore = (int)ThreadState.X1;
|
||||||
long AffinityMask = (long)ThreadState.X2;
|
long AffinityMask = (long)ThreadState.X2;
|
||||||
|
|
||||||
Logger.PrintDebug(LogClass.KernelSvc,
|
Logger.PrintDebug(LogClass.KernelSvc,
|
||||||
"Handle = 0x" + Handle .ToString("x8") + ", " +
|
"Handle = 0x" + Handle .ToString("x8") + ", " +
|
||||||
"PrefferedCore = 0x" + PrefferedCore.ToString("x8") + ", " +
|
"PreferredCore = 0x" + PreferredCore.ToString("x8") + ", " +
|
||||||
"AffinityMask = 0x" + AffinityMask .ToString("x16"));
|
"AffinityMask = 0x" + AffinityMask .ToString("x16"));
|
||||||
|
|
||||||
if (PrefferedCore == -2)
|
KernelResult Result = SetThreadCoreMask(Handle, PreferredCore, AffinityMask);
|
||||||
{
|
|
||||||
//TODO: Get this value from the NPDM file.
|
|
||||||
PrefferedCore = 0;
|
|
||||||
|
|
||||||
AffinityMask = 1 << PrefferedCore;
|
if (Result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error \"{Result}\".");
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
else
|
||||||
{
|
{
|
||||||
//TODO: Check allowed cores from NPDM file.
|
if ((CurrentProcess.Capabilities.AllowedCpuCoresMask | AffinityMask) !=
|
||||||
|
CurrentProcess.Capabilities.AllowedCpuCoresMask)
|
||||||
if ((uint)PrefferedCore > 3)
|
|
||||||
{
|
{
|
||||||
if ((PrefferedCore | 2) != -1)
|
return KernelResult.InvalidCpuCore;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AffinityMask == 0)
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{PrefferedCore:x8}!");
|
return KernelResult.InvalidCombination;
|
||||||
|
}
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
|
if ((uint)PreferredCore > 3)
|
||||||
|
{
|
||||||
return;
|
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}!");
|
return KernelResult.InvalidCombination;
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,26 +273,15 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
|
||||||
if (Thread == null)
|
if (Thread == null)
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
|
return KernelResult.InvalidHandle;
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long Result = Thread.SetCoreAndAffinityMask(PrefferedCore, AffinityMask);
|
return Thread.SetCoreAndAffinityMask(PreferredCore, AffinityMask);
|
||||||
|
|
||||||
if (Result != 0)
|
|
||||||
{
|
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadState.X0 = (ulong)Result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcGetCurrentProcessorNumber(CpuThreadState ThreadState)
|
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)
|
private void SvcGetThreadId(CpuThreadState ThreadState)
|
||||||
|
@ -254,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
if (Thread != null)
|
if (Thread != null)
|
||||||
{
|
{
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = 0;
|
||||||
ThreadState.X1 = (ulong)Thread.ThreadId;
|
ThreadState.X1 = (ulong)Thread.ThreadUid;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -280,15 +319,24 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||||
|
|
||||||
return;
|
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);
|
long Result = Thread.SetActivity(Pause);
|
||||||
|
|
||||||
if (Result != 0)
|
if (Result != 0)
|
||||||
|
@ -304,6 +352,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
long Position = (long)ThreadState.X0;
|
long Position = (long)ThreadState.X0;
|
||||||
int Handle = (int)ThreadState.X1;
|
int Handle = (int)ThreadState.X1;
|
||||||
|
|
||||||
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
KThread CurrentThread = System.Scheduler.GetCurrentThread();
|
||||||
|
|
||||||
KThread Thread = Process.HandleTable.GetObject<KThread>(Handle);
|
KThread Thread = Process.HandleTable.GetObject<KThread>(Handle);
|
||||||
|
|
||||||
if (Thread == null)
|
if (Thread == null)
|
||||||
|
@ -315,9 +366,18 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Process.GetThread(ThreadState.Tpidr) == Thread)
|
if (Thread.Owner != CurrentProcess)
|
||||||
{
|
{
|
||||||
Logger.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is current thread!");
|
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, "Invalid thread, current thread is not accepted.");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidThread);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidThread);
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
int Handle = Memory.ReadInt32(HandlesPtr + Index * 4);
|
int Handle = Memory.ReadInt32(HandlesPtr + Index * 4);
|
||||||
|
|
||||||
|
Logger.PrintDebug(LogClass.KernelSvc, $"Sync handle 0x{Handle:x8}");
|
||||||
|
|
||||||
KSynchronizationObject SyncObj = Process.HandleTable.GetObject<KSynchronizationObject>(Handle);
|
KSynchronizationObject SyncObj = Process.HandleTable.GetObject<KSynchronizationObject>(Handle);
|
||||||
|
|
||||||
if (SyncObj == null)
|
if (SyncObj == null)
|
||||||
|
@ -116,12 +118,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long Result = System.AddressArbiter.ArbitrateLock(
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
Process,
|
|
||||||
Memory,
|
long Result = CurrentProcess.AddressArbiter.ArbitrateLock(OwnerHandle, MutexAddress, RequesterHandle);
|
||||||
OwnerHandle,
|
|
||||||
MutexAddress,
|
|
||||||
RequesterHandle);
|
|
||||||
|
|
||||||
if (Result != 0)
|
if (Result != 0)
|
||||||
{
|
{
|
||||||
|
@ -155,7 +154,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long Result = System.AddressArbiter.ArbitrateUnlock(Memory, MutexAddress);
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
|
long Result = CurrentProcess.AddressArbiter.ArbitrateUnlock(MutexAddress);
|
||||||
|
|
||||||
if (Result != 0)
|
if (Result != 0)
|
||||||
{
|
{
|
||||||
|
@ -196,8 +197,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long Result = System.AddressArbiter.WaitProcessWideKeyAtomic(
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
Memory,
|
|
||||||
|
long Result = CurrentProcess.AddressArbiter.WaitProcessWideKeyAtomic(
|
||||||
MutexAddress,
|
MutexAddress,
|
||||||
CondVarAddress,
|
CondVarAddress,
|
||||||
ThreadHandle,
|
ThreadHandle,
|
||||||
|
@ -227,7 +229,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
"Address = 0x" + Address.ToString("x16") + ", " +
|
"Address = 0x" + Address.ToString("x16") + ", " +
|
||||||
"Count = 0x" + Count .ToString("x8"));
|
"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;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
@ -263,20 +267,22 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
long Result;
|
long Result;
|
||||||
|
|
||||||
switch (Type)
|
switch (Type)
|
||||||
{
|
{
|
||||||
case ArbitrationType.WaitIfLessThan:
|
case ArbitrationType.WaitIfLessThan:
|
||||||
Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, false, Timeout);
|
Result = CurrentProcess.AddressArbiter.WaitForAddressIfLessThan(Address, Value, false, Timeout);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ArbitrationType.DecrementAndWaitIfLessThan:
|
case ArbitrationType.DecrementAndWaitIfLessThan:
|
||||||
Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, true, Timeout);
|
Result = CurrentProcess.AddressArbiter.WaitForAddressIfLessThan(Address, Value, true, Timeout);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ArbitrationType.WaitIfEqual:
|
case ArbitrationType.WaitIfEqual:
|
||||||
Result = System.AddressArbiter.WaitForAddressIfEqual(Memory, Address, Value, Timeout);
|
Result = CurrentProcess.AddressArbiter.WaitForAddressIfEqual(Address, Value, Timeout);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -323,20 +329,22 @@ namespace Ryujinx.HLE.HOS.Kernel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
|
||||||
|
|
||||||
long Result;
|
long Result;
|
||||||
|
|
||||||
switch (Type)
|
switch (Type)
|
||||||
{
|
{
|
||||||
case SignalType.Signal:
|
case SignalType.Signal:
|
||||||
Result = System.AddressArbiter.Signal(Address, Count);
|
Result = CurrentProcess.AddressArbiter.Signal(Address, Count);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SignalType.SignalAndIncrementIfEqual:
|
case SignalType.SignalAndIncrementIfEqual:
|
||||||
Result = System.AddressArbiter.SignalAndIncrementIfEqual(Memory, Address, Value, Count);
|
Result = CurrentProcess.AddressArbiter.SignalAndIncrementIfEqual(Address, Value, Count);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SignalType.SignalAndModifyIfEqual:
|
case SignalType.SignalAndModifyIfEqual:
|
||||||
Result = System.AddressArbiter.SignalAndModifyIfEqual(Memory, Address, Value, Count);
|
Result = CurrentProcess.AddressArbiter.SignalAndModifyIfEqual(Address, Value, Count);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
namespace Ryujinx.HLE.HOS.Kernel
|
namespace Ryujinx.HLE.HOS.Kernel
|
||||||
{
|
{
|
||||||
enum ThreadSchedState : byte
|
enum ThreadSchedState : ushort
|
||||||
{
|
{
|
||||||
LowNibbleMask = 0xf,
|
LowMask = 0xf,
|
||||||
HighNibbleMask = 0xf0,
|
HighMask = 0xfff0,
|
||||||
ExceptionalMask = 0x70,
|
ForcePauseMask = 0x70,
|
||||||
ForcePauseFlag = 0x20,
|
|
||||||
|
ProcessPauseFlag = 1 << 4,
|
||||||
|
ThreadPauseFlag = 1 << 5,
|
||||||
|
ProcessDebugPauseFlag = 1 << 6,
|
||||||
|
KernelInitPauseFlag = 1 << 8,
|
||||||
|
|
||||||
None = 0,
|
None = 0,
|
||||||
Paused = 1,
|
Paused = 1,
|
||||||
|
|
10
Ryujinx.HLE/HOS/Kernel/ThreadType.cs
Normal file
10
Ryujinx.HLE/HOS/Kernel/ThreadType.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Kernel
|
||||||
|
{
|
||||||
|
enum ThreadType
|
||||||
|
{
|
||||||
|
Dummy,
|
||||||
|
Kernel,
|
||||||
|
Kernel2,
|
||||||
|
User
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
292
Ryujinx.HLE/HOS/ProgramLoader.cs
Normal file
292
Ryujinx.HLE/HOS/ProgramLoader.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
class ServiceCtx
|
class ServiceCtx
|
||||||
{
|
{
|
||||||
public Switch Device { get; private set; }
|
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 MemoryManager Memory { get; private set; }
|
||||||
public KSession Session { get; private set; }
|
public KSession Session { get; private set; }
|
||||||
public IpcMessage Request { get; private set; }
|
public IpcMessage Request { get; private set; }
|
||||||
|
@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
public ServiceCtx(
|
public ServiceCtx(
|
||||||
Switch Device,
|
Switch Device,
|
||||||
Process Process,
|
KProcess Process,
|
||||||
MemoryManager Memory,
|
MemoryManager Memory,
|
||||||
KSession Session,
|
KSession Session,
|
||||||
IpcMessage Request,
|
IpcMessage Request,
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
|
||||||
|
|
||||||
public long GetEventHandle(ServiceCtx Context)
|
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)
|
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)
|
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);
|
return MakeError(ErrorModule.Am, AmErr.NoMessages);
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
|
||||||
|
|
||||||
public long GetCurrentFocusState(ServiceCtx Context)
|
public long GetCurrentFocusState(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
Context.ResponseData.Write((byte)Context.Process.AppletState.FocusState);
|
Context.ResponseData.Write((byte)Context.Device.System.AppletState.FocusState);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.Loaders.Executables;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System;
|
using System;
|
||||||
|
@ -62,16 +65,31 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
|
||||||
|
|
||||||
class NroInfo
|
class NroInfo
|
||||||
{
|
{
|
||||||
public Nro Executable { get; private set; }
|
public NxRelocatableObject 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 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.Executable = Executable;
|
||||||
this.Hash = Hash;
|
this.Hash = Hash;
|
||||||
|
this.NroAddress = NroAddress;
|
||||||
|
this.NroSize = NroSize;
|
||||||
|
this.BssAddress = BssAddress;
|
||||||
|
this.BssSize = BssSize;
|
||||||
this.TotalSize = TotalSize;
|
this.TotalSize = TotalSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,7 +192,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
|
||||||
return false;
|
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;
|
Res = null;
|
||||||
|
|
||||||
|
@ -182,28 +200,28 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Loader, LoaderErr.MaxNro);
|
return MakeError(ErrorModule.Loader, LoaderErr.MaxNro);
|
||||||
}
|
}
|
||||||
else if (NroSize == 0 || NroHeapAddress + NroSize <= NroHeapAddress || (NroSize & 0xFFF) != 0)
|
else if (NroSize == 0 || NroAddress + NroSize <= NroAddress || (NroSize & 0xFFF) != 0)
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Loader, LoaderErr.BadSize);
|
return MakeError(ErrorModule.Loader, LoaderErr.BadSize);
|
||||||
}
|
}
|
||||||
else if (BssSize != 0 && (BssHeapAddress + BssSize) <= BssHeapAddress)
|
else if (BssSize != 0 && BssAddress + BssSize <= BssAddress)
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Loader, LoaderErr.BadSize);
|
return MakeError(ErrorModule.Loader, LoaderErr.BadSize);
|
||||||
}
|
}
|
||||||
else if ((NroHeapAddress & 0xFFF) != 0)
|
else if ((NroAddress & 0xFFF) != 0)
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress);
|
return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint Magic = Context.Memory.ReadUInt32(NroHeapAddress + 0x10);
|
uint Magic = Context.Memory.ReadUInt32((long)NroAddress + 0x10);
|
||||||
uint NroFileSize = Context.Memory.ReadUInt32(NroHeapAddress + 0x18);
|
uint NroFileSize = Context.Memory.ReadUInt32((long)NroAddress + 0x18);
|
||||||
|
|
||||||
if (Magic != NroMagic || NroSize != NroFileSize)
|
if (Magic != NroMagic || NroSize != NroFileSize)
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
|
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] NroData = Context.Memory.ReadBytes(NroHeapAddress, NroSize);
|
byte[] NroData = Context.Memory.ReadBytes((long)NroAddress, (long)NroSize);
|
||||||
byte[] NroHash = null;
|
byte[] NroHash = null;
|
||||||
|
|
||||||
MemoryStream Stream = new MemoryStream(NroData);
|
MemoryStream Stream = new MemoryStream(NroData);
|
||||||
|
@ -225,67 +243,106 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
|
||||||
|
|
||||||
Stream.Position = 0;
|
Stream.Position = 0;
|
||||||
|
|
||||||
Nro Executable = new Nro(Stream, "memory", NroHeapAddress, BssHeapAddress);
|
NxRelocatableObject Executable = new NxRelocatableObject(Stream, NroAddress, BssAddress);
|
||||||
|
|
||||||
// check if everything is page align.
|
// check if everything is page align.
|
||||||
if ((Executable.Text.Length & 0xFFF) != 0 || (Executable.RO.Length & 0xFFF) != 0
|
if ((Executable.Text.Length & 0xFFF) != 0 || (Executable.RO.Length & 0xFFF) != 0 ||
|
||||||
|| (Executable.Data.Length & 0xFFF) != 0 || (Executable.BssSize & 0xFFF) != 0)
|
(Executable.Data.Length & 0xFFF) != 0 || (Executable.BssSize & 0xFFF) != 0)
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
|
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if everything is contiguous.
|
// check if everything is contiguous.
|
||||||
if (Executable.ROOffset != Executable.TextOffset + Executable.Text.Length
|
if (Executable.ROOffset != Executable.TextOffset + Executable.Text.Length ||
|
||||||
|| Executable.DataOffset != Executable.ROOffset + Executable.RO.Length
|
Executable.DataOffset != Executable.ROOffset + Executable.RO.Length ||
|
||||||
|| NroFileSize != Executable.DataOffset + Executable.Data.Length)
|
NroFileSize != Executable.DataOffset + Executable.Data.Length)
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
|
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally check the bss size match.
|
// finally check the bss size match.
|
||||||
if (Executable.BssSize != BssSize)
|
if ((ulong)Executable.BssSize != BssSize)
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long MapNro(ServiceCtx Context, NroInfo Info, out long NroMappedAddress)
|
private long MapNro(ServiceCtx Context, NroInfo Info, out ulong NroMappedAddress)
|
||||||
{
|
{
|
||||||
NroMappedAddress = 0;
|
NroMappedAddress = 0;
|
||||||
long TargetAddress = Context.Process.MemoryManager.AddrSpaceStart;
|
|
||||||
|
|
||||||
long HeapRegionStart = Context.Process.MemoryManager.HeapRegionStart;
|
KMemoryManager MemMgr = Context.Process.MemoryManager;
|
||||||
long HeapRegionEnd = Context.Process.MemoryManager.HeapRegionEnd;
|
|
||||||
|
|
||||||
long MapRegionStart = Context.Process.MemoryManager.MapRegionStart;
|
ulong TargetAddress = MemMgr.GetAddrSpaceBaseAddr();
|
||||||
long MapRegionEnd = Context.Process.MemoryManager.MapRegionEnd;
|
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (TargetAddress + Info.TotalSize >= Context.Process.MemoryManager.AddrSpaceEnd)
|
if (TargetAddress + Info.TotalSize >= MemMgr.AddrSpaceEnd)
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState);
|
return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidAddress = !(HeapRegionStart > 0 && HeapRegionStart <= TargetAddress + Info.TotalSize - 1
|
KMemoryInfo MemInfo = MemMgr.QueryMemory(TargetAddress);
|
||||||
&& TargetAddress <= HeapRegionEnd - 1)
|
|
||||||
&& !(MapRegionStart > 0
|
|
||||||
&& MapRegionStart <= TargetAddress + Info.TotalSize - 1
|
|
||||||
&& TargetAddress <= MapRegionEnd - 1);
|
|
||||||
|
|
||||||
if (IsValidAddress && Context.Process.MemoryManager.HleIsUnmapped(TargetAddress, Info.TotalSize))
|
if (MemInfo.State == MemoryState.Unmapped && MemInfo.Size >= Info.TotalSize)
|
||||||
|
{
|
||||||
|
if (!MemMgr.InsideHeapRegion (TargetAddress, Info.TotalSize) &&
|
||||||
|
!MemMgr.InsideAliasRegion(TargetAddress, Info.TotalSize))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetAddress += 0x1000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.Process.LoadProgram(Info.Executable, TargetAddress);
|
TargetAddress += MemInfo.Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelResult Result = MemMgr.MapProcessCodeMemory(TargetAddress, Info.NroAddress, Info.NroSize);
|
||||||
|
|
||||||
|
if (Result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState);
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong BssTargetAddress = TargetAddress + Info.NroSize;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
Info.NroMappedAddress = TargetAddress;
|
||||||
NroMappedAddress = TargetAddress;
|
NroMappedAddress = TargetAddress;
|
||||||
|
@ -293,6 +350,41 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private KernelResult LoadNroIntoMemory(KProcess Process, IExecutable RelocatableObject, ulong BaseAddress)
|
||||||
|
{
|
||||||
|
ulong TextStart = BaseAddress + (ulong)RelocatableObject.TextOffset;
|
||||||
|
ulong ROStart = BaseAddress + (ulong)RelocatableObject.ROOffset;
|
||||||
|
ulong DataStart = BaseAddress + (ulong)RelocatableObject.DataOffset;
|
||||||
|
|
||||||
|
ulong BssStart = DataStart + (ulong)RelocatableObject.Data.Length;
|
||||||
|
|
||||||
|
ulong BssEnd = BitUtils.AlignUp(BssStart + (ulong)RelocatableObject.BssSize, KMemoryManager.PageSize);
|
||||||
|
|
||||||
|
Process.CpuMemory.WriteBytes((long)TextStart, RelocatableObject.Text);
|
||||||
|
Process.CpuMemory.WriteBytes((long)ROStart, RelocatableObject.RO);
|
||||||
|
Process.CpuMemory.WriteBytes((long)DataStart, RelocatableObject.Data);
|
||||||
|
|
||||||
|
MemoryHelper.FillWithZeros(Process.CpuMemory, (long)BssStart, (int)(BssEnd - BssStart));
|
||||||
|
|
||||||
|
KernelResult Result;
|
||||||
|
|
||||||
|
Result = Process.MemoryManager.SetProcessMemoryPermission(TextStart, ROStart - TextStart, MemoryPermission.ReadAndExecute);
|
||||||
|
|
||||||
|
if (Result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result = Process.MemoryManager.SetProcessMemoryPermission(ROStart, DataStart - ROStart, MemoryPermission.Read);
|
||||||
|
|
||||||
|
if (Result != KernelResult.Success)
|
||||||
|
{
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Process.MemoryManager.SetProcessMemoryPermission(DataStart, BssEnd - DataStart, MemoryPermission.ReadAndWrite);
|
||||||
|
}
|
||||||
|
|
||||||
private long RemoveNrrInfo(long NrrAddress)
|
private long RemoveNrrInfo(long NrrAddress)
|
||||||
{
|
{
|
||||||
foreach (NrrInfo Info in NrrInfos)
|
foreach (NrrInfo Info in NrrInfos)
|
||||||
|
@ -308,24 +400,46 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
|
||||||
return MakeError(ErrorModule.Loader, LoaderErr.BadNrrAddress);
|
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)
|
foreach (NroInfo Info in NroInfos)
|
||||||
{
|
{
|
||||||
if (Info.NroMappedAddress == NroMappedAddress && Info.Executable.SourceAddress == NroHeapAddress)
|
if (Info.NroMappedAddress == NroMappedAddress)
|
||||||
{
|
{
|
||||||
NroInfos.Remove(Info);
|
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
|
// Zero
|
||||||
Context.RequestData.ReadUInt64();
|
Context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
long NroHeapAddress = Context.RequestData.ReadInt64();
|
ulong NroHeapAddress = Context.RequestData.ReadUInt64();
|
||||||
long NroSize = Context.RequestData.ReadInt64();
|
ulong NroSize = Context.RequestData.ReadUInt64();
|
||||||
long BssHeapAddress = Context.RequestData.ReadInt64();
|
ulong BssHeapAddress = Context.RequestData.ReadUInt64();
|
||||||
long BssSize = Context.RequestData.ReadInt64();
|
ulong BssSize = Context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
long NroMappedAddress = 0;
|
ulong NroMappedAddress = 0;
|
||||||
|
|
||||||
if (IsInitialized)
|
if (IsInitialized)
|
||||||
{
|
{
|
||||||
|
@ -374,17 +488,19 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
|
||||||
{
|
{
|
||||||
long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization);
|
long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization);
|
||||||
|
|
||||||
long NroMappedAddress = Context.RequestData.ReadInt64();
|
// Zero
|
||||||
long NroHeapAddress = Context.RequestData.ReadInt64();
|
Context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
|
ulong NroMappedAddress = Context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
if (IsInitialized)
|
if (IsInitialized)
|
||||||
{
|
{
|
||||||
if ((NroMappedAddress & 0xFFF) != 0 || (NroHeapAddress & 0xFFF) != 0)
|
if ((NroMappedAddress & 0xFFF) != 0)
|
||||||
{
|
{
|
||||||
return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress);
|
return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result = RemoveNroInfo(Context, NroMappedAddress, NroHeapAddress);
|
Result = RemoveNroInfo(Context, NroMappedAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
|
|
|
@ -205,7 +205,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
return ((Cmd >> 31) & 1) != 0;
|
return ((Cmd >> 31) & 1) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UnloadProcess(Process Process)
|
public static void UnloadProcess(KProcess Process)
|
||||||
{
|
{
|
||||||
Fds.DeleteProcess(Process);
|
Fds.DeleteProcess(Process);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Memory;
|
using Ryujinx.Graphics.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvMap;
|
using Ryujinx.HLE.HOS.Services.Nv.NvMap;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
@ -13,11 +14,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS
|
||||||
|
|
||||||
private const int FlagRemapSubRange = 0x100;
|
private const int FlagRemapSubRange = 0x100;
|
||||||
|
|
||||||
private static ConcurrentDictionary<Process, NvGpuASCtx> ASCtxs;
|
private static ConcurrentDictionary<KProcess, NvGpuASCtx> ASCtxs;
|
||||||
|
|
||||||
static NvGpuASIoctl()
|
static NvGpuASIoctl()
|
||||||
{
|
{
|
||||||
ASCtxs = new ConcurrentDictionary<Process, NvGpuASCtx>();
|
ASCtxs = new ConcurrentDictionary<KProcess, NvGpuASCtx>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int ProcessIoctl(ServiceCtx Context, int Cmd)
|
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));
|
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 _);
|
ASCtxs.TryRemove(Process, out _);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Memory;
|
using Ryujinx.Graphics.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS;
|
using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
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()
|
static NvHostChannelIoctl()
|
||||||
{
|
{
|
||||||
Channels = new ConcurrentDictionary<Process, ChannelsPerProcess>();
|
Channels = new ConcurrentDictionary<KProcess, ChannelsPerProcess>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int ProcessIoctlGpu(ServiceCtx Context, int Cmd)
|
public static int ProcessIoctlGpu(ServiceCtx Context, int Cmd)
|
||||||
|
@ -194,7 +195,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
|
||||||
return Cpp.Channels[Channel];
|
return Cpp.Channels[Channel];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UnloadProcess(Process Process)
|
public static void UnloadProcess(KProcess Process)
|
||||||
{
|
{
|
||||||
Channels.TryRemove(Process, out _);
|
Channels.TryRemove(Process, out _);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -9,13 +10,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl
|
||||||
{
|
{
|
||||||
class NvHostCtrlIoctl
|
class NvHostCtrlIoctl
|
||||||
{
|
{
|
||||||
private static ConcurrentDictionary<Process, NvHostCtrlUserCtx> UserCtxs;
|
private static ConcurrentDictionary<KProcess, NvHostCtrlUserCtx> UserCtxs;
|
||||||
|
|
||||||
private static bool IsProductionMode = true;
|
private static bool IsProductionMode = true;
|
||||||
|
|
||||||
static NvHostCtrlIoctl()
|
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))
|
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());
|
return UserCtxs.GetOrAdd(Context.Process, (Key) => new NvHostCtrlUserCtx());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UnloadProcess(Process Process)
|
public static void UnloadProcess(KProcess Process)
|
||||||
{
|
{
|
||||||
UserCtxs.TryRemove(Process, out _);
|
UserCtxs.TryRemove(Process, out _);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Memory;
|
using Ryujinx.Graphics.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
@ -10,11 +11,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap
|
||||||
{
|
{
|
||||||
private const int FlagNotFreedYet = 1;
|
private const int FlagNotFreedYet = 1;
|
||||||
|
|
||||||
private static ConcurrentDictionary<Process, IdDictionary> Maps;
|
private static ConcurrentDictionary<KProcess, IdDictionary> Maps;
|
||||||
|
|
||||||
static NvMapIoctl()
|
static NvMapIoctl()
|
||||||
{
|
{
|
||||||
Maps = new ConcurrentDictionary<Process, IdDictionary>();
|
Maps = new ConcurrentDictionary<KProcess, IdDictionary>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int ProcessIoctl(ServiceCtx Context, int Cmd)
|
public static int ProcessIoctl(ServiceCtx Context, int Cmd)
|
||||||
|
@ -130,11 +131,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap
|
||||||
//When the address is zero, we need to allocate
|
//When the address is zero, we need to allocate
|
||||||
//our own backing memory for the NvMap.
|
//our own backing memory for the NvMap.
|
||||||
//TODO: Is this allocation inside the transfer memory?
|
//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)
|
if (Result == NvResult.Success)
|
||||||
{
|
{
|
||||||
|
@ -294,7 +292,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UnloadProcess(Process Process)
|
public static void UnloadProcess(KProcess Process)
|
||||||
{
|
{
|
||||||
Maps.TryRemove(Process, out _);
|
Maps.TryRemove(Process, out _);
|
||||||
}
|
}
|
||||||
|
|
|
@ -275,8 +275,7 @@ namespace Ryujinx.HLE.HOS.Services.Android
|
||||||
|
|
||||||
private long MakeReplyParcel(ServiceCtx Context, byte[] Data)
|
private long MakeReplyParcel(ServiceCtx Context, byte[] Data)
|
||||||
{
|
{
|
||||||
long ReplyPos = Context.Request.ReceiveBuff[0].Position;
|
(long ReplyPos, long ReplySize) = Context.Request.GetBufferType0x22();
|
||||||
long ReplySize = Context.Request.ReceiveBuff[0].Size;
|
|
||||||
|
|
||||||
byte[] Reply = MakeParcel(Data, new byte[0]);
|
byte[] Reply = MakeParcel(Data, new byte[0]);
|
||||||
|
|
||||||
|
|
BIN
Ryujinx.HLE/Homebrew.npdm
Normal file
BIN
Ryujinx.HLE/Homebrew.npdm
Normal file
Binary file not shown.
105
Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs
Normal file
105
Ryujinx.HLE/Loaders/Compression/BackwardsLz.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs
Normal file
15
Ryujinx.HLE/Loaders/Elf/ElfDynamic.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.HLE.Loaders
|
namespace Ryujinx.HLE.Loaders.Elf
|
||||||
{
|
{
|
||||||
enum ElfDynTag
|
enum ElfDynamicTag
|
||||||
{
|
{
|
||||||
DT_NULL = 0,
|
DT_NULL = 0,
|
||||||
DT_NEEDED = 1,
|
DT_NEEDED = 1,
|
40
Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs
Normal file
40
Ryujinx.HLE/Loaders/Elf/ElfSymbol.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.HLE.Loaders
|
namespace Ryujinx.HLE.Loaders.Elf
|
||||||
{
|
{
|
||||||
enum ElfSymBinding
|
enum ElfSymbolBinding
|
||||||
{
|
{
|
||||||
STB_LOCAL = 0,
|
STB_LOCAL = 0,
|
||||||
STB_GLOBAL = 1,
|
STB_GLOBAL = 1,
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.HLE.Loaders
|
namespace Ryujinx.HLE.Loaders.Elf
|
||||||
{
|
{
|
||||||
enum ElfSymType
|
enum ElfSymbolType
|
||||||
{
|
{
|
||||||
STT_NOTYPE = 0,
|
STT_NOTYPE = 0,
|
||||||
STT_OBJECT = 1,
|
STT_OBJECT = 1,
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Ryujinx.HLE.Loaders
|
namespace Ryujinx.HLE.Loaders.Elf
|
||||||
{
|
{
|
||||||
enum ElfSymVisibility
|
enum ElfSymbolVisibility
|
||||||
{
|
{
|
||||||
STV_DEFAULT = 0,
|
STV_DEFAULT = 0,
|
||||||
STV_INTERNAL = 1,
|
STV_INTERNAL = 1,
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +1,15 @@
|
||||||
namespace Ryujinx.HLE.Loaders.Executables
|
namespace Ryujinx.HLE.Loaders.Executables
|
||||||
{
|
{
|
||||||
public interface IExecutable
|
interface IExecutable
|
||||||
{
|
{
|
||||||
string FilePath { get; }
|
|
||||||
|
|
||||||
byte[] Text { get; }
|
byte[] Text { get; }
|
||||||
byte[] RO { get; }
|
byte[] RO { get; }
|
||||||
byte[] Data { get; }
|
byte[] Data { get; }
|
||||||
|
|
||||||
long SourceAddress { get; }
|
|
||||||
long BssAddress { get; }
|
|
||||||
|
|
||||||
int Mod0Offset { get; }
|
|
||||||
int TextOffset { get; }
|
int TextOffset { get; }
|
||||||
int ROOffset { get; }
|
int ROOffset { get; }
|
||||||
int DataOffset { get; }
|
int DataOffset { get; }
|
||||||
|
int BssOffset { get; }
|
||||||
int BssSize { get; }
|
int BssSize { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue