Started rewriting the memory manager

This commit is contained in:
gdkchan 2018-07-27 00:57:34 -03:00
parent add87856db
commit ef9b87ef79
48 changed files with 2001 additions and 1361 deletions

View file

@ -2,8 +2,6 @@ using System.Runtime.Intrinsics.X86;
public static class AOptimizations
{
public static bool DisableMemoryChecks = false;
public static bool GenerateCallStack = true;
private static bool UseAllSseIfAvailable = true;

View file

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

View file

@ -45,46 +45,21 @@ namespace ChocolArm64.Instruction
{
switch (Size)
{
case 0: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.ReadVector8Unchecked)
: nameof(AMemory.ReadVector8); break;
case 1: Name = AOptimizations.DisableMemoryChecks
? 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;
case 0: Name = nameof(AMemory.ReadVector8); break;
case 1: Name = nameof(AMemory.ReadVector16); break;
case 2: Name = nameof(AMemory.ReadVector32); break;
case 3: Name = nameof(AMemory.ReadVector64); break;
case 4: Name = nameof(AMemory.ReadVector128); break;
}
}
else
{
switch (Size)
{
case 0: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.ReadByteUnchecked)
: nameof(AMemory.ReadByte); 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;
case 0: Name = nameof(AMemory.ReadByte); break;
case 1: Name = nameof(AMemory.ReadUInt16); break;
case 2: Name = nameof(AMemory.ReadUInt32); break;
case 3: Name = nameof(AMemory.ReadUInt64); break;
}
}
@ -132,46 +107,21 @@ namespace ChocolArm64.Instruction
{
switch (Size)
{
case 0: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.WriteVector8Unchecked)
: nameof(AMemory.WriteVector8); break;
case 1: Name = AOptimizations.DisableMemoryChecks
? 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;
case 0: Name = nameof(AMemory.WriteVector8); break;
case 1: Name = nameof(AMemory.WriteVector16); break;
case 2: Name = nameof(AMemory.WriteVector32); break;
case 3: Name = nameof(AMemory.WriteVector64); break;
case 4: Name = nameof(AMemory.WriteVector128); break;
}
}
else
{
switch (Size)
{
case 0: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.WriteByteUnchecked)
: nameof(AMemory.WriteByte); 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;
case 0: Name = nameof(AMemory.WriteByte); break;
case 1: Name = nameof(AMemory.WriteUInt16); break;
case 2: Name = nameof(AMemory.WriteUInt32); break;
case 3: Name = nameof(AMemory.WriteUInt64); break;
}
}

View file

