Better process implementation (#491)

* Initial implementation of KProcess

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

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

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

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

* Attempt at implementing ldr:ro with new KProcess

* Allow BSS with size 0 on ldr:ro

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

* Use KMemoryBlockSize const from KMemoryManager

* Allow all methods to read from non-contiguous locations

* Fix for TransactParcelAuto

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

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

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

View File

@ -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);

View File

@ -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)) { }
}
}

View File

@ -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)

View File

@ -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;
@ -197,17 +198,41 @@ namespace ChocolArm64.Memory
public ushort ReadUInt16(long position) public ushort ReadUInt16(long position)
{ {
return *((ushort*)Translate(position)); if ((position & 1) == 0)
{
return *((ushort*)Translate(position));
}
else
{
return (ushort)(ReadByte(position + 0) << 0 |
ReadByte(position + 1) << 8);
}
} }
public uint ReadUInt32(long position) public uint ReadUInt32(long position)
{ {
return *((uint*)Translate(position)); if ((position & 3) == 0)
{
return *((uint*)Translate(position));
}
else
{
return (uint)(ReadUInt16(position + 0) << 0 |
ReadUInt16(position + 2) << 16);
}
} }
public ulong ReadUInt64(long position) public ulong ReadUInt64(long position)
{ {
return *((ulong*)Translate(position)); if ((position & 7) == 0)
{
return *((ulong*)Translate(position));
}
else
{
return (ulong)ReadUInt32(position + 0) << 0 |
(ulong)ReadUInt32(position + 4) << 32;
}
} }
public Vector128<float> ReadVector8(long position) 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)
@ -325,17 +420,41 @@ namespace ChocolArm64.Memory
public void WriteUInt16(long position, ushort value) public void WriteUInt16(long position, ushort value)
{ {
*((ushort*)TranslateWrite(position)) = value; if ((position & 1) == 0)
{
*((ushort*)TranslateWrite(position)) = value;
}
else
{
WriteByte(position + 0, (byte)(value >> 0));
WriteByte(position + 1, (byte)(value >> 8));
}
} }
public void WriteUInt32(long position, uint value) public void WriteUInt32(long position, uint value)
{ {
*((uint*)TranslateWrite(position)) = value; if ((position & 3) == 0)
{
*((uint*)TranslateWrite(position)) = value;
}
else
{
WriteUInt16(position + 0, (ushort)(value >> 0));
WriteUInt16(position + 2, (ushort)(value >> 16));
}
} }
public void WriteUInt64(long position, ulong value) public void WriteUInt64(long position, ulong value)
{ {
*((ulong*)TranslateWrite(position)) = value; if ((position & 7) == 0)
{
*((ulong*)TranslateWrite(position)) = value;
}
else
{
WriteUInt32(position + 0, (uint)(value >> 0));
WriteUInt32(position + 4, (uint)(value >> 32));
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -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,22 +559,48 @@ 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* dstPtr = TranslateWrite(dst);
byte* srcPtr = Translate(src); Buffer.MemoryCopy(srcPtr, dstPtr, size, size);
byte* dstPtr = TranslateWrite(dst); }
else
Buffer.MemoryCopy(srcPtr, dstPtr, size, size); {
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;
}
ptr = IntPtr.Zero;
return false;
} }
internal void EnsureRangeIsValid(long position, long size) 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
View 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);
}
}
}

View File

@ -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;

View File

@ -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);
} }
} }

View File

@ -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);

View File

@ -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))

View File

