forked from Mirror/Ryujinx
More flexible memory manager (#307)
* Keep track mapped buffers with fixed offsets * Started rewriting the memory manager * Initial support for MapPhysicalMemory and UnmapPhysicalMemory, other tweaks * MapPhysicalMemory/UnmapPhysicalMemory support, other tweaks * Rebased * Optimize the map/unmap physical memory svcs * Integrate shared font support * Fix address space reserve alignment * Some fixes related to gpu memory mapping * Some cleanup * Only try uploading const buffers that are really used * Check if memory region is contiguous * Rebased * Add missing count increment on IsRegionModified * Check for reads/writes outside of the address space, optimize translation with a tail call
This commit is contained in:
parent
76d95dee05
commit
c393cdf8e3
64 changed files with 3289 additions and 1852 deletions
|
@ -2,8 +2,6 @@ using System.Runtime.Intrinsics.X86;
|
||||||
|
|
||||||
public static class AOptimizations
|
public static class AOptimizations
|
||||||
{
|
{
|
||||||
public static bool DisableMemoryChecks = false;
|
|
||||||
|
|
||||||
public static bool GenerateCallStack = true;
|
public static bool GenerateCallStack = true;
|
||||||
|
|
||||||
private static bool UseAllSseIfAvailable = true;
|
private static bool UseAllSseIfAvailable = true;
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
using ChocolArm64.Memory;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace ChocolArm64.Exceptions
|
|
||||||
{
|
|
||||||
public class VmmAccessViolationException : Exception
|
|
||||||
{
|
|
||||||
private const string ExMsg = "Address 0x{0:x16} does not have \"{1}\" permission!";
|
|
||||||
|
|
||||||
public VmmAccessViolationException() { }
|
|
||||||
|
|
||||||
public VmmAccessViolationException(long Position, AMemoryPerm Perm) : base(string.Format(ExMsg, Position, Perm)) { }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,12 +2,12 @@ using System;
|
||||||
|
|
||||||
namespace ChocolArm64.Exceptions
|
namespace ChocolArm64.Exceptions
|
||||||
{
|
{
|
||||||
public class VmmOutOfMemoryException : Exception
|
public class VmmAccessException : Exception
|
||||||
{
|
{
|
||||||
private const string ExMsg = "Failed to allocate {0} bytes of memory!";
|
private const string ExMsg = "Memory region at 0x{0} with size 0x{1} is not contiguous!";
|
||||||
|
|
||||||
public VmmOutOfMemoryException() { }
|
public VmmAccessException() { }
|
||||||
|
|
||||||
public VmmOutOfMemoryException(long Size) : base(string.Format(ExMsg, Size)) { }
|
public VmmAccessException(long Position, long Size) : base(string.Format(ExMsg, Position, Size)) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -45,46 +45,21 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
switch (Size)
|
switch (Size)
|
||||||
{
|
{
|
||||||
case 0: Name = AOptimizations.DisableMemoryChecks
|
case 0: Name = nameof(AMemory.ReadVector8); break;
|
||||||
? nameof(AMemory.ReadVector8Unchecked)
|
case 1: Name = nameof(AMemory.ReadVector16); break;
|
||||||
: nameof(AMemory.ReadVector8); break;
|
case 2: Name = nameof(AMemory.ReadVector32); break;
|
||||||
|
case 3: Name = nameof(AMemory.ReadVector64); break;
|
||||||
case 1: Name = AOptimizations.DisableMemoryChecks
|
case 4: Name = nameof(AMemory.ReadVector128); break;
|
||||||
? nameof(AMemory.ReadVector16Unchecked)
|
|
||||||
: nameof(AMemory.ReadVector16); break;
|
|
||||||
|
|
||||||
case 2: Name = AOptimizations.DisableMemoryChecks
|
|
||||||
? nameof(AMemory.ReadVector32Unchecked)
|
|
||||||
: nameof(AMemory.ReadVector32); break;
|
|
||||||
|
|
||||||
case 3: Name = AOptimizations.DisableMemoryChecks
|
|
||||||
? nameof(AMemory.ReadVector64Unchecked)
|
|
||||||
: nameof(AMemory.ReadVector64); break;
|
|
||||||
|
|
||||||
case 4: Name = AOptimizations.DisableMemoryChecks
|
|
||||||
? nameof(AMemory.ReadVector128Unchecked)
|
|
||||||
: nameof(AMemory.ReadVector128); break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (Size)
|
switch (Size)
|
||||||
{
|
{
|
||||||
case 0: Name = AOptimizations.DisableMemoryChecks
|
case 0: Name = nameof(AMemory.ReadByte); break;
|
||||||
? nameof(AMemory.ReadByteUnchecked)
|
case 1: Name = nameof(AMemory.ReadUInt16); break;
|
||||||
: nameof(AMemory.ReadByte); break;
|
case 2: Name = nameof(AMemory.ReadUInt32); break;
|
||||||
|
case 3: Name = nameof(AMemory.ReadUInt64); break;
|
||||||
case 1: Name = AOptimizations.DisableMemoryChecks
|
|
||||||
? nameof(AMemory.ReadUInt16Unchecked)
|
|
||||||
: nameof(AMemory.ReadUInt16); break;
|
|
||||||
|
|
||||||
case 2: Name = AOptimizations.DisableMemoryChecks
|
|
||||||
? nameof(AMemory.ReadUInt32Unchecked)
|
|
||||||
: nameof(AMemory.ReadUInt32); break;
|
|
||||||
|
|
||||||
case 3: Name = AOptimizations.DisableMemoryChecks
|
|
||||||
? nameof(AMemory.ReadUInt64Unchecked)
|
|
||||||
: nameof(AMemory.ReadUInt64); break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,46 +107,21 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
switch (Size)
|
switch (Size)
|
||||||
{
|
{
|
||||||
case 0: Name = AOptimizations.DisableMemoryChecks
|
case 0: Name = nameof(AMemory.WriteVector8); break;
|
||||||
? nameof(AMemory.WriteVector8Unchecked)
|
case 1: Name = nameof(AMemory.WriteVector16); break;
|
||||||
: nameof(AMemory.WriteVector8); break;
|
case 2: Name = nameof(AMemory.WriteVector32); break;
|
||||||
|
case 3: Name = nameof(AMemory.WriteVector64); break;
|
||||||
case 1: Name = AOptimizations.DisableMemoryChecks
|
case 4: Name = nameof(AMemory.WriteVector128); break;
|
||||||
? nameof(AMemory.WriteVector16Unchecked)
|
|
||||||
: nameof(AMemory.WriteVector16); break;
|
|
||||||
|
|
||||||
case 2: Name = AOptimizations.DisableMemoryChecks
|
|
||||||
? nameof(AMemory.WriteVector32Unchecked)
|
|
||||||
: nameof(AMemory.WriteVector32); break;
|
|
||||||
|
|
||||||
case 3: Name = AOptimizations.DisableMemoryChecks
|
|
||||||
? nameof(AMemory.WriteVector64Unchecked)
|
|
||||||
: nameof(AMemory.WriteVector64); break;
|
|
||||||
|
|
||||||
case 4: Name = AOptimizations.DisableMemoryChecks
|
|
||||||
? nameof(AMemory.WriteVector128Unchecked)
|
|
||||||
: nameof(AMemory.WriteVector128); break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (Size)
|
switch (Size)
|
||||||
{
|
{
|
||||||
case 0: Name = AOptimizations.DisableMemoryChecks
|
case 0: Name = nameof(AMemory.WriteByte); break;
|
||||||
? nameof(AMemory.WriteByteUnchecked)
|
case 1: Name = nameof(AMemory.WriteUInt16); break;
|
||||||
: nameof(AMemory.WriteByte); break;
|
case 2: Name = nameof(AMemory.WriteUInt32); break;
|
||||||
|
case 3: Name = nameof(AMemory.WriteUInt64); break;
|
||||||
case 1: Name = AOptimizations.DisableMemoryChecks
|
|
||||||
? nameof(AMemory.WriteUInt16Unchecked)
|
|
||||||
: nameof(AMemory.WriteUInt16); break;
|
|
||||||
|
|
||||||
case 2: Name = AOptimizations.DisableMemoryChecks
|
|
||||||
? nameof(AMemory.WriteUInt32Unchecked)
|
|
||||||
: nameof(AMemory.WriteUInt32); break;
|
|
||||||
|
|
||||||
case 3: Name = AOptimizations.DisableMemoryChecks
|
|
||||||
? nameof(AMemory.WriteUInt64Unchecked)
|
|
||||||
: nameof(AMemory.WriteUInt64); break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using ChocolArm64.Exceptions;
|
using ChocolArm64.Exceptions;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
@ -12,9 +13,22 @@ namespace ChocolArm64.Memory
|
||||||
{
|
{
|
||||||
public unsafe class AMemory : IAMemory, IDisposable
|
public unsafe class AMemory : IAMemory, IDisposable
|
||||||
{
|
{
|
||||||
private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1;
|
private const int PTLvl0Bits = 13;
|
||||||
|
private const int PTLvl1Bits = 14;
|
||||||
|
private const int PTPageBits = 12;
|
||||||
|
|
||||||
public AMemoryMgr Manager { get; private set; }
|
private const int PTLvl0Size = 1 << PTLvl0Bits;
|
||||||
|
private const int PTLvl1Size = 1 << PTLvl1Bits;
|
||||||
|
public const int PageSize = 1 << PTPageBits;
|
||||||
|
|
||||||
|
private const int PTLvl0Mask = PTLvl0Size - 1;
|
||||||
|
private const int PTLvl1Mask = PTLvl1Size - 1;
|
||||||
|
public const int PageMask = PageSize - 1;
|
||||||
|
|
||||||
|
private const int PTLvl0Bit = PTPageBits + PTLvl1Bits;
|
||||||
|
private const int PTLvl1Bit = PTPageBits;
|
||||||
|
|
||||||
|
private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1;
|
||||||
|
|
||||||
private class ArmMonitor
|
private class ArmMonitor
|
||||||
{
|
{
|
||||||
|
@ -29,32 +43,30 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
private Dictionary<int, ArmMonitor> Monitors;
|
private Dictionary<int, ArmMonitor> Monitors;
|
||||||
|
|
||||||
|
private ConcurrentDictionary<long, IntPtr> ObservedPages;
|
||||||
|
|
||||||
public IntPtr Ram { get; private set; }
|
public IntPtr Ram { get; private set; }
|
||||||
|
|
||||||
private byte* RamPtr;
|
private byte* RamPtr;
|
||||||
|
|
||||||
private int HostPageSize;
|
private byte*** PageTable;
|
||||||
|
|
||||||
public AMemory()
|
public AMemory(IntPtr Ram)
|
||||||
{
|
{
|
||||||
Manager = new AMemoryMgr();
|
|
||||||
|
|
||||||
Monitors = new Dictionary<int, ArmMonitor>();
|
Monitors = new Dictionary<int, ArmMonitor>();
|
||||||
|
|
||||||
IntPtr Size = (IntPtr)AMemoryMgr.RamSize + AMemoryMgr.PageSize;
|
ObservedPages = new ConcurrentDictionary<long, IntPtr>();
|
||||||
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
this.Ram = Ram;
|
||||||
{
|
|
||||||
Ram = AMemoryWin32.Allocate(Size);
|
|
||||||
|
|
||||||
HostPageSize = AMemoryWin32.GetPageSize(Ram, Size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Ram = Marshal.AllocHGlobal(Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
RamPtr = (byte*)Ram;
|
RamPtr = (byte*)Ram;
|
||||||
|
|
||||||
|
PageTable = (byte***)Marshal.AllocHGlobal(PTLvl0Size * IntPtr.Size);
|
||||||
|
|
||||||
|
for (int L0 = 0; L0 < PTLvl0Size; L0++)
|
||||||
|
{
|
||||||
|
PageTable[L0] = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveMonitor(AThreadState State)
|
public void RemoveMonitor(AThreadState State)
|
||||||
|
@ -155,62 +167,6 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetHostPageSize()
|
|
||||||
{
|
|
||||||
return HostPageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public (bool[], long) IsRegionModified(long Position, long Size)
|
|
||||||
{
|
|
||||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
|
||||||
return (null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
long EndPos = Position + Size;
|
|
||||||
|
|
||||||
if ((ulong)EndPos < (ulong)Position)
|
|
||||||
{
|
|
||||||
return (null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ulong)EndPos > AMemoryMgr.RamSize)
|
|
||||||
{
|
|
||||||
return (null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
IntPtr MemAddress = new IntPtr(RamPtr + Position);
|
|
||||||
IntPtr MemSize = new IntPtr(Size);
|
|
||||||
|
|
||||||
int HostPageMask = HostPageSize - 1;
|
|
||||||
|
|
||||||
Position &= ~HostPageMask;
|
|
||||||
|
|
||||||
Size = EndPos - Position;
|
|
||||||
|
|
||||||
IntPtr[] Addresses = new IntPtr[(Size + HostPageMask) / HostPageSize];
|
|
||||||
|
|
||||||
AMemoryWin32.IsRegionModified(MemAddress, MemSize, Addresses, out int Count);
|
|
||||||
|
|
||||||
bool[] Modified = new bool[Addresses.Length];
|
|
||||||
|
|
||||||
for (int Index = 0; Index < Count; Index++)
|
|
||||||
{
|
|
||||||
long VA = Addresses[Index].ToInt64() - Ram.ToInt64();
|
|
||||||
|
|
||||||
Modified[(VA - Position) / HostPageSize] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (Modified, Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr GetHostAddress(long Position, long Size)
|
|
||||||
{
|
|
||||||
EnsureRangeIsValid(Position, Size, AMemoryPerm.Read);
|
|
||||||
|
|
||||||
return (IntPtr)(RamPtr + (ulong)Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public sbyte ReadSByte(long Position)
|
public sbyte ReadSByte(long Position)
|
||||||
{
|
{
|
||||||
return (sbyte)ReadByte(Position);
|
return (sbyte)ReadByte(Position);
|
||||||
|
@ -233,33 +189,22 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
public byte ReadByte(long Position)
|
public byte ReadByte(long Position)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
return *((byte*)Translate(Position));
|
||||||
|
|
||||||
return ReadByteUnchecked(Position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ushort ReadUInt16(long Position)
|
public ushort ReadUInt16(long Position)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
|
return *((ushort*)Translate(Position));
|
||||||
EnsureAccessIsValid(Position + 1, AMemoryPerm.Read);
|
|
||||||
|
|
||||||
return ReadUInt16Unchecked(Position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint ReadUInt32(long Position)
|
public uint ReadUInt32(long Position)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
|
return *((uint*)Translate(Position));
|
||||||
EnsureAccessIsValid(Position + 3, AMemoryPerm.Read);
|
|
||||||
|
|
||||||
return ReadUInt32Unchecked(Position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong ReadUInt64(long Position)
|
public ulong ReadUInt64(long Position)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
|
return *((ulong*)Translate(Position));
|
||||||
EnsureAccessIsValid(Position + 7, AMemoryPerm.Read);
|
|
||||||
|
|
||||||
return ReadUInt64Unchecked(Position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector128<float> ReadVector8(long Position)
|
public Vector128<float> ReadVector8(long Position)
|
||||||
|
@ -274,6 +219,7 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public Vector128<float> ReadVector16(long Position)
|
public Vector128<float> ReadVector16(long Position)
|
||||||
{
|
{
|
||||||
if (Sse2.IsSupported)
|
if (Sse2.IsSupported)
|
||||||
|
@ -286,14 +232,12 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public Vector128<float> ReadVector32(long Position)
|
public Vector128<float> ReadVector32(long Position)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
|
|
||||||
EnsureAccessIsValid(Position + 3, AMemoryPerm.Read);
|
|
||||||
|
|
||||||
if (Sse.IsSupported)
|
if (Sse.IsSupported)
|
||||||
{
|
{
|
||||||
return Sse.LoadScalarVector128((float*)(RamPtr + (uint)Position));
|
return Sse.LoadScalarVector128((float*)Translate(Position));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -301,14 +245,12 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public Vector128<float> ReadVector64(long Position)
|
public Vector128<float> ReadVector64(long Position)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
|
|
||||||
EnsureAccessIsValid(Position + 7, AMemoryPerm.Read);
|
|
||||||
|
|
||||||
if (Sse2.IsSupported)
|
if (Sse2.IsSupported)
|
||||||
{
|
{
|
||||||
return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)(RamPtr + (uint)Position)));
|
return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)Translate(Position)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -316,118 +258,12 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public Vector128<float> ReadVector128(long Position)
|
public Vector128<float> ReadVector128(long Position)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
|
|
||||||
EnsureAccessIsValid(Position + 15, AMemoryPerm.Read);
|
|
||||||
|
|
||||||
if (Sse.IsSupported)
|
if (Sse.IsSupported)
|
||||||
{
|
{
|
||||||
return Sse.LoadVector128((float*)(RamPtr + (uint)Position));
|
return Sse.LoadVector128((float*)Translate(Position));
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new PlatformNotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sbyte ReadSByteUnchecked(long Position)
|
|
||||||
{
|
|
||||||
return (sbyte)ReadByteUnchecked(Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public short ReadInt16Unchecked(long Position)
|
|
||||||
{
|
|
||||||
return (short)ReadUInt16Unchecked(Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ReadInt32Unchecked(long Position)
|
|
||||||
{
|
|
||||||
return (int)ReadUInt32Unchecked(Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long ReadInt64Unchecked(long Position)
|
|
||||||
{
|
|
||||||
return (long)ReadUInt64Unchecked(Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte ReadByteUnchecked(long Position)
|
|
||||||
{
|
|
||||||
return *((byte*)(RamPtr + (uint)Position));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ushort ReadUInt16Unchecked(long Position)
|
|
||||||
{
|
|
||||||
return *((ushort*)(RamPtr + (uint)Position));
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint ReadUInt32Unchecked(long Position)
|
|
||||||
{
|
|
||||||
return *((uint*)(RamPtr + (uint)Position));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ulong ReadUInt64Unchecked(long Position)
|
|
||||||
{
|
|
||||||
return *((ulong*)(RamPtr + (uint)Position));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector128<float> ReadVector8Unchecked(long Position)
|
|
||||||
{
|
|
||||||
if (Sse2.IsSupported)
|
|
||||||
{
|
|
||||||
return Sse.StaticCast<byte, float>(Sse2.SetVector128(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ReadByte(Position)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new PlatformNotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public Vector128<float> ReadVector16Unchecked(long Position)
|
|
||||||
{
|
|
||||||
if (Sse2.IsSupported)
|
|
||||||
{
|
|
||||||
return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse2.SetZeroVector128<ushort>(), ReadUInt16Unchecked(Position), 0));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new PlatformNotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
public Vector128<float> ReadVector32Unchecked(long Position)
|
|
||||||
{
|
|
||||||
if (Sse.IsSupported)
|
|
||||||
{
|
|
||||||
return Sse.LoadScalarVector128((float*)(RamPtr + (uint)Position));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new PlatformNotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
public Vector128<float> ReadVector64Unchecked(long Position)
|
|
||||||
{
|
|
||||||
if (Sse2.IsSupported)
|
|
||||||
{
|
|
||||||
return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)(RamPtr + (uint)Position)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new PlatformNotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public Vector128<float> ReadVector128Unchecked(long Position)
|
|
||||||
{
|
|
||||||
if (Sse.IsSupported)
|
|
||||||
{
|
|
||||||
return Sse.LoadVector128((float*)(RamPtr + (uint)Position));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -442,11 +278,11 @@ namespace ChocolArm64.Memory
|
||||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureRangeIsValid(Position, Size, AMemoryPerm.Read);
|
EnsureRangeIsValid(Position, Size);
|
||||||
|
|
||||||
byte[] Data = new byte[Size];
|
byte[] Data = new byte[Size];
|
||||||
|
|
||||||
Marshal.Copy((IntPtr)(RamPtr + (uint)Position), Data, 0, (int)Size);
|
Marshal.Copy((IntPtr)Translate(Position), Data, 0, (int)Size);
|
||||||
|
|
||||||
return Data;
|
return Data;
|
||||||
}
|
}
|
||||||
|
@ -473,35 +309,25 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
public void WriteByte(long Position, byte Value)
|
public void WriteByte(long Position, byte Value)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
*((byte*)TranslateWrite(Position)) = Value;
|
||||||
|
|
||||||
WriteByteUnchecked(Position, Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteUInt16(long Position, ushort Value)
|
public void WriteUInt16(long Position, ushort Value)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
|
*((ushort*)TranslateWrite(Position)) = Value;
|
||||||
EnsureAccessIsValid(Position + 1, AMemoryPerm.Write);
|
|
||||||
|
|
||||||
WriteUInt16Unchecked(Position, Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteUInt32(long Position, uint Value)
|
public void WriteUInt32(long Position, uint Value)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
|
*((uint*)TranslateWrite(Position)) = Value;
|
||||||
EnsureAccessIsValid(Position + 3, AMemoryPerm.Write);
|
|
||||||
|
|
||||||
WriteUInt32Unchecked(Position, Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteUInt64(long Position, ulong Value)
|
public void WriteUInt64(long Position, ulong Value)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
|
*((ulong*)TranslateWrite(Position)) = Value;
|
||||||
EnsureAccessIsValid(Position + 7, AMemoryPerm.Write);
|
|
||||||
|
|
||||||
WriteUInt64Unchecked(Position, Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteVector8(long Position, Vector128<float> Value)
|
public void WriteVector8(long Position, Vector128<float> Value)
|
||||||
{
|
{
|
||||||
if (Sse41.IsSupported)
|
if (Sse41.IsSupported)
|
||||||
|
@ -518,6 +344,7 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteVector16(long Position, Vector128<float> Value)
|
public void WriteVector16(long Position, Vector128<float> Value)
|
||||||
{
|
{
|
||||||
if (Sse2.IsSupported)
|
if (Sse2.IsSupported)
|
||||||
|
@ -530,14 +357,12 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public void WriteVector32(long Position, Vector128<float> Value)
|
public void WriteVector32(long Position, Vector128<float> Value)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
|
|
||||||
EnsureAccessIsValid(Position + 3, AMemoryPerm.Write);
|
|
||||||
|
|
||||||
if (Sse.IsSupported)
|
if (Sse.IsSupported)
|
||||||
{
|
{
|
||||||
Sse.StoreScalar((float*)(RamPtr + (uint)Position), Value);
|
Sse.StoreScalar((float*)TranslateWrite(Position), Value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -545,14 +370,12 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
public void WriteVector64(long Position, Vector128<float> Value)
|
public void WriteVector64(long Position, Vector128<float> Value)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
|
|
||||||
EnsureAccessIsValid(Position + 7, AMemoryPerm.Write);
|
|
||||||
|
|
||||||
if (Sse2.IsSupported)
|
if (Sse2.IsSupported)
|
||||||
{
|
{
|
||||||
Sse2.StoreScalar((double*)(RamPtr + (uint)Position), Sse.StaticCast<float, double>(Value));
|
Sse2.StoreScalar((double*)TranslateWrite(Position), Sse.StaticCast<float, double>(Value));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -560,123 +383,12 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteVector128(long Position, Vector128<float> Value)
|
public void WriteVector128(long Position, Vector128<float> Value)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
|
|
||||||
EnsureAccessIsValid(Position + 15, AMemoryPerm.Write);
|
|
||||||
|
|
||||||
if (Sse.IsSupported)
|
if (Sse.IsSupported)
|
||||||
{
|
{
|
||||||
Sse.Store((float*)(RamPtr + (uint)Position), Value);
|
Sse.Store((float*)TranslateWrite(Position), Value);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new PlatformNotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WriteSByteUnchecked(long Position, sbyte Value)
|
|
||||||
{
|
|
||||||
WriteByteUnchecked(Position, (byte)Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WriteInt16Unchecked(long Position, short Value)
|
|
||||||
{
|
|
||||||
WriteUInt16Unchecked(Position, (ushort)Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WriteInt32Unchecked(long Position, int Value)
|
|
||||||
{
|
|
||||||
WriteUInt32Unchecked(Position, (uint)Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WriteInt64Unchecked(long Position, long Value)
|
|
||||||
{
|
|
||||||
WriteUInt64Unchecked(Position, (ulong)Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WriteByteUnchecked(long Position, byte Value)
|
|
||||||
{
|
|
||||||
*((byte*)(RamPtr + (uint)Position)) = Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WriteUInt16Unchecked(long Position, ushort Value)
|
|
||||||
{
|
|
||||||
*((ushort*)(RamPtr + (uint)Position)) = Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WriteUInt32Unchecked(long Position, uint Value)
|
|
||||||
{
|
|
||||||
*((uint*)(RamPtr + (uint)Position)) = Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WriteUInt64Unchecked(long Position, ulong Value)
|
|
||||||
{
|
|
||||||
*((ulong*)(RamPtr + (uint)Position)) = Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void WriteVector8Unchecked(long Position, Vector128<float> Value)
|
|
||||||
{
|
|
||||||
if (Sse41.IsSupported)
|
|
||||||
{
|
|
||||||
WriteByteUnchecked(Position, Sse41.Extract(Sse.StaticCast<float, byte>(Value), 0));
|
|
||||||
}
|
|
||||||
else if (Sse2.IsSupported)
|
|
||||||
{
|
|
||||||
WriteByteUnchecked(Position, (byte)Sse2.Extract(Sse.StaticCast<float, ushort>(Value), 0));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new PlatformNotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void WriteVector16Unchecked(long Position, Vector128<float> Value)
|
|
||||||
{
|
|
||||||
if (Sse2.IsSupported)
|
|
||||||
{
|
|
||||||
WriteUInt16Unchecked(Position, Sse2.Extract(Sse.StaticCast<float, ushort>(Value), 0));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new PlatformNotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
public void WriteVector32Unchecked(long Position, Vector128<float> Value)
|
|
||||||
{
|
|
||||||
if (Sse.IsSupported)
|
|
||||||
{
|
|
||||||
Sse.StoreScalar((float*)(RamPtr + (uint)Position), Value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new PlatformNotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
||||||
public void WriteVector64Unchecked(long Position, Vector128<float> Value)
|
|
||||||
{
|
|
||||||
if (Sse2.IsSupported)
|
|
||||||
{
|
|
||||||
Sse2.StoreScalar((double*)(RamPtr + (uint)Position), Sse.StaticCast<float, double>(Value));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new PlatformNotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void WriteVector128Unchecked(long Position, Vector128<float> Value)
|
|
||||||
{
|
|
||||||
if (Sse.IsSupported)
|
|
||||||
{
|
|
||||||
Sse.Store((float*)(RamPtr + (uint)Position), Value);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -686,36 +398,285 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
public void WriteBytes(long Position, byte[] Data)
|
public void WriteBytes(long Position, byte[] Data)
|
||||||
{
|
{
|
||||||
EnsureRangeIsValid(Position, (uint)Data.Length, AMemoryPerm.Write);
|
EnsureRangeIsValid(Position, (uint)Data.Length);
|
||||||
|
|
||||||
Marshal.Copy(Data, 0, (IntPtr)(RamPtr + (uint)Position), Data.Length);
|
Marshal.Copy(Data, 0, (IntPtr)TranslateWrite(Position), Data.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureRangeIsValid(long Position, long Size, AMemoryPerm Perm)
|
public void Map(long VA, long PA, long Size)
|
||||||
|
{
|
||||||
|
SetPTEntries(VA, RamPtr + PA, Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unmap(long Position, long Size)
|
||||||
|
{
|
||||||
|
SetPTEntries(Position, null, Size);
|
||||||
|
|
||||||
|
StopObservingRegion(Position, Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMapped(long Position)
|
||||||
|
{
|
||||||
|
if (!(IsValidPosition(Position)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
|
||||||
|
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
|
||||||
|
|
||||||
|
if (PageTable[L0] == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PageTable[L0][L1] != null || ObservedPages.ContainsKey(Position >> PTPageBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long GetPhysicalAddress(long VirtualAddress)
|
||||||
|
{
|
||||||
|
byte* Ptr = Translate(VirtualAddress);
|
||||||
|
|
||||||
|
return (long)(Ptr - RamPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal byte* Translate(long Position)
|
||||||
|
{
|
||||||
|
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
|
||||||
|
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
|
||||||
|
|
||||||
|
long Old = Position;
|
||||||
|
|
||||||
|
byte** Lvl1 = PageTable[L0];
|
||||||
|
|
||||||
|
if ((Position >> (PTLvl0Bit + PTLvl0Bits)) != 0)
|
||||||
|
{
|
||||||
|
goto Unmapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Lvl1 == null)
|
||||||
|
{
|
||||||
|
goto Unmapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position &= PageMask;
|
||||||
|
|
||||||
|
byte* Ptr = Lvl1[L1];
|
||||||
|
|
||||||
|
if (Ptr == null)
|
||||||
|
{
|
||||||
|
goto Unmapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ptr + Position;
|
||||||
|
|
||||||
|
Unmapped:
|
||||||
|
return HandleNullPte(Old);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte* HandleNullPte(long Position)
|
||||||
|
{
|
||||||
|
long Key = Position >> PTPageBits;
|
||||||
|
|
||||||
|
if (ObservedPages.TryGetValue(Key, out IntPtr Ptr))
|
||||||
|
{
|
||||||
|
return (byte*)Ptr + (Position & PageMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new VmmPageFaultException(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal byte* TranslateWrite(long Position)
|
||||||
|
{
|
||||||
|
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
|
||||||
|
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
|
||||||
|
|
||||||
|
long Old = Position;
|
||||||
|
|
||||||
|
byte** Lvl1 = PageTable[L0];
|
||||||
|
|
||||||
|
if ((Position >> (PTLvl0Bit + PTLvl0Bits)) != 0)
|
||||||
|
{
|
||||||
|
goto Unmapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Lvl1 == null)
|
||||||
|
{
|
||||||
|
goto Unmapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position &= PageMask;
|
||||||
|
|
||||||
|
byte* Ptr = Lvl1[L1];
|
||||||
|
|
||||||
|
if (Ptr == null)
|
||||||
|
{
|
||||||
|
goto Unmapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ptr + Position;
|
||||||
|
|
||||||
|
Unmapped:
|
||||||
|
return HandleNullPteWrite(Old);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte* HandleNullPteWrite(long Position)
|
||||||
|
{
|
||||||
|
long Key = Position >> PTPageBits;
|
||||||
|
|
||||||
|
if (ObservedPages.TryGetValue(Key, out IntPtr Ptr))
|
||||||
|
{
|
||||||
|
SetPTEntry(Position, (byte*)Ptr);
|
||||||
|
|
||||||
|
return (byte*)Ptr + (Position & PageMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new VmmPageFaultException(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetPTEntries(long VA, byte* Ptr, long Size)
|
||||||
|
{
|
||||||
|
long EndPosition = (VA + Size + PageMask) & ~PageMask;
|
||||||
|
|
||||||
|
while ((ulong)VA < (ulong)EndPosition)
|
||||||
|
{
|
||||||
|
SetPTEntry(VA, Ptr);
|
||||||
|
|
||||||
|
VA += PageSize;
|
||||||
|
|
||||||
|
if (Ptr != null)
|
||||||
|
{
|
||||||
|
Ptr += PageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetPTEntry(long Position, byte* Ptr)
|
||||||
|
{
|
||||||
|
if (!IsValidPosition(Position))
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(Position));
|
||||||
|
}
|
||||||
|
|
||||||
|
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
|
||||||
|
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
|
||||||
|
|
||||||
|
if (PageTable[L0] == null)
|
||||||
|
{
|
||||||
|
byte** Lvl1 = (byte**)Marshal.AllocHGlobal(PTLvl1Size * IntPtr.Size);
|
||||||
|
|
||||||
|
for (int ZL1 = 0; ZL1 < PTLvl1Size; ZL1++)
|
||||||
|
{
|
||||||
|
Lvl1[ZL1] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.MemoryBarrier();
|
||||||
|
|
||||||
|
PageTable[L0] = Lvl1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PageTable[L0][L1] = Ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public (bool[], int) IsRegionModified(long Position, long Size)
|
||||||
|
{
|
||||||
|
long EndPosition = (Position + Size + PageMask) & ~PageMask;
|
||||||
|
|
||||||
|
Position &= ~PageMask;
|
||||||
|
|
||||||
|
Size = EndPosition - Position;
|
||||||
|
|
||||||
|
bool[] Modified = new bool[Size >> PTPageBits];
|
||||||
|
|
||||||
|
int Count = 0;
|
||||||
|
|
||||||
|
lock (ObservedPages)
|
||||||
|
{
|
||||||
|
for (int Page = 0; Page < Modified.Length; Page++)
|
||||||
|
{
|
||||||
|
byte* Ptr = Translate(Position);
|
||||||
|
|
||||||
|
if (ObservedPages.TryAdd(Position >> PTPageBits, (IntPtr)Ptr))
|
||||||
|
{
|
||||||
|
Modified[Page] = true;
|
||||||
|
|
||||||
|
Count++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
|
||||||
|
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
|
||||||
|
|
||||||
|
byte** Lvl1 = PageTable[L0];
|
||||||
|
|
||||||
|
if (Lvl1 != null)
|
||||||
|
{
|
||||||
|
if (Modified[Page] = Lvl1[L1] != null)
|
||||||
|
{
|
||||||
|
Count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetPTEntry(Position, null);
|
||||||
|
|
||||||
|
Position += PageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Modified, Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopObservingRegion(long Position, long Size)
|
||||||
|
{
|
||||||
|
long EndPosition = (Position + Size + PageMask) & ~PageMask;
|
||||||
|
|
||||||
|
while (Position < EndPosition)
|
||||||
|
{
|
||||||
|
lock (ObservedPages)
|
||||||
|
{
|
||||||
|
if (ObservedPages.TryRemove(Position >> PTPageBits, out IntPtr Ptr))
|
||||||
|
{
|
||||||
|
SetPTEntry(Position, (byte*)Ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Position += PageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntPtr GetHostAddress(long Position, long Size)
|
||||||
|
{
|
||||||
|
EnsureRangeIsValid(Position, Size);
|
||||||
|
|
||||||
|
return (IntPtr)Translate(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void EnsureRangeIsValid(long Position, long Size)
|
||||||
{
|
{
|
||||||
long EndPos = Position + Size;
|
long EndPos = Position + Size;
|
||||||
|
|
||||||
Position &= ~AMemoryMgr.PageMask;
|
Position &= ~PageMask;
|
||||||
|
|
||||||
|
long ExpectedPA = GetPhysicalAddress(Position);
|
||||||
|
|
||||||
while ((ulong)Position < (ulong)EndPos)
|
while ((ulong)Position < (ulong)EndPos)
|
||||||
{
|
{
|
||||||
EnsureAccessIsValid(Position, Perm);
|
long PA = GetPhysicalAddress(Position);
|
||||||
|
|
||||||
Position += AMemoryMgr.PageSize;
|
if (PA != ExpectedPA)
|
||||||
|
{
|
||||||
|
throw new VmmAccessException(Position, Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Position += PageSize;
|
||||||
|
ExpectedPA += PageSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureAccessIsValid(long Position, AMemoryPerm Perm)
|
public bool IsValidPosition(long Position)
|
||||||
{
|
{
|
||||||
if (!Manager.IsMapped(Position))
|
return Position >> (PTLvl0Bits + PTLvl1Bits + PTPageBits) == 0;
|
||||||
{
|
|
||||||
throw new VmmPageFaultException(Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Manager.HasPermission(Position, Perm))
|
|
||||||
{
|
|
||||||
throw new VmmAccessViolationException(Position, Perm);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -725,19 +686,24 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (Ram != IntPtr.Zero)
|
if (PageTable == null)
|
||||||
{
|
{
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int L0 = 0; L0 < PTLvl0Size; L0++)
|
||||||
|
{
|
||||||
|
if (PageTable[L0] != null)
|
||||||
{
|
{
|
||||||
AMemoryWin32.Free(Ram);
|
Marshal.FreeHGlobal((IntPtr)PageTable[L0]);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(Ram);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ram = IntPtr.Zero;
|
PageTable[L0] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Marshal.FreeHGlobal((IntPtr)PageTable);
|
||||||
|
|
||||||
|
PageTable = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -26,12 +26,9 @@ namespace ChocolArm64.Memory
|
||||||
{
|
{
|
||||||
long Size = Marshal.SizeOf<T>();
|
long Size = Marshal.SizeOf<T>();
|
||||||
|
|
||||||
if ((ulong)(Position + Size) > AMemoryMgr.AddrSize)
|
Memory.EnsureRangeIsValid(Position, Size);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(Position));
|
|
||||||
}
|
|
||||||
|
|
||||||
IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position);
|
IntPtr Ptr = (IntPtr)Memory.Translate(Position);
|
||||||
|
|
||||||
return Marshal.PtrToStructure<T>(Ptr);
|
return Marshal.PtrToStructure<T>(Ptr);
|
||||||
}
|
}
|
||||||
|
@ -40,12 +37,9 @@ namespace ChocolArm64.Memory
|
||||||
{
|
{
|
||||||
long Size = Marshal.SizeOf<T>();
|
long Size = Marshal.SizeOf<T>();
|
||||||
|
|
||||||
if ((ulong)(Position + Size) > AMemoryMgr.AddrSize)
|
Memory.EnsureRangeIsValid(Position, Size);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(Position));
|
|
||||||
}
|
|
||||||
|
|
||||||
IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position);
|
IntPtr Ptr = (IntPtr)Memory.TranslateWrite(Position);
|
||||||
|
|
||||||
Marshal.StructureToPtr<T>(Value, Ptr, false);
|
Marshal.StructureToPtr<T>(Value, Ptr, false);
|
||||||
}
|
}
|
||||||
|
@ -69,15 +63,5 @@ namespace ChocolArm64.Memory
|
||||||
return Encoding.ASCII.GetString(MS.ToArray());
|
return Encoding.ASCII.GetString(MS.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long PageRoundUp(long Value)
|
|
||||||
{
|
|
||||||
return (Value + AMemoryMgr.PageMask) & ~AMemoryMgr.PageMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long PageRoundDown(long Value)
|
|
||||||
{
|
|
||||||
return Value & ~AMemoryMgr.PageMask;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,21 +0,0 @@
|
||||||
namespace ChocolArm64.Memory
|
|
||||||
{
|
|
||||||
public class AMemoryMapInfo
|
|
||||||
{
|
|
||||||
public long Position { get; private set; }
|
|
||||||
public long Size { get; private set; }
|
|
||||||
public int Type { get; private set; }
|
|
||||||
public int Attr { get; private set; }
|
|
||||||
|
|
||||||
public AMemoryPerm Perm { get; private set; }
|
|
||||||
|
|
||||||
public AMemoryMapInfo(long Position, long Size, int Type, int Attr, AMemoryPerm Perm)
|
|
||||||
{
|
|
||||||
this.Position = Position;
|
|
||||||
this.Size = Size;
|
|
||||||
this.Type = Type;
|
|
||||||
this.Attr = Attr;
|
|
||||||
this.Perm = Perm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,258 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace ChocolArm64.Memory
|
|
||||||
{
|
|
||||||
public class AMemoryMgr
|
|
||||||
{
|
|
||||||
public const long RamSize = 4L * 1024 * 1024 * 1024;
|
|
||||||
public const long AddrSize = RamSize;
|
|
||||||
|
|
||||||
private const int PTLvl0Bits = 10;
|
|
||||||
private const int PTLvl1Bits = 10;
|
|
||||||
private const int PTPageBits = 12;
|
|
||||||
|
|
||||||
private const int PTLvl0Size = 1 << PTLvl0Bits;
|
|
||||||
private const int PTLvl1Size = 1 << PTLvl1Bits;
|
|
||||||
public const int PageSize = 1 << PTPageBits;
|
|
||||||
|
|
||||||
private const int PTLvl0Mask = PTLvl0Size - 1;
|
|
||||||
private const int PTLvl1Mask = PTLvl1Size - 1;
|
|
||||||
public const int PageMask = PageSize - 1;
|
|
||||||
|
|
||||||
private const int PTLvl0Bit = PTPageBits + PTLvl1Bits;
|
|
||||||
private const int PTLvl1Bit = PTPageBits;
|
|
||||||
|
|
||||||
private enum PTMap
|
|
||||||
{
|
|
||||||
Unmapped,
|
|
||||||
Mapped
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct PTEntry
|
|
||||||
{
|
|
||||||
public PTMap Map;
|
|
||||||
public AMemoryPerm Perm;
|
|
||||||
|
|
||||||
public int Type;
|
|
||||||
public int Attr;
|
|
||||||
|
|
||||||
public PTEntry(PTMap Map, AMemoryPerm Perm, int Type, int Attr)
|
|
||||||
{
|
|
||||||
this.Map = Map;
|
|
||||||
this.Perm = Perm;
|
|
||||||
this.Type = Type;
|
|
||||||
this.Attr = Attr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private PTEntry[][] PageTable;
|
|
||||||
|
|
||||||
public AMemoryMgr()
|
|
||||||
{
|
|
||||||
PageTable = new PTEntry[PTLvl0Size][];
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Map(long Position, long Size, int Type, AMemoryPerm Perm)
|
|
||||||
{
|
|
||||||
SetPTEntry(Position, Size, new PTEntry(PTMap.Mapped, Perm, Type, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Unmap(long Position, long Size)
|
|
||||||
{
|
|
||||||
SetPTEntry(Position, Size, new PTEntry(PTMap.Unmapped, 0, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Unmap(long Position, long Size, int Type)
|
|
||||||
{
|
|
||||||
SetPTEntry(Position, Size, Type, new PTEntry(PTMap.Unmapped, 0, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reprotect(long Position, long Size, AMemoryPerm Perm)
|
|
||||||
{
|
|
||||||
Position = AMemoryHelper.PageRoundDown(Position);
|
|
||||||
|
|
||||||
Size = AMemoryHelper.PageRoundUp(Size);
|
|
||||||
|
|
||||||
long PagesCount = Size / PageSize;
|
|
||||||
|
|
||||||
while (PagesCount-- > 0)
|
|
||||||
{
|
|
||||||
PTEntry Entry = GetPTEntry(Position);
|
|
||||||
|
|
||||||
Entry.Perm = Perm;
|
|
||||||
|
|
||||||
SetPTEntry(Position, Entry);
|
|
||||||
|
|
||||||
Position += PageSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AMemoryMapInfo GetMapInfo(long Position)
|
|
||||||
{
|
|
||||||
if (!IsValidPosition(Position))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Position = AMemoryHelper.PageRoundDown(Position);
|
|
||||||
|
|
||||||
PTEntry BaseEntry = GetPTEntry(Position);
|
|
||||||
|
|
||||||
bool IsSameSegment(long Pos)
|
|
||||||
{
|
|
||||||
if (!IsValidPosition(Pos))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PTEntry Entry = GetPTEntry(Pos);
|
|
||||||
|
|
||||||
return Entry.Map == BaseEntry.Map &&
|
|
||||||
Entry.Perm == BaseEntry.Perm &&
|
|
||||||
Entry.Type == BaseEntry.Type &&
|
|
||||||
Entry.Attr == BaseEntry.Attr;
|
|
||||||
}
|
|
||||||
|
|
||||||
long Start = Position;
|
|
||||||
long End = Position + PageSize;
|
|
||||||
|
|
||||||
while (Start > 0 && IsSameSegment(Start - PageSize))
|
|
||||||
{
|
|
||||||
Start -= PageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (End < AddrSize && IsSameSegment(End))
|
|
||||||
{
|
|
||||||
End += PageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
long Size = End - Start;
|
|
||||||
|
|
||||||
return new AMemoryMapInfo(
|
|
||||||
Start,
|
|
||||||
Size,
|
|
||||||
BaseEntry.Type,
|
|
||||||
BaseEntry.Attr,
|
|
||||||
BaseEntry.Perm);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearAttrBit(long Position, long Size, int Bit)
|
|
||||||
{
|
|
||||||
while (Size > 0)
|
|
||||||
{
|
|
||||||
PTEntry Entry = GetPTEntry(Position);
|
|
||||||
|
|
||||||
Entry.Attr &= ~(1 << Bit);
|
|
||||||
|
|
||||||
SetPTEntry(Position, Entry);
|
|
||||||
|
|
||||||
Position += PageSize;
|
|
||||||
Size -= PageSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetAttrBit(long Position, long Size, int Bit)
|
|
||||||
{
|
|
||||||
while (Size > 0)
|
|
||||||
{
|
|
||||||
PTEntry Entry = GetPTEntry(Position);
|
|
||||||
|
|
||||||
Entry.Attr |= (1 << Bit);
|
|
||||||
|
|
||||||
SetPTEntry(Position, Entry);
|
|
||||||
|
|
||||||
Position += PageSize;
|
|
||||||
Size -= PageSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasPermission(long Position, AMemoryPerm Perm)
|
|
||||||
{
|
|
||||||
return GetPTEntry(Position).Perm.HasFlag(Perm);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsValidPosition(long Position)
|
|
||||||
{
|
|
||||||
if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsMapped(long Position)
|
|
||||||
{
|
|
||||||
if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
|
|
||||||
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
|
|
||||||
|
|
||||||
if (PageTable[L0] == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PageTable[L0][L1].Map != PTMap.Unmapped;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PTEntry GetPTEntry(long Position)
|
|
||||||
{
|
|
||||||
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
|
|
||||||
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
|
|
||||||
|
|
||||||
if (PageTable[L0] == null)
|
|
||||||
{
|
|
||||||
return default(PTEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return PageTable[L0][L1];
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetPTEntry(long Position, long Size, PTEntry Entry)
|
|
||||||
{
|
|
||||||
while (Size > 0)
|
|
||||||
{
|
|
||||||
SetPTEntry(Position, Entry);
|
|
||||||
|
|
||||||
Position += PageSize;
|
|
||||||
Size -= PageSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetPTEntry(long Position, long Size, int Type, PTEntry Entry)
|
|
||||||
{
|
|
||||||
while (Size > 0)
|
|
||||||
{
|
|
||||||
if (GetPTEntry(Position).Type == Type)
|
|
||||||
{
|
|
||||||
SetPTEntry(Position, Entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
Position += PageSize;
|
|
||||||
Size -= PageSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetPTEntry(long Position, PTEntry Entry)
|
|
||||||
{
|
|
||||||
if (!IsValidPosition(Position))
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(Position));
|
|
||||||
}
|
|
||||||
|
|
||||||
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
|
|
||||||
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
|
|
||||||
|
|
||||||
if (PageTable[L0] == null)
|
|
||||||
{
|
|
||||||
PageTable[L0] = new PTEntry[PTLvl1Size];
|
|
||||||
}
|
|
||||||
|
|
||||||
PageTable[L0][L1] = Entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace ChocolArm64.Memory
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
public enum AMemoryPerm
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Read = 1 << 0,
|
|
||||||
Write = 1 << 1,
|
|
||||||
Execute = 1 << 2,
|
|
||||||
RW = Read | Write,
|
|
||||||
RX = Read | Execute
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace ChocolArm64.Memory
|
|
||||||
{
|
|
||||||
static class AMemoryWin32
|
|
||||||
{
|
|
||||||
private const int MEM_COMMIT = 0x00001000;
|
|
||||||
private const int MEM_RESERVE = 0x00002000;
|
|
||||||
private const int MEM_WRITE_WATCH = 0x00200000;
|
|
||||||
|
|
||||||
private const int PAGE_READWRITE = 0x04;
|
|
||||||
|
|
||||||
private const int MEM_RELEASE = 0x8000;
|
|
||||||
|
|
||||||
private const int WRITE_WATCH_FLAG_RESET = 1;
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
|
||||||
private static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, int flAllocationType, int flProtect);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
|
||||||
private static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, int dwFreeType);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
|
||||||
private unsafe static extern int GetWriteWatch(
|
|
||||||
int dwFlags,
|
|
||||||
IntPtr lpBaseAddress,
|
|
||||||
IntPtr dwRegionSize,
|
|
||||||
IntPtr[] lpAddresses,
|
|
||||||
long* lpdwCount,
|
|
||||||
long* lpdwGranularity);
|
|
||||||
|
|
||||||
public static IntPtr Allocate(IntPtr Size)
|
|
||||||
{
|
|
||||||
const int Flags = MEM_COMMIT | MEM_RESERVE | MEM_WRITE_WATCH;
|
|
||||||
|
|
||||||
IntPtr Address = VirtualAlloc(IntPtr.Zero, Size, Flags, PAGE_READWRITE);
|
|
||||||
|
|
||||||
if (Address == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Free(IntPtr Address)
|
|
||||||
{
|
|
||||||
VirtualFree(Address, IntPtr.Zero, MEM_RELEASE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe static int GetPageSize(IntPtr Address, IntPtr Size)
|
|
||||||
{
|
|
||||||
IntPtr[] Addresses = new IntPtr[1];
|
|
||||||
|
|
||||||
long Count = Addresses.Length;
|
|
||||||
|
|
||||||
long Granularity;
|
|
||||||
|
|
||||||
GetWriteWatch(
|
|
||||||
0,
|
|
||||||
Address,
|
|
||||||
Size,
|
|
||||||
Addresses,
|
|
||||||
&Count,
|
|
||||||
&Granularity);
|
|
||||||
|
|
||||||
return (int)Granularity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe static void IsRegionModified(
|
|
||||||
IntPtr Address,
|
|
||||||
IntPtr Size,
|
|
||||||
IntPtr[] Addresses,
|
|
||||||
out int AddrCount)
|
|
||||||
{
|
|
||||||
long Count = Addresses.Length;
|
|
||||||
|
|
||||||
long Granularity;
|
|
||||||
|
|
||||||
GetWriteWatch(
|
|
||||||
WRITE_WATCH_FLAG_RESET,
|
|
||||||
Address,
|
|
||||||
Size,
|
|
||||||
Addresses,
|
|
||||||
&Count,
|
|
||||||
&Granularity);
|
|
||||||
|
|
||||||
AddrCount = (int)Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Gal
|
||||||
|
|
||||||
void Create(IGalMemory Memory, long VpAPos, long Key, GalShaderType Type);
|
void Create(IGalMemory Memory, long VpAPos, long Key, GalShaderType Type);
|
||||||
|
|
||||||
|
IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key);
|
||||||
IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key);
|
IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key);
|
||||||
|
|
||||||
void EnsureTextureBinding(string UniformName, int Value);
|
void EnsureTextureBinding(string UniformName, int Value);
|
||||||
|
|
|
@ -279,7 +279,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
if (Stage != null)
|
if (Stage != null)
|
||||||
{
|
{
|
||||||
foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage)
|
foreach (ShaderDeclInfo DeclInfo in Stage.ConstBufferUsage)
|
||||||
{
|
{
|
||||||
long Key = New.ConstBufferKeys[(int)Stage.Type][DeclInfo.Cbuf];
|
long Key = New.ConstBufferKeys[(int)Stage.Type][DeclInfo.Cbuf];
|
||||||
|
|
||||||
|
|
|
@ -72,8 +72,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
return new OGLShaderStage(
|
return new OGLShaderStage(
|
||||||
Type,
|
Type,
|
||||||
Program.Code,
|
Program.Code,
|
||||||
Program.Textures,
|
Program.Uniforms,
|
||||||
Program.Uniforms);
|
Program.Textures);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key)
|
||||||
|
{
|
||||||
|
if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
|
||||||
|
{
|
||||||
|
return Stage.ConstBufferUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Enumerable.Empty<ShaderDeclInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key)
|
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key)
|
||||||
|
@ -224,7 +234,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
if (Stage != null)
|
if (Stage != null)
|
||||||
{
|
{
|
||||||
foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage)
|
foreach (ShaderDeclInfo DeclInfo in Stage.ConstBufferUsage)
|
||||||
{
|
{
|
||||||
int BlockIndex = GL.GetUniformBlockIndex(ProgramHandle, DeclInfo.Name);
|
int BlockIndex = GL.GetUniformBlockIndex(ProgramHandle, DeclInfo.Name);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
|
@ -23,19 +24,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
public string Code { get; private set; }
|
public string Code { get; private set; }
|
||||||
|
|
||||||
public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
|
public IEnumerable<ShaderDeclInfo> ConstBufferUsage { get; private set; }
|
||||||
public IEnumerable<ShaderDeclInfo> UniformUsage { get; private set; }
|
public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
|
||||||
|
|
||||||
public OGLShaderStage(
|
public OGLShaderStage(
|
||||||
GalShaderType Type,
|
GalShaderType Type,
|
||||||
string Code,
|
string Code,
|
||||||
IEnumerable<ShaderDeclInfo> TextureUsage,
|
IEnumerable<ShaderDeclInfo> ConstBufferUsage,
|
||||||
IEnumerable<ShaderDeclInfo> UniformUsage)
|
IEnumerable<ShaderDeclInfo> TextureUsage)
|
||||||
{
|
{
|
||||||
this.Type = Type;
|
this.Type = Type;
|
||||||
this.Code = Code;
|
this.Code = Code;
|
||||||
this.TextureUsage = TextureUsage;
|
this.ConstBufferUsage = ConstBufferUsage;
|
||||||
this.UniformUsage = UniformUsage;
|
this.TextureUsage = TextureUsage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Compile()
|
public void Compile()
|
||||||
|
|
|
@ -1,177 +0,0 @@
|
||||||
using ChocolArm64.Exceptions;
|
|
||||||
using ChocolArm64.Memory;
|
|
||||||
using Ryujinx.HLE.Logging;
|
|
||||||
using Ryujinx.HLE.OsHle;
|
|
||||||
using Ryujinx.HLE.OsHle.Handles;
|
|
||||||
using Ryujinx.HLE.Resource;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Font
|
|
||||||
{
|
|
||||||
public class SharedFontManager
|
|
||||||
{
|
|
||||||
private const uint SharedMemorySize = 0x1100000;
|
|
||||||
private Logger Log;
|
|
||||||
|
|
||||||
private string FontsPath;
|
|
||||||
|
|
||||||
private object ShMemLock;
|
|
||||||
|
|
||||||
private (AMemory, long, long)[] ShMemPositions;
|
|
||||||
|
|
||||||
private Dictionary<SharedFontType, byte[]> FontData;
|
|
||||||
|
|
||||||
private uint[] LoadedFonts;
|
|
||||||
|
|
||||||
public SharedFontManager(Logger Log, string SystemPath)
|
|
||||||
{
|
|
||||||
this.Log = Log;
|
|
||||||
this.FontsPath = Path.Combine(SystemPath, "fonts");
|
|
||||||
|
|
||||||
ShMemLock = new object();
|
|
||||||
|
|
||||||
ShMemPositions = new(AMemory, long, long)[0];
|
|
||||||
|
|
||||||
FontData = new Dictionary<SharedFontType, byte[]>()
|
|
||||||
{
|
|
||||||
{ SharedFontType.JapanUsEurope, GetData("FontStandard") },
|
|
||||||
{ SharedFontType.SimplifiedChinese, GetData("FontChineseSimplified") },
|
|
||||||
{ SharedFontType.SimplifiedChineseEx, GetData("FontExtendedChineseSimplified") },
|
|
||||||
{ SharedFontType.TraditionalChinese, GetData("FontChineseTraditional") },
|
|
||||||
{ SharedFontType.Korean, GetData("FontKorean") },
|
|
||||||
{ SharedFontType.NintendoEx, GetData("FontNintendoExtended") }
|
|
||||||
};
|
|
||||||
|
|
||||||
int FontMemoryUsage = 0;
|
|
||||||
foreach (byte[] data in FontData.Values)
|
|
||||||
{
|
|
||||||
FontMemoryUsage += data.Length;
|
|
||||||
FontMemoryUsage += 0x8;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FontMemoryUsage > SharedMemorySize)
|
|
||||||
{
|
|
||||||
throw new InvalidSystemResourceException($"The sum of all fonts size exceed the shared memory size. Please make sure that the fonts don't exceed {SharedMemorySize} bytes in total. (actual size: {FontMemoryUsage} bytes)");
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadedFonts = new uint[FontData.Count];
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] GetData(string FontName)
|
|
||||||
{
|
|
||||||
string FontFilePath = Path.Combine(FontsPath, $"{FontName}.ttf");
|
|
||||||
if (File.Exists(FontFilePath))
|
|
||||||
{
|
|
||||||
return File.ReadAllBytes(FontFilePath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidSystemResourceException($"Font \"{FontName}.ttf\" not found. Please provide it in \"{FontsPath}\".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MapFont(SharedFontType FontType, AMemory Memory, long Position)
|
|
||||||
{
|
|
||||||
uint SharedMemoryAddressOffset = GetSharedMemoryAddressOffset(FontType);
|
|
||||||
// TODO: find what are the 8 bytes before the font
|
|
||||||
Memory.WriteUInt64(Position + SharedMemoryAddressOffset - 8, 0);
|
|
||||||
Memory.WriteBytes(Position + SharedMemoryAddressOffset, FontData[FontType]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PropagateNewMapFont(SharedFontType Type)
|
|
||||||
{
|
|
||||||
lock (ShMemLock)
|
|
||||||
{
|
|
||||||
foreach ((AMemory Memory, long Position, long Size) in ShMemPositions)
|
|
||||||
{
|
|
||||||
AMemoryMapInfo MemoryInfo = Memory.Manager.GetMapInfo(Position);
|
|
||||||
|
|
||||||
if (MemoryInfo == null)
|
|
||||||
{
|
|
||||||
throw new VmmPageFaultException(Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The memory is read only, we need to changes that to add the new font
|
|
||||||
AMemoryPerm originalPerms = MemoryInfo.Perm;
|
|
||||||
Memory.Manager.Reprotect(Position, Size, AMemoryPerm.RW);
|
|
||||||
MapFont(Type, Memory, Position);
|
|
||||||
Memory.Manager.Reprotect(Position, Size, originalPerms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ShMemMap(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
HSharedMem SharedMem = (HSharedMem)sender;
|
|
||||||
|
|
||||||
lock (ShMemLock)
|
|
||||||
{
|
|
||||||
ShMemPositions = SharedMem.GetVirtualPositions();
|
|
||||||
|
|
||||||
(AMemory Memory, long Position, long Size) = ShMemPositions[ShMemPositions.Length - 1];
|
|
||||||
|
|
||||||
for (int Type = 0; Type < LoadedFonts.Length; Type++)
|
|
||||||
{
|
|
||||||
if (LoadedFonts[(int)Type] == 1)
|
|
||||||
{
|
|
||||||
MapFont((SharedFontType)Type, Memory, Position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ShMemUnmap(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
HSharedMem SharedMem = (HSharedMem)sender;
|
|
||||||
|
|
||||||
lock (ShMemLock)
|
|
||||||
{
|
|
||||||
ShMemPositions = SharedMem.GetVirtualPositions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Load(SharedFontType FontType)
|
|
||||||
{
|
|
||||||
if (LoadedFonts[(int)FontType] == 0)
|
|
||||||
{
|
|
||||||
PropagateNewMapFont(FontType);
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadedFonts[(int)FontType] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint GetLoadState(SharedFontType FontType)
|
|
||||||
{
|
|
||||||
if (LoadedFonts[(int)FontType] != 1)
|
|
||||||
{
|
|
||||||
// Some games don't request a load, so we need to load it here.
|
|
||||||
Load(FontType);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return LoadedFonts[(int)FontType];
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint GetFontSize(SharedFontType FontType)
|
|
||||||
{
|
|
||||||
return (uint)FontData[FontType].Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint GetSharedMemoryAddressOffset(SharedFontType FontType)
|
|
||||||
{
|
|
||||||
uint Pos = 0x8;
|
|
||||||
|
|
||||||
for (SharedFontType Type = SharedFontType.JapanUsEurope; Type < FontType; Type++)
|
|
||||||
{
|
|
||||||
Pos += GetFontSize(Type);
|
|
||||||
Pos += 0x8;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count => FontData.Count;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -109,7 +109,7 @@ namespace Ryujinx.HLE.Gpu.Engines
|
||||||
Gpu.Renderer.Shader.BindProgram();
|
Gpu.Renderer.Shader.BindProgram();
|
||||||
|
|
||||||
UploadTextures(Vmm, State, Keys);
|
UploadTextures(Vmm, State, Keys);
|
||||||
UploadConstBuffers(Vmm, State);
|
UploadConstBuffers(Vmm, State, Keys);
|
||||||
UploadVertexArrays(Vmm, State);
|
UploadVertexArrays(Vmm, State);
|
||||||
|
|
||||||
DispatchRender(Vmm, State);
|
DispatchRender(Vmm, State);
|
||||||
|
@ -426,6 +426,12 @@ namespace Ryujinx.HLE.Gpu.Engines
|
||||||
|
|
||||||
Key = Vmm.GetPhysicalAddress(Key);
|
Key = Vmm.GetPhysicalAddress(Key);
|
||||||
|
|
||||||
|
if (Key == -1)
|
||||||
|
{
|
||||||
|
//FIXME: Should'nt ignore invalid addresses.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (IsFrameBufferPosition(Key))
|
if (IsFrameBufferPosition(Key))
|
||||||
{
|
{
|
||||||
//This texture is a frame buffer texture,
|
//This texture is a frame buffer texture,
|
||||||
|
@ -465,24 +471,29 @@ namespace Ryujinx.HLE.Gpu.Engines
|
||||||
Gpu.Renderer.Texture.SetSampler(Sampler);
|
Gpu.Renderer.Texture.SetSampler(Sampler);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UploadConstBuffers(NvGpuVmm Vmm, GalPipelineState State)
|
private void UploadConstBuffers(NvGpuVmm Vmm, GalPipelineState State, long[] Keys)
|
||||||
{
|
{
|
||||||
for (int Stage = 0; Stage < State.ConstBufferKeys.Length; Stage++)
|
for (int Stage = 0; Stage < Keys.Length; Stage++)
|
||||||
{
|
{
|
||||||
for (int Index = 0; Index < State.ConstBufferKeys[Stage].Length; Index++)
|
foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.Shader.GetConstBufferUsage(Keys[Stage]))
|
||||||
{
|
{
|
||||||
ConstBuffer Cb = ConstBuffers[Stage][Index];
|
ConstBuffer Cb = ConstBuffers[Stage][DeclInfo.Cbuf];
|
||||||
|
|
||||||
long Key = Cb.Position;
|
if (!Cb.Enabled)
|
||||||
|
|
||||||
if (Cb.Enabled && QueryKeyUpload(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer))
|
|
||||||
{
|
{
|
||||||
IntPtr Source = Vmm.GetHostAddress(Key, Cb.Size);
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
long Key = Vmm.GetPhysicalAddress(Cb.Position);
|
||||||
|
|
||||||
|
if (QueryKeyUpload(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer))
|
||||||
|
{
|
||||||
|
IntPtr Source = Vmm.GetHostAddress(Cb.Position, Cb.Size);
|
||||||
|
|
||||||
Gpu.Renderer.Buffer.SetData(Key, Cb.Size, Source);
|
Gpu.Renderer.Buffer.SetData(Key, Cb.Size, Source);
|
||||||
}
|
}
|
||||||
|
|
||||||
State.ConstBufferKeys[Stage][Index] = Key;
|
State.ConstBufferKeys[Stage][DeclInfo.Cbuf] = Key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -668,11 +679,13 @@ namespace Ryujinx.HLE.Gpu.Engines
|
||||||
|
|
||||||
long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress);
|
long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress);
|
||||||
|
|
||||||
|
long CbKey = Vmm.GetPhysicalAddress(Position);
|
||||||
|
|
||||||
int Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize);
|
int Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize);
|
||||||
|
|
||||||
if (!Gpu.Renderer.Buffer.IsCached(Position, Size))
|
if (!Gpu.Renderer.Buffer.IsCached(CbKey, Size))
|
||||||
{
|
{
|
||||||
Gpu.Renderer.Buffer.Create(Position, Size);
|
Gpu.Renderer.Buffer.Create(CbKey, Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstBuffer Cb = ConstBuffers[Stage][Index];
|
ConstBuffer Cb = ConstBuffers[Stage][Index];
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Graphics.Gal;
|
using Ryujinx.Graphics.Gal;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Gpu.Memory
|
namespace Ryujinx.HLE.Gpu.Memory
|
||||||
{
|
{
|
||||||
|
@ -26,18 +25,6 @@ namespace Ryujinx.HLE.Gpu.Memory
|
||||||
|
|
||||||
public AMemory Memory { get; private set; }
|
public AMemory Memory { get; private set; }
|
||||||
|
|
||||||
private struct MappedMemory
|
|
||||||
{
|
|
||||||
public long Size;
|
|
||||||
|
|
||||||
public MappedMemory(long Size)
|
|
||||||
{
|
|
||||||
this.Size = Size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConcurrentDictionary<long, MappedMemory> Maps;
|
|
||||||
|
|
||||||
private NvGpuVmmCache Cache;
|
private NvGpuVmmCache Cache;
|
||||||
|
|
||||||
private const long PteUnmapped = -1;
|
private const long PteUnmapped = -1;
|
||||||
|
@ -49,8 +36,6 @@ namespace Ryujinx.HLE.Gpu.Memory
|
||||||
{
|
{
|
||||||
this.Memory = Memory;
|
this.Memory = Memory;
|
||||||
|
|
||||||
Maps = new ConcurrentDictionary<long, MappedMemory>();
|
|
||||||
|
|
||||||
Cache = new NvGpuVmmCache();
|
Cache = new NvGpuVmmCache();
|
||||||
|
|
||||||
PageTable = new long[PTLvl0Size][];
|
PageTable = new long[PTLvl0Size][];
|
||||||
|
@ -60,14 +45,6 @@ namespace Ryujinx.HLE.Gpu.Memory
|
||||||
{
|
{
|
||||||
lock (PageTable)
|
lock (PageTable)
|
||||||
{
|
{
|
||||||
for (long Offset = 0; Offset < Size; Offset += PageSize)
|
|
||||||
{
|
|
||||||
if (GetPte(VA + Offset) != PteReserved)
|
|
||||||
{
|
|
||||||
return Map(PA, Size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (long Offset = 0; Offset < Size; Offset += PageSize)
|
for (long Offset = 0; Offset < Size; Offset += PageSize)
|
||||||
{
|
{
|
||||||
SetPte(VA + Offset, PA + Offset);
|
SetPte(VA + Offset, PA + Offset);
|
||||||
|
@ -85,10 +62,6 @@ namespace Ryujinx.HLE.Gpu.Memory
|
||||||
|
|
||||||
if (VA != -1)
|
if (VA != -1)
|
||||||
{
|
{
|
||||||
MappedMemory Map = new MappedMemory(Size);
|
|
||||||
|
|
||||||
Maps.AddOrUpdate(VA, Map, (Key, Old) => Map);
|
|
||||||
|
|
||||||
for (long Offset = 0; Offset < Size; Offset += PageSize)
|
for (long Offset = 0; Offset < Size; Offset += PageSize)
|
||||||
{
|
{
|
||||||
SetPte(VA + Offset, PA + Offset);
|
SetPte(VA + Offset, PA + Offset);
|
||||||
|
@ -99,19 +72,7 @@ namespace Ryujinx.HLE.Gpu.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Unmap(long VA)
|
public long ReserveFixed(long VA, long Size)
|
||||||
{
|
|
||||||
if (Maps.TryRemove(VA, out MappedMemory Map))
|
|
||||||
{
|
|
||||||
Free(VA, Map.Size);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long Reserve(long VA, long Size, long Align)
|
|
||||||
{
|
{
|
||||||
lock (PageTable)
|
lock (PageTable)
|
||||||
{
|
{
|
||||||
|
@ -119,7 +80,7 @@ namespace Ryujinx.HLE.Gpu.Memory
|
||||||
{
|
{
|
||||||
if (IsPageInUse(VA + Offset))
|
if (IsPageInUse(VA + Offset))
|
||||||
{
|
{
|
||||||
return Reserve(Size, Align);
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +124,9 @@ namespace Ryujinx.HLE.Gpu.Memory
|
||||||
|
|
||||||
private long GetFreePosition(long Size, long Align = 1)
|
private long GetFreePosition(long Size, long Align = 1)
|
||||||
{
|
{
|
||||||
long Position = 0;
|
//Note: Address 0 is not considered valid by the driver,
|
||||||
|
//when 0 is returned it's considered a mapping error.
|
||||||
|
long Position = PageSize;
|
||||||
long FreeSize = 0;
|
long FreeSize = 0;
|
||||||
|
|
||||||
if (Align < 1)
|
if (Align < 1)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
|
using Ryujinx.HLE.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
@ -129,14 +130,11 @@ namespace Ryujinx.HLE.Gpu.Memory
|
||||||
{
|
{
|
||||||
(bool[] Modified, long ModifiedCount) = Memory.IsRegionModified(PA, Size);
|
(bool[] Modified, long ModifiedCount) = Memory.IsRegionModified(PA, Size);
|
||||||
|
|
||||||
if (Modified == null)
|
PA = Memory.GetPhysicalAddress(PA);
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClearCachedPagesIfNeeded();
|
ClearCachedPagesIfNeeded();
|
||||||
|
|
||||||
long PageSize = Memory.GetHostPageSize();
|
long PageSize = AMemory.PageSize;
|
||||||
|
|
||||||
EnsureResidencyInitialized(PageSize);
|
EnsureResidencyInitialized(PageSize);
|
||||||
|
|
||||||
|
@ -159,9 +157,9 @@ namespace Ryujinx.HLE.Gpu.Memory
|
||||||
|
|
||||||
while (PA < PAEnd)
|
while (PA < PAEnd)
|
||||||
{
|
{
|
||||||
long Key = PA & ~Mask;
|
long Key = PA & ~AMemory.PageMask;
|
||||||
|
|
||||||
long PAPgEnd = Math.Min((PA + PageSize) & ~Mask, PAEnd);
|
long PAPgEnd = Math.Min((PA + AMemory.PageSize) & ~AMemory.PageMask, PAEnd);
|
||||||
|
|
||||||
bool IsCached = Cache.TryGetValue(Key, out CachedPage Cp);
|
bool IsCached = Cache.TryGetValue(Key, out CachedPage Cp);
|
||||||
|
|
||||||
|
@ -228,7 +226,7 @@ namespace Ryujinx.HLE.Gpu.Memory
|
||||||
{
|
{
|
||||||
if (Residency == null)
|
if (Residency == null)
|
||||||
{
|
{
|
||||||
Residency = new HashSet<long>[AMemoryMgr.RamSize / PageSize];
|
Residency = new HashSet<long>[DeviceMemory.RamSize / PageSize];
|
||||||
|
|
||||||
for (int i = 0; i < Residency.Length; i++)
|
for (int i = 0; i < Residency.Length; i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -72,7 +72,7 @@ namespace Ryujinx.HLE.Gpu.Texture
|
||||||
{
|
{
|
||||||
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||||
|
|
||||||
byte Pixel = CpuMem.ReadByteUnchecked(Position + Offset);
|
byte Pixel = CpuMem.ReadByte(Position + Offset);
|
||||||
|
|
||||||
*(BuffPtr + OutOffs) = Pixel;
|
*(BuffPtr + OutOffs) = Pixel;
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ namespace Ryujinx.HLE.Gpu.Texture
|
||||||
{
|
{
|
||||||
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||||
|
|
||||||
uint Pixel = (uint)CpuMem.ReadInt16Unchecked(Position + Offset);
|
uint Pixel = (uint)CpuMem.ReadInt16(Position + Offset);
|
||||||
|
|
||||||
Pixel = (Pixel & 0x001f) << 11 |
|
Pixel = (Pixel & 0x001f) << 11 |
|
||||||
(Pixel & 0x03e0) << 1 |
|
(Pixel & 0x03e0) << 1 |
|
||||||
|
@ -143,7 +143,7 @@ namespace Ryujinx.HLE.Gpu.Texture
|
||||||
{
|
{
|
||||||
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||||
|
|
||||||
uint Pixel = (uint)CpuMem.ReadInt16Unchecked(Position + Offset);
|
uint Pixel = (uint)CpuMem.ReadInt16(Position + Offset);
|
||||||
|
|
||||||
Pixel = (Pixel & 0x001f) << 11 |
|
Pixel = (Pixel & 0x001f) << 11 |
|
||||||
(Pixel & 0x07e0) |
|
(Pixel & 0x07e0) |
|
||||||
|
@ -180,7 +180,7 @@ namespace Ryujinx.HLE.Gpu.Texture
|
||||||
{
|
{
|
||||||
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||||
|
|
||||||
short Pixel = CpuMem.ReadInt16Unchecked(Position + Offset);
|
short Pixel = CpuMem.ReadInt16(Position + Offset);
|
||||||
|
|
||||||
*(short*)(BuffPtr + OutOffs) = Pixel;
|
*(short*)(BuffPtr + OutOffs) = Pixel;
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ namespace Ryujinx.HLE.Gpu.Texture
|
||||||
{
|
{
|
||||||
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||||
|
|
||||||
int Pixel = CpuMem.ReadInt32Unchecked(Position + Offset);
|
int Pixel = CpuMem.ReadInt32(Position + Offset);
|
||||||
|
|
||||||
*(int*)(BuffPtr + OutOffs) = Pixel;
|
*(int*)(BuffPtr + OutOffs) = Pixel;
|
||||||
|
|
||||||
|
@ -246,7 +246,7 @@ namespace Ryujinx.HLE.Gpu.Texture
|
||||||
{
|
{
|
||||||
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||||
|
|
||||||
long Pixel = CpuMem.ReadInt64Unchecked(Position + Offset);
|
long Pixel = CpuMem.ReadInt64(Position + Offset);
|
||||||
|
|
||||||
*(long*)(BuffPtr + OutOffs) = Pixel;
|
*(long*)(BuffPtr + OutOffs) = Pixel;
|
||||||
|
|
||||||
|
@ -279,8 +279,8 @@ namespace Ryujinx.HLE.Gpu.Texture
|
||||||
{
|
{
|
||||||
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||||
|
|
||||||
long PxLow = CpuMem.ReadInt64Unchecked(Position + Offset + 0);
|
long PxLow = CpuMem.ReadInt64(Position + Offset + 0);
|
||||||
long PxHigh = CpuMem.ReadInt64Unchecked(Position + Offset + 8);
|
long PxHigh = CpuMem.ReadInt64(Position + Offset + 8);
|
||||||
|
|
||||||
*(long*)(BuffPtr + OutOffs + 0) = PxLow;
|
*(long*)(BuffPtr + OutOffs + 0) = PxLow;
|
||||||
*(long*)(BuffPtr + OutOffs + 8) = PxHigh;
|
*(long*)(BuffPtr + OutOffs + 8) = PxHigh;
|
||||||
|
@ -314,7 +314,7 @@ namespace Ryujinx.HLE.Gpu.Texture
|
||||||
{
|
{
|
||||||
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||||
|
|
||||||
long Tile = CpuMem.ReadInt64Unchecked(Position + Offset);
|
long Tile = CpuMem.ReadInt64(Position + Offset);
|
||||||
|
|
||||||
*(long*)(BuffPtr + OutOffs) = Tile;
|
*(long*)(BuffPtr + OutOffs) = Tile;
|
||||||
|
|
||||||
|
@ -347,8 +347,8 @@ namespace Ryujinx.HLE.Gpu.Texture
|
||||||
{
|
{
|
||||||
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
|
||||||
|
|
||||||
long Tile0 = CpuMem.ReadInt64Unchecked(Position + Offset + 0);
|
long Tile0 = CpuMem.ReadInt64(Position + Offset + 0);
|
||||||
long Tile1 = CpuMem.ReadInt64Unchecked(Position + Offset + 8);
|
long Tile1 = CpuMem.ReadInt64(Position + Offset + 8);
|
||||||
|
|
||||||
*(long*)(BuffPtr + OutOffs + 0) = Tile0;
|
*(long*)(BuffPtr + OutOffs + 0) = Tile0;
|
||||||
*(long*)(BuffPtr + OutOffs + 8) = Tile1;
|
*(long*)(BuffPtr + OutOffs + 8) = Tile1;
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace Ryujinx.HLE.Gpu.Texture
|
||||||
|
|
||||||
int Pixel = *(int*)(BuffPtr + InOffs);
|
int Pixel = *(int*)(BuffPtr + InOffs);
|
||||||
|
|
||||||
CpuMem.WriteInt32Unchecked(Position + Offset, Pixel);
|
CpuMem.WriteInt32(Position + Offset, Pixel);
|
||||||
|
|
||||||
InOffs += 4;
|
InOffs += 4;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
using ChocolArm64.Memory;
|
using Ryujinx.HLE.OsHle;
|
||||||
using Ryujinx.HLE.Logging;
|
|
||||||
using Ryujinx.HLE.OsHle;
|
|
||||||
using Ryujinx.HLE.OsHle.Handles;
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Input
|
namespace Ryujinx.HLE.Input
|
||||||
|
@ -63,57 +60,18 @@ namespace Ryujinx.HLE.Input
|
||||||
|
|
||||||
private const int HidEntryCount = 17;
|
private const int HidEntryCount = 17;
|
||||||
|
|
||||||
private Logger Log;
|
private Switch Device;
|
||||||
|
|
||||||
private object ShMemLock;
|
private long HidPosition;
|
||||||
|
|
||||||
private (AMemory, long, long)[] ShMemPositions;
|
public Hid(Switch Device, long HidPosition)
|
||||||
|
|
||||||
public Hid(Logger Log)
|
|
||||||
{
|
{
|
||||||
this.Log = Log;
|
this.Device = Device;
|
||||||
|
this.HidPosition = HidPosition;
|
||||||
|
|
||||||
ShMemLock = new object();
|
Device.Memory.FillWithZeros(HidPosition, Horizon.HidSize);
|
||||||
|
|
||||||
ShMemPositions = new (AMemory, long, long)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ShMemMap(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
HSharedMem SharedMem = (HSharedMem)sender;
|
|
||||||
|
|
||||||
lock (ShMemLock)
|
|
||||||
{
|
|
||||||
ShMemPositions = SharedMem.GetVirtualPositions();
|
|
||||||
|
|
||||||
(AMemory Memory, long Position, long Size) = ShMemPositions[ShMemPositions.Length - 1];
|
|
||||||
|
|
||||||
for (long Offset = 0; Offset < Horizon.HidSize; Offset += 8)
|
|
||||||
{
|
|
||||||
Memory.WriteInt64Unchecked(Position + Offset, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.PrintInfo(LogClass.Hid, $"HID shared memory successfully mapped to 0x{Position:x16}!");
|
|
||||||
|
|
||||||
Init(Memory, Position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ShMemUnmap(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
HSharedMem SharedMem = (HSharedMem)sender;
|
|
||||||
|
|
||||||
lock (ShMemLock)
|
|
||||||
{
|
|
||||||
ShMemPositions = SharedMem.GetVirtualPositions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Init(AMemory Memory, long Position)
|
|
||||||
{
|
|
||||||
InitializeJoyconPair(
|
InitializeJoyconPair(
|
||||||
Memory,
|
|
||||||
Position,
|
|
||||||
JoyConColor.Body_Neon_Red,
|
JoyConColor.Body_Neon_Red,
|
||||||
JoyConColor.Buttons_Neon_Red,
|
JoyConColor.Buttons_Neon_Red,
|
||||||
JoyConColor.Body_Neon_Blue,
|
JoyConColor.Body_Neon_Blue,
|
||||||
|
@ -121,14 +79,12 @@ namespace Ryujinx.HLE.Input
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeJoyconPair(
|
private void InitializeJoyconPair(
|
||||||
AMemory Memory,
|
|
||||||
long Position,
|
|
||||||
JoyConColor LeftColorBody,
|
JoyConColor LeftColorBody,
|
||||||
JoyConColor LeftColorButtons,
|
JoyConColor LeftColorButtons,
|
||||||
JoyConColor RightColorBody,
|
JoyConColor RightColorBody,
|
||||||
JoyConColor RightColorButtons)
|
JoyConColor RightColorButtons)
|
||||||
{
|
{
|
||||||
long BaseControllerOffset = Position + HidControllersOffset + 8 * HidControllerSize;
|
long BaseControllerOffset = HidPosition + HidControllersOffset + 8 * HidControllerSize;
|
||||||
|
|
||||||
HidControllerType Type = HidControllerType.ControllerType_Handheld;
|
HidControllerType Type = HidControllerType.ControllerType_Handheld;
|
||||||
|
|
||||||
|
@ -142,20 +98,20 @@ namespace Ryujinx.HLE.Input
|
||||||
|
|
||||||
HidControllerColorDesc SplitColorDesc = 0;
|
HidControllerColorDesc SplitColorDesc = 0;
|
||||||
|
|
||||||
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x0, (int)Type);
|
Device.Memory.WriteInt32(BaseControllerOffset + 0x0, (int)Type);
|
||||||
|
|
||||||
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x4, IsHalf ? 1 : 0);
|
Device.Memory.WriteInt32(BaseControllerOffset + 0x4, IsHalf ? 1 : 0);
|
||||||
|
|
||||||
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x8, (int)SingleColorDesc);
|
Device.Memory.WriteInt32(BaseControllerOffset + 0x8, (int)SingleColorDesc);
|
||||||
Memory.WriteInt32Unchecked(BaseControllerOffset + 0xc, (int)SingleColorBody);
|
Device.Memory.WriteInt32(BaseControllerOffset + 0xc, (int)SingleColorBody);
|
||||||
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x10, (int)SingleColorButtons);
|
Device.Memory.WriteInt32(BaseControllerOffset + 0x10, (int)SingleColorButtons);
|
||||||
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x14, (int)SplitColorDesc);
|
Device.Memory.WriteInt32(BaseControllerOffset + 0x14, (int)SplitColorDesc);
|
||||||
|
|
||||||
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x18, (int)LeftColorBody);
|
Device.Memory.WriteInt32(BaseControllerOffset + 0x18, (int)LeftColorBody);
|
||||||
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x1c, (int)LeftColorButtons);
|
Device.Memory.WriteInt32(BaseControllerOffset + 0x1c, (int)LeftColorButtons);
|
||||||
|
|
||||||
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x20, (int)RightColorBody);
|
Device.Memory.WriteInt32(BaseControllerOffset + 0x20, (int)RightColorBody);
|
||||||
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x24, (int)RightColorButtons);
|
Device.Memory.WriteInt32(BaseControllerOffset + 0x24, (int)RightColorButtons);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetJoyconButton(
|
public void SetJoyconButton(
|
||||||
|
@ -165,107 +121,95 @@ namespace Ryujinx.HLE.Input
|
||||||
HidJoystickPosition LeftStick,
|
HidJoystickPosition LeftStick,
|
||||||
HidJoystickPosition RightStick)
|
HidJoystickPosition RightStick)
|
||||||
{
|
{
|
||||||
lock (ShMemLock)
|
long ControllerOffset = HidPosition + HidControllersOffset;
|
||||||
{
|
|
||||||
foreach ((AMemory Memory, long Position, long Size) in ShMemPositions)
|
|
||||||
{
|
|
||||||
long ControllerOffset = Position + HidControllersOffset;
|
|
||||||
|
|
||||||
ControllerOffset += (int)ControllerId * HidControllerSize;
|
ControllerOffset += (int)ControllerId * HidControllerSize;
|
||||||
|
|
||||||
ControllerOffset += HidControllerHeaderSize;
|
ControllerOffset += HidControllerHeaderSize;
|
||||||
|
|
||||||
ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize;
|
ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize;
|
||||||
|
|
||||||
long LastEntry = Memory.ReadInt64Unchecked(ControllerOffset + 0x10);
|
long LastEntry = Device.Memory.ReadInt64(ControllerOffset + 0x10);
|
||||||
|
|
||||||
long CurrEntry = (LastEntry + 1) % HidEntryCount;
|
long CurrEntry = (LastEntry + 1) % HidEntryCount;
|
||||||
|
|
||||||
long Timestamp = GetTimestamp();
|
long Timestamp = GetTimestamp();
|
||||||
|
|
||||||
Memory.WriteInt64Unchecked(ControllerOffset + 0x0, Timestamp);
|
Device.Memory.WriteInt64(ControllerOffset + 0x0, Timestamp);
|
||||||
Memory.WriteInt64Unchecked(ControllerOffset + 0x8, HidEntryCount);
|
Device.Memory.WriteInt64(ControllerOffset + 0x8, HidEntryCount);
|
||||||
Memory.WriteInt64Unchecked(ControllerOffset + 0x10, CurrEntry);
|
Device.Memory.WriteInt64(ControllerOffset + 0x10, CurrEntry);
|
||||||
Memory.WriteInt64Unchecked(ControllerOffset + 0x18, HidEntryCount - 1);
|
Device.Memory.WriteInt64(ControllerOffset + 0x18, HidEntryCount - 1);
|
||||||
|
|
||||||
ControllerOffset += HidControllersLayoutHeaderSize;
|
ControllerOffset += HidControllersLayoutHeaderSize;
|
||||||
|
|
||||||
long LastEntryOffset = ControllerOffset + LastEntry * HidControllersInputEntrySize;
|
long LastEntryOffset = ControllerOffset + LastEntry * HidControllersInputEntrySize;
|
||||||
|
|
||||||
ControllerOffset += CurrEntry * HidControllersInputEntrySize;
|
ControllerOffset += CurrEntry * HidControllersInputEntrySize;
|
||||||
|
|
||||||
long SampleCounter = Memory.ReadInt64Unchecked(LastEntryOffset) + 1;
|
long SampleCounter = Device.Memory.ReadInt64(LastEntryOffset) + 1;
|
||||||
|
|
||||||
Memory.WriteInt64Unchecked(ControllerOffset + 0x0, SampleCounter);
|
Device.Memory.WriteInt64(ControllerOffset + 0x0, SampleCounter);
|
||||||
Memory.WriteInt64Unchecked(ControllerOffset + 0x8, SampleCounter);
|
Device.Memory.WriteInt64(ControllerOffset + 0x8, SampleCounter);
|
||||||
|
|
||||||
Memory.WriteInt64Unchecked(ControllerOffset + 0x10, (uint)Buttons);
|
Device.Memory.WriteInt64(ControllerOffset + 0x10, (uint)Buttons);
|
||||||
|
|
||||||
Memory.WriteInt32Unchecked(ControllerOffset + 0x18, LeftStick.DX);
|
Device.Memory.WriteInt32(ControllerOffset + 0x18, LeftStick.DX);
|
||||||
Memory.WriteInt32Unchecked(ControllerOffset + 0x1c, LeftStick.DY);
|
Device.Memory.WriteInt32(ControllerOffset + 0x1c, LeftStick.DY);
|
||||||
|
|
||||||
Memory.WriteInt32Unchecked(ControllerOffset + 0x20, RightStick.DX);
|
Device.Memory.WriteInt32(ControllerOffset + 0x20, RightStick.DX);
|
||||||
Memory.WriteInt32Unchecked(ControllerOffset + 0x24, RightStick.DY);
|
Device.Memory.WriteInt32(ControllerOffset + 0x24, RightStick.DY);
|
||||||
|
|
||||||
Memory.WriteInt64Unchecked(ControllerOffset + 0x28,
|
Device.Memory.WriteInt64(ControllerOffset + 0x28,
|
||||||
(uint)HidControllerConnState.Controller_State_Connected |
|
(uint)HidControllerConnState.Controller_State_Connected |
|
||||||
(uint)HidControllerConnState.Controller_State_Wired);
|
(uint)HidControllerConnState.Controller_State_Wired);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTouchPoints(params HidTouchPoint[] Points)
|
public void SetTouchPoints(params HidTouchPoint[] Points)
|
||||||
{
|
{
|
||||||
lock (ShMemLock)
|
long TouchScreenOffset = HidPosition + HidTouchScreenOffset;
|
||||||
|
|
||||||
|
long LastEntry = Device.Memory.ReadInt64(TouchScreenOffset + 0x10);
|
||||||
|
|
||||||
|
long CurrEntry = (LastEntry + 1) % HidEntryCount;
|
||||||
|
|
||||||
|
long Timestamp = GetTimestamp();
|
||||||
|
|
||||||
|
Device.Memory.WriteInt64(TouchScreenOffset + 0x0, Timestamp);
|
||||||
|
Device.Memory.WriteInt64(TouchScreenOffset + 0x8, HidEntryCount);
|
||||||
|
Device.Memory.WriteInt64(TouchScreenOffset + 0x10, CurrEntry);
|
||||||
|
Device.Memory.WriteInt64(TouchScreenOffset + 0x18, HidEntryCount - 1);
|
||||||
|
Device.Memory.WriteInt64(TouchScreenOffset + 0x20, Timestamp);
|
||||||
|
|
||||||
|
long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize;
|
||||||
|
|
||||||
|
long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize;
|
||||||
|
|
||||||
|
long SampleCounter = Device.Memory.ReadInt64(LastEntryOffset) + 1;
|
||||||
|
|
||||||
|
TouchEntryOffset += CurrEntry * HidTouchEntrySize;
|
||||||
|
|
||||||
|
Device.Memory.WriteInt64(TouchEntryOffset + 0x0, SampleCounter);
|
||||||
|
Device.Memory.WriteInt64(TouchEntryOffset + 0x8, Points.Length);
|
||||||
|
|
||||||
|
TouchEntryOffset += HidTouchEntryHeaderSize;
|
||||||
|
|
||||||
|
const int Padding = 0;
|
||||||
|
|
||||||
|
int Index = 0;
|
||||||
|
|
||||||
|
foreach (HidTouchPoint Point in Points)
|
||||||
{
|
{
|
||||||
foreach ((AMemory Memory, long Position, long Size) in ShMemPositions)
|
Device.Memory.WriteInt64(TouchEntryOffset + 0x0, Timestamp);
|
||||||
{
|
Device.Memory.WriteInt32(TouchEntryOffset + 0x8, Padding);
|
||||||
long TouchScreenOffset = Position + HidTouchScreenOffset;
|
Device.Memory.WriteInt32(TouchEntryOffset + 0xc, Index++);
|
||||||
|
Device.Memory.WriteInt32(TouchEntryOffset + 0x10, Point.X);
|
||||||
|
Device.Memory.WriteInt32(TouchEntryOffset + 0x14, Point.Y);
|
||||||
|
Device.Memory.WriteInt32(TouchEntryOffset + 0x18, Point.DiameterX);
|
||||||
|
Device.Memory.WriteInt32(TouchEntryOffset + 0x1c, Point.DiameterY);
|
||||||
|
Device.Memory.WriteInt32(TouchEntryOffset + 0x20, Point.Angle);
|
||||||
|
Device.Memory.WriteInt32(TouchEntryOffset + 0x24, Padding);
|
||||||
|
|
||||||
long LastEntry = Memory.ReadInt64Unchecked(TouchScreenOffset + 0x10);
|
TouchEntryOffset += HidTouchEntryTouchSize;
|
||||||
|
|
||||||
long CurrEntry = (LastEntry + 1) % HidEntryCount;
|
|
||||||
|
|
||||||
long Timestamp = GetTimestamp();
|
|
||||||
|
|
||||||
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x0, Timestamp);
|
|
||||||
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x8, HidEntryCount);
|
|
||||||
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x10, CurrEntry);
|
|
||||||
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x18, HidEntryCount - 1);
|
|
||||||
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x20, Timestamp);
|
|
||||||
|
|
||||||
long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize;
|
|
||||||
|
|
||||||
long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize;
|
|
||||||
|
|
||||||
long SampleCounter = Memory.ReadInt64Unchecked(LastEntryOffset) + 1;
|
|
||||||
|
|
||||||
TouchEntryOffset += CurrEntry * HidTouchEntrySize;
|
|
||||||
|
|
||||||
Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0, SampleCounter);
|
|
||||||
Memory.WriteInt64Unchecked(TouchEntryOffset + 0x8, Points.Length);
|
|
||||||
|
|
||||||
TouchEntryOffset += HidTouchEntryHeaderSize;
|
|
||||||
|
|
||||||
const int Padding = 0;
|
|
||||||
|
|
||||||
int Index = 0;
|
|
||||||
|
|
||||||
foreach (HidTouchPoint Point in Points)
|
|
||||||
{
|
|
||||||
Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0, Timestamp);
|
|
||||||
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x8, Padding);
|
|
||||||
Memory.WriteInt32Unchecked(TouchEntryOffset + 0xc, Index++);
|
|
||||||
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x10, Point.X);
|
|
||||||
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x14, Point.Y);
|
|
||||||
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x18, Point.DiameterX);
|
|
||||||
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x1c, Point.DiameterY);
|
|
||||||
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x20, Point.Angle);
|
|
||||||
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x24, Padding);
|
|
||||||
|
|
||||||
TouchEntryOffset += HidTouchEntryTouchSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
using Ryujinx.HLE.OsHle;
|
using Ryujinx.HLE.OsHle;
|
||||||
|
using Ryujinx.HLE.OsHle.Handles;
|
||||||
|
using Ryujinx.HLE.OsHle.Utilities;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
|
@ -18,12 +20,14 @@ namespace Ryujinx.HLE.Loaders
|
||||||
|
|
||||||
public string FilePath { get; private set; }
|
public string FilePath { get; private set; }
|
||||||
|
|
||||||
private AMemory Memory;
|
|
||||||
|
|
||||||
public long ImageBase { get; private set; }
|
public long ImageBase { get; private set; }
|
||||||
public long ImageEnd { get; private set; }
|
public long ImageEnd { get; private set; }
|
||||||
|
|
||||||
public Executable(IExecutable Exe, AMemory Memory, long ImageBase)
|
private AMemory Memory;
|
||||||
|
|
||||||
|
private KMemoryManager MemoryManager;
|
||||||
|
|
||||||
|
public Executable(IExecutable Exe, KMemoryManager MemoryManager, AMemory Memory, long ImageBase)
|
||||||
{
|
{
|
||||||
Dynamic = new List<ElfDyn>();
|
Dynamic = new List<ElfDyn>();
|
||||||
|
|
||||||
|
@ -36,23 +40,34 @@ namespace Ryujinx.HLE.Loaders
|
||||||
Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, ""));
|
Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Memory = Memory;
|
this.Memory = Memory;
|
||||||
this.ImageBase = ImageBase;
|
this.MemoryManager = MemoryManager;
|
||||||
this.ImageEnd = ImageBase;
|
this.ImageBase = ImageBase;
|
||||||
|
this.ImageEnd = ImageBase;
|
||||||
|
|
||||||
WriteData(ImageBase + Exe.TextOffset, Exe.Text, MemoryType.CodeStatic, AMemoryPerm.RX);
|
long TextPosition = ImageBase + (uint)Exe.TextOffset;
|
||||||
WriteData(ImageBase + Exe.ROOffset, Exe.RO, MemoryType.CodeMutable, AMemoryPerm.Read);
|
long ROPosition = ImageBase + (uint)Exe.ROOffset;
|
||||||
WriteData(ImageBase + Exe.DataOffset, Exe.Data, MemoryType.CodeMutable, AMemoryPerm.RW);
|
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 DataAndBssSize = (uint)IntUtils.AlignUp(Exe.BssSize, KMemoryManager.PageSize) + DataSize;
|
||||||
|
|
||||||
|
ImageEnd = DataPosition + DataAndBssSize;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
if (Exe.Mod0Offset == 0)
|
if (Exe.Mod0Offset == 0)
|
||||||
{
|
{
|
||||||
int BssOffset = Exe.DataOffset + Exe.Data.Length;
|
|
||||||
int BssSize = Exe.BssSize;
|
|
||||||
|
|
||||||
MapBss(ImageBase + BssOffset, BssSize);
|
|
||||||
|
|
||||||
ImageEnd = ImageBase + BssOffset + BssSize;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,10 +81,6 @@ namespace Ryujinx.HLE.Loaders
|
||||||
long EhHdrEndOffset = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset;
|
long EhHdrEndOffset = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset;
|
||||||
long ModObjOffset = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset;
|
long ModObjOffset = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset;
|
||||||
|
|
||||||
MapBss(BssStartOffset, BssEndOffset - BssStartOffset);
|
|
||||||
|
|
||||||
ImageEnd = BssEndOffset;
|
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
long TagVal = Memory.ReadInt64(DynamicOffset + 0);
|
long TagVal = Memory.ReadInt64(DynamicOffset + 0);
|
||||||
|
@ -102,24 +113,6 @@ namespace Ryujinx.HLE.Loaders
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteData(
|
|
||||||
long Position,
|
|
||||||
byte[] Data,
|
|
||||||
MemoryType Type,
|
|
||||||
AMemoryPerm Perm)
|
|
||||||
{
|
|
||||||
Memory.Manager.Map(Position, Data.Length, (int)Type, AMemoryPerm.Write);
|
|
||||||
|
|
||||||
Memory.WriteBytes(Position, Data);
|
|
||||||
|
|
||||||
Memory.Manager.Reprotect(Position, Data.Length, Perm);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MapBss(long Position, long Size)
|
|
||||||
{
|
|
||||||
Memory.Manager.Map(Position, Size, (int)MemoryType.Normal, AMemoryPerm.RW);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ElfRel GetRelocation(long Position)
|
private ElfRel GetRelocation(long Position)
|
||||||
{
|
{
|
||||||
long Offset = Memory.ReadInt64(Position + 0);
|
long Offset = Memory.ReadInt64(Position + 0);
|
||||||
|
|
112
Ryujinx.HLE/Memory/ArenaAllocator.cs
Normal file
112
Ryujinx.HLE/Memory/ArenaAllocator.cs
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.Memory
|
||||||
|
{
|
||||||
|
class ArenaAllocator
|
||||||
|
{
|
||||||
|
private class Region
|
||||||
|
{
|
||||||
|
public long Position { get; set; }
|
||||||
|
public long Size { get; set; }
|
||||||
|
|
||||||
|
public Region(long Position, long Size)
|
||||||
|
{
|
||||||
|
this.Position = Position;
|
||||||
|
this.Size = Size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinkedList<Region> FreeRegions;
|
||||||
|
|
||||||
|
public long TotalAvailableSize { get; private set; }
|
||||||
|
public long TotalUsedSize { get; private set; }
|
||||||
|
|
||||||
|
public ArenaAllocator(long ArenaSize)
|
||||||
|
{
|
||||||
|
TotalAvailableSize = ArenaSize;
|
||||||
|
|
||||||
|
FreeRegions = new LinkedList<Region>();
|
||||||
|
|
||||||
|
FreeRegions.AddFirst(new Region(0, ArenaSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryAllocate(long Size, out long Position)
|
||||||
|
{
|
||||||
|
LinkedListNode<Region> Node = FreeRegions.First;
|
||||||
|
|
||||||
|
while (Node != null)
|
||||||
|
{
|
||||||
|
Region Rg = Node.Value;
|
||||||
|
|
||||||
|
if ((ulong)Rg.Size >= (ulong)Size)
|
||||||
|
{
|
||||||
|
Position = Rg.Position;
|
||||||
|
|
||||||
|
Rg.Position += Size;
|
||||||
|
Rg.Size -= Size;
|
||||||
|
|
||||||
|
TotalUsedSize += Size;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node = Node.Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position = 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Free(long Position, long Size)
|
||||||
|
{
|
||||||
|
long End = Position + Size;
|
||||||
|
|
||||||
|
Region NewRg = new Region(Position, Size);
|
||||||
|
|
||||||
|
LinkedListNode<Region> Node = FreeRegions.First;
|
||||||
|
LinkedListNode<Region> PrevSz = Node;
|
||||||
|
|
||||||
|
while (Node != null)
|
||||||
|
{
|
||||||
|
LinkedListNode<Region> NextNode = Node.Next;
|
||||||
|
|
||||||
|
Region Rg = Node.Value;
|
||||||
|
|
||||||
|
long RgEnd = Rg.Position + Rg.Size;
|
||||||
|
|
||||||
|
if (Rg.Position == End)
|
||||||
|
{
|
||||||
|
NewRg.Size += Rg.Size;
|
||||||
|
|
||||||
|
FreeRegions.Remove(Node);
|
||||||
|
}
|
||||||
|
else if (RgEnd == Position)
|
||||||
|
{
|
||||||
|
NewRg.Position = Rg.Position;
|
||||||
|
NewRg.Size += Rg.Size;
|
||||||
|
|
||||||
|
FreeRegions.Remove(Node);
|
||||||
|
}
|
||||||
|
else if ((ulong)Rg.Size < (ulong)NewRg.Size &&
|
||||||
|
(ulong)Rg.Size > (ulong)PrevSz.Value.Size)
|
||||||
|
{
|
||||||
|
PrevSz = Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node = NextNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ulong)PrevSz.Value.Size < (ulong)Size)
|
||||||
|
{
|
||||||
|
FreeRegions.AddAfter(PrevSz, NewRg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FreeRegions.AddFirst(NewRg);
|
||||||
|
}
|
||||||
|
|
||||||
|
TotalUsedSize -= Size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
130
Ryujinx.HLE/Memory/DeviceMemory.cs
Normal file
130
Ryujinx.HLE/Memory/DeviceMemory.cs
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.Memory
|
||||||
|
{
|
||||||
|
class DeviceMemory : IDisposable
|
||||||
|
{
|
||||||
|
public const long RamSize = 4L * 1024 * 1024 * 1024;
|
||||||
|
|
||||||
|
public ArenaAllocator Allocator { get; private set; }
|
||||||
|
|
||||||
|
public IntPtr RamPointer { get; private set; }
|
||||||
|
|
||||||
|
private unsafe byte* RamPtr;
|
||||||
|
|
||||||
|
public unsafe DeviceMemory()
|
||||||
|
{
|
||||||
|
Allocator = new ArenaAllocator(RamSize);
|
||||||
|
|
||||||
|
RamPointer = Marshal.AllocHGlobal(new IntPtr(RamSize));
|
||||||
|
|
||||||
|
RamPtr = (byte*)RamPointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sbyte ReadSByte(long Position)
|
||||||
|
{
|
||||||
|
return (sbyte)ReadByte(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short ReadInt16(long Position)
|
||||||
|
{
|
||||||
|
return (short)ReadUInt16(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ReadInt32(long Position)
|
||||||
|
{
|
||||||
|
return (int)ReadUInt32(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long ReadInt64(long Position)
|
||||||
|
{
|
||||||
|
return (long)ReadUInt64(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe byte ReadByte(long Position)
|
||||||
|
{
|
||||||
|
return *((byte*)(RamPtr + Position));
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe ushort ReadUInt16(long Position)
|
||||||
|
{
|
||||||
|
return *((ushort*)(RamPtr + Position));
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe uint ReadUInt32(long Position)
|
||||||
|
{
|
||||||
|
return *((uint*)(RamPtr + Position));
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe ulong ReadUInt64(long Position)
|
||||||
|
{
|
||||||
|
return *((ulong*)(RamPtr + Position));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteSByte(long Position, sbyte Value)
|
||||||
|
{
|
||||||
|
WriteByte(Position, (byte)Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteInt16(long Position, short Value)
|
||||||
|
{
|
||||||
|
WriteUInt16(Position, (ushort)Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteInt32(long Position, int Value)
|
||||||
|
{
|
||||||
|
WriteUInt32(Position, (uint)Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteInt64(long Position, long Value)
|
||||||
|
{
|
||||||
|
WriteUInt64(Position, (ulong)Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void WriteByte(long Position, byte Value)
|
||||||
|
{
|
||||||
|
*((byte*)(RamPtr + Position)) = Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void WriteUInt16(long Position, ushort Value)
|
||||||
|
{
|
||||||
|
*((ushort*)(RamPtr + Position)) = Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void WriteUInt32(long Position, uint Value)
|
||||||
|
{
|
||||||
|
*((uint*)(RamPtr + Position)) = Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void WriteUInt64(long Position, ulong Value)
|
||||||
|
{
|
||||||
|
*((ulong*)(RamPtr + Position)) = Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FillWithZeros(long Position, int Size)
|
||||||
|
{
|
||||||
|
int Size8 = Size & ~(8 - 1);
|
||||||
|
|
||||||
|
for (int Offs = 0; Offs < Size8; Offs += 8)
|
||||||
|
{
|
||||||
|
WriteInt64(Position + Offs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int Offs = Size8; Offs < (Size - Size8); Offs++)
|
||||||
|
{
|
||||||
|
WriteByte(Position + Offs, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool Disposing)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(RamPointer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
122
Ryujinx.HLE/OsHle/Font/SharedFontManager.cs
Normal file
122
Ryujinx.HLE/OsHle/Font/SharedFontManager.cs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
using Ryujinx.HLE.Memory;
|
||||||
|
using Ryujinx.HLE.OsHle.Utilities;
|
||||||
|
using Ryujinx.HLE.Resource;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.OsHle.Font
|
||||||
|
{
|
||||||
|
class SharedFontManager
|
||||||
|
{
|
||||||
|
private DeviceMemory Memory;
|
||||||
|
|
||||||
|
private long PhysicalAddress;
|
||||||
|
|
||||||
|
private string FontsPath;
|
||||||
|
|
||||||
|
private struct FontInfo
|
||||||
|
{
|
||||||
|
public int Offset;
|
||||||
|
public int Size;
|
||||||
|
|
||||||
|
public FontInfo(int Offset, int Size)
|
||||||
|
{
|
||||||
|
this.Offset = Offset;
|
||||||
|
this.Size = Size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<SharedFontType, FontInfo> FontData;
|
||||||
|
|
||||||
|
public SharedFontManager(Switch Device, long PhysicalAddress)
|
||||||
|
{
|
||||||
|
this.PhysicalAddress = PhysicalAddress;
|
||||||
|
|
||||||
|
Memory = Device.Memory;
|
||||||
|
|
||||||
|
FontsPath = Path.Combine(Device.VFs.GetSystemPath(), "fonts");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnsureInitialized()
|
||||||
|
{
|
||||||
|
if (FontData == null)
|
||||||
|
{
|
||||||
|
Memory.FillWithZeros(PhysicalAddress, Horizon.FontSize);
|
||||||
|
|
||||||
|
uint FontOffset = 0;
|
||||||
|
|
||||||
|
FontInfo CreateFont(string Name)
|
||||||
|
{
|
||||||
|
string FontFilePath = Path.Combine(FontsPath, Name + ".ttf");
|
||||||
|
|
||||||
|
if (File.Exists(FontFilePath))
|
||||||
|
{
|
||||||
|
byte[] Data = File.ReadAllBytes(FontFilePath);
|
||||||
|
|
||||||
|
FontInfo Info = new FontInfo((int)FontOffset, Data.Length);
|
||||||
|
|
||||||
|
WriteMagicAndSize(PhysicalAddress + FontOffset, Data.Length);
|
||||||
|
|
||||||
|
FontOffset += 8;
|
||||||
|
|
||||||
|
uint Start = FontOffset;
|
||||||
|
|
||||||
|
for (; FontOffset - Start < Data.Length; FontOffset++)
|
||||||
|
{
|
||||||
|
Memory.WriteByte(PhysicalAddress + FontOffset, Data[FontOffset - Start]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Info;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidSystemResourceException($"Font \"{Name}.ttf\" not found. Please provide it in \"{FontsPath}\".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FontData = new Dictionary<SharedFontType, FontInfo>()
|
||||||
|
{
|
||||||
|
{ SharedFontType.JapanUsEurope, CreateFont("FontStandard") },
|
||||||
|
{ SharedFontType.SimplifiedChinese, CreateFont("FontChineseSimplified") },
|
||||||
|
{ SharedFontType.SimplifiedChineseEx, CreateFont("FontExtendedChineseSimplified") },
|
||||||
|
{ SharedFontType.TraditionalChinese, CreateFont("FontChineseTraditional") },
|
||||||
|
{ SharedFontType.Korean, CreateFont("FontKorean") },
|
||||||
|
{ SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (FontOffset > Horizon.FontSize)
|
||||||
|
{
|
||||||
|
throw new InvalidSystemResourceException(
|
||||||
|
$"The sum of all fonts size exceed the shared memory size. " +
|
||||||
|
$"Please make sure that the fonts don't exceed {Horizon.FontSize} bytes in total. " +
|
||||||
|
$"(actual size: {FontOffset} bytes).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteMagicAndSize(long Position, int Size)
|
||||||
|
{
|
||||||
|
const int DecMagic = 0x18029a7f;
|
||||||
|
const int Key = 0x49621806;
|
||||||
|
|
||||||
|
int EncryptedSize = EndianSwap.Swap32(Size ^ Key);
|
||||||
|
|
||||||
|
Memory.WriteInt32(Position + 0, DecMagic);
|
||||||
|
Memory.WriteInt32(Position + 4, EncryptedSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetFontSize(SharedFontType FontType)
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
|
||||||
|
return FontData[FontType].Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetSharedMemoryAddressOffset(SharedFontType FontType)
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
|
||||||
|
return FontData[FontType].Offset + 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.Font
|
namespace Ryujinx.HLE.OsHle.Font
|
||||||
{
|
{
|
||||||
public enum SharedFontType
|
public enum SharedFontType
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,7 @@ namespace Ryujinx.HLE.Font
|
||||||
SimplifiedChineseEx = 2,
|
SimplifiedChineseEx = 2,
|
||||||
TraditionalChinese = 3,
|
TraditionalChinese = 3,
|
||||||
Korean = 4,
|
Korean = 4,
|
||||||
NintendoEx = 5
|
NintendoEx = 5,
|
||||||
|
Count
|
||||||
}
|
}
|
||||||
}
|
}
|
10
Ryujinx.HLE/OsHle/Handles/AddressSpaceType.cs
Normal file
10
Ryujinx.HLE/OsHle/Handles/AddressSpaceType.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Ryujinx.HLE.OsHle.Handles
|
||||||
|
{
|
||||||
|
enum AddressSpaceType
|
||||||
|
{
|
||||||
|
Addr32Bits = 0,
|
||||||
|
Addr36Bits = 1,
|
||||||
|
Addr36BitsNoMap = 2,
|
||||||
|
Addr39Bits = 3
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,44 +0,0 @@
|
||||||
using ChocolArm64.Memory;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.OsHle.Handles
|
|
||||||
{
|
|
||||||
class HSharedMem
|
|
||||||
{
|
|
||||||
private List<(AMemory, long, long)> Positions;
|
|
||||||
|
|
||||||
public EventHandler<EventArgs> MemoryMapped;
|
|
||||||
public EventHandler<EventArgs> MemoryUnmapped;
|
|
||||||
|
|
||||||
public HSharedMem()
|
|
||||||
{
|
|
||||||
Positions = new List<(AMemory, long, long)>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddVirtualPosition(AMemory Memory, long Position, long Size)
|
|
||||||
{
|
|
||||||
lock (Positions)
|
|
||||||
{
|
|
||||||
Positions.Add((Memory, Position, Size));
|
|
||||||
|
|
||||||
MemoryMapped?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveVirtualPosition(AMemory Memory, long Position, long Size)
|
|
||||||
{
|
|
||||||
lock (Positions)
|
|
||||||
{
|
|
||||||
Positions.Remove((Memory, Position, Size));
|
|
||||||
|
|
||||||
MemoryUnmapped?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public (AMemory, long, long)[] GetVirtualPositions()
|
|
||||||
{
|
|
||||||
return Positions.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
using ChocolArm64.Memory;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.OsHle.Handles
|
|
||||||
{
|
|
||||||
class HTransferMem
|
|
||||||
{
|
|
||||||
public AMemory Memory { get; private set; }
|
|
||||||
public AMemoryPerm Perm { get; private set; }
|
|
||||||
|
|
||||||
public long Position { get; private set; }
|
|
||||||
public long Size { get; private set; }
|
|
||||||
|
|
||||||
public HTransferMem(AMemory Memory, AMemoryPerm Perm, long Position, long Size)
|
|
||||||
{
|
|
||||||
this.Memory = Memory;
|
|
||||||
this.Perm = Perm;
|
|
||||||
this.Position = Position;
|
|
||||||
this.Size = Size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
43
Ryujinx.HLE/OsHle/Handles/KMemoryBlock.cs
Normal file
43
Ryujinx.HLE/OsHle/Handles/KMemoryBlock.cs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
namespace Ryujinx.HLE.OsHle.Handles
|
||||||
|
{
|
||||||
|
class KMemoryBlock
|
||||||
|
{
|
||||||
|
public long BasePosition { get; set; }
|
||||||
|
public long PagesCount { get; set; }
|
||||||
|
|
||||||
|
public MemoryState State { get; set; }
|
||||||
|
public MemoryPermission Permission { get; set; }
|
||||||
|
public MemoryAttribute Attribute { get; set; }
|
||||||
|
|
||||||
|
public int IpcRefCount { get; set; }
|
||||||
|
public int DeviceRefCount { get; set; }
|
||||||
|
|
||||||
|
public KMemoryBlock(
|
||||||
|
long BasePosition,
|
||||||
|
long PagesCount,
|
||||||
|
MemoryState State,
|
||||||
|
MemoryPermission Permission,
|
||||||
|
MemoryAttribute Attribute)
|
||||||
|
{
|
||||||
|
this.BasePosition = BasePosition;
|
||||||
|
this.PagesCount = PagesCount;
|
||||||
|
this.State = State;
|
||||||
|
this.Attribute = Attribute;
|
||||||
|
this.Permission = Permission;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KMemoryInfo GetInfo()
|
||||||
|
{
|
||||||
|
long Size = PagesCount * KMemoryManager.PageSize;
|
||||||
|
|
||||||
|
return new KMemoryInfo(
|
||||||
|
BasePosition,
|
||||||
|
Size,
|
||||||
|
State,
|
||||||
|
Permission,
|
||||||
|
Attribute,
|
||||||
|
IpcRefCount,
|
||||||
|
DeviceRefCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
Ryujinx.HLE/OsHle/Handles/KMemoryInfo.cs
Normal file
33
Ryujinx.HLE/OsHle/Handles/KMemoryInfo.cs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
namespace Ryujinx.HLE.OsHle.Handles
|
||||||
|
{
|
||||||
|
class KMemoryInfo
|
||||||
|
{
|
||||||
|
public long Position { get; private set; }
|
||||||
|
public long Size { get; private set; }
|
||||||
|
|
||||||
|
public MemoryState State { get; private set; }
|
||||||
|
public MemoryPermission Permission { get; private set; }
|
||||||
|
public MemoryAttribute Attribute { get; private set; }
|
||||||
|
|
||||||
|
public int IpcRefCount { get; private set; }
|
||||||
|
public int DeviceRefCount { get; private set; }
|
||||||
|
|
||||||
|
public KMemoryInfo(
|
||||||
|
long Position,
|
||||||
|
long Size,
|
||||||
|
MemoryState State,
|
||||||
|
MemoryPermission Permission,
|
||||||
|
MemoryAttribute Attribute,
|
||||||
|
int IpcRefCount,
|
||||||
|
int DeviceRefCount)
|
||||||
|
{
|
||||||
|
this.Position = Position;
|
||||||
|
this.Size = Size;
|
||||||
|
this.State = State;
|
||||||
|
this.Attribute = Attribute;
|
||||||
|
this.Permission = Permission;
|
||||||
|
this.IpcRefCount = IpcRefCount;
|
||||||
|
this.DeviceRefCount = DeviceRefCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1082
Ryujinx.HLE/OsHle/Handles/KMemoryManager.cs
Normal file
1082
Ryujinx.HLE/OsHle/Handles/KMemoryManager.cs
Normal file
File diff suppressed because it is too large
Load diff
14
Ryujinx.HLE/OsHle/Handles/KSharedMemory.cs
Normal file
14
Ryujinx.HLE/OsHle/Handles/KSharedMemory.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace Ryujinx.HLE.OsHle.Handles
|
||||||
|
{
|
||||||
|
class KSharedMemory
|
||||||
|
{
|
||||||
|
public long PA { get; private set; }
|
||||||
|
public long Size { get; private set; }
|
||||||
|
|
||||||
|
public KSharedMemory(long PA, long Size)
|
||||||
|
{
|
||||||
|
this.PA = PA;
|
||||||
|
this.Size = Size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
Ryujinx.HLE/OsHle/Handles/KTlsPageManager.cs
Normal file
60
Ryujinx.HLE/OsHle/Handles/KTlsPageManager.cs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.OsHle.Handles
|
||||||
|
{
|
||||||
|
class KTlsPageManager
|
||||||
|
{
|
||||||
|
private const int TlsEntrySize = 0x200;
|
||||||
|
|
||||||
|
private long PagePosition;
|
||||||
|
|
||||||
|
private int UsedSlots;
|
||||||
|
|
||||||
|
private bool[] Slots;
|
||||||
|
|
||||||
|
public bool IsEmpty => UsedSlots == 0;
|
||||||
|
public bool IsFull => UsedSlots == Slots.Length;
|
||||||
|
|
||||||
|
public KTlsPageManager(long PagePosition)
|
||||||
|
{
|
||||||
|
this.PagePosition = PagePosition;
|
||||||
|
|
||||||
|
Slots = new bool[KMemoryManager.PageSize / TlsEntrySize];
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetFreeTlsAddr(out long Position)
|
||||||
|
{
|
||||||
|
Position = PagePosition;
|
||||||
|
|
||||||
|
for (int Index = 0; Index < Slots.Length; Index++)
|
||||||
|
{
|
||||||
|
if (!Slots[Index])
|
||||||
|
{
|
||||||
|
Slots[Index] = true;
|
||||||
|
|
||||||
|
UsedSlots++;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position += TlsEntrySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position = 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FreeTlsSlot(int Slot)
|
||||||
|
{
|
||||||
|
if ((uint)Slot > Slots.Length)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(Slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
Slots[Slot] = false;
|
||||||
|
|
||||||
|
UsedSlots--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.HLE/OsHle/Handles/KTransferMemory.cs
Normal file
14
Ryujinx.HLE/OsHle/Handles/KTransferMemory.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace Ryujinx.HLE.OsHle.Handles
|
||||||
|
{
|
||||||
|
class KTransferMemory
|
||||||
|
{
|
||||||
|
public long Position { get; private set; }
|
||||||
|
public long Size { get; private set; }
|
||||||
|
|
||||||
|
public KTransferMemory(long Position, long Size)
|
||||||
|
{
|
||||||
|
this.Position = Position;
|
||||||
|
this.Size = Size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
Ryujinx.HLE/OsHle/Handles/MemoryAttribute.cs
Normal file
22
Ryujinx.HLE/OsHle/Handles/MemoryAttribute.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.OsHle.Handles
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum MemoryAttribute : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Mask = 0xff,
|
||||||
|
|
||||||
|
Borrowed = 1 << 0,
|
||||||
|
IpcMapped = 1 << 1,
|
||||||
|
DeviceMapped = 1 << 2,
|
||||||
|
Uncached = 1 << 3,
|
||||||
|
|
||||||
|
IpcAndDeviceMapped = IpcMapped | DeviceMapped,
|
||||||
|
|
||||||
|
BorrowedAndIpcMapped = Borrowed | IpcMapped,
|
||||||
|
|
||||||
|
DeviceMappedAndUncached = DeviceMapped | Uncached
|
||||||
|
}
|
||||||
|
}
|
18
Ryujinx.HLE/OsHle/Handles/MemoryPermission.cs
Normal file
18
Ryujinx.HLE/OsHle/Handles/MemoryPermission.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.OsHle.Handles
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum MemoryPermission : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Mask = 0xff,
|
||||||
|
|
||||||
|
Read = 1 << 0,
|
||||||
|
Write = 1 << 1,
|
||||||
|
Execute = 1 << 2,
|
||||||
|
|
||||||
|
ReadAndWrite = Read | Write,
|
||||||
|
ReadAndExecute = Read | Execute
|
||||||
|
}
|
||||||
|
}
|
49
Ryujinx.HLE/OsHle/Handles/MemoryState.cs
Normal file
49
Ryujinx.HLE/OsHle/Handles/MemoryState.cs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.OsHle
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum MemoryState : uint
|
||||||
|
{
|
||||||
|
Unmapped = 0x00000000,
|
||||||
|
Io = 0x00002001,
|
||||||
|
Normal = 0x00042002,
|
||||||
|
CodeStatic = 0x00DC7E03,
|
||||||
|
CodeMutable = 0x03FEBD04,
|
||||||
|
Heap = 0x037EBD05,
|
||||||
|
SharedMemory = 0x00402006,
|
||||||
|
ModCodeStatic = 0x00DD7E08,
|
||||||
|
ModCodeMutable = 0x03FFBD09,
|
||||||
|
IpcBuffer0 = 0x005C3C0A,
|
||||||
|
MappedMemory = 0x005C3C0B,
|
||||||
|
ThreadLocal = 0x0040200C,
|
||||||
|
TransferMemoryIsolated = 0x015C3C0D,
|
||||||
|
TransferMemory = 0x005C380E,
|
||||||
|
ProcessMemory = 0x0040380F,
|
||||||
|
Reserved = 0x00000010,
|
||||||
|
IpcBuffer1 = 0x005C3811,
|
||||||
|
IpcBuffer3 = 0x004C2812,
|
||||||
|
KernelStack = 0x00002013,
|
||||||
|
CodeReadOnly = 0x00402214,
|
||||||
|
CodeWritable = 0x00402015,
|
||||||
|
Mask = 0xffffffff,
|
||||||
|
|
||||||
|
PermissionChangeAllowed = 1 << 8,
|
||||||
|
ForceReadWritableByDebugSyscalls = 1 << 9,
|
||||||
|
IpcSendAllowedType0 = 1 << 10,
|
||||||
|
IpcSendAllowedType3 = 1 << 11,
|
||||||
|
IpcSendAllowedType1 = 1 << 12,
|
||||||
|
ProcessPermissionChangeAllowed = 1 << 14,
|
||||||
|
MapAllowed = 1 << 15,
|
||||||
|
UnmapProcessCodeMemoryAllowed = 1 << 16,
|
||||||
|
TransferMemoryAllowed = 1 << 17,
|
||||||
|
QueryPhysicalAddressAllowed = 1 << 18,
|
||||||
|
MapDeviceAllowed = 1 << 19,
|
||||||
|
MapDeviceAlignedAllowed = 1 << 20,
|
||||||
|
IpcBufferAllowed = 1 << 21,
|
||||||
|
IsPoolAllocated = 1 << 22,
|
||||||
|
MapProcessAllowed = 1 << 23,
|
||||||
|
AttributeChangeAllowed = 1 << 24,
|
||||||
|
CodeMemoryAllowed = 1 << 25
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,23 +10,23 @@ namespace Ryujinx.HLE.OsHle
|
||||||
//http://switchbrew.org/index.php?title=Homebrew_ABI
|
//http://switchbrew.org/index.php?title=Homebrew_ABI
|
||||||
public static void WriteHbAbiData(AMemory Memory, long Position, int MainThreadHandle, string SwitchPath)
|
public static void WriteHbAbiData(AMemory Memory, long Position, int MainThreadHandle, string SwitchPath)
|
||||||
{
|
{
|
||||||
Memory.Manager.Map(Position, AMemoryMgr.PageSize, (int)MemoryType.Normal, AMemoryPerm.RW);
|
//MainThreadHandle.
|
||||||
|
|
||||||
//MainThreadHandle
|
|
||||||
WriteConfigEntry(Memory, ref Position, 1, 0, MainThreadHandle);
|
WriteConfigEntry(Memory, ref Position, 1, 0, MainThreadHandle);
|
||||||
|
|
||||||
//NextLoadPath
|
//NextLoadPath.
|
||||||
WriteConfigEntry(Memory, ref Position, 2, 0, Position + 0x200, Position + 0x400);
|
WriteConfigEntry(Memory, ref Position, 2, 0, Position + 0x200, Position + 0x400);
|
||||||
|
|
||||||
// Argv
|
//Argv.
|
||||||
long ArgvPosition = Position + 0xC00;
|
long ArgvPosition = Position + 0xC00;
|
||||||
WriteConfigEntry(Memory, ref Position, 5, 0, 0, ArgvPosition);
|
|
||||||
Memory.WriteBytes(ArgvPosition, Encoding.ASCII.GetBytes(SwitchPath + "\0"));
|
Memory.WriteBytes(ArgvPosition, Encoding.ASCII.GetBytes(SwitchPath + "\0"));
|
||||||
|
|
||||||
//AppletType
|
WriteConfigEntry(Memory, ref Position, 5, 0, 0, ArgvPosition);
|
||||||
|
|
||||||
|
//AppletType.
|
||||||
WriteConfigEntry(Memory, ref Position, 7);
|
WriteConfigEntry(Memory, ref Position, 7);
|
||||||
|
|
||||||
//EndOfList
|
//EndOfList.
|
||||||
WriteConfigEntry(Memory, ref Position, 0);
|
WriteConfigEntry(Memory, ref Position, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
using Ryujinx.HLE.Loaders.Npdm;
|
using Ryujinx.HLE.Loaders.Npdm;
|
||||||
using Ryujinx.HLE.Logging;
|
using Ryujinx.HLE.Logging;
|
||||||
|
using Ryujinx.HLE.OsHle.Font;
|
||||||
using Ryujinx.HLE.OsHle.Handles;
|
using Ryujinx.HLE.OsHle.Handles;
|
||||||
using Ryujinx.HLE.OsHle.SystemState;
|
using Ryujinx.HLE.OsHle.SystemState;
|
||||||
using System;
|
using System;
|
||||||
|
@ -12,7 +13,7 @@ namespace Ryujinx.HLE.OsHle
|
||||||
public class Horizon : IDisposable
|
public class Horizon : IDisposable
|
||||||
{
|
{
|
||||||
internal const int HidSize = 0x40000;
|
internal const int HidSize = 0x40000;
|
||||||
internal const int FontSize = 0x50;
|
internal const int FontSize = 0x1100000;
|
||||||
|
|
||||||
private Switch Ns;
|
private Switch Ns;
|
||||||
|
|
||||||
|
@ -22,10 +23,10 @@ namespace Ryujinx.HLE.OsHle
|
||||||
|
|
||||||
public SystemStateMgr SystemState { get; private set; }
|
public SystemStateMgr SystemState { get; private set; }
|
||||||
|
|
||||||
internal MemoryAllocator Allocator { get; private set; }
|
internal KSharedMemory HidSharedMem { get; private set; }
|
||||||
|
internal KSharedMemory FontSharedMem { get; private set; }
|
||||||
|
|
||||||
internal HSharedMem HidSharedMem { get; private set; }
|
internal SharedFontManager Font { get; private set; }
|
||||||
internal HSharedMem FontSharedMem { get; private set; }
|
|
||||||
|
|
||||||
internal KEvent VsyncEvent { get; private set; }
|
internal KEvent VsyncEvent { get; private set; }
|
||||||
|
|
||||||
|
@ -39,10 +40,16 @@ namespace Ryujinx.HLE.OsHle
|
||||||
|
|
||||||
SystemState = new SystemStateMgr();
|
SystemState = new SystemStateMgr();
|
||||||
|
|
||||||
Allocator = new MemoryAllocator();
|
if (!Ns.Memory.Allocator.TryAllocate(HidSize, out long HidPA) ||
|
||||||
|
!Ns.Memory.Allocator.TryAllocate(FontSize, out long FontPA))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
HidSharedMem = new HSharedMem();
|
HidSharedMem = new KSharedMemory(HidPA, HidSize);
|
||||||
FontSharedMem = new HSharedMem();
|
FontSharedMem = new KSharedMemory(FontPA, FontSize);
|
||||||
|
|
||||||
|
Font = new SharedFontManager(Ns, FontSharedMem.PA);
|
||||||
|
|
||||||
VsyncEvent = new KEvent();
|
VsyncEvent = new KEvent();
|
||||||
}
|
}
|
||||||
|
@ -54,7 +61,25 @@ namespace Ryujinx.HLE.OsHle
|
||||||
Ns.VFs.LoadRomFs(RomFsFile);
|
Ns.VFs.LoadRomFs(RomFsFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
Process MainProcess = MakeProcess();
|
string NpdmFileName = Path.Combine(ExeFsDir, "main.npdm");
|
||||||
|
|
||||||
|
Npdm MetaData = null;
|
||||||
|
|
||||||
|
if (File.Exists(NpdmFileName))
|
||||||
|
{
|
||||||
|
Ns.Log.PrintInfo(LogClass.Loader, $"Loading main.npdm...");
|
||||||
|
|
||||||
|
using (FileStream Input = new FileStream(NpdmFileName, FileMode.Open))
|
||||||
|
{
|
||||||
|
MetaData = new Npdm(Input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Process MainProcess = MakeProcess(MetaData);
|
||||||
|
|
||||||
void LoadNso(string FileName)
|
void LoadNso(string FileName)
|
||||||
{
|
{
|
||||||
|
@ -78,21 +103,7 @@ namespace Ryujinx.HLE.OsHle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadNpdm(string FileName)
|
if (!MainProcess.MetaData.Is64Bits)
|
||||||
{
|
|
||||||
string File = Directory.GetFiles(ExeFsDir, FileName)[0];
|
|
||||||
|
|
||||||
Ns.Log.PrintInfo(LogClass.Loader, "Loading Title Metadata...");
|
|
||||||
|
|
||||||
using (FileStream Input = new FileStream(File, FileMode.Open))
|
|
||||||
{
|
|
||||||
MainProcess.Metadata = new Npdm(Input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadNpdm("*.npdm");
|
|
||||||
|
|
||||||
if (!MainProcess.Metadata.Is64Bits)
|
|
||||||
{
|
{
|
||||||
throw new NotImplementedException("32-bit titles are unsupported!");
|
throw new NotImplementedException("32-bit titles are unsupported!");
|
||||||
}
|
}
|
||||||
|
@ -145,7 +156,7 @@ namespace Ryujinx.HLE.OsHle
|
||||||
|
|
||||||
public void SignalVsync() => VsyncEvent.WaitEvent.Set();
|
public void SignalVsync() => VsyncEvent.WaitEvent.Set();
|
||||||
|
|
||||||
private Process MakeProcess()
|
private Process MakeProcess(Npdm MetaData = null)
|
||||||
{
|
{
|
||||||
Process Process;
|
Process Process;
|
||||||
|
|
||||||
|
@ -158,7 +169,7 @@ namespace Ryujinx.HLE.OsHle
|
||||||
ProcessId++;
|
ProcessId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Process = new Process(Ns, Scheduler, ProcessId);
|
Process = new Process(Ns, Scheduler, ProcessId, MetaData);
|
||||||
|
|
||||||
Processes.TryAdd(ProcessId, Process);
|
Processes.TryAdd(ProcessId, Process);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,21 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{
|
{
|
||||||
static class KernelErr
|
static class KernelErr
|
||||||
{
|
{
|
||||||
public const int InvalidAlignment = 102;
|
public const int InvalidSize = 101;
|
||||||
public const int InvalidAddress = 106;
|
public const int InvalidAddress = 102;
|
||||||
public const int InvalidMemRange = 110;
|
public const int OutOfMemory = 104;
|
||||||
public const int InvalidPriority = 112;
|
public const int NoAccessPerm = 106;
|
||||||
public const int InvalidCoreId = 113;
|
public const int InvalidPermission = 108;
|
||||||
public const int InvalidHandle = 114;
|
public const int InvalidMemRange = 110;
|
||||||
public const int InvalidCoreMask = 116;
|
public const int InvalidPriority = 112;
|
||||||
public const int Timeout = 117;
|
public const int InvalidCoreId = 113;
|
||||||
public const int Canceled = 118;
|
public const int InvalidHandle = 114;
|
||||||
public const int CountOutOfRange = 119;
|
public const int InvalidMaskValue = 116;
|
||||||
public const int InvalidEnumValue = 120;
|
public const int Timeout = 117;
|
||||||
public const int InvalidThread = 122;
|
public const int Canceled = 118;
|
||||||
public const int InvalidState = 125;
|
public const int CountOutOfRange = 119;
|
||||||
|
public const int InvalidEnumValue = 120;
|
||||||
|
public const int InvalidThread = 122;
|
||||||
|
public const int InvalidState = 125;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,7 +10,7 @@ using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.OsHle.Kernel
|
namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{
|
{
|
||||||
partial class SvcHandler : IDisposable
|
partial class SvcHandler
|
||||||
{
|
{
|
||||||
private delegate void SvcFunc(AThreadState ThreadState);
|
private delegate void SvcFunc(AThreadState ThreadState);
|
||||||
|
|
||||||
|
@ -22,10 +22,6 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
|
|
||||||
private ConcurrentDictionary<KThread, AutoResetEvent> SyncWaits;
|
private ConcurrentDictionary<KThread, AutoResetEvent> SyncWaits;
|
||||||
|
|
||||||
private HashSet<(HSharedMem, long, long)> MappedSharedMems;
|
|
||||||
|
|
||||||
private ulong CurrentHeapSize;
|
|
||||||
|
|
||||||
private const uint SelfThreadHandle = 0xffff8000;
|
private const uint SelfThreadHandle = 0xffff8000;
|
||||||
private const uint SelfProcessHandle = 0xffff8001;
|
private const uint SelfProcessHandle = 0xffff8001;
|
||||||
|
|
||||||
|
@ -82,8 +78,6 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
this.Memory = Process.Memory;
|
this.Memory = Process.Memory;
|
||||||
|
|
||||||
SyncWaits = new ConcurrentDictionary<KThread, AutoResetEvent>();
|
SyncWaits = new ConcurrentDictionary<KThread, AutoResetEvent>();
|
||||||
|
|
||||||
MappedSharedMems = new HashSet<(HSharedMem, long, long)>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static SvcHandler()
|
static SvcHandler()
|
||||||
|
@ -126,26 +120,5 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
return Process.HandleTable.GetData<KThread>(Handle);
|
return Process.HandleTable.GetData<KThread>(Handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool Disposing)
|
|
||||||
{
|
|
||||||
if (Disposing)
|
|
||||||
{
|
|
||||||
lock (MappedSharedMems)
|
|
||||||
{
|
|
||||||
foreach ((HSharedMem SharedMem, long Position, long Size) in MappedSharedMems)
|
|
||||||
{
|
|
||||||
SharedMem.RemoveVirtualPosition(Memory, Position, Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
MappedSharedMems.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
using ChocolArm64.Memory;
|
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using Ryujinx.HLE.Logging;
|
using Ryujinx.HLE.Logging;
|
||||||
using Ryujinx.HLE.OsHle.Handles;
|
using Ryujinx.HLE.OsHle.Handles;
|
||||||
|
@ -11,43 +10,85 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{
|
{
|
||||||
private void SvcSetHeapSize(AThreadState ThreadState)
|
private void SvcSetHeapSize(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
uint Size = (uint)ThreadState.X1;
|
long Size = (long)ThreadState.X1;
|
||||||
|
|
||||||
long Position = MemoryRegions.HeapRegionAddress;
|
if ((Size & 0x1fffff) != 0 || Size != (uint)Size)
|
||||||
|
|
||||||
if (Size > CurrentHeapSize)
|
|
||||||
{
|
{
|
||||||
Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW);
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long Result = Process.MemoryManager.TrySetHeapSize(Size, out long Position);
|
||||||
|
|
||||||
|
ThreadState.X0 = (ulong)Result;
|
||||||
|
|
||||||
|
if (Result == 0)
|
||||||
|
{
|
||||||
|
ThreadState.X1 = (ulong)Position;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Memory.Manager.Unmap(Position + Size, (long)CurrentHeapSize - Size);
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentHeapSize = Size;
|
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
|
||||||
ThreadState.X1 = (ulong)Position;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcSetMemoryAttribute(AThreadState ThreadState)
|
private void SvcSetMemoryAttribute(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Position = (long)ThreadState.X0;
|
long Position = (long)ThreadState.X0;
|
||||||
long Size = (long)ThreadState.X1;
|
long Size = (long)ThreadState.X1;
|
||||||
int State0 = (int)ThreadState.X2;
|
|
||||||
int State1 = (int)ThreadState.X3;
|
|
||||||
|
|
||||||
if ((State0 == 0 && State1 == 0) ||
|
if (!PageAligned(Position))
|
||||||
(State0 == 8 && State1 == 0))
|
|
||||||
{
|
{
|
||||||
Memory.Manager.ClearAttrBit(Position, Size, 3);
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
|
||||||
}
|
|
||||||
else if (State0 == 8 && State1 == 8)
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
{
|
|
||||||
Memory.Manager.SetAttrBit(Position, Size, 3);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
if (!PageAligned(Size) || Size == 0)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryAttribute AttributeMask = (MemoryAttribute)ThreadState.X2;
|
||||||
|
MemoryAttribute AttributeValue = (MemoryAttribute)ThreadState.X3;
|
||||||
|
|
||||||
|
MemoryAttribute Attributes = AttributeMask | AttributeValue;
|
||||||
|
|
||||||
|
if (Attributes != AttributeMask ||
|
||||||
|
(Attributes | MemoryAttribute.Uncached) != MemoryAttribute.Uncached)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, "Invalid memory attributes!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long Result = Process.MemoryManager.SetMemoryAttribute(
|
||||||
|
Position,
|
||||||
|
Size,
|
||||||
|
AttributeMask,
|
||||||
|
AttributeValue);
|
||||||
|
|
||||||
|
if (Result != 0)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Memory.StopObservingRegion(Position, Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadState.X0 = (ulong)Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcMapMemory(AThreadState ThreadState)
|
private void SvcMapMemory(AThreadState ThreadState)
|
||||||
|
@ -56,33 +97,59 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
long Src = (long)ThreadState.X1;
|
long Src = (long)ThreadState.X1;
|
||||||
long Size = (long)ThreadState.X2;
|
long Size = (long)ThreadState.X2;
|
||||||
|
|
||||||
if (!IsValidPosition(Src))
|
if (!PageAligned(Src | Dst))
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid src address {Src:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PageAligned(Size) || Size == 0)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!InsideAddrSpace(Src, Size))
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!InsideNewMapRegion(Dst, Size))
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsValidMapPosition(Dst))
|
long Result = Process.MemoryManager.Map(Src, Dst, Size);
|
||||||
|
|
||||||
|
if (Result != 0)
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid dst address {Dst:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src);
|
ThreadState.X0 = (ulong)Result;
|
||||||
|
|
||||||
Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm);
|
|
||||||
|
|
||||||
Memory.Manager.Reprotect(Src, Size, AMemoryPerm.None);
|
|
||||||
|
|
||||||
Memory.Manager.SetAttrBit(Src, Size, 0);
|
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcUnmapMemory(AThreadState ThreadState)
|
private void SvcUnmapMemory(AThreadState ThreadState)
|
||||||
|
@ -91,33 +158,59 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
long Src = (long)ThreadState.X1;
|
long Src = (long)ThreadState.X1;
|
||||||
long Size = (long)ThreadState.X2;
|
long Size = (long)ThreadState.X2;
|
||||||
|
|
||||||
if (!IsValidPosition(Src))
|
if (!PageAligned(Src | Dst))
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid src address {Src:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PageAligned(Size) || Size == 0)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!InsideAddrSpace(Src, Size))
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!InsideNewMapRegion(Dst, Size))
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsValidMapPosition(Dst))
|
long Result = Process.MemoryManager.Unmap(Src, Dst, Size);
|
||||||
|
|
||||||
|
if (Result != 0)
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid dst address {Dst:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst);
|
ThreadState.X0 = (ulong)Result;
|
||||||
|
|
||||||
Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory);
|
|
||||||
|
|
||||||
Memory.Manager.Reprotect(Src, Size, DstInfo.Perm);
|
|
||||||
|
|
||||||
Memory.Manager.ClearAttrBit(Src, Size, 0);
|
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcQueryMemory(AThreadState ThreadState)
|
private void SvcQueryMemory(AThreadState ThreadState)
|
||||||
|
@ -125,26 +218,16 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
long InfoPtr = (long)ThreadState.X0;
|
long InfoPtr = (long)ThreadState.X0;
|
||||||
long Position = (long)ThreadState.X2;
|
long Position = (long)ThreadState.X2;
|
||||||
|
|
||||||
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position);
|
KMemoryInfo BlkInfo = Process.MemoryManager.QueryMemory(Position);
|
||||||
|
|
||||||
if (MapInfo == null)
|
Memory.WriteInt64(InfoPtr + 0x00, BlkInfo.Position);
|
||||||
{
|
Memory.WriteInt64(InfoPtr + 0x08, BlkInfo.Size);
|
||||||
long AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
|
Memory.WriteInt32(InfoPtr + 0x10, (int)BlkInfo.State & 0xff);
|
||||||
|
Memory.WriteInt32(InfoPtr + 0x14, (int)BlkInfo.Attribute);
|
||||||
long ReservedSize = (long)(ulong.MaxValue - (ulong)AddrSpaceEnd) + 1;
|
Memory.WriteInt32(InfoPtr + 0x18, (int)BlkInfo.Permission);
|
||||||
|
Memory.WriteInt32(InfoPtr + 0x1c, BlkInfo.IpcRefCount);
|
||||||
MapInfo = new AMemoryMapInfo(AddrSpaceEnd, ReservedSize, (int)MemoryType.Reserved, 0, AMemoryPerm.None);
|
Memory.WriteInt32(InfoPtr + 0x20, BlkInfo.DeviceRefCount);
|
||||||
}
|
|
||||||
|
|
||||||
Memory.WriteInt64(InfoPtr + 0x00, MapInfo.Position);
|
|
||||||
Memory.WriteInt64(InfoPtr + 0x08, MapInfo.Size);
|
|
||||||
Memory.WriteInt32(InfoPtr + 0x10, MapInfo.Type);
|
|
||||||
Memory.WriteInt32(InfoPtr + 0x14, MapInfo.Attr);
|
|
||||||
Memory.WriteInt32(InfoPtr + 0x18, (int)MapInfo.Perm);
|
|
||||||
Memory.WriteInt32(InfoPtr + 0x1c, 0);
|
|
||||||
Memory.WriteInt32(InfoPtr + 0x20, 0);
|
|
||||||
Memory.WriteInt32(InfoPtr + 0x24, 0);
|
Memory.WriteInt32(InfoPtr + 0x24, 0);
|
||||||
//TODO: X1.
|
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = 0;
|
||||||
ThreadState.X1 = 0;
|
ThreadState.X1 = 0;
|
||||||
|
@ -152,134 +235,344 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
|
|
||||||
private void SvcMapSharedMemory(AThreadState ThreadState)
|
private void SvcMapSharedMemory(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
long Src = (long)ThreadState.X1;
|
long Position = (long)ThreadState.X1;
|
||||||
long Size = (long)ThreadState.X2;
|
long Size = (long)ThreadState.X2;
|
||||||
int Perm = (int)ThreadState.X3;
|
|
||||||
|
|
||||||
if (!IsValidPosition(Src))
|
if (!PageAligned(Position))
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
|
if (!PageAligned(Size) || Size == 0)
|
||||||
|
|
||||||
if (SharedMem != null)
|
|
||||||
{
|
{
|
||||||
Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, AMemoryPerm.Write);
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
|
||||||
|
|
||||||
AMemoryHelper.FillWithZeros(Memory, Src, (int)Size);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
||||||
|
|
||||||
SharedMem.AddVirtualPosition(Memory, Src, Size);
|
return;
|
||||||
|
|
||||||
Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
|
|
||||||
|
|
||||||
lock (MappedSharedMems)
|
|
||||||
{
|
|
||||||
MappedSharedMems.Add((SharedMem, Src, Size));
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Error codes.
|
if ((ulong)(Position + Size) <= (ulong)Position)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryPermission Permission = (MemoryPermission)ThreadState.X3;
|
||||||
|
|
||||||
|
if ((Permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KSharedMemory SharedMemory = Process.HandleTable.GetData<KSharedMemory>(Handle);
|
||||||
|
|
||||||
|
if (SharedMemory == null)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size))
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SharedMemory.Size != Size)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} does not match shared memory size 0x{SharedMemory.Size:16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long Result = Process.MemoryManager.MapSharedMemory(SharedMemory, Permission, Position);
|
||||||
|
|
||||||
|
if (Result != 0)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadState.X0 = (ulong)Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcUnmapSharedMemory(AThreadState ThreadState)
|
private void SvcUnmapSharedMemory(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
long Src = (long)ThreadState.X1;
|
long Position = (long)ThreadState.X1;
|
||||||
long Size = (long)ThreadState.X2;
|
long Size = (long)ThreadState.X2;
|
||||||
|
|
||||||
if (!IsValidPosition(Src))
|
if (!PageAligned(Position))
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
|
if (!PageAligned(Size) || Size == 0)
|
||||||
|
|
||||||
if (SharedMem != null)
|
|
||||||
{
|
{
|
||||||
Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory);
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
|
||||||
|
|
||||||
SharedMem.RemoveVirtualPosition(Memory, Src, Size);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
||||||
|
|
||||||
lock (MappedSharedMems)
|
return;
|
||||||
{
|
|
||||||
MappedSharedMems.Remove((SharedMem, Src, Size));
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Error codes.
|
if ((ulong)(Position + Size) <= (ulong)Position)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KSharedMemory SharedMemory = Process.HandleTable.GetData<KSharedMemory>(Handle);
|
||||||
|
|
||||||
|
if (SharedMemory == null)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size))
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long Result = Process.MemoryManager.UnmapSharedMemory(Position, Size);
|
||||||
|
|
||||||
|
if (Result != 0)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadState.X0 = (ulong)Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcCreateTransferMemory(AThreadState ThreadState)
|
private void SvcCreateTransferMemory(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Src = (long)ThreadState.X1;
|
long Position = (long)ThreadState.X1;
|
||||||
long Size = (long)ThreadState.X2;
|
long Size = (long)ThreadState.X2;
|
||||||
int Perm = (int)ThreadState.X3;
|
|
||||||
|
|
||||||
if (!IsValidPosition(Src))
|
if (!PageAligned(Position))
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Src);
|
if (!PageAligned(Size) || Size == 0)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
|
||||||
|
|
||||||
Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem);
|
if ((ulong)(Position + Size) <= (ulong)Position)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryPermission Permission = (MemoryPermission)ThreadState.X3;
|
||||||
|
|
||||||
|
if (Permission > MemoryPermission.ReadAndWrite || Permission == MemoryPermission.Write)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Process.MemoryManager.ReserveTransferMemory(Position, Size, Permission);
|
||||||
|
|
||||||
|
KTransferMemory TransferMemory = new KTransferMemory(Position, Size);
|
||||||
|
|
||||||
|
int Handle = Process.HandleTable.OpenHandle(TransferMemory);
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = 0;
|
||||||
ThreadState.X1 = Handle;
|
ThreadState.X1 = (ulong)Handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcMapPhysicalMemory(AThreadState ThreadState)
|
private void SvcMapPhysicalMemory(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Position = (long)ThreadState.X0;
|
long Position = (long)ThreadState.X0;
|
||||||
uint Size = (uint)ThreadState.X1;
|
long Size = (long)ThreadState.X1;
|
||||||
|
|
||||||
Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW);
|
if (!PageAligned(Position))
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PageAligned(Size) || Size == 0)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ulong)(Position + Size) <= (ulong)Position)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!InsideAddrSpace(Position, Size))
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long Result = Process.MemoryManager.MapPhysicalMemory(Position, Size);
|
||||||
|
|
||||||
|
if (Result != 0)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadState.X0 = (ulong)Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcUnmapPhysicalMemory(AThreadState ThreadState)
|
private void SvcUnmapPhysicalMemory(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Position = (long)ThreadState.X0;
|
long Position = (long)ThreadState.X0;
|
||||||
uint Size = (uint)ThreadState.X1;
|
long Size = (long)ThreadState.X1;
|
||||||
|
|
||||||
Memory.Manager.Unmap(Position, Size);
|
if (!PageAligned(Position))
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PageAligned(Size) || Size == 0)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ulong)(Position + Size) <= (ulong)Position)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!InsideAddrSpace(Position, Size))
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long Result = Process.MemoryManager.UnmapPhysicalMemory(Position, Size);
|
||||||
|
|
||||||
|
if (Result != 0)
|
||||||
|
{
|
||||||
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadState.X0 = (ulong)Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsValidPosition(long Position)
|
private static bool PageAligned(long Position)
|
||||||
{
|
{
|
||||||
return Position >= MemoryRegions.AddrSpaceStart &&
|
return (Position & (KMemoryManager.PageSize - 1)) == 0;
|
||||||
Position < MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsValidMapPosition(long Position)
|
private bool InsideAddrSpace(long Position, long Size)
|
||||||
{
|
{
|
||||||
return Position >= MemoryRegions.MapRegionAddress &&
|
ulong Start = (ulong)Position;
|
||||||
Position < MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -18,8 +18,6 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
|
|
||||||
private const bool EnableProcessDebugging = false;
|
private const bool EnableProcessDebugging = false;
|
||||||
|
|
||||||
private const bool IsVirtualMemoryEnabled = true; //This is always true(?)
|
|
||||||
|
|
||||||
private void SvcExitProcess(AThreadState ThreadState)
|
private void SvcExitProcess(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
Ns.Os.ExitProcess(ThreadState.ProcessId);
|
Ns.Os.ExitProcess(ThreadState.ProcessId);
|
||||||
|
@ -53,12 +51,11 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{
|
{
|
||||||
Session.Dispose();
|
Session.Dispose();
|
||||||
}
|
}
|
||||||
else if (Obj is HTransferMem TMem)
|
else if (Obj is KTransferMemory TransferMemory)
|
||||||
{
|
{
|
||||||
TMem.Memory.Manager.Reprotect(
|
Process.MemoryManager.ResetTransferMemory(
|
||||||
TMem.Position,
|
TransferMemory.Position,
|
||||||
TMem.Size,
|
TransferMemory.Size);
|
||||||
TMem.Perm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = 0;
|
||||||
|
@ -306,27 +303,29 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
ThreadState.X1 = MemoryRegions.MapRegionAddress;
|
ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionStart;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
ThreadState.X1 = MemoryRegions.MapRegionSize;
|
ThreadState.X1 = (ulong)Process.MemoryManager.MapRegionEnd -
|
||||||
|
(ulong)Process.MemoryManager.MapRegionStart;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
ThreadState.X1 = MemoryRegions.HeapRegionAddress;
|
ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionStart;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
ThreadState.X1 = MemoryRegions.HeapRegionSize;
|
ThreadState.X1 = (ulong)Process.MemoryManager.HeapRegionEnd -
|
||||||
|
(ulong)Process.MemoryManager.HeapRegionStart;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
ThreadState.X1 = MemoryRegions.TotalMemoryAvailable;
|
ThreadState.X1 = (ulong)Process.Ns.Memory.Allocator.TotalAvailableSize;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 7:
|
case 7:
|
||||||
ThreadState.X1 = MemoryRegions.TotalMemoryUsed + CurrentHeapSize;
|
ThreadState.X1 = (ulong)Process.Ns.Memory.Allocator.TotalUsedSize;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 8:
|
case 8:
|
||||||
|
@ -338,23 +337,29 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 12:
|
case 12:
|
||||||
ThreadState.X1 = MemoryRegions.AddrSpaceStart;
|
ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceStart;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 13:
|
case 13:
|
||||||
ThreadState.X1 = MemoryRegions.AddrSpaceSize;
|
ThreadState.X1 = (ulong)Process.MemoryManager.AddrSpaceEnd -
|
||||||
|
(ulong)Process.MemoryManager.AddrSpaceStart;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 14:
|
case 14:
|
||||||
ThreadState.X1 = MemoryRegions.MapRegionAddress;
|
ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionStart;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 15:
|
case 15:
|
||||||
ThreadState.X1 = MemoryRegions.MapRegionSize;
|
ThreadState.X1 = (ulong)Process.MemoryManager.NewMapRegionEnd -
|
||||||
|
(ulong)Process.MemoryManager.NewMapRegionStart;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 16:
|
case 16:
|
||||||
ThreadState.X1 = IsVirtualMemoryEnabled ? 1 : 0;
|
ThreadState.X1 = (ulong)(Process.MetaData?.SystemResourceSize ?? 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 17:
|
||||||
|
ThreadState.X1 = (ulong)Process.MemoryManager.PersonalMmHeapUsage;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -204,7 +204,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreMask);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -226,7 +226,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{CoreMask:x8}!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreMask);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
|
||||||
{
|
{
|
||||||
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!");
|
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!");
|
||||||
|
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.OsHle
|
|
||||||
{
|
|
||||||
class MemoryAllocator
|
|
||||||
{
|
|
||||||
public bool TryAllocate(long Size, out long Address)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
using ChocolArm64.Memory;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.OsHle
|
|
||||||
{
|
|
||||||
static class MemoryRegions
|
|
||||||
{
|
|
||||||
public const long AddrSpaceStart = 0x08000000;
|
|
||||||
|
|
||||||
public const long MapRegionAddress = 0x10000000;
|
|
||||||
public const long MapRegionSize = 0x20000000;
|
|
||||||
|
|
||||||
public const long HeapRegionAddress = MapRegionAddress + MapRegionSize;
|
|
||||||
public const long HeapRegionSize = TlsPagesAddress - HeapRegionAddress;
|
|
||||||
|
|
||||||
public const long MainStackSize = 0x100000;
|
|
||||||
|
|
||||||
public const long MainStackAddress = AMemoryMgr.AddrSize - MainStackSize;
|
|
||||||
|
|
||||||
public const long TlsPagesSize = 0x20000;
|
|
||||||
|
|
||||||
public const long TlsPagesAddress = MainStackAddress - TlsPagesSize;
|
|
||||||
|
|
||||||
public const long TotalMemoryUsed = HeapRegionAddress + TlsPagesSize + MainStackSize;
|
|
||||||
|
|
||||||
public const long TotalMemoryAvailable = AMemoryMgr.RamSize - AddrSpaceStart;
|
|
||||||
|
|
||||||
public const long AddrSpaceSize = AMemoryMgr.AddrSize - AddrSpaceStart;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,6 +12,7 @@ using Ryujinx.HLE.OsHle.Handles;
|
||||||
using Ryujinx.HLE.OsHle.Kernel;
|
using Ryujinx.HLE.OsHle.Kernel;
|
||||||
using Ryujinx.HLE.OsHle.Services.Nv;
|
using Ryujinx.HLE.OsHle.Services.Nv;
|
||||||
using Ryujinx.HLE.OsHle.SystemState;
|
using Ryujinx.HLE.OsHle.SystemState;
|
||||||
|
using Ryujinx.HLE.OsHle.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -22,13 +23,9 @@ namespace Ryujinx.HLE.OsHle
|
||||||
{
|
{
|
||||||
class Process : IDisposable
|
class Process : IDisposable
|
||||||
{
|
{
|
||||||
private const int TlsSize = 0x200;
|
|
||||||
|
|
||||||
private const int TotalTlsSlots = (int)MemoryRegions.TlsPagesSize / TlsSize;
|
|
||||||
|
|
||||||
private const int TickFreq = 19_200_000;
|
private const int TickFreq = 19_200_000;
|
||||||
|
|
||||||
private Switch Ns;
|
public Switch Ns { get; private set; }
|
||||||
|
|
||||||
public bool NeedsHbAbi { get; private set; }
|
public bool NeedsHbAbi { get; private set; }
|
||||||
|
|
||||||
|
@ -40,22 +37,24 @@ namespace Ryujinx.HLE.OsHle
|
||||||
|
|
||||||
public AMemory Memory { get; private set; }
|
public AMemory Memory { get; private set; }
|
||||||
|
|
||||||
|
public KMemoryManager MemoryManager { get; private set; }
|
||||||
|
|
||||||
|
private List<KTlsPageManager> TlsPages;
|
||||||
|
|
||||||
public KProcessScheduler Scheduler { get; private set; }
|
public KProcessScheduler Scheduler { get; private set; }
|
||||||
|
|
||||||
public List<KThread> ThreadArbiterList { get; private set; }
|
public List<KThread> ThreadArbiterList { get; private set; }
|
||||||
|
|
||||||
public object ThreadSyncLock { get; private set; }
|
public object ThreadSyncLock { get; private set; }
|
||||||
|
|
||||||
|
public Npdm MetaData { get; private set; }
|
||||||
|
|
||||||
public KProcessHandleTable HandleTable { get; private set; }
|
public KProcessHandleTable HandleTable { get; private set; }
|
||||||
|
|
||||||
public AppletStateMgr AppletState { get; private set; }
|
public AppletStateMgr AppletState { get; private set; }
|
||||||
|
|
||||||
public Npdm Metadata { get; set; }
|
|
||||||
|
|
||||||
private SvcHandler SvcHandler;
|
private SvcHandler SvcHandler;
|
||||||
|
|
||||||
private ConcurrentDictionary<int, AThread> TlsSlots;
|
|
||||||
|
|
||||||
private ConcurrentDictionary<long, KThread> Threads;
|
private ConcurrentDictionary<long, KThread> Threads;
|
||||||
|
|
||||||
private KThread MainThread;
|
private KThread MainThread;
|
||||||
|
@ -70,13 +69,18 @@ namespace Ryujinx.HLE.OsHle
|
||||||
|
|
||||||
private bool Disposed;
|
private bool Disposed;
|
||||||
|
|
||||||
public Process(Switch Ns, KProcessScheduler Scheduler, int ProcessId)
|
public Process(Switch Ns, KProcessScheduler Scheduler, int ProcessId, Npdm MetaData)
|
||||||
{
|
{
|
||||||
this.Ns = Ns;
|
this.Ns = Ns;
|
||||||
this.Scheduler = Scheduler;
|
this.Scheduler = Scheduler;
|
||||||
|
this.MetaData = MetaData;
|
||||||
this.ProcessId = ProcessId;
|
this.ProcessId = ProcessId;
|
||||||
|
|
||||||
Memory = new AMemory();
|
Memory = new AMemory(Ns.Memory.RamPointer);
|
||||||
|
|
||||||
|
MemoryManager = new KMemoryManager(this);
|
||||||
|
|
||||||
|
TlsPages = new List<KTlsPageManager>();
|
||||||
|
|
||||||
ThreadArbiterList = new List<KThread>();
|
ThreadArbiterList = new List<KThread>();
|
||||||
|
|
||||||
|
@ -88,18 +92,11 @@ namespace Ryujinx.HLE.OsHle
|
||||||
|
|
||||||
SvcHandler = new SvcHandler(Ns, this);
|
SvcHandler = new SvcHandler(Ns, this);
|
||||||
|
|
||||||
TlsSlots = new ConcurrentDictionary<int, AThread>();
|
|
||||||
|
|
||||||
Threads = new ConcurrentDictionary<long, KThread>();
|
Threads = new ConcurrentDictionary<long, KThread>();
|
||||||
|
|
||||||
Executables = new List<Executable>();
|
Executables = new List<Executable>();
|
||||||
|
|
||||||
ImageBase = MemoryRegions.AddrSpaceStart;
|
ImageBase = MemoryManager.CodeRegionStart;
|
||||||
|
|
||||||
MapRWMemRegion(
|
|
||||||
MemoryRegions.TlsPagesAddress,
|
|
||||||
MemoryRegions.TlsPagesSize,
|
|
||||||
MemoryType.ThreadLocal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadProgram(IExecutable Program)
|
public void LoadProgram(IExecutable Program)
|
||||||
|
@ -111,17 +108,17 @@ namespace Ryujinx.HLE.OsHle
|
||||||
|
|
||||||
Ns.Log.PrintInfo(LogClass.Loader, $"Image base at 0x{ImageBase:x16}.");
|
Ns.Log.PrintInfo(LogClass.Loader, $"Image base at 0x{ImageBase:x16}.");
|
||||||
|
|
||||||
Executable Executable = new Executable(Program, Memory, ImageBase);
|
Executable Executable = new Executable(Program, MemoryManager, Memory, ImageBase);
|
||||||
|
|
||||||
Executables.Add(Executable);
|
Executables.Add(Executable);
|
||||||
|
|
||||||
ImageBase = AMemoryHelper.PageRoundUp(Executable.ImageEnd);
|
ImageBase = IntUtils.AlignUp(Executable.ImageEnd, KMemoryManager.PageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetEmptyArgs()
|
public void SetEmptyArgs()
|
||||||
{
|
{
|
||||||
//TODO: This should be part of Run.
|
//TODO: This should be part of Run.
|
||||||
ImageBase += AMemoryMgr.PageSize;
|
ImageBase += KMemoryManager.PageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Run(bool NeedsHbAbi = false)
|
public bool Run(bool NeedsHbAbi = false)
|
||||||
|
@ -140,14 +137,19 @@ namespace Ryujinx.HLE.OsHle
|
||||||
|
|
||||||
MakeSymbolTable();
|
MakeSymbolTable();
|
||||||
|
|
||||||
MapRWMemRegion(
|
long MainStackTop = MemoryManager.CodeRegionEnd - KMemoryManager.PageSize;
|
||||||
MemoryRegions.MainStackAddress,
|
|
||||||
MemoryRegions.MainStackSize,
|
|
||||||
MemoryType.Normal);
|
|
||||||
|
|
||||||
long StackTop = MemoryRegions.MainStackAddress + MemoryRegions.MainStackSize;
|
long MainStackSize = 1 * 1024 * 1024;
|
||||||
|
|
||||||
int Handle = MakeThread(Executables[0].ImageBase, StackTop, 0, 44, 0);
|
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)
|
if (Handle == -1)
|
||||||
{
|
{
|
||||||
|
@ -158,7 +160,15 @@ namespace Ryujinx.HLE.OsHle
|
||||||
|
|
||||||
if (NeedsHbAbi)
|
if (NeedsHbAbi)
|
||||||
{
|
{
|
||||||
HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd);
|
HbAbiDataPosition = IntUtils.AlignUp(Executables[0].ImageEnd, KMemoryManager.PageSize);
|
||||||
|
|
||||||
|
const long HbAbiDataSize = KMemoryManager.PageSize;
|
||||||
|
|
||||||
|
MemoryManager.HleMapCustom(
|
||||||
|
HbAbiDataPosition,
|
||||||
|
HbAbiDataSize,
|
||||||
|
MemoryState.MappedMemory,
|
||||||
|
MemoryPermission.ReadAndWrite);
|
||||||
|
|
||||||
string SwitchPath = Ns.VFs.SystemPathToSwitchPath(Executables[0].FilePath);
|
string SwitchPath = Ns.VFs.SystemPathToSwitchPath(Executables[0].FilePath);
|
||||||
|
|
||||||
|
@ -173,11 +183,6 @@ namespace Ryujinx.HLE.OsHle
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MapRWMemRegion(long Position, long Size, MemoryType Type)
|
|
||||||
{
|
|
||||||
Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StopAllThreadsAsync()
|
public void StopAllThreadsAsync()
|
||||||
{
|
{
|
||||||
if (Disposed)
|
if (Disposed)
|
||||||
|
@ -190,9 +195,9 @@ namespace Ryujinx.HLE.OsHle
|
||||||
MainThread.Thread.StopExecution();
|
MainThread.Thread.StopExecution();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (AThread Thread in TlsSlots.Values)
|
foreach (KThread Thread in Threads.Values)
|
||||||
{
|
{
|
||||||
Thread.StopExecution();
|
Thread.Thread.StopExecution();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,9 +221,9 @@ namespace Ryujinx.HLE.OsHle
|
||||||
|
|
||||||
int Handle = HandleTable.OpenHandle(Thread);
|
int Handle = HandleTable.OpenHandle(Thread);
|
||||||
|
|
||||||
int ThreadId = GetFreeTlsSlot(CpuThread);
|
long Tpidr = GetFreeTls();
|
||||||
|
|
||||||
long Tpidr = MemoryRegions.TlsPagesAddress + ThreadId * TlsSize;
|
int ThreadId = (int)((Tpidr - MemoryManager.TlsIoRegionStart) / 0x200) + 1;
|
||||||
|
|
||||||
CpuThread.ThreadState.ProcessId = ProcessId;
|
CpuThread.ThreadState.ProcessId = ProcessId;
|
||||||
CpuThread.ThreadState.ThreadId = ThreadId;
|
CpuThread.ThreadState.ThreadId = ThreadId;
|
||||||
|
@ -240,6 +245,32 @@ namespace Ryujinx.HLE.OsHle
|
||||||
return Handle;
|
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 BreakHandler(object sender, AInstExceptionEventArgs e)
|
private void BreakHandler(object sender, AInstExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
throw new GuestBrokeExecutionException();
|
throw new GuestBrokeExecutionException();
|
||||||
|
@ -346,25 +377,10 @@ namespace Ryujinx.HLE.OsHle
|
||||||
return Name;
|
return Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetFreeTlsSlot(AThread Thread)
|
|
||||||
{
|
|
||||||
for (int Index = 1; Index < TotalTlsSlots; Index++)
|
|
||||||
{
|
|
||||||
if (TlsSlots.TryAdd(Index, Thread))
|
|
||||||
{
|
|
||||||
return Index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ThreadFinished(object sender, EventArgs e)
|
private void ThreadFinished(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is AThread Thread)
|
if (sender is AThread Thread)
|
||||||
{
|
{
|
||||||
TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _);
|
|
||||||
|
|
||||||
Threads.TryRemove(Thread.ThreadState.Tpidr, out KThread KernelThread);
|
Threads.TryRemove(Thread.ThreadState.Tpidr, out KThread KernelThread);
|
||||||
|
|
||||||
Scheduler.RemoveThread(KernelThread);
|
Scheduler.RemoveThread(KernelThread);
|
||||||
|
@ -372,7 +388,7 @@ namespace Ryujinx.HLE.OsHle
|
||||||
KernelThread.WaitEvent.Set();
|
KernelThread.WaitEvent.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TlsSlots.Count == 0)
|
if (Threads.Count == 0)
|
||||||
{
|
{
|
||||||
if (ShouldDispose)
|
if (ShouldDispose)
|
||||||
{
|
{
|
||||||
|
@ -383,11 +399,6 @@ namespace Ryujinx.HLE.OsHle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetTlsSlot(long Position)
|
|
||||||
{
|
|
||||||
return (int)((Position - MemoryRegions.TlsPagesAddress) / TlsSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KThread GetThread(long Tpidr)
|
public KThread GetThread(long Tpidr)
|
||||||
{
|
{
|
||||||
if (!Threads.TryGetValue(Tpidr, out KThread Thread))
|
if (!Threads.TryGetValue(Tpidr, out KThread Thread))
|
||||||
|
@ -411,7 +422,7 @@ namespace Ryujinx.HLE.OsHle
|
||||||
//safe as the thread may try to access those resources. Instead, we set
|
//safe as the thread may try to access those resources. Instead, we set
|
||||||
//the flag to have the Process disposed when all threads finishes.
|
//the flag to have the Process disposed when all threads finishes.
|
||||||
//Note: This may not happen if the guest code gets stuck on a infinite loop.
|
//Note: This may not happen if the guest code gets stuck on a infinite loop.
|
||||||
if (TlsSlots.Count > 0)
|
if (Threads.Count > 0)
|
||||||
{
|
{
|
||||||
ShouldDispose = true;
|
ShouldDispose = true;
|
||||||
|
|
||||||
|
@ -439,10 +450,6 @@ namespace Ryujinx.HLE.OsHle
|
||||||
|
|
||||||
AppletState.Dispose();
|
AppletState.Dispose();
|
||||||
|
|
||||||
SvcHandler.Dispose();
|
|
||||||
|
|
||||||
Memory.Dispose();
|
|
||||||
|
|
||||||
Ns.Log.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting...");
|
Ns.Log.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,9 @@ namespace Ryujinx.HLE.OsHle.Services.Hid
|
||||||
|
|
||||||
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
private HSharedMem HidSharedMem;
|
private KSharedMemory HidSharedMem;
|
||||||
|
|
||||||
public IAppletResource(HSharedMem HidSharedMem)
|
public IAppletResource(KSharedMemory HidSharedMem)
|
||||||
{
|
{
|
||||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
{
|
{
|
||||||
|
|
198
Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASCtx.cs
Normal file
198
Ryujinx.HLE/OsHle/Services/Nv/NvGpuAS/NvGpuASCtx.cs
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
using ChocolArm64.Memory;
|
||||||
|
using Ryujinx.HLE.Gpu.Memory;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
|
||||||
|
{
|
||||||
|
class NvGpuASCtx
|
||||||
|
{
|
||||||
|
public NvGpuVmm Vmm { get; private set; }
|
||||||
|
|
||||||
|
private class Range
|
||||||
|
{
|
||||||
|
public ulong Start { get; private set; }
|
||||||
|
public ulong End { get; private set; }
|
||||||
|
|
||||||
|
public Range(long Position, long Size)
|
||||||
|
{
|
||||||
|
Start = (ulong)Position;
|
||||||
|
End = (ulong)Size + Start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MappedMemory : Range
|
||||||
|
{
|
||||||
|
public long PhysicalAddress { get; private set; }
|
||||||
|
public bool VaAllocated { get; private set; }
|
||||||
|
|
||||||
|
public MappedMemory(
|
||||||
|
long Position,
|
||||||
|
long Size,
|
||||||
|
long PhysicalAddress,
|
||||||
|
bool VaAllocated) : base(Position, Size)
|
||||||
|
{
|
||||||
|
this.PhysicalAddress = PhysicalAddress;
|
||||||
|
this.VaAllocated = VaAllocated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SortedList<long, Range> Maps;
|
||||||
|
private SortedList<long, Range> Reservations;
|
||||||
|
|
||||||
|
public NvGpuASCtx(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
Vmm = new NvGpuVmm(Context.Memory);
|
||||||
|
|
||||||
|
Maps = new SortedList<long, Range>();
|
||||||
|
Reservations = new SortedList<long, Range>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ValidateFixedBuffer(long Position, long Size)
|
||||||
|
{
|
||||||
|
long MapEnd = Position + Size;
|
||||||
|
|
||||||
|
//Check if size is valid (0 is also not allowed).
|
||||||
|
if ((ulong)MapEnd <= (ulong)Position)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if address is page aligned.
|
||||||
|
if ((Position & NvGpuVmm.PageMask) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if region is reserved.
|
||||||
|
if (BinarySearch(Reservations, Position) == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check for overlap with already mapped buffers.
|
||||||
|
Range Map = BinarySearchLt(Maps, MapEnd);
|
||||||
|
|
||||||
|
if (Map != null && Map.End > (ulong)Position)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddMap(
|
||||||
|
long Position,
|
||||||
|
long Size,
|
||||||
|
long PhysicalAddress,
|
||||||
|
bool VaAllocated)
|
||||||
|
{
|
||||||
|
Maps.Add(Position, new MappedMemory(Position, Size, PhysicalAddress, VaAllocated));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveMap(long Position, out long Size)
|
||||||
|
{
|
||||||
|
Size = 0;
|
||||||
|
|
||||||
|
if (Maps.Remove(Position, out Range Value))
|
||||||
|
{
|
||||||
|
MappedMemory Map = (MappedMemory)Value;
|
||||||
|
|
||||||
|
if (Map.VaAllocated)
|
||||||
|
{
|
||||||
|
Size = (long)(Map.End - Map.Start);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetMapPhysicalAddress(long Position, out long PhysicalAddress)
|
||||||
|
{
|
||||||
|
Range Map = BinarySearch(Maps, Position);
|
||||||
|
|
||||||
|
if (Map != null)
|
||||||
|
{
|
||||||
|
PhysicalAddress = ((MappedMemory)Map).PhysicalAddress;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicalAddress = 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddReservation(long Position, long Size)
|
||||||
|
{
|
||||||
|
Reservations.Add(Position, new Range(Position, Size));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveReservation(long Position)
|
||||||
|
{
|
||||||
|
return Reservations.Remove(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Range BinarySearch(SortedList<long, Range> Lst, long Position)
|
||||||
|
{
|
||||||
|
int Left = 0;
|
||||||
|
int Right = Lst.Count - 1;
|
||||||
|
|
||||||
|
while (Left <= Right)
|
||||||
|
{
|
||||||
|
int Size = Right - Left;
|
||||||
|
|
||||||
|
int Middle = Left + (Size >> 1);
|
||||||
|
|
||||||
|
Range Rg = Lst.Values[Middle];
|
||||||
|
|
||||||
|
if ((ulong)Position >= Rg.Start && (ulong)Position < Rg.End)
|
||||||
|
{
|
||||||
|
return Rg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ulong)Position < Rg.Start)
|
||||||
|
{
|
||||||
|
Right = Middle - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Left = Middle + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Range BinarySearchLt(SortedList<long, Range> Lst, long Position)
|
||||||
|
{
|
||||||
|
Range LtRg = null;
|
||||||
|
|
||||||
|
int Left = 0;
|
||||||
|
int Right = Lst.Count - 1;
|
||||||
|
|
||||||
|
while (Left <= Right)
|
||||||
|
{
|
||||||
|
int Size = Right - Left;
|
||||||
|
|
||||||
|
int Middle = Left + (Size >> 1);
|
||||||
|
|
||||||
|
Range Rg = Lst.Values[Middle];
|
||||||
|
|
||||||
|
if ((ulong)Position < Rg.Start)
|
||||||
|
{
|
||||||
|
Right = Middle - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Left = Middle + 1;
|
||||||
|
|
||||||
|
LtRg = Rg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return LtRg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,11 +11,13 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
|
||||||
{
|
{
|
||||||
private const int FlagFixedOffset = 1;
|
private const int FlagFixedOffset = 1;
|
||||||
|
|
||||||
private static ConcurrentDictionary<Process, NvGpuVmm> Vmms;
|
private const int FlagRemapSubRange = 0x100;
|
||||||
|
|
||||||
|
private static ConcurrentDictionary<Process, NvGpuASCtx> ASCtxs;
|
||||||
|
|
||||||
static NvGpuASIoctl()
|
static NvGpuASIoctl()
|
||||||
{
|
{
|
||||||
Vmms = new ConcurrentDictionary<Process, NvGpuVmm>();
|
ASCtxs = new ConcurrentDictionary<Process, NvGpuASCtx>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int ProcessIoctl(ServiceCtx Context, int Cmd)
|
public static int ProcessIoctl(ServiceCtx Context, int Cmd)
|
||||||
|
@ -29,7 +31,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
|
||||||
case 0x4106: return MapBufferEx (Context);
|
case 0x4106: return MapBufferEx (Context);
|
||||||
case 0x4108: return GetVaRegions(Context);
|
case 0x4108: return GetVaRegions(Context);
|
||||||
case 0x4109: return InitializeEx(Context);
|
case 0x4109: return InitializeEx(Context);
|
||||||
case 0x4114: return Remap (Context);
|
case 0x4114: return Remap (Context, Cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotImplementedException(Cmd.ToString("x8"));
|
throw new NotImplementedException(Cmd.ToString("x8"));
|
||||||
|
@ -52,29 +54,38 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
|
||||||
|
|
||||||
NvGpuASAllocSpace Args = AMemoryHelper.Read<NvGpuASAllocSpace>(Context.Memory, InputPosition);
|
NvGpuASAllocSpace Args = AMemoryHelper.Read<NvGpuASAllocSpace>(Context.Memory, InputPosition);
|
||||||
|
|
||||||
NvGpuVmm Vmm = GetVmm(Context);
|
NvGpuASCtx ASCtx = GetASCtx(Context);
|
||||||
|
|
||||||
ulong Size = (ulong)Args.Pages *
|
ulong Size = (ulong)Args.Pages *
|
||||||
(ulong)Args.PageSize;
|
(ulong)Args.PageSize;
|
||||||
|
|
||||||
if ((Args.Flags & FlagFixedOffset) != 0)
|
|
||||||
{
|
|
||||||
Args.Offset = Vmm.Reserve(Args.Offset, (long)Size, 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Args.Offset = Vmm.Reserve((long)Size, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Result = NvResult.Success;
|
int Result = NvResult.Success;
|
||||||
|
|
||||||
if (Args.Offset < 0)
|
lock (ASCtx)
|
||||||
{
|
{
|
||||||
Args.Offset = 0;
|
//Note: When the fixed offset flag is not set,
|
||||||
|
//the Offset field holds the alignment size instead.
|
||||||
|
if ((Args.Flags & FlagFixedOffset) != 0)
|
||||||
|
{
|
||||||
|
Args.Offset = ASCtx.Vmm.ReserveFixed(Args.Offset, (long)Size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Args.Offset = ASCtx.Vmm.Reserve((long)Size, Args.Offset);
|
||||||
|
}
|
||||||
|
|
||||||
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"No memory to allocate size {Size:x16}!");
|
if (Args.Offset < 0)
|
||||||
|
{
|
||||||
|
Args.Offset = 0;
|
||||||
|
|
||||||
Result = NvResult.OutOfMemory;
|
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Failed to allocate size {Size:x16}!");
|
||||||
|
|
||||||
|
Result = NvResult.OutOfMemory;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASCtx.AddReservation(Args.Offset, (long)Size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
||||||
|
@ -89,14 +100,29 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
|
||||||
|
|
||||||
NvGpuASAllocSpace Args = AMemoryHelper.Read<NvGpuASAllocSpace>(Context.Memory, InputPosition);
|
NvGpuASAllocSpace Args = AMemoryHelper.Read<NvGpuASAllocSpace>(Context.Memory, InputPosition);
|
||||||
|
|
||||||
NvGpuVmm Vmm = GetVmm(Context);
|
NvGpuASCtx ASCtx = GetASCtx(Context);
|
||||||
|
|
||||||
ulong Size = (ulong)Args.Pages *
|
int Result = NvResult.Success;
|
||||||
(ulong)Args.PageSize;
|
|
||||||
|
|
||||||
Vmm.Free(Args.Offset, (long)Size);
|
lock (ASCtx)
|
||||||
|
{
|
||||||
|
ulong Size = (ulong)Args.Pages *
|
||||||
|
(ulong)Args.PageSize;
|
||||||
|
|
||||||
return NvResult.Success;
|
if (ASCtx.RemoveReservation(Args.Offset))
|
||||||
|
{
|
||||||
|
ASCtx.Vmm.Free(Args.Offset, (long)Size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Context.Ns.Log.PrintWarning(LogClass.ServiceNv,
|
||||||
|
$"Failed to free offset 0x{Args.Offset:x16} size 0x{Size:x16}!");
|
||||||
|
|
||||||
|
Result = NvResult.InvalidInput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int UnmapBuffer(ServiceCtx Context)
|
private static int UnmapBuffer(ServiceCtx Context)
|
||||||
|
@ -106,11 +132,21 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
|
||||||
|
|
||||||
NvGpuASUnmapBuffer Args = AMemoryHelper.Read<NvGpuASUnmapBuffer>(Context.Memory, InputPosition);
|
NvGpuASUnmapBuffer Args = AMemoryHelper.Read<NvGpuASUnmapBuffer>(Context.Memory, InputPosition);
|
||||||
|
|
||||||
NvGpuVmm Vmm = GetVmm(Context);
|
NvGpuASCtx ASCtx = GetASCtx(Context);
|
||||||
|
|
||||||
if (!Vmm.Unmap(Args.Offset))
|
lock (ASCtx)
|
||||||
{
|
{
|
||||||
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {Args.Offset:x16}!");
|
if (ASCtx.RemoveMap(Args.Offset, out long Size))
|
||||||
|
{
|
||||||
|
if (Size != 0)
|
||||||
|
{
|
||||||
|
ASCtx.Vmm.Free(Args.Offset, Size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {Args.Offset:x16}!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NvResult.Success;
|
return NvResult.Success;
|
||||||
|
@ -118,12 +154,14 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
|
||||||
|
|
||||||
private static int MapBufferEx(ServiceCtx Context)
|
private static int MapBufferEx(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
|
const string MapErrorMsg = "Failed to map fixed buffer with offset 0x{0:x16} and size 0x{1:x16}!";
|
||||||
|
|
||||||
long InputPosition = Context.Request.GetBufferType0x21().Position;
|
long InputPosition = Context.Request.GetBufferType0x21().Position;
|
||||||
long OutputPosition = Context.Request.GetBufferType0x22().Position;
|
long OutputPosition = Context.Request.GetBufferType0x22().Position;
|
||||||
|
|
||||||
NvGpuASMapBufferEx Args = AMemoryHelper.Read<NvGpuASMapBufferEx>(Context.Memory, InputPosition);
|
NvGpuASMapBufferEx Args = AMemoryHelper.Read<NvGpuASMapBufferEx>(Context.Memory, InputPosition);
|
||||||
|
|
||||||
NvGpuVmm Vmm = GetVmm(Context);
|
NvGpuASCtx ASCtx = GetASCtx(Context);
|
||||||
|
|
||||||
NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle);
|
NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle);
|
||||||
|
|
||||||
|
@ -134,7 +172,39 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
|
||||||
return NvResult.InvalidInput;
|
return NvResult.InvalidInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
long PA = Map.Address + Args.BufferOffset;
|
long PA;
|
||||||
|
|
||||||
|
if ((Args.Flags & FlagRemapSubRange) != 0)
|
||||||
|
{
|
||||||
|
lock (ASCtx)
|
||||||
|
{
|
||||||
|
if (ASCtx.TryGetMapPhysicalAddress(Args.Offset, out PA))
|
||||||
|
{
|
||||||
|
long VA = Args.Offset + Args.BufferOffset;
|
||||||
|
|
||||||
|
PA += Args.BufferOffset;
|
||||||
|
|
||||||
|
if (ASCtx.Vmm.Map(PA, VA, Args.MappingSize) < 0)
|
||||||
|
{
|
||||||
|
string Msg = string.Format(MapErrorMsg, VA, Args.MappingSize);
|
||||||
|
|
||||||
|
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, Msg);
|
||||||
|
|
||||||
|
return NvResult.InvalidInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NvResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Address 0x{Args.Offset:x16} not mapped!");
|
||||||
|
|
||||||
|
return NvResult.InvalidInput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PA = Map.Address + Args.BufferOffset;
|
||||||
|
|
||||||
long Size = Args.MappingSize;
|
long Size = Args.MappingSize;
|
||||||
|
|
||||||
|
@ -145,40 +215,44 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
|
||||||
|
|
||||||
int Result = NvResult.Success;
|
int Result = NvResult.Success;
|
||||||
|
|
||||||
//Note: When the fixed offset flag is not set,
|
lock (ASCtx)
|
||||||
//the Offset field holds the alignment size instead.
|
|
||||||
if ((Args.Flags & FlagFixedOffset) != 0)
|
|
||||||
{
|
{
|
||||||
long MapEnd = Args.Offset + Args.MappingSize;
|
//Note: When the fixed offset flag is not set,
|
||||||
|
//the Offset field holds the alignment size instead.
|
||||||
|
bool VaAllocated = (Args.Flags & FlagFixedOffset) == 0;
|
||||||
|
|
||||||
if ((ulong)MapEnd <= (ulong)Args.Offset)
|
if (!VaAllocated)
|
||||||
{
|
{
|
||||||
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Offset 0x{Args.Offset:x16} and size 0x{Args.MappingSize:x16} results in a overflow!");
|
if (ASCtx.ValidateFixedBuffer(Args.Offset, Size))
|
||||||
|
{
|
||||||
|
Args.Offset = ASCtx.Vmm.Map(PA, Args.Offset, Size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string Msg = string.Format(MapErrorMsg, Args.Offset, Size);
|
||||||
|
|
||||||
return NvResult.InvalidInput;
|
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, Msg);
|
||||||
|
|
||||||
|
Result = NvResult.InvalidInput;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if ((Args.Offset & NvGpuVmm.PageMask) != 0)
|
|
||||||
{
|
{
|
||||||
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Offset 0x{Args.Offset:x16} is not page aligned!");
|
Args.Offset = ASCtx.Vmm.Map(PA, Size);
|
||||||
|
|
||||||
return NvResult.InvalidInput;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Args.Offset = Vmm.Map(PA, Args.Offset, Size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Args.Offset = Vmm.Map(PA, Size);
|
|
||||||
|
|
||||||
if (Args.Offset < 0)
|
if (Args.Offset < 0)
|
||||||
{
|
{
|
||||||
Args.Offset = 0;
|
Args.Offset = 0;
|
||||||
|
|
||||||
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"No memory to map size {Args.MappingSize:x16}!");
|
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Failed to map size 0x{Size:x16}!");
|
||||||
|
|
||||||
Result = NvResult.InvalidInput;
|
Result = NvResult.InvalidInput;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASCtx.AddMap(Args.Offset, Size, PA, VaAllocated);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
||||||
|
@ -206,38 +280,50 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS
|
||||||
return NvResult.Success;
|
return NvResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int Remap(ServiceCtx Context)
|
private static int Remap(ServiceCtx Context, int Cmd)
|
||||||
{
|
{
|
||||||
|
int Count = ((Cmd >> 16) & 0xff) / 0x14;
|
||||||
|
|
||||||
long InputPosition = Context.Request.GetBufferType0x21().Position;
|
long InputPosition = Context.Request.GetBufferType0x21().Position;
|
||||||
|
|
||||||
NvGpuASRemap Args = AMemoryHelper.Read<NvGpuASRemap>(Context.Memory, InputPosition);
|
for (int Index = 0; Index < Count; Index++, InputPosition += 0x14)
|
||||||
|
|
||||||
NvGpuVmm Vmm = GetVmm(Context);
|
|
||||||
|
|
||||||
NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle);
|
|
||||||
|
|
||||||
if (Map == null)
|
|
||||||
{
|
{
|
||||||
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!");
|
NvGpuASRemap Args = AMemoryHelper.Read<NvGpuASRemap>(Context.Memory, InputPosition);
|
||||||
|
|
||||||
return NvResult.InvalidInput;
|
NvGpuVmm Vmm = GetASCtx(Context).Vmm;
|
||||||
|
|
||||||
|
NvMapHandle Map = NvMapIoctl.GetNvMapWithFb(Context, Args.NvMapHandle);
|
||||||
|
|
||||||
|
if (Map == null)
|
||||||
|
{
|
||||||
|
Context.Ns.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!");
|
||||||
|
|
||||||
|
return NvResult.InvalidInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
long Result = Vmm.Map(Map.Address, (long)(uint)Args.Offset << 16,
|
||||||
|
(long)(uint)Args.Pages << 16);
|
||||||
|
|
||||||
|
if (Result < 0)
|
||||||
|
{
|
||||||
|
Context.Ns.Log.PrintWarning(LogClass.ServiceNv,
|
||||||
|
$"Page 0x{Args.Offset:x16} size 0x{Args.Pages:x16} not allocated!");
|
||||||
|
|
||||||
|
return NvResult.InvalidInput;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//FIXME: This is most likely wrong...
|
|
||||||
Vmm.Map(Map.Address, (long)(uint)Args.Offset << 16,
|
|
||||||
(long)(uint)Args.Pages << 16);
|
|
||||||
|
|
||||||
return NvResult.Success;
|
return NvResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NvGpuVmm GetVmm(ServiceCtx Context)
|
public static NvGpuASCtx GetASCtx(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
return Vmms.GetOrAdd(Context.Process, (Key) => new NvGpuVmm(Context.Memory));
|
return ASCtxs.GetOrAdd(Context.Process, (Key) => new NvGpuASCtx(Context));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UnloadProcess(Process Process)
|
public static void UnloadProcess(Process Process)
|
||||||
{
|
{
|
||||||
Vmms.TryRemove(Process, out _);
|
ASCtxs.TryRemove(Process, out _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -88,7 +88,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel
|
||||||
|
|
||||||
NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read<NvHostChannelSubmitGpfifo>(Context.Memory, InputPosition);
|
NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read<NvHostChannelSubmitGpfifo>(Context.Memory, InputPosition);
|
||||||
|
|
||||||
NvGpuVmm Vmm = NvGpuASIoctl.GetVmm(Context);
|
NvGpuVmm Vmm = NvGpuASIoctl.GetASCtx(Context).Vmm;;
|
||||||
|
|
||||||
for (int Index = 0; Index < Args.NumEntries; Index++)
|
for (int Index = 0; Index < Args.NumEntries; Index++)
|
||||||
{
|
{
|
||||||
|
@ -162,7 +162,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel
|
||||||
|
|
||||||
NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read<NvHostChannelSubmitGpfifo>(Context.Memory, InputPosition);
|
NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read<NvHostChannelSubmitGpfifo>(Context.Memory, InputPosition);
|
||||||
|
|
||||||
NvGpuVmm Vmm = NvGpuASIoctl.GetVmm(Context);
|
NvGpuVmm Vmm = NvGpuASIoctl.GetASCtx(Context).Vmm;;
|
||||||
|
|
||||||
for (int Index = 0; Index < Args.NumEntries; Index++)
|
for (int Index = 0; Index < Args.NumEntries; Index++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -120,7 +120,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostCtrl
|
||||||
|
|
||||||
Context.Ns.Log.PrintDebug(Logging.LogClass.ServiceNv, $"Got setting {Domain}!{Name}");
|
Context.Ns.Log.PrintDebug(Logging.LogClass.ServiceNv, $"Got setting {Domain}!{Name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return NvResult.Success;
|
return NvResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
|
||||||
{
|
{
|
||||||
public int Handle;
|
public int Handle;
|
||||||
public int Padding;
|
public int Padding;
|
||||||
public long RefCount;
|
public long Address;
|
||||||
public int Size;
|
public int Size;
|
||||||
public int Flags;
|
public int Flags;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
|
||||||
|
|
||||||
public long DecrementRefCount()
|
public long DecrementRefCount()
|
||||||
{
|
{
|
||||||
return Interlocked.Decrement(ref Dupes) + 1;
|
return Interlocked.Decrement(ref Dupes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -129,7 +129,8 @@ namespace Ryujinx.HLE.OsHle.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.
|
||||||
if (!Context.Ns.Os.Allocator.TryAllocate((uint)Size, out Address))
|
//TODO: Is this allocation inside the transfer memory?
|
||||||
|
if (!Context.Ns.Memory.Allocator.TryAllocate((uint)Size, out Address))
|
||||||
{
|
{
|
||||||
Result = NvResult.OutOfMemory;
|
Result = NvResult.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
@ -163,23 +164,22 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
|
||||||
return NvResult.InvalidInput;
|
return NvResult.InvalidInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
long OldRefCount = Map.DecrementRefCount();
|
if (Map.DecrementRefCount() <= 0)
|
||||||
|
|
||||||
if (OldRefCount <= 1)
|
|
||||||
{
|
{
|
||||||
DeleteNvMap(Context, Args.Handle);
|
DeleteNvMap(Context, Args.Handle);
|
||||||
|
|
||||||
Context.Ns.Log.PrintInfo(LogClass.ServiceNv, $"Deleted map {Args.Handle}!");
|
Context.Ns.Log.PrintInfo(LogClass.ServiceNv, $"Deleted map {Args.Handle}!");
|
||||||
|
|
||||||
Args.Flags = 0;
|
Args.Address = Map.Address;
|
||||||
|
Args.Flags = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Args.Flags = FlagNotFreedYet;
|
Args.Address = 0;
|
||||||
|
Args.Flags = FlagNotFreedYet;
|
||||||
}
|
}
|
||||||
|
|
||||||
Args.RefCount = OldRefCount;
|
Args.Size = Map.Size;
|
||||||
Args.Size = Map.Size;
|
|
||||||
|
|
||||||
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
AMemoryHelper.Write(Context.Memory, OutputPosition, Args);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using Ryujinx.HLE.Font;
|
using Ryujinx.HLE.OsHle.Font;
|
||||||
using Ryujinx.HLE.OsHle.Ipc;
|
using Ryujinx.HLE.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ namespace Ryujinx.HLE.OsHle.Services.Pl
|
||||||
{
|
{
|
||||||
SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32();
|
SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32();
|
||||||
|
|
||||||
Context.Ns.Font.Load(FontType);
|
//We don't need to do anything here because we do lazy initialization
|
||||||
|
//on SharedFontManager (the font is loaded when necessary).
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,9 @@ namespace Ryujinx.HLE.OsHle.Services.Pl
|
||||||
{
|
{
|
||||||
SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32();
|
SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32();
|
||||||
|
|
||||||
Context.ResponseData.Write(Context.Ns.Font.GetLoadState(FontType));
|
//1 (true) indicates that the font is already loaded.
|
||||||
|
//All fonts are already loaded.
|
||||||
|
Context.ResponseData.Write(1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +47,7 @@ namespace Ryujinx.HLE.OsHle.Services.Pl
|
||||||
{
|
{
|
||||||
SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32();
|
SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32();
|
||||||
|
|
||||||
Context.ResponseData.Write(Context.Ns.Font.GetFontSize(FontType));
|
Context.ResponseData.Write(Context.Ns.Os.Font.GetFontSize(FontType));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -54,13 +56,15 @@ namespace Ryujinx.HLE.OsHle.Services.Pl
|
||||||
{
|
{
|
||||||
SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32();
|
SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32();
|
||||||
|
|
||||||
Context.ResponseData.Write(Context.Ns.Font.GetSharedMemoryAddressOffset(FontType));
|
Context.ResponseData.Write(Context.Ns.Os.Font.GetSharedMemoryAddressOffset(FontType));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetSharedMemoryNativeHandle(ServiceCtx Context)
|
public long GetSharedMemoryNativeHandle(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
|
Context.Ns.Os.Font.EnsureInitialized();
|
||||||
|
|
||||||
int Handle = Context.Process.HandleTable.OpenHandle(Context.Ns.Os.FontSharedMem);
|
int Handle = Context.Process.HandleTable.OpenHandle(Context.Ns.Os.FontSharedMem);
|
||||||
|
|
||||||
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
|
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
|
||||||
|
@ -68,50 +72,54 @@ namespace Ryujinx.HLE.OsHle.Services.Pl
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private uint AddFontToOrderOfPriorityList(ServiceCtx Context, SharedFontType FontType, uint BufferPos, out uint LoadState)
|
public long GetSharedFontInOrderOfPriority(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long TypesPosition = Context.Request.ReceiveBuff[0].Position;
|
long LanguageCode = Context.RequestData.ReadInt64();
|
||||||
long TypesSize = Context.Request.ReceiveBuff[0].Size;
|
int LoadedCount = 0;
|
||||||
|
|
||||||
long OffsetsPosition = Context.Request.ReceiveBuff[1].Position;
|
for (SharedFontType Type = 0; Type < SharedFontType.Count; Type++)
|
||||||
long OffsetsSize = Context.Request.ReceiveBuff[1].Size;
|
{
|
||||||
|
int Offset = (int)Type * 4;
|
||||||
|
|
||||||
|
if (!AddFontToOrderOfPriorityList(Context, (SharedFontType)Type, Offset))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Context.ResponseData.Write(LoadedCount);
|
||||||
|
Context.ResponseData.Write((int)SharedFontType.Count);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AddFontToOrderOfPriorityList(ServiceCtx Context, SharedFontType FontType, int Offset)
|
||||||
|
{
|
||||||
|
long TypesPosition = Context.Request.ReceiveBuff[0].Position;
|
||||||
|
long TypesSize = Context.Request.ReceiveBuff[0].Size;
|
||||||
|
|
||||||
|
long OffsetsPosition = Context.Request.ReceiveBuff[1].Position;
|
||||||
|
long OffsetsSize = Context.Request.ReceiveBuff[1].Size;
|
||||||
|
|
||||||
long FontSizeBufferPosition = Context.Request.ReceiveBuff[2].Position;
|
long FontSizeBufferPosition = Context.Request.ReceiveBuff[2].Position;
|
||||||
long FontSizeBufferSize = Context.Request.ReceiveBuff[2].Size;
|
long FontSizeBufferSize = Context.Request.ReceiveBuff[2].Size;
|
||||||
|
|
||||||
LoadState = Context.Ns.Font.GetLoadState(FontType);
|
if ((uint)Offset + 4 > (uint)TypesSize ||
|
||||||
|
(uint)Offset + 4 > (uint)OffsetsSize ||
|
||||||
if (BufferPos >= TypesSize || BufferPos >= OffsetsSize || BufferPos >= FontSizeBufferSize)
|
(uint)Offset + 4 > (uint)FontSizeBufferSize)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.Memory.WriteUInt32(TypesPosition + BufferPos, (uint)FontType);
|
Context.Memory.WriteInt32(TypesPosition + Offset, (int)FontType);
|
||||||
Context.Memory.WriteUInt32(OffsetsPosition + BufferPos, Context.Ns.Font.GetSharedMemoryAddressOffset(FontType));
|
|
||||||
Context.Memory.WriteUInt32(FontSizeBufferPosition + BufferPos, Context.Ns.Font.GetFontSize(FontType));
|
|
||||||
|
|
||||||
BufferPos += 4;
|
Context.Memory.WriteInt32(OffsetsPosition + Offset, Context.Ns.Os.Font.GetSharedMemoryAddressOffset(FontType));
|
||||||
|
|
||||||
return BufferPos;
|
Context.Memory.WriteInt32(FontSizeBufferPosition + Offset, Context.Ns.Os.Font.GetFontSize(FontType));
|
||||||
}
|
|
||||||
|
|
||||||
public long GetSharedFontInOrderOfPriority(ServiceCtx Context)
|
return true;
|
||||||
{
|
|
||||||
ulong LanguageCode = Context.RequestData.ReadUInt64();
|
|
||||||
uint LoadedCount = 0;
|
|
||||||
uint BufferPos = 0;
|
|
||||||
uint Loaded = 0;
|
|
||||||
|
|
||||||
for (int Type = 0; Type < Context.Ns.Font.Count; Type++)
|
|
||||||
{
|
|
||||||
BufferPos = AddFontToOrderOfPriorityList(Context, (SharedFontType)Type, BufferPos, out Loaded);
|
|
||||||
LoadedCount += Loaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
Context.ResponseData.Write(LoadedCount);
|
|
||||||
Context.ResponseData.Write(Context.Ns.Font.Count);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -159,7 +159,7 @@ namespace Ryujinx.HLE.OsHle.Services.Android
|
||||||
|
|
||||||
int Slot = GetFreeSlotBlocking(Width, Height);
|
int Slot = GetFreeSlotBlocking(Width, Height);
|
||||||
|
|
||||||
return MakeReplyParcel(Context, Slot, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
return MakeReplyParcel(Context, Slot, 1, 0x24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader)
|
private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader)
|
||||||
|
|
|
@ -11,5 +11,15 @@ namespace Ryujinx.HLE.OsHle.Utilities
|
||||||
{
|
{
|
||||||
return (Value + (Size - 1)) & ~((long)Size - 1);
|
return (Value + (Size - 1)) & ~((long)Size - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int AlignDown(int Value, int Size)
|
||||||
|
{
|
||||||
|
return Value & ~(Size - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long AlignDown(long Value, int Size)
|
||||||
|
{
|
||||||
|
return Value & ~((long)Size - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE
|
namespace Ryujinx.HLE
|
||||||
{
|
{
|
||||||
public class PerformanceStatistics
|
public class PerformanceStatistics
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
using Ryujinx.Audio;
|
using Ryujinx.Audio;
|
||||||
using Ryujinx.Graphics.Gal;
|
using Ryujinx.Graphics.Gal;
|
||||||
using Ryujinx.HLE.Font;
|
|
||||||
using Ryujinx.HLE.Gpu;
|
using Ryujinx.HLE.Gpu;
|
||||||
using Ryujinx.HLE.Input;
|
using Ryujinx.HLE.Input;
|
||||||
using Ryujinx.HLE.Logging;
|
using Ryujinx.HLE.Logging;
|
||||||
|
using Ryujinx.HLE.Memory;
|
||||||
using Ryujinx.HLE.OsHle;
|
using Ryujinx.HLE.OsHle;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@ namespace Ryujinx.HLE
|
||||||
|
|
||||||
public Logger Log { get; private set; }
|
public Logger Log { get; private set; }
|
||||||
|
|
||||||
|
internal DeviceMemory Memory { get; private set; }
|
||||||
|
|
||||||
internal NvGpu Gpu { get; private set; }
|
internal NvGpu Gpu { get; private set; }
|
||||||
|
|
||||||
internal VirtualFileSystem VFs { get; private set; }
|
internal VirtualFileSystem VFs { get; private set; }
|
||||||
|
@ -25,8 +27,6 @@ namespace Ryujinx.HLE
|
||||||
|
|
||||||
public Hid Hid { get; private set; }
|
public Hid Hid { get; private set; }
|
||||||
|
|
||||||
public SharedFontManager Font { get; private set; }
|
|
||||||
|
|
||||||
public event EventHandler Finish;
|
public event EventHandler Finish;
|
||||||
|
|
||||||
public Switch(IGalRenderer Renderer, IAalOutput AudioOut)
|
public Switch(IGalRenderer Renderer, IAalOutput AudioOut)
|
||||||
|
@ -45,6 +45,8 @@ namespace Ryujinx.HLE
|
||||||
|
|
||||||
Log = new Logger();
|
Log = new Logger();
|
||||||
|
|
||||||
|
Memory = new DeviceMemory();
|
||||||
|
|
||||||
Gpu = new NvGpu(Renderer);
|
Gpu = new NvGpu(Renderer);
|
||||||
|
|
||||||
VFs = new VirtualFileSystem();
|
VFs = new VirtualFileSystem();
|
||||||
|
@ -53,15 +55,7 @@ namespace Ryujinx.HLE
|
||||||
|
|
||||||
Statistics = new PerformanceStatistics();
|
Statistics = new PerformanceStatistics();
|
||||||
|
|
||||||
Hid = new Hid(Log);
|
Hid = new Hid(this, Os.HidSharedMem.PA);
|
||||||
|
|
||||||
Font = new SharedFontManager(Log, VFs.GetSystemPath());
|
|
||||||
|
|
||||||
Os.HidSharedMem.MemoryMapped += Hid.ShMemMap;
|
|
||||||
Os.HidSharedMem.MemoryUnmapped += Hid.ShMemUnmap;
|
|
||||||
|
|
||||||
Os.FontSharedMem.MemoryMapped += Font.ShMemMap;
|
|
||||||
Os.FontSharedMem.MemoryUnmapped += Font.ShMemUnmap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadCart(string ExeFsDir, string RomFsFile = null)
|
public void LoadCart(string ExeFsDir, string RomFsFile = null)
|
||||||
|
|
|
@ -5,6 +5,7 @@ using ChocolArm64.State;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Intrinsics;
|
using System.Runtime.Intrinsics;
|
||||||
using System.Runtime.Intrinsics.X86;
|
using System.Runtime.Intrinsics.X86;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -19,6 +20,8 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
private long EntryPoint;
|
private long EntryPoint;
|
||||||
|
|
||||||
|
private IntPtr RamPointer;
|
||||||
|
|
||||||
private AMemory Memory;
|
private AMemory Memory;
|
||||||
private AThread Thread;
|
private AThread Thread;
|
||||||
|
|
||||||
|
@ -31,15 +34,16 @@ namespace Ryujinx.Tests.Cpu
|
||||||
EntryPoint = Position;
|
EntryPoint = Position;
|
||||||
|
|
||||||
ATranslator Translator = new ATranslator();
|
ATranslator Translator = new ATranslator();
|
||||||
Memory = new AMemory();
|
RamPointer = Marshal.AllocHGlobal(new IntPtr(Size));
|
||||||
Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute);
|
Memory = new AMemory(RamPointer);
|
||||||
|
Memory.Map(Position, 0, Size);
|
||||||
Thread = new AThread(Translator, Memory, EntryPoint);
|
Thread = new AThread(Translator, Memory, EntryPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TearDown]
|
[TearDown]
|
||||||
public void Teardown()
|
public void Teardown()
|
||||||
{
|
{
|
||||||
Memory.Dispose();
|
Marshal.FreeHGlobal(RamPointer);
|
||||||
Memory = null;
|
Memory = null;
|
||||||
Thread = null;
|
Thread = null;
|
||||||
}
|
}
|
||||||
|
@ -52,7 +56,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
protected void Opcode(uint Opcode)
|
protected void Opcode(uint Opcode)
|
||||||
{
|
{
|
||||||
Thread.Memory.WriteUInt32Unchecked(Position, Opcode);
|
Thread.Memory.WriteUInt32(Position, Opcode);
|
||||||
Position += 4;
|
Position += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,6 @@ namespace Ryujinx
|
||||||
|
|
||||||
IniParser Parser = new IniParser(IniPath);
|
IniParser Parser = new IniParser(IniPath);
|
||||||
|
|
||||||
AOptimizations.DisableMemoryChecks = !Convert.ToBoolean(Parser.Value("Enable_Memory_Checks"));
|
|
||||||
|
|
||||||
GraphicsConfig.ShadersDumpPath = Parser.Value("Graphics_Shaders_Dump_Path");
|
GraphicsConfig.ShadersDumpPath = Parser.Value("Graphics_Shaders_Dump_Path");
|
||||||
|
|
||||||
Device.Log.SetEnable(LogLevel.Debug, Convert.ToBoolean(Parser.Value("Logging_Enable_Debug")));
|
Device.Log.SetEnable(LogLevel.Debug, Convert.ToBoolean(Parser.Value("Logging_Enable_Debug")));
|
||||||
|
|
Loading…
Reference in a new issue