@ -1,6 +1,7 @@
using ChocolArm64.Exceptions;
using ChocolArm64.State;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -10,11 +11,24 @@ using System.Threading;
namespace ChocolArm64.Memory
{
public unsafe class AMemory : IAMemory, IDisposable
public unsafe class AMemory : IAMemory
{
private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1;
private const int PTLvl0Bits = 10;
private const int PTLvl1Bits = 10;
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
{
@ -33,28 +47,257 @@ namespace ChocolArm64.Memory
private byte* RamPtr;
private int HostPageSize;
private byte*** PageTable;
public AMemory()
private ConcurrentDictionary<long, IntPtr> ObservedPages;
public AMemory(IntPtr Ram)
{
Manager = new AMemoryMgr();
Monitors = new Dictionary<int, ArmMonitor>();
IntPtr Size = (IntPtr)AMemoryMgr.RamSize + AMemoryMgr.PageSize;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Ram = AMemoryWin32.Allocate(Size);
HostPageSize = AMemoryWin32.GetPageSize(Ram, Size);
}
else
{
Ram = Marshal.AllocHGlobal(Size);
}
this.Ram = Ram;
RamPtr = (byte*)Ram;
PageTable = (byte***)Marshal.AllocHGlobal(PTLvl0Size * IntPtr.Size);
for (int L0 = 0; L0 < PTLvl0Size; L0++)
{
PageTable[L0] = null;
}
ObservedPages = new ConcurrentDictionary<long, IntPtr>();
}
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;
byte** Lvl1 = PageTable[L0];
if (Lvl1 == null)
{
return HandleNullPte(Position);
}
long Old = Position;
Position &= PageMask;
byte* Ptr = Lvl1[L1];
if (Ptr == null)
{
return HandleNullPte(Old);
}
return Ptr + Position;
}
private byte* HandleNullPte(long Position)
{
long Key = Position >> PTPageBits;
if (ObservedPages.TryGetValue(Key, out IntPtr Ptr))
{
return (byte*)Ptr + (Position & PageMask);
}
return RamPtr;
}
internal byte* TranslateWrite(long Position)
{
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
byte** Lvl1 = PageTable[L0];
if (Lvl1 == null)
{
return HandleNullPteWrite(Position);
}
long Old = Position;
Position &= PageMask;
byte* Ptr = Lvl1[L1];
if (Ptr == null)
{
return HandleNullPteWrite(Old);
}
return Ptr + Position;
}
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);
}
return RamPtr;
}
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[] IsRegionModified(long Position, long Size)
{
long EndPosition = (Position + Size + PageMask) & ~PageMask;
Position &= ~PageMask;
Size = EndPosition - Position;
bool[] Modified = new bool[Size >> PTPageBits];
lock (ObservedPages)
{
for (int Page = 0; Page < Modified.Length; Page++)
{
byte* Ptr = Translate(Position);
if (ObservedPages.TryAdd(Position >> PTPageBits, (IntPtr)Ptr))
{
Modified[Page] = true;
}
else
{
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
byte** Lvl1 = PageTable[L0];
if (Lvl1 != null)
{
Modified[Page] = Lvl1[L1] != null;
}
}
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);
}
public bool IsValidPosition(long Position)
{
return Position >> (PTLvl0Bits + PTLvl1Bits + PTPageBits) == 0;
}
public void RemoveMonitor(AThreadState State)
@ -155,62 +398,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)
{
return (sbyte)ReadByte(Position);
@ -233,33 +420,22 @@ namespace ChocolArm64.Memory
public byte ReadByte(long Position)
{
EnsureAccessIsValid(Position, AMemoryPerm.Read);
return ReadByteUnchecked(Position);
return *((byte*)Translate(Position));
}
public ushort ReadUInt16(long Position)
{
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
EnsureAccessIsValid(Position + 1, AMemoryPerm.Read);
return ReadUInt16Unchecked(Position);
return *((ushort*)Translate(Position));
}
public uint ReadUInt32(long Position)
{
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
EnsureAccessIsValid(Position + 3, AMemoryPerm.Read);
return ReadUInt32Unchecked(Position);
return *((uint*)Translate(Position));
}
public ulong ReadUInt64(long Position)
{
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
EnsureAccessIsValid(Position + 7, AMemoryPerm.Read);
return ReadUInt64Unchecked(Position);
return *((ulong*)Translate(Position));
}
public Vector128<float> ReadVector8(long Position)
@ -274,6 +450,7 @@ namespace ChocolArm64.Memory
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector128<float> ReadVector16(long Position)
{
if (Sse2.IsSupported)
@ -286,14 +463,12 @@ namespace ChocolArm64.Memory
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
public Vector128<float> ReadVector32(long Position)
{
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
EnsureAccessIsValid(Position + 3, AMemoryPerm.Read);
if (Sse.IsSupported)
{
return Sse.LoadScalarVector128((float*)(RamPtr + (uint)Position));
return Sse.LoadScalarVector128((float*)Translate(Position));
}
else
{
@ -301,14 +476,12 @@ namespace ChocolArm64.Memory
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
public Vector128<float> ReadVector64(long Position)
{
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
EnsureAccessIsValid(Position + 7, AMemoryPerm.Read);
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
{
@ -316,118 +489,12 @@ namespace ChocolArm64.Memory
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector128<float> ReadVector128(long Position)
{
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
EnsureAccessIsValid(Position + 15, AMemoryPerm.Read);
if (Sse.IsSupported)
{
return Sse.LoadVector128((float*)(RamPtr + (uint)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));
return Sse.LoadVector128((float*)Translate(Position));
}
else
{
@ -442,11 +509,11 @@ namespace ChocolArm64.Memory
throw new ArgumentOutOfRangeException(nameof(Size));
}
EnsureRangeIsValid(Position, Size, AMemoryPerm.Read);
EnsureRangeIsValid(Position, 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;
}
@ -473,35 +540,25 @@ namespace ChocolArm64.Memory
public void WriteByte(long Position, byte Value)
{
EnsureAccessIsValid(Position, AMemoryPerm.Write);
WriteByteUnchecked(Position, Value);
*((byte*)TranslateWrite(Position)) = Value;
}
public void WriteUInt16(long Position, ushort Value)
{
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
EnsureAccessIsValid(Position + 1, AMemoryPerm.Write);
WriteUInt16Unchecked(Position, Value);
*((ushort*)TranslateWrite(Position)) = Value;
}
public void WriteUInt32(long Position, uint Value)
{
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
EnsureAccessIsValid(Position + 3, AMemoryPerm.Write);
WriteUInt32Unchecked(Position, Value);
*((uint*)TranslateWrite(Position)) = Value;
}
public void WriteUInt64(long Position, ulong Value)
{
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
EnsureAccessIsValid(Position + 7, AMemoryPerm.Write);
WriteUInt64Unchecked(Position, Value);
*((ulong*)TranslateWrite(Position)) = Value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector8(long Position, Vector128<float> Value)
{
if (Sse41.IsSupported)
@ -518,6 +575,7 @@ namespace ChocolArm64.Memory
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector16(long Position, Vector128<float> Value)
{
if (Sse2.IsSupported)
@ -530,14 +588,12 @@ namespace ChocolArm64.Memory
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
public void WriteVector32(long Position, Vector128<float> Value)
{
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
EnsureAccessIsValid(Position + 3, AMemoryPerm.Write);
if (Sse.IsSupported)
{
Sse.StoreScalar((float*)(RamPtr + (uint)Position), Value);
Sse.StoreScalar((float*)TranslateWrite(Position), Value);
}
else
{
@ -545,14 +601,12 @@ namespace ChocolArm64.Memory
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
public void WriteVector64(long Position, Vector128<float> Value)
{
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
EnsureAccessIsValid(Position + 7, AMemoryPerm.Write);
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
{
@ -560,123 +614,12 @@ namespace ChocolArm64.Memory
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector128(long Position, Vector128<float> Value)
{
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
EnsureAccessIsValid(Position + 15, AMemoryPerm.Write);
if (Sse.IsSupported)
{
Sse.Store((float*)(RamPtr + (uint)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);
Sse.Store((float*)TranslateWrite(Position), Value);
}
else
{
@ -686,58 +629,31 @@ namespace ChocolArm64.Memory
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)
internal void EnsureRangeIsValid(long Position, long Size)
{
long EndPos = Position + Size;
Position &= ~AMemoryMgr.PageMask;
Position &= ~PageMask;
while ((ulong)Position < (ulong)EndPos)
{
EnsureAccessIsValid(Position, Perm);
EnsureAccessIsValid(Position);
Position += AMemoryMgr.PageSize;
Position += PageSize;
}
}
private void EnsureAccessIsValid(long Position, AMemoryPerm Perm)
private void EnsureAccessIsValid(long Position)
{
if (!Manager.IsMapped(Position))
if (!IsMapped(Position))
{
throw new VmmPageFaultException(Position);
}
if (!Manager.HasPermission(Position, Perm))
{
throw new VmmAccessViolationException(Position, Perm);
}
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (Ram != IntPtr.Zero)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
AMemoryWin32.Free(Ram);
}
else
{
Marshal.FreeHGlobal(Ram);
}
Ram = IntPtr.Zero;
}
}
}
}

View file

@ -26,12 +26,9 @@ namespace ChocolArm64.Memory
{
long Size = Marshal.SizeOf<T>();
if ((ulong)(Position + Size) > AMemoryMgr.AddrSize)
{
throw new ArgumentOutOfRangeException(nameof(Position));
}
Memory.EnsureRangeIsValid(Position, Size);
IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position);
IntPtr Ptr = (IntPtr)Memory.Translate(Position);
return Marshal.PtrToStructure<T>(Ptr);
}
@ -40,12 +37,9 @@ namespace ChocolArm64.Memory
{
long Size = Marshal.SizeOf<T>();
if ((ulong)(Position + Size) > AMemoryMgr.AddrSize)
{
throw new ArgumentOutOfRangeException(nameof(Position));
}
Memory.EnsureRangeIsValid(Position, Size);
IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position);
IntPtr Ptr = (IntPtr)Memory.TranslateWrite(Position);
Marshal.StructureToPtr<T>(Value, Ptr, false);
}
@ -69,15 +63,5 @@ namespace ChocolArm64.Memory
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;
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -418,6 +418,12 @@ namespace Ryujinx.HLE.Gpu.Engines
Key = Vmm.GetPhysicalAddress(Key);
if (Key == -1)
{
//FIXME: Should'nt ignore invalid addresses.
return;
}
if (IsFrameBufferPosition(Key))
{
//This texture is a frame buffer texture,

View file

@ -1,4 +1,5 @@
using ChocolArm64.Memory;
using Ryujinx.HLE.Memory;
using System;
using System.Collections.Generic;
@ -129,14 +130,9 @@ namespace Ryujinx.HLE.Gpu.Memory
{
(bool[] Modified, long ModifiedCount) = Memory.IsRegionModified(PA, Size);
if (Modified == null)
{
return true;
}
ClearCachedPagesIfNeeded();
long PageSize = Memory.GetHostPageSize();
long PageSize = AMemory.PageSize;
EnsureResidencyInitialized(PageSize);
@ -159,9 +155,9 @@ namespace Ryujinx.HLE.Gpu.Memory
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);
@ -228,7 +224,7 @@ namespace Ryujinx.HLE.Gpu.Memory
{
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++)
{

View file

@ -72,7 +72,7 @@ namespace Ryujinx.HLE.Gpu.Texture
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
byte Pixel = CpuMem.ReadByteUnchecked(Position + Offset);
byte Pixel = CpuMem.ReadByte(Position + Offset);
*(BuffPtr + OutOffs) = Pixel;
@ -105,7 +105,7 @@ namespace Ryujinx.HLE.Gpu.Texture
{
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 & 0x03e0) << 1 |
@ -143,7 +143,7 @@ namespace Ryujinx.HLE.Gpu.Texture
{
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 & 0x07e0) |
@ -180,7 +180,7 @@ namespace Ryujinx.HLE.Gpu.Texture
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
short Pixel = CpuMem.ReadInt16Unchecked(Position + Offset);
short Pixel = CpuMem.ReadInt16(Position + Offset);
*(short*)(BuffPtr + OutOffs) = Pixel;
@ -213,7 +213,7 @@ namespace Ryujinx.HLE.Gpu.Texture
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
int Pixel = CpuMem.ReadInt32Unchecked(Position + Offset);
int Pixel = CpuMem.ReadInt32(Position + Offset);
*(int*)(BuffPtr + OutOffs) = Pixel;
@ -246,7 +246,7 @@ namespace Ryujinx.HLE.Gpu.Texture
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
long Pixel = CpuMem.ReadInt64Unchecked(Position + Offset);
long Pixel = CpuMem.ReadInt64(Position + Offset);
*(long*)(BuffPtr + OutOffs) = Pixel;
@ -279,8 +279,8 @@ namespace Ryujinx.HLE.Gpu.Texture
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
long PxLow = CpuMem.ReadInt64Unchecked(Position + Offset + 0);
long PxHigh = CpuMem.ReadInt64Unchecked(Position + Offset + 8);
long PxLow = CpuMem.ReadInt64(Position + Offset + 0);
long PxHigh = CpuMem.ReadInt64(Position + Offset + 8);
*(long*)(BuffPtr + OutOffs + 0) = PxLow;
*(long*)(BuffPtr + OutOffs + 8) = PxHigh;
@ -314,7 +314,7 @@ namespace Ryujinx.HLE.Gpu.Texture
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
long Tile = CpuMem.ReadInt64Unchecked(Position + Offset);
long Tile = CpuMem.ReadInt64(Position + Offset);
*(long*)(BuffPtr + OutOffs) = Tile;
@ -347,8 +347,8 @@ namespace Ryujinx.HLE.Gpu.Texture
{
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
long Tile0 = CpuMem.ReadInt64Unchecked(Position + Offset + 0);
long Tile1 = CpuMem.ReadInt64Unchecked(Position + Offset + 8);
long Tile0 = CpuMem.ReadInt64(Position + Offset + 0);
long Tile1 = CpuMem.ReadInt64(Position + Offset + 8);
*(long*)(BuffPtr + OutOffs + 0) = Tile0;
*(long*)(BuffPtr + OutOffs + 8) = Tile1;

View file

@ -25,7 +25,7 @@ namespace Ryujinx.HLE.Gpu.Texture
int Pixel = *(int*)(BuffPtr + InOffs);
CpuMem.WriteInt32Unchecked(Position + Offset, Pixel);
CpuMem.WriteInt32(Position + Offset, Pixel);
InOffs += 4;
}

View file

@ -63,57 +63,21 @@ namespace Ryujinx.HLE.Input
private const int HidEntryCount = 17;
private Logger Log;
private Switch Device;
private object ShMemLock;
private long HidPosition;
private (AMemory, long, long)[] ShMemPositions;
public Hid(Logger Log)
public Hid(Switch Device, long HidPosition)
{
this.Log = Log;
this.Device = Device;
this.HidPosition = HidPosition;
ShMemLock = new object();
ShMemPositions = new (AMemory, long, long)[0];
}
internal void ShMemMap(object sender, EventArgs e)
{
HSharedMem SharedMem = (HSharedMem)sender;
lock (ShMemLock)
for (long Offset = 0; Offset < Horizon.HidSize; Offset += 8)
{
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);
Device.Memory.WriteInt64(HidPosition + Offset, 0);
}
}
internal void ShMemUnmap(object sender, EventArgs e)
{
HSharedMem SharedMem = (HSharedMem)sender;
lock (ShMemLock)
{
ShMemPositions = SharedMem.GetVirtualPositions();
}
}
private void Init(AMemory Memory, long Position)
{
InitializeJoyconPair(
Memory,
Position,
JoyConColor.Body_Neon_Red,
JoyConColor.Buttons_Neon_Red,
JoyConColor.Body_Neon_Blue,
@ -121,14 +85,12 @@ namespace Ryujinx.HLE.Input
}
private void InitializeJoyconPair(
AMemory Memory,
long Position,
JoyConColor LeftColorBody,
JoyConColor LeftColorButtons,
JoyConColor RightColorBody,
JoyConColor RightColorButtons)
{
long BaseControllerOffset = Position + HidControllersOffset + 8 * HidControllerSize;
long BaseControllerOffset = HidPosition + HidControllersOffset + 8 * HidControllerSize;
HidControllerType Type = HidControllerType.ControllerType_Handheld;
@ -142,20 +104,20 @@ namespace Ryujinx.HLE.Input
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);
Memory.WriteInt32Unchecked(BaseControllerOffset + 0xc, (int)SingleColorBody);
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x10, (int)SingleColorButtons);
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x14, (int)SplitColorDesc);
Device.Memory.WriteInt32(BaseControllerOffset + 0x8, (int)SingleColorDesc);
Device.Memory.WriteInt32(BaseControllerOffset + 0xc, (int)SingleColorBody);
Device.Memory.WriteInt32(BaseControllerOffset + 0x10, (int)SingleColorButtons);
Device.Memory.WriteInt32(BaseControllerOffset + 0x14, (int)SplitColorDesc);
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x18, (int)LeftColorBody);
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x1c, (int)LeftColorButtons);
Device.Memory.WriteInt32(BaseControllerOffset + 0x18, (int)LeftColorBody);
Device.Memory.WriteInt32(BaseControllerOffset + 0x1c, (int)LeftColorButtons);
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x20, (int)RightColorBody);
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x24, (int)RightColorButtons);
Device.Memory.WriteInt32(BaseControllerOffset + 0x20, (int)RightColorBody);
Device.Memory.WriteInt32(BaseControllerOffset + 0x24, (int)RightColorButtons);
}
public void SetJoyconButton(
@ -165,107 +127,95 @@ namespace Ryujinx.HLE.Input
HidJoystickPosition LeftStick,
HidJoystickPosition RightStick)
{
lock (ShMemLock)
{
foreach ((AMemory Memory, long Position, long Size) in ShMemPositions)
{
long ControllerOffset = Position + HidControllersOffset;
long ControllerOffset = HidPosition + 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);
Memory.WriteInt64Unchecked(ControllerOffset + 0x8, HidEntryCount);
Memory.WriteInt64Unchecked(ControllerOffset + 0x10, CurrEntry);
Memory.WriteInt64Unchecked(ControllerOffset + 0x18, HidEntryCount - 1);
Device.Memory.WriteInt64(ControllerOffset + 0x0, Timestamp);
Device.Memory.WriteInt64(ControllerOffset + 0x8, HidEntryCount);
Device.Memory.WriteInt64(ControllerOffset + 0x10, CurrEntry);
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);
Memory.WriteInt64Unchecked(ControllerOffset + 0x8, SampleCounter);
Device.Memory.WriteInt64(ControllerOffset + 0x0, 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);
Memory.WriteInt32Unchecked(ControllerOffset + 0x1c, LeftStick.DY);
Device.Memory.WriteInt32(ControllerOffset + 0x18, LeftStick.DX);
Device.Memory.WriteInt32(ControllerOffset + 0x1c, LeftStick.DY);
Memory.WriteInt32Unchecked(ControllerOffset + 0x20, RightStick.DX);
Memory.WriteInt32Unchecked(ControllerOffset + 0x24, RightStick.DY);
Device.Memory.WriteInt32(ControllerOffset + 0x20, RightStick.DX);
Device.Memory.WriteInt32(ControllerOffset + 0x24, RightStick.DY);
Memory.WriteInt64Unchecked(ControllerOffset + 0x28,
(uint)HidControllerConnState.Controller_State_Connected |
(uint)HidControllerConnState.Controller_State_Wired);
}
}
Device.Memory.WriteInt64(ControllerOffset + 0x28,
(uint)HidControllerConnState.Controller_State_Connected |
(uint)HidControllerConnState.Controller_State_Wired);
}
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)
{
long TouchScreenOffset = Position + HidTouchScreenOffset;
Device.Memory.WriteInt64(TouchEntryOffset + 0x0, Timestamp);
Device.Memory.WriteInt32(TouchEntryOffset + 0x8, Padding);
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);
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;
}
}
TouchEntryOffset += HidTouchEntryTouchSize;
}
}