@ -600,122 +600,125 @@ namespace Ryujinx.Graphics.Gal.OpenGL
ThrowUnsupportedAttrib(Attrib); ThrowUnsupportedAttrib(Attrib);
} }
if (Attrib.Type == GalVertexAttribType.Unorm) fixed (byte* Ptr = Attrib.Data)
{ {
switch (Attrib.Size) if (Attrib.Type == GalVertexAttribType.Unorm)
{ {
case GalVertexAttribSize._8: switch (Attrib.Size)
case GalVertexAttribSize._8_8: {
case GalVertexAttribSize._8_8_8: case GalVertexAttribSize._8:
case GalVertexAttribSize._8_8_8_8: case GalVertexAttribSize._8_8:
GL.VertexAttrib4N((uint)Attrib.Index, (byte*)Attrib.Pointer); case GalVertexAttribSize._8_8_8:
break; case GalVertexAttribSize._8_8_8_8:
GL.VertexAttrib4N((uint)Attrib.Index, Ptr);
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;
}
} }
} else if (Attrib.Type == GalVertexAttribType.Snorm)
else if (Attrib.Type == GalVertexAttribType.Snorm)
{
switch (Attrib.Size)
{ {
case GalVertexAttribSize._8: switch (Attrib.Size)
case GalVertexAttribSize._8_8: {
case GalVertexAttribSize._8_8_8: case GalVertexAttribSize._8:
case GalVertexAttribSize._8_8_8_8: case GalVertexAttribSize._8_8:
GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Attrib.Pointer); case GalVertexAttribSize._8_8_8:
break; case GalVertexAttribSize._8_8_8_8:
GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Ptr);
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;
}
} }
} else if (Attrib.Type == GalVertexAttribType.Uint)
else if (Attrib.Type == GalVertexAttribType.Uint)
{
switch (Attrib.Size)
{ {
case GalVertexAttribSize._8: switch (Attrib.Size)
case GalVertexAttribSize._8_8: {
case GalVertexAttribSize._8_8_8: case GalVertexAttribSize._8:
case GalVertexAttribSize._8_8_8_8: case GalVertexAttribSize._8_8:
GL.VertexAttribI4((uint)Attrib.Index, (byte*)Attrib.Pointer); case GalVertexAttribSize._8_8_8:
break; case GalVertexAttribSize._8_8_8_8:
GL.VertexAttribI4((uint)Attrib.Index, Ptr);
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;
}
} }
} else if (Attrib.Type == GalVertexAttribType.Sint)
else if (Attrib.Type == GalVertexAttribType.Sint)
{
switch (Attrib.Size)
{ {
case GalVertexAttribSize._8: switch (Attrib.Size)
case GalVertexAttribSize._8_8: {
case GalVertexAttribSize._8_8_8: case GalVertexAttribSize._8:
case GalVertexAttribSize._8_8_8_8: case GalVertexAttribSize._8_8:
GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Attrib.Pointer); case GalVertexAttribSize._8_8_8:
break; case GalVertexAttribSize._8_8_8_8:
GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Ptr);
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;
}
} }
} else if (Attrib.Type == GalVertexAttribType.Float)
else if (Attrib.Type == GalVertexAttribType.Float)
{
switch (Attrib.Size)
{ {
case GalVertexAttribSize._32: switch (Attrib.Size)
case GalVertexAttribSize._32_32: {
case GalVertexAttribSize._32_32_32: case GalVertexAttribSize._32:
case GalVertexAttribSize._32_32_32_32: case GalVertexAttribSize._32_32:
GL.VertexAttrib4(Attrib.Index, (float*)Attrib.Pointer); case GalVertexAttribSize._32_32_32:
break; case GalVertexAttribSize._32_32_32_32:
GL.VertexAttrib4(Attrib.Index, (float*)Ptr);
break;
default: ThrowUnsupportedAttrib(Attrib); break; default: ThrowUnsupportedAttrib(Attrib); break;
}
} }
} }
} }

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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))
{ {

View File

@ -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);
Processes.TryAdd(ProcessId, Process);
}
InitializeProcess(Process);
return Process;
} }
private void InitializeProcess(Process Process) internal long GetKipId()
{ {
Process.AppletState.SetFocus(true); return Interlocked.Increment(ref KipId) - 1;
} }
internal void ExitProcess(int ProcessId) internal long GetProcessId()
{ {
if (Processes.TryRemove(ProcessId, out Process Process)) 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,10 +599,25 @@ 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();
} }
} }
} }

View File

@ -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,

View File

@ -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
} }
} }

View 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;
}
}

View File

@ -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();
} }
} }
} }

View 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);
}
}
}

View File

@ -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)

View File

@ -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;
}
} }
} }

View 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;
}
}
}

View 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);
}
}
}

View 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();
}
}
}

View 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));
}
}
}

View File

@ -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();
} }

View File

@ -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)
{ {

View File

@ -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,12 +20,24 @@ 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;
this.Size = Size; }
public KernelResult Initialize(int Size)
{
if ((uint)Size > 1024)
{
return KernelResult.OutOfMemory;
}
if (Size < 1)
{
Size = 1024;
}
this.Size = Size;
IdCounter = 1; 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)
{ {
@ -95,12 +107,12 @@ namespace Ryujinx.HLE.HOS.Kernel
return false; return false;
} }
int Index = (Handle >> 0) & 0x7fff; int Index = (Handle >> 0) & 0x7fff;
int HandleId = (Handle >> 15); int HandleId = (Handle >> 15);
bool Result = false; bool Result = false;
lock (LockObj) lock (Table)
{ {
if (HandleId != 0 && Index < Size) if (HandleId != 0 && Index < Size)
{ {
@ -125,10 +137,10 @@ namespace Ryujinx.HLE.HOS.Kernel
public T GetObject<T>(int Handle) public T GetObject<T>(int Handle)
{ {
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++)
{ {

View 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;
}
}
}

View 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;
}
}
}

View File

@ -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,25 +13,25 @@ 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;
this.Permission = Permission; this.Permission = Permission;
} }
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,