View file

@ -1,6 +1,8 @@
using ChocolArm64.Memory;
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.OsHle;
using Ryujinx.HLE.OsHle.Handles;
using Ryujinx.HLE.OsHle.Utilities;
using System.Collections.Generic;
using System.IO;
@ -18,12 +20,14 @@ namespace Ryujinx.HLE.Loaders
public string FilePath { get; private set; }
private AMemory Memory;
public long ImageBase { 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>();
@ -36,23 +40,34 @@ namespace Ryujinx.HLE.Loaders
Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, ""));
}
this.Memory = Memory;
this.ImageBase = ImageBase;
this.ImageEnd = ImageBase;
this.Memory = Memory;
this.MemoryManager = MemoryManager;
this.ImageBase = ImageBase;
this.ImageEnd = ImageBase;
WriteData(ImageBase + Exe.TextOffset, Exe.Text, MemoryType.CodeStatic, AMemoryPerm.RX);
WriteData(ImageBase + Exe.ROOffset, Exe.RO, MemoryType.CodeMutable, AMemoryPerm.Read);
WriteData(ImageBase + Exe.DataOffset, Exe.Data, MemoryType.CodeMutable, AMemoryPerm.RW);
long TextPosition = ImageBase + (uint)Exe.TextOffset;
long ROPosition = ImageBase + (uint)Exe.ROOffset;
long DataPosition = ImageBase + (uint)Exe.DataOffset;
long TextSize = (uint)IntUtils.AlignUp(Exe.Text.Length, KMemoryManager.PageSize);
long ROSize = (uint)IntUtils.AlignUp(Exe.RO.Length, KMemoryManager.PageSize);
long DataSize = (uint)IntUtils.AlignUp(Exe.Data.Length, KMemoryManager.PageSize);
long 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)
{
int BssOffset = Exe.DataOffset + Exe.Data.Length;
int BssSize = Exe.BssSize;
MapBss(ImageBase + BssOffset, BssSize);
ImageEnd = ImageBase + BssOffset + BssSize;
return;
}
@ -66,10 +81,6 @@ namespace Ryujinx.HLE.Loaders
long EhHdrEndOffset = Memory.ReadInt32(Mod0Offset + 0x14) + Mod0Offset;
long ModObjOffset = Memory.ReadInt32(Mod0Offset + 0x18) + Mod0Offset;
MapBss(BssStartOffset, BssEndOffset - BssStartOffset);
ImageEnd = BssEndOffset;
while (true)
{
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)
{
long Offset = Memory.ReadInt64(Position + 0);

View file

@ -0,0 +1,103 @@
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 ArenaAllocator(long 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;
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);
}
}
}
}

View file

@ -0,0 +1,115 @@
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 Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
Marshal.FreeHGlobal(RamPointer);
}
}
}

View file

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

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

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

View file

@ -0,0 +1,745 @@
using ChocolArm64.Memory;
using Ryujinx.HLE.Memory;
using Ryujinx.HLE.OsHle.Kernel;
using System;
using System.Collections.Generic;
using static Ryujinx.HLE.OsHle.ErrorCode;
namespace Ryujinx.HLE.OsHle.Handles
{
class KMemoryManager
{
public const int PageSize = 0x1000;
private LinkedList<KMemoryBlock> Blocks;
private AMemory CpuMemoryManager;
private ArenaAllocator Allocator;
public long AddrSpaceStart { get; private set; }
public long AddrSpaceEnd { get; private set; }
public long HeapRegionStart { get; private set; }
public long HeapRegionEnd { get; private set; }
public long TlsIoRegionStart { get; private set; }
public long TlsIoRegionEnd { get; private set; }
private long CurrentHeapAddr;
public KMemoryManager(AMemory CpuMemoryManager, ArenaAllocator Allocator)
{
this.CpuMemoryManager = CpuMemoryManager;
this.Allocator = Allocator;
AddrSpaceStart = 0;
AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
HeapRegionStart = MemoryRegions.HeapRegionAddress;
HeapRegionEnd = MemoryRegions.HeapRegionAddress + MemoryRegions.HeapRegionSize;
CurrentHeapAddr = HeapRegionStart;
Blocks = new LinkedList<KMemoryBlock>();
long AddrSpacePagesCount = (AddrSpaceEnd - AddrSpaceStart) / PageSize;
InsertBlockUnsafe(
AddrSpaceStart,
AddrSpacePagesCount,
MemoryState.Unmapped,
MemoryPermission.None,
MemoryAttribute.None);
}
public void HleMapProcessCode(long Position, long Size)
{
long PagesCount = Size / PageSize;
if (!Allocator.TryAllocate(Size, out long PA))
{
throw new InvalidOperationException();
}
lock (Blocks)
{
InsertBlockUnsafe(
Position,
PagesCount,
MemoryState.CodeStatic,
MemoryPermission.ReadAndExecute,
MemoryAttribute.None);
CpuMemoryManager.Map(Position, PA, Size);
}
}
public void HleMapCustom(long Position, long Size, MemoryState State, MemoryPermission Permission)
{
long PagesCount = Size / PageSize;
if (!Allocator.TryAllocate(Size, out long PA))
{
throw new InvalidOperationException();
}
lock (Blocks)
{
InsertBlockUnsafe(Position, PagesCount, State, Permission, MemoryAttribute.None);
CpuMemoryManager.Map(Position, PA, Size);
}
}
public long TrySetHeapSize(long Size, out long Position)
{
Position = 0;
if ((ulong)Size > (ulong)(HeapRegionEnd - HeapRegionStart))
{
return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
}
bool Success = false;
long CurrentHeapSize = GetHeapSize();
if ((ulong)CurrentHeapSize <= (ulong)Size)
{
//Expand.
long DiffSize = Size - CurrentHeapSize;
if (!Allocator.TryAllocate(DiffSize, out long PA))
{
return MakeError(ErrorModule.Kernel, KernelErr.OutOfMemory);
}
lock (Blocks)
{
if (Success = CheckUnmappedUnsafe(CurrentHeapAddr, DiffSize))
{
long PagesCount = DiffSize / PageSize;
InsertBlockUnsafe(
CurrentHeapAddr,
PagesCount,
MemoryState.Heap,
MemoryPermission.ReadAndWrite,
MemoryAttribute.None);
CpuMemoryManager.Map(CurrentHeapAddr, PA, DiffSize);
}
}
}
else
{
//Shrink.
long FreeAddr = HeapRegionStart + Size;
long DiffSize = CurrentHeapSize - Size;
lock (Blocks)
{
Success = CheckRangeUnsafe(
FreeAddr,
DiffSize,
MemoryState.Mask,
MemoryState.Heap,
MemoryPermission.Mask,
MemoryPermission.ReadAndWrite,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
out _);
if (Success)
{
long PagesCount = DiffSize / PageSize;
InsertBlockUnsafe(
FreeAddr,
PagesCount,
MemoryState.Unmapped,
MemoryPermission.None,
MemoryAttribute.None);
CpuMemoryManager.Unmap(FreeAddr, DiffSize);
}
}
}
CurrentHeapAddr = HeapRegionStart + Size;
if (Success)
{
Position = HeapRegionStart;
return 0;
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long GetHeapSize()
{
return CurrentHeapAddr - HeapRegionStart;
}
public long SetMemoryAttribute(
long Position,
long Size,
MemoryAttribute AttributeMask,
MemoryAttribute AttributeValue)
{
lock (Blocks)
{
if (CheckRangeUnsafe(
Position,
Size,
MemoryState.AttributeChangeAllowed,
MemoryState.AttributeChangeAllowed,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.BorrowedAndIpcMapped,
MemoryAttribute.None,
MemoryAttribute.DeviceMappedAndUncached,
out MemoryState State,
out MemoryPermission Permission,
out MemoryAttribute Attribute))
{
long PagesCount = Size / PageSize;
Attribute &= ~AttributeMask;
Attribute |= AttributeMask & AttributeValue;
InsertBlockUnsafe(Position, PagesCount, State, Permission, Attribute);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public KMemoryInfo QueryMemory(long Position)
{
if ((ulong)Position >= (ulong)AddrSpaceStart &&
(ulong)Position < (ulong)AddrSpaceEnd)
{
lock (Blocks)
{
return FindBlockUnsafe(Position).GetInfo();
}
}
else
{
return new KMemoryInfo(
AddrSpaceEnd,
-AddrSpaceEnd,
MemoryState.Reserved,
MemoryPermission.None,
MemoryAttribute.None,
0,
0);
}
}
public long Map(long Src, long Dst, long Size)
{
long PagesCount = Size / PageSize;
bool Success;
lock (Blocks)
{
Success = CheckRangeUnsafe(
Src,
Size,
MemoryState.MapAllowed,
MemoryState.MapAllowed,
MemoryPermission.Mask,
MemoryPermission.ReadAndWrite,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out MemoryState SrcState,
out _,
out _);
Success &= CheckUnmappedUnsafe(Dst, Size);
if (Success)
{
InsertBlockUnsafe(
Src,
PagesCount,
SrcState,
MemoryPermission.None,
MemoryAttribute.Borrowed);
InsertBlockUnsafe(
Dst,
PagesCount,
MemoryState.MappedMemory,
MemoryPermission.ReadAndWrite,
MemoryAttribute.None);
long PA = CpuMemoryManager.GetPhysicalAddress(Src);
CpuMemoryManager.Map(Dst, PA, Size);
}
}
return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long Unmap(long Src, long Dst, long Size)
{
long PagesCount = Size / PageSize;
bool Success;
lock (Blocks)
{
Success = CheckRangeUnsafe(
Src,
Size,
MemoryState.MapAllowed,
MemoryState.MapAllowed,
MemoryPermission.Mask,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.Borrowed,
MemoryAttribute.IpcAndDeviceMapped,
out MemoryState SrcState,
out _,
out _);
Success &= CheckRangeUnsafe(
Dst,
Size,
MemoryState.Mask,
MemoryState.MappedMemory,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
out _);
if (Success)
{
InsertBlockUnsafe(
Src,
PagesCount,
SrcState,
MemoryPermission.ReadAndWrite,
MemoryAttribute.None);
InsertBlockUnsafe(
Dst,
PagesCount,
MemoryState.Unmapped,
MemoryPermission.None,
MemoryAttribute.None);
CpuMemoryManager.Unmap(Dst, Size);
}
}
return Success ? 0 : MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long MapSharedMemory(KSharedMemory SharedMemory, MemoryPermission Permission, long Position)
{
lock (Blocks)
{
if (CheckUnmappedUnsafe(Position, SharedMemory.Size))
{
long PagesCount = SharedMemory.Size / PageSize;
InsertBlockUnsafe(
Position,
PagesCount,
MemoryState.SharedMemory,
Permission,
MemoryAttribute.None);
CpuMemoryManager.Map(Position, SharedMemory.PA, SharedMemory.Size);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long UnmapSharedMemory(long Position, long Size)
{
lock (Blocks)
{
if (CheckRangeUnsafe(
Position,
Size,
MemoryState.Mask,
MemoryState.SharedMemory,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out MemoryState State,
out _,
out _))
{
long PagesCount = Size / PageSize;
InsertBlockUnsafe(
Position,
PagesCount,
MemoryState.Unmapped,
MemoryPermission.None,
MemoryAttribute.None);
CpuMemoryManager.Unmap(Position, Size);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long ReserveTransferMemory(long Position, long Size, MemoryPermission Permission)
{
lock (Blocks)
{
if (CheckRangeUnsafe(
Position,
Size,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
MemoryPermission.Mask,
MemoryPermission.ReadAndWrite,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out MemoryState State,
out _,
out MemoryAttribute Attribute))
{
long PagesCount = Size / PageSize;
Attribute |= MemoryAttribute.Borrowed;
InsertBlockUnsafe(
Position,
PagesCount,
State,
Permission,
Attribute);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long ResetTransferMemory(long Position, long Size)
{
lock (Blocks)
{
if (CheckRangeUnsafe(
Position,
Size,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
MemoryState.TransferMemoryAllowed | MemoryState.IsPoolAllocated,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.Borrowed,
MemoryAttribute.IpcAndDeviceMapped,
out MemoryState State,
out _,
out _))
{
long PagesCount = Size / PageSize;
InsertBlockUnsafe(
Position,
PagesCount,
State,
MemoryPermission.ReadAndWrite,
MemoryAttribute.None);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
public long SetProcessMemoryPermission(long Position, long Size, MemoryPermission Permission)
{
lock (Blocks)
{
if (CheckRangeUnsafe(
Position,
Size,
MemoryState.ProcessPermissionChangeAllowed,
MemoryState.ProcessPermissionChangeAllowed,
MemoryPermission.None,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out MemoryState State,
out _,
out _))
{
if (State == MemoryState.CodeStatic)
{
State = MemoryState.CodeMutable;
}
else if (State == MemoryState.ModCodeStatic)
{
State = MemoryState.ModCodeMutable;
}
else
{
throw new InvalidOperationException();
}
long PagesCount = Size / PageSize;
InsertBlockUnsafe(Position, PagesCount, State, Permission, MemoryAttribute.None);
return 0;
}
}
return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
}
private bool CheckUnmappedUnsafe(long Dst, long Size)
{
return CheckRangeUnsafe(
Dst,
Size,
MemoryState.Mask,
MemoryState.Unmapped,
MemoryPermission.Mask,
MemoryPermission.None,
MemoryAttribute.Mask,
MemoryAttribute.None,
MemoryAttribute.IpcAndDeviceMapped,
out _,
out _,
out _);
}
private bool CheckRangeUnsafe(
long Dst,
long Size,
MemoryState StateMask,
MemoryState StateExpected,
MemoryPermission PermissionMask,
MemoryPermission PermissionExpected,
MemoryAttribute AttributeMask,
MemoryAttribute AttributeExpected,
MemoryAttribute AttributeIgnoreMask,
out MemoryState OutState,
out MemoryPermission OutPermission,
out MemoryAttribute OutAttribute)
{
KMemoryInfo BlkInfo = FindBlockUnsafe(Dst).GetInfo();
ulong Start = (ulong)Dst;
ulong End = (ulong)Size + Start;
if (End <= (ulong)(BlkInfo.Position + BlkInfo.Size))
{
if ((BlkInfo.Attribute & AttributeMask) == AttributeExpected &&
(BlkInfo.State & StateMask) == StateExpected &&
(BlkInfo.Permission & PermissionMask) == PermissionExpected)
{
OutState = BlkInfo.State;
OutPermission = BlkInfo.Permission;
OutAttribute = BlkInfo.Attribute & ~AttributeIgnoreMask;
return true;
}
}
OutState = MemoryState.Unmapped;
OutPermission = MemoryPermission.None;
OutAttribute = MemoryAttribute.None;
return false;
}
private void InsertBlockUnsafe(
long BasePosition,
long PagesCount,
MemoryState State,
MemoryPermission Permission,
MemoryAttribute Attribute)
{
KMemoryBlock Block = new KMemoryBlock(
BasePosition,
PagesCount,
State,
Permission,
Attribute);
ulong Start = (ulong)BasePosition;
ulong End = (ulong)PagesCount * PageSize + Start;
LinkedListNode<KMemoryBlock> NewNode = null;
LinkedListNode<KMemoryBlock> Node = Blocks.First;
while (Node != null)
{
KMemoryBlock CurrBlock = Node.Value;
LinkedListNode<KMemoryBlock> NextNode = Node.Next;
ulong CurrStart = (ulong)CurrBlock.BasePosition;
ulong CurrEnd = (ulong)CurrBlock.PagesCount * PageSize + CurrStart;
if (Start < CurrEnd && CurrStart < End)
{
if (Start >= CurrStart && End <= CurrEnd)
{
Block.Attribute |= CurrBlock.Attribute & MemoryAttribute.IpcAndDeviceMapped;
}
if (Start > CurrStart && End < CurrEnd)
{
CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
Node = Blocks.AddAfter(Node, Block);
KMemoryBlock NewBlock = new KMemoryBlock(
(long)End,
(long)((CurrEnd - End) / PageSize),
CurrBlock.State,
CurrBlock.Permission,
CurrBlock.Attribute);
Blocks.AddAfter(Node, NewBlock);
return;
}
else if (Start <= CurrStart && End < CurrEnd)
{
CurrBlock.BasePosition = (long)End;
CurrBlock.PagesCount = (long)((CurrEnd - End) / PageSize);
if (NewNode == null)
{
NewNode = Blocks.AddBefore(Node, Block);
}
}
else if (Start > CurrStart && End >= CurrEnd)
{
CurrBlock.PagesCount = (long)((Start - CurrStart) / PageSize);
if (NewNode == null)
{
NewNode = Blocks.AddAfter(Node, Block);
}
}
else
{
if (NewNode == null)
{
NewNode = Blocks.AddBefore(Node, Block);
}
Blocks.Remove(Node);
}
}
Node = NextNode;
}
if (NewNode == null)
{
NewNode = Blocks.AddFirst(Block);
}
if (NewNode.Previous != null)
{
KMemoryBlock Previous = NewNode.Previous.Value;
if (BlockStateEquals(Block, Previous))
{
Blocks.Remove(NewNode.Previous);
Block.BasePosition = Previous.BasePosition;
Start = (ulong)Block.BasePosition;
}
}
if (NewNode.Next != null)
{
KMemoryBlock Next = NewNode.Next.Value;
if (BlockStateEquals(Block, Next))
{
Blocks.Remove(NewNode.Next);
End = (ulong)(Next.BasePosition + Next.PagesCount * PageSize);
}
}
Block.PagesCount = (long)((End - Start) / PageSize);
}
private static bool BlockStateEquals(KMemoryBlock LHS, KMemoryBlock RHS)
{
return LHS.State == RHS.State &&
LHS.Permission == RHS.Permission &&
LHS.Attribute == RHS.Attribute &&
LHS.DeviceRefCount == RHS.DeviceRefCount &&
LHS.IpcRefCount == RHS.IpcRefCount;
}
private KMemoryBlock FindBlockUnsafe(long Position)
{
ulong Addr = (ulong)Position;
lock (Blocks)
{
LinkedListNode<KMemoryBlock> Node = Blocks.First;
while (Node != null)
{
KMemoryBlock Block = Node.Value;
ulong Start = (ulong)Block.BasePosition;
ulong End = (ulong)Block.PagesCount * PageSize + Start;
if (Start <= Addr && End - 1 >= Addr)
{
return Block;
}
Node = Node.Next;
}
}
return null;
}
}
}

View file

@ -0,0 +1,18 @@
using ChocolArm64.Memory;
using System;
using System.Collections.Generic;
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;
}
}
}

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

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

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

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

View file

@ -10,23 +10,23 @@ namespace Ryujinx.HLE.OsHle
//http://switchbrew.org/index.php?title=Homebrew_ABI
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);
//NextLoadPath
//NextLoadPath.
WriteConfigEntry(Memory, ref Position, 2, 0, Position + 0x200, Position + 0x400);
// Argv
//Argv.
long ArgvPosition = Position + 0xC00;
WriteConfigEntry(Memory, ref Position, 5, 0, 0, ArgvPosition);
Memory.WriteBytes(ArgvPosition, Encoding.ASCII.GetBytes(SwitchPath + "\0"));
//AppletType
WriteConfigEntry(Memory, ref Position, 5, 0, 0, ArgvPosition);
//AppletType.
WriteConfigEntry(Memory, ref Position, 7);
//EndOfList
//EndOfList.
WriteConfigEntry(Memory, ref Position, 0);
}

View file

@ -12,7 +12,7 @@ namespace Ryujinx.HLE.OsHle
public class Horizon : IDisposable
{
internal const int HidSize = 0x40000;
internal const int FontSize = 0x50;
internal const int FontSize = 0x1100000;
private Switch Ns;
@ -22,10 +22,8 @@ namespace Ryujinx.HLE.OsHle
public SystemStateMgr SystemState { get; private set; }
internal MemoryAllocator Allocator { get; private set; }
internal HSharedMem HidSharedMem { get; private set; }
internal HSharedMem FontSharedMem { get; private set; }
internal KSharedMemory HidSharedMem { get; private set; }
internal KSharedMemory FontSharedMem { get; private set; }
internal KEvent VsyncEvent { get; private set; }
@ -39,10 +37,14 @@ namespace Ryujinx.HLE.OsHle
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();
FontSharedMem = new HSharedMem();
HidSharedMem = new KSharedMemory(HidPA, HidSize);
FontSharedMem = new KSharedMemory(FontPA, FontSize);
VsyncEvent = new KEvent();
}

View file

@ -2,18 +2,21 @@ namespace Ryujinx.HLE.OsHle.Kernel
{
static class KernelErr
{
public const int InvalidAlignment = 102;
public const int InvalidAddress = 106;
public const int InvalidMemRange = 110;
public const int InvalidPriority = 112;
public const int InvalidCoreId = 113;
public const int InvalidHandle = 114;
public const int InvalidCoreMask = 116;
public const int Timeout = 117;
public const int Canceled = 118;
public const int CountOutOfRange = 119;
public const int InvalidEnumValue = 120;
public const int InvalidThread = 122;
public const int InvalidState = 125;
public const int InvalidSize = 101;
public const int InvalidAddress = 102;
public const int OutOfMemory = 104;
public const int NoAccessPerm = 106;
public const int InvalidPermission = 108;
public const int InvalidMemRange = 110;
public const int InvalidPriority = 112;
public const int InvalidCoreId = 113;
public const int InvalidHandle = 114;
public const int InvalidMaskValue = 116;
public const int Timeout = 117;
public const int Canceled = 118;
public const int CountOutOfRange = 119;
public const int InvalidEnumValue = 120;
public const int InvalidThread = 122;
public const int InvalidState = 125;
}
}

View file

@ -10,7 +10,7 @@ using System.Threading;
namespace Ryujinx.HLE.OsHle.Kernel
{
partial class SvcHandler : IDisposable
partial class SvcHandler
{
private delegate void SvcFunc(AThreadState ThreadState);
@ -22,10 +22,6 @@ namespace Ryujinx.HLE.OsHle.Kernel
private ConcurrentDictionary<KThread, AutoResetEvent> SyncWaits;
private HashSet<(HSharedMem, long, long)> MappedSharedMems;
private ulong CurrentHeapSize;
private const uint SelfThreadHandle = 0xffff8000;
private const uint SelfProcessHandle = 0xffff8001;
@ -70,8 +66,8 @@ namespace Ryujinx.HLE.OsHle.Kernel
{ 0x26, SvcBreak },
{ 0x27, SvcOutputDebugString },
{ 0x29, SvcGetInfo },
{ 0x2c, SvcMapPhysicalMemory },
{ 0x2d, SvcUnmapPhysicalMemory },
//{ 0x2c, SvcMapPhysicalMemory },
//{ 0x2d, SvcUnmapPhysicalMemory },
{ 0x32, SvcSetThreadActivity },
{ 0x33, SvcGetThreadContext3 },
{ 0x34, SvcWaitForAddress }
@ -82,8 +78,6 @@ namespace Ryujinx.HLE.OsHle.Kernel
this.Memory = Process.Memory;
SyncWaits = new ConcurrentDictionary<KThread, AutoResetEvent>();
MappedSharedMems = new HashSet<(HSharedMem, long, long)>();
}
static SvcHandler()
@ -126,26 +120,5 @@ namespace Ryujinx.HLE.OsHle.Kernel
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();
}
}
}
}
}

View file

@ -13,41 +13,83 @@ namespace Ryujinx.HLE.OsHle.Kernel
{
uint Size = (uint)ThreadState.X1;
long Position = MemoryRegions.HeapRegionAddress;
if (Size > CurrentHeapSize)
if ((Size & 0x1fffff) != 0)
{
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
{
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)
{
long Position = (long)ThreadState.X0;
long Size = (long)ThreadState.X1;
int State0 = (int)ThreadState.X2;
int State1 = (int)ThreadState.X3;
if ((State0 == 0 && State1 == 0) ||
(State0 == 8 && State1 == 0))
if (!PageAligned(Position))
{
Memory.Manager.ClearAttrBit(Position, Size, 3);
}
else if (State0 == 8 && State1 == 8)
{
Memory.Manager.SetAttrBit(Position, Size, 3);
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
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)
@ -56,33 +98,50 @@ namespace Ryujinx.HLE.OsHle.Kernel
long Src = (long)ThreadState.X1;
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 (!InsideAddrSpace(Src, Size) || Src == 0)
{
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
if (!InsideMapRegion(Dst, Size))
{
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
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}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
return;
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src);
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;
ThreadState.X0 = (ulong)Result;
}
private void SvcUnmapMemory(AThreadState ThreadState)
@ -91,33 +150,50 @@ namespace Ryujinx.HLE.OsHle.Kernel
long Src = (long)ThreadState.X1;
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 (!InsideAddrSpace(Src, Size) || Src == 0)
{
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
if (!InsideMapRegion(Dst, Size))
{
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
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}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
return;
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst);
Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory);
Memory.Manager.Reprotect(Src, Size, DstInfo.Perm);
Memory.Manager.ClearAttrBit(Src, Size, 0);
ThreadState.X0 = 0;
ThreadState.X0 = (ulong)Result;
}
private void SvcQueryMemory(AThreadState ThreadState)
@ -125,26 +201,16 @@ namespace Ryujinx.HLE.OsHle.Kernel
long InfoPtr = (long)ThreadState.X0;
long Position = (long)ThreadState.X2;
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position);
KMemoryInfo BlkInfo = Process.MemoryManager.QueryMemory(Position);
if (MapInfo == null)
{
long AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
long ReservedSize = (long)(ulong.MaxValue - (ulong)AddrSpaceEnd) + 1;
MapInfo = new AMemoryMapInfo(AddrSpaceEnd, ReservedSize, (int)MemoryType.Reserved, 0, AMemoryPerm.None);
}
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.WriteInt64(InfoPtr + 0x00, BlkInfo.Position);
Memory.WriteInt64(InfoPtr + 0x08, BlkInfo.Size);
Memory.WriteInt32(InfoPtr + 0x10, (int)BlkInfo.State & 0xff);
Memory.WriteInt32(InfoPtr + 0x14, (int)BlkInfo.Attribute);
Memory.WriteInt32(InfoPtr + 0x18, (int)BlkInfo.Permission);
Memory.WriteInt32(InfoPtr + 0x1c, BlkInfo.IpcRefCount);
Memory.WriteInt32(InfoPtr + 0x20, BlkInfo.DeviceRefCount);
Memory.WriteInt32(InfoPtr + 0x24, 0);
//TODO: X1.
ThreadState.X0 = 0;
ThreadState.X1 = 0;
@ -152,84 +218,140 @@ namespace Ryujinx.HLE.OsHle.Kernel
private void SvcMapSharedMemory(AThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
long Src = (long)ThreadState.X1;
long Size = (long)ThreadState.X2;
int Perm = (int)ThreadState.X3;
int Handle = (int)ThreadState.X0;
long Position = (long)ThreadState.X1;
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;
}
HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
if (SharedMem != null)
if (!PageAligned(Size) || Size == 0)
{
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);
Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
lock (MappedSharedMems)
{
MappedSharedMems.Add((SharedMem, Src, Size));
}
ThreadState.X0 = 0;
return;
}
//TODO: Error codes.
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)
{
int Handle = (int)ThreadState.X0;
long Src = (long)ThreadState.X1;
long Size = (long)ThreadState.X2;
int Handle = (int)ThreadState.X0;
long Position = (long)ThreadState.X1;
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;
}
HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
if (SharedMem != null)
if (!PageAligned(Size) || Size == 0)
{
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)
{
MappedSharedMems.Remove((SharedMem, Src, Size));
}
ThreadState.X0 = 0;
return;
}
//TODO: Error codes.
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)
{
long Src = (long)ThreadState.X1;
long Size = (long)ThreadState.X2;
int Perm = (int)ThreadState.X3;
if (!IsValidPosition(Src))
MemoryPermission Permission = (MemoryPermission)ThreadState.X3;
if (!InsideAddrSpace(Src, Size))
{
Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Src:x16}!");
@ -238,48 +360,56 @@ namespace Ryujinx.HLE.OsHle.Kernel
return;
}
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Src);
Process.MemoryManager.ReserveTransferMemory(Src, Size, Permission);
Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
KTransferMemory TransferMemory = new KTransferMemory(Src, Size);
HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size);
ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem);
int Handle = Process.HandleTable.OpenHandle(TransferMemory);
ThreadState.X0 = 0;
ThreadState.X1 = Handle;
ThreadState.X1 = (ulong)Handle;
}
private void SvcMapPhysicalMemory(AThreadState ThreadState)
{
long Position = (long)ThreadState.X0;
uint Size = (uint)ThreadState.X1;
Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW);
ThreadState.X0 = 0;
//TODO.
}
private void SvcUnmapPhysicalMemory(AThreadState ThreadState)
{
long Position = (long)ThreadState.X0;
uint Size = (uint)ThreadState.X1;
Memory.Manager.Unmap(Position, Size);
ThreadState.X0 = 0;
//TODO.
}
private static bool IsValidPosition(long Position)
private static bool PageAligned(long Position)
{
return Position >= MemoryRegions.AddrSpaceStart &&
Position < MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
return (Position & (KMemoryManager.PageSize - 1)) == 0;
}
private static bool IsValidMapPosition(long Position)
private bool InsideAddrSpace(long Position, long Size)
{
return Position >= MemoryRegions.MapRegionAddress &&
Position < MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize;
ulong Start = (ulong)Position;
ulong End = (ulong)Size + Start;
return Start >= (ulong)Process.MemoryManager.AddrSpaceStart &&
End < (ulong)Process.MemoryManager.AddrSpaceEnd;
}
private bool InsideMapRegion(long Position, long Size)
{
ulong Start = (ulong)Position;
ulong End = (ulong)Size + Start;
return Start >= MemoryRegions.MapRegionAddress &&
End < MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize;
}
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;
}
}
}