View 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;
}
}
}

View File

@ -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

View 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;
}
}
}

View 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;
}
}
}

View 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();
}
}
}

View 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;
}
}
}

View 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;
}
}
}

File diff suppressed because it is too large Load Diff

View 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;
}
}
}

View File

@ -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;
} }

View 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;
}
}
}

View File

@ -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);

View 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;
}
}
}

View File

@ -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);
} }
} }
} }

View 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);
}
}
}
}

View File

@ -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();
} }
} }
} }

View File

@ -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>();
} }

View File

@ -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; }
public long MutexAddress { get; set; }
public Process Owner { get; private set; } private ulong Entrypoint;
public long LastScheduledTicks { get; set; } public long MutexAddress { get; set; }
public KProcess Owner { get; private 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();
} }
} }
} }

View File

@ -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)
{ {

View 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;
}
}
}

View File

@ -2,13 +2,13 @@ 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;
} }
} }
} }

View 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;
}
}
}
}

View File

@ -2,9 +2,30 @@ namespace Ryujinx.HLE.HOS.Kernel
{ {
enum KernelResult enum KernelResult
{ {
Success = 0, Success = 0,
HandleTableFull = 0xd201, InvalidCapability = 0x1c01,
InvalidHandle = 0xe401, ThreadTerminating = 0x7601,
InvalidState = 0xfa01 InvalidSize = 0xca01,
InvalidAddress = 0xcc01,
OutOfResource = 0xce01,
OutOfMemory = 0xd001,
HandleTableFull = 0xd201,
InvalidMemState = 0xd401,
InvalidPermission = 0xd801,
InvalidMemRange = 0xdc01,
InvalidPriority = 0xe001,
InvalidCpuCore = 0xe201,
InvalidHandle = 0xe401,
UserCopyFailed = 0xe601,
InvalidCombination = 0xe801,
TimedOut = 0xea01,
Cancelled = 0xec01,
MaximumExceeded = 0xee01,
InvalidEnumValue = 0xf001,
NotFound = 0xf201,
InvalidThread = 0xf401,
InvalidState = 0xfa01,
ReservedValue = 0xfc01,
ResLimitExceeded = 0x10801
} }
} }

View 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;
}
}
}

View 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
}
}

View File

@ -0,0 +1,12 @@
namespace Ryujinx.HLE.HOS.Kernel
{
enum MemoryOperation
{
MapPa,
MapVa,
Allocate,
Unmap,
ChangePermRw,
ChangePermsAndAttributes
}
}

View File

@ -0,0 +1,10 @@
namespace Ryujinx.HLE.HOS.Kernel
{
enum MemoryRegion
{
Application = 0,
Applet = 1,
Service = 2,
NvServices = 3
}
}

View File

@ -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,

View 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;
}
}
}

View 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;
}
}
}

View 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
}
}

View File

@ -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}");
} }

View File

@ -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}!");
} }
@ -214,19 +222,19 @@ 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;
@ -234,13 +242,13 @@ 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;
@ -317,13 +325,13 @@ 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;
}
} }
} }

View File