View file

@ -53,12 +53,11 @@ namespace Ryujinx.HLE.OsHle.Kernel
{
Session.Dispose();
}
else if (Obj is HTransferMem TMem)
else if (Obj is KTransferMemory TransferMemory)
{
TMem.Memory.Manager.Reprotect(
TMem.Position,
TMem.Size,
TMem.Perm);
Process.MemoryManager.ResetTransferMemory(
TransferMemory.Position,
TransferMemory.Size);
}
ThreadState.X0 = 0;
@ -326,7 +325,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
break;
case 7:
ThreadState.X1 = MemoryRegions.TotalMemoryUsed + CurrentHeapSize;
ThreadState.X1 = MemoryRegions.TotalMemoryUsed + (ulong)Process.MemoryManager.GetHeapSize();
break;
case 8:

View file

@ -204,7 +204,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
{
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;
}
@ -226,7 +226,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
{
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;
}

View file

@ -26,7 +26,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
{
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;
}
@ -35,7 +35,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
{
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;
}
@ -79,7 +79,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
{
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;
}
@ -88,7 +88,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
{
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;
}
@ -115,7 +115,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
{
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;
}
@ -124,7 +124,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
{
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;
}
@ -214,7 +214,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
{
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;
}
@ -223,7 +223,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
{
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;
}

View file

@ -1,12 +0,0 @@
using System;
namespace Ryujinx.HLE.OsHle
{
class MemoryAllocator
{
public bool TryAllocate(long Size, out long Address)
{
throw new NotImplementedException();
}
}
}

View file

@ -14,7 +14,7 @@ namespace Ryujinx.HLE.OsHle
public const long MainStackSize = 0x100000;
public const long MainStackAddress = AMemoryMgr.AddrSize - MainStackSize;
public const long MainStackAddress = 0x100000000L - MainStackSize;
public const long TlsPagesSize = 0x20000;
@ -22,8 +22,8 @@ namespace Ryujinx.HLE.OsHle
public const long TotalMemoryUsed = HeapRegionAddress + TlsPagesSize + MainStackSize;
public const long TotalMemoryAvailable = AMemoryMgr.RamSize - AddrSpaceStart;
public const long TotalMemoryAvailable = 0x100000000L - AddrSpaceStart;
public const long AddrSpaceSize = AMemoryMgr.AddrSize - AddrSpaceStart;
public const long AddrSpaceSize = 0x100000000L - AddrSpaceStart;
}
}

View file

@ -40,6 +40,8 @@ namespace Ryujinx.HLE.OsHle
public AMemory Memory { get; private set; }
public KMemoryManager MemoryManager { get; private set; }
public KProcessScheduler Scheduler { get; private set; }
public List<KThread> ThreadArbiterList { get; private set; }
@ -76,7 +78,9 @@ namespace Ryujinx.HLE.OsHle
this.Scheduler = Scheduler;
this.ProcessId = ProcessId;
Memory = new AMemory();
Memory = new AMemory(Ns.Memory.RamPointer);
MemoryManager = new KMemoryManager(Memory, Ns.Memory.Allocator);
ThreadArbiterList = new List<KThread>();
@ -96,10 +100,11 @@ namespace Ryujinx.HLE.OsHle
ImageBase = MemoryRegions.AddrSpaceStart;
MapRWMemRegion(
MemoryManager.HleMapCustom(
MemoryRegions.TlsPagesAddress,
MemoryRegions.TlsPagesSize,
MemoryType.ThreadLocal);
MemoryState.ThreadLocal,
MemoryPermission.ReadAndWrite);
}
public void LoadProgram(IExecutable Program)
@ -111,17 +116,17 @@ namespace Ryujinx.HLE.OsHle
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);
ImageBase = AMemoryHelper.PageRoundUp(Executable.ImageEnd);
ImageBase = IntUtils.AlignUp(Executable.ImageEnd, KMemoryManager.PageSize);
}
public void SetEmptyArgs()
{
//TODO: This should be part of Run.
ImageBase += AMemoryMgr.PageSize;
ImageBase += KMemoryManager.PageSize;
}
public bool Run(bool NeedsHbAbi = false)
@ -140,10 +145,11 @@ namespace Ryujinx.HLE.OsHle
MakeSymbolTable();
MapRWMemRegion(
MemoryManager.HleMapCustom(
MemoryRegions.MainStackAddress,
MemoryRegions.MainStackSize,
MemoryType.Normal);
MemoryState.MappedMemory,
MemoryPermission.ReadAndWrite);
long StackTop = MemoryRegions.MainStackAddress + MemoryRegions.MainStackSize;
@ -158,7 +164,15 @@ namespace Ryujinx.HLE.OsHle
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);
@ -173,11 +187,6 @@ namespace Ryujinx.HLE.OsHle
return true;
}
private void MapRWMemRegion(long Position, long Size, MemoryType Type)
{
Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW);
}
public void StopAllThreadsAsync()
{
if (Disposed)
@ -439,10 +448,6 @@ namespace Ryujinx.HLE.OsHle
AppletState.Dispose();
SvcHandler.Dispose();
Memory.Dispose();
Ns.Log.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting...");
}
}