@ -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,18 +117,28 @@ 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
{ {
Result = KernelResult.InvalidHandle; KProcess Process = CurrentProcess.HandleTable.GetKProcess(Handle);
if (Process != null)
{
Result = Process.ClearIfNotExited();
}
else
{
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; case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18:
case 20:
case 21:
case 22:
{
if (SubId != 0)
{
return KernelResult.InvalidCombination;
}
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
KProcess Process = CurrentProcess.HandleTable.GetKProcess(Handle);
if (Process == null)
{
return KernelResult.InvalidHandle;
}
switch (Id)
{
case 0: Value = Process.Capabilities.AllowedCpuCoresMask; break;
case 1: Value = Process.Capabilities.AllowedThreadPriosMask; break;
case 2: Value = (long)Process.MemoryManager.AliasRegionStart; break;
case 3: Value = (long)(Process.MemoryManager.AliasRegionEnd -
Process.MemoryManager.AliasRegionStart); break;
case 4: Value = (long)Process.MemoryManager.HeapRegionStart; break;
case 5: Value = (long)(Process.MemoryManager.HeapRegionEnd -
Process.MemoryManager.HeapRegionStart); break;
case 6: Value = (long)Process.GetMemoryCapacity(); break;
case 7: Value = (long)Process.GetMemoryUsage(); break;
case 12: Value = (long)Process.MemoryManager.GetAddrSpaceBaseAddr(); break;
case 13: Value = (long)Process.MemoryManager.GetAddrSpaceSize(); break;
case 14: Value = (long)Process.MemoryManager.StackRegionStart; break;
case 15: Value = (long)(Process.MemoryManager.StackRegionEnd -
Process.MemoryManager.StackRegionStart); break;
case 16: Value = (long)Process.PersonalMmHeapPagesCount * KMemoryManager.PageSize; break;
case 17:
if (Process.PersonalMmHeapPagesCount != 0)
{
Value = Process.MemoryManager.GetMmUsedPages() * KMemoryManager.PageSize;
}
break;
case 18: Value = Process.TitleId; break;
case 20: Value = (long)Process.UserExceptionContextAddress; break;
case 21: Value = (long)Process.GetMemoryCapacityWithoutPersonalMmHeap(); break;
case 22: Value = (long)Process.GetMemoryUsageWithoutPersonalMmHeap(); break;
}
break; break;
}
case 8: case 8:
ThreadState.X1 = EnableProcessDebugging ? 1 : 0; {
if (Handle != 0)
{
return KernelResult.InvalidHandle;
}
if (SubId != 0)
{
return KernelResult.InvalidCombination;
}
Value = System.Scheduler.GetCurrentProcess().Debug ? 1 : 0;
break; 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: case 11:
ThreadState.X1 = (ulong)Rng.Next() + ((ulong)Rng.Next() << 32); {
if (Handle != 0)
{
return KernelResult.InvalidHandle;
}
if ((ulong)SubId > 3)
{
return KernelResult.InvalidCombination;
}
KProcess CurrentProcess = System.Scheduler.GetCurrentProcess();
Value = CurrentProcess.RandomEntropy[SubId];
break; 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);
}
case 12:
ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceStart;
break; break;
}
case 13: default: return KernelResult.InvalidEnumValue;
ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceEnd -
(ulong)Process.MemoryManager.AddrSpaceStart;
break;
case 14:
ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionStart;
break;
case 15:
ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionEnd -
(ulong)Process.MemoryManager.NewMapRegionStart;
break;
case 16:
ThreadState.X1 = (ulong)(Process.MetaData?.SystemResourceSize ?? 0);
break;
case 17:
ThreadState.X1 = (ulong)Process.MemoryManager.PersonalMmHeapUsage;
break;
default:
Process.PrintStackTrace(ThreadState);
throw new NotImplementedException($"SvcGetInfo: {InfoType} 0x{Handle:x8} {InfoId}");
} }
ThreadState.X0 = 0; 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;
}
} }
} }

View File

@ -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)
{
return KernelResult.InvalidCombination;
}
if ((uint)PreferredCore > 3)
{
if ((PreferredCore | 2) != -1)
{ {
Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{PrefferedCore:x8}!"); return KernelResult.InvalidCpuCore;
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId);
return;
} }
} }
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);

View File

@ -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:

View File

@ -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,

View File

@ -0,0 +1,10 @@
namespace Ryujinx.HLE.HOS.Kernel
{
enum ThreadType
{
Dummy,
Kernel,
Kernel2,
User
}
}

View File

@ -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();
}
}
}
}
}

View 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);
}
}
}

View File

@ -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,

View File

@ -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;
} }

View File

@ -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,17 +65,32 @@ 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.TotalSize = TotalSize; this.NroAddress = NroAddress;
this.NroSize = NroSize;
this.BssAddress = BssAddress;
this.BssSize = BssSize;
this.TotalSize = TotalSize;
} }
} }
@ -174,7 +192,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldr
return false; 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)
{ {
break; if (!MemMgr.InsideHeapRegion (TargetAddress, Info.TotalSize) &&
!MemMgr.InsideAliasRegion(TargetAddress, Info.TotalSize))
{
break;
}
} }
TargetAddress += 0x1000; TargetAddress += MemInfo.Size;
} }
Context.Process.LoadProgram(Info.Executable, TargetAddress); KernelResult Result = MemMgr.MapProcessCodeMemory(TargetAddress, Info.NroAddress, Info.NroSize);
if (Result != KernelResult.Success)
{
return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState);
}
ulong BssTargetAddress = TargetAddress + Info.NroSize;
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;

View File

@ -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);

View File

@ -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 _);
} }

View File

@ -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 _);
} }

View File

@ -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 _);
} }

View File

@ -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,10 +131,7 @@ 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 _);
} }

View File

@ -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

Binary file not shown.

View 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;
}
}
}

View 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;
}
}
}

View File

@ -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,

View 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;
}
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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