View file

@ -10,9 +10,9 @@ namespace Ryujinx.HLE.OsHle.Services.Hid
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>()
{

View file

@ -120,7 +120,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostCtrl
Context.Ns.Log.PrintDebug(Logging.LogClass.ServiceNv, $"Got setting {Domain}!{Name}");
}
return NvResult.Success;
}

View file

@ -4,7 +4,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
{
public int Handle;
public int Padding;
public long RefCount;
public long Address;
public int Size;
public int Flags;
}

View file

@ -31,7 +31,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
public long DecrementRefCount()
{
return Interlocked.Decrement(ref Dupes) + 1;
return Interlocked.Decrement(ref Dupes);
}
}
}

View file

@ -129,7 +129,8 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
{
//When the address is zero, we need to allocate
//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;
}
@ -163,23 +164,22 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap
return NvResult.InvalidInput;
}
long OldRefCount = Map.DecrementRefCount();
if (OldRefCount <= 1)
if (Map.DecrementRefCount() <= 0)
{
DeleteNvMap(Context, Args.Handle);
Context.Ns.Log.PrintInfo(LogClass.ServiceNv, $"Deleted map {Args.Handle}!");
Args.Flags = 0;
Args.Address = Map.Address;
Args.Flags = 0;
}
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);

View file

@ -159,7 +159,7 @@ namespace Ryujinx.HLE.OsHle.Services.Android
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)

View file

@ -11,5 +11,15 @@ namespace Ryujinx.HLE.OsHle.Utilities
{
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);
}
}
}

View file

@ -1,7 +1,6 @@
using System.Diagnostics;
using System.Timers;
namespace Ryujinx.HLE
{
public class PerformanceStatistics

View file

@ -4,6 +4,7 @@ using Ryujinx.HLE.Font;
using Ryujinx.HLE.Gpu;
using Ryujinx.HLE.Input;
using Ryujinx.HLE.Logging;
using Ryujinx.HLE.Memory;
using Ryujinx.HLE.OsHle;
using System;
@ -15,6 +16,8 @@ namespace Ryujinx.HLE
public Logger Log { get; private set; }
internal DeviceMemory Memory { get; private set; }
internal NvGpu Gpu { get; private set; }
internal VirtualFileSystem VFs { get; private set; }
@ -45,6 +48,8 @@ namespace Ryujinx.HLE
Log = new Logger();
Memory = new DeviceMemory();
Gpu = new NvGpu(Renderer);
VFs = new VirtualFileSystem();
@ -53,15 +58,7 @@ namespace Ryujinx.HLE
Statistics = new PerformanceStatistics();
Hid = new Hid(Log);
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;
Hid = new Hid(this, Os.HidSharedMem.PA);
}
public void LoadCart(string ExeFsDir, string RomFsFile = null)

View file

@ -5,6 +5,7 @@ using ChocolArm64.State;
using NUnit.Framework;
using System;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using System.Threading;
@ -19,6 +20,8 @@ namespace Ryujinx.Tests.Cpu
private long EntryPoint;
private IntPtr RamPointer;
private AMemory Memory;
private AThread Thread;
@ -31,15 +34,16 @@ namespace Ryujinx.Tests.Cpu
EntryPoint = Position;
ATranslator Translator = new ATranslator();
Memory = new AMemory();
Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute);
RamPointer = Marshal.AllocHGlobal(new IntPtr(Size));
Memory = new AMemory(RamPointer);
Memory.Map(Position, 0, Size);
Thread = new AThread(Translator, Memory, EntryPoint);
}
[TearDown]
public void Teardown()
{
Memory.Dispose();
Marshal.FreeHGlobal(RamPointer);
Memory = null;
Thread = null;
}
@ -52,7 +56,7 @@ namespace Ryujinx.Tests.Cpu
protected void Opcode(uint Opcode)
{
Thread.Memory.WriteUInt32Unchecked(Position, Opcode);
Thread.Memory.WriteUInt32(Position, Opcode);
Position += 4;
}

View file

@ -23,8 +23,6 @@ namespace Ryujinx
IniParser Parser = new IniParser(IniPath);
AOptimizations.DisableMemoryChecks = !Convert.ToBoolean(Parser.Value("Enable_Memory_Checks"));
GraphicsConfig.ShadersDumpPath = Parser.Value("Graphics_Shaders_Dump_Path");
Device.Log.SetEnable(LogLevel.Debug, Convert.ToBoolean(Parser.Value("Logging_Enable_Debug")));