aboutsummaryrefslogtreecommitdiff
path: root/ChocolArm64/Memory
diff options
context:
space:
mode:
Diffstat (limited to 'ChocolArm64/Memory')
-rw-r--r--ChocolArm64/Memory/AMemory.cs345
-rw-r--r--ChocolArm64/Memory/AMemoryAlloc.cs35
-rw-r--r--ChocolArm64/Memory/AMemoryHelper.cs73
-rw-r--r--ChocolArm64/Memory/AMemoryMapInfo.cs21
-rw-r--r--ChocolArm64/Memory/AMemoryMgr.cs286
-rw-r--r--ChocolArm64/Memory/AMemoryPerm.cs15
6 files changed, 775 insertions, 0 deletions
diff --git a/ChocolArm64/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs
new file mode 100644
index 00000000..f2abffbf
--- /dev/null
+++ b/ChocolArm64/Memory/AMemory.cs
@@ -0,0 +1,345 @@
+using ChocolArm64.Exceptions;
+using ChocolArm64.State;
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace ChocolArm64.Memory
+{
+ public unsafe class AMemory
+ {
+ private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1;
+
+ public AMemoryMgr Manager { get; private set; }
+
+ private struct ExMonitor
+ {
+ public long Position { get; private set; }
+
+ private bool ExState;
+
+ public ExMonitor(long Position, bool ExState)
+ {
+ this.Position = Position;
+ this.ExState = ExState;
+ }
+
+ public bool HasExclusiveAccess(long Position)
+ {
+ return this.Position == Position && ExState;
+ }
+
+ public void Reset()
+ {
+ ExState = false;
+ }
+ }
+
+ private Dictionary<int, ExMonitor> Monitors;
+
+ private HashSet<long> ExAddrs;
+
+ private byte* RamPtr;
+
+ public AMemory(IntPtr Ram, AMemoryAlloc Allocator)
+ {
+ Manager = new AMemoryMgr(Allocator);
+
+ Monitors = new Dictionary<int, ExMonitor>();
+
+ ExAddrs = new HashSet<long>();
+
+ RamPtr = (byte*)Ram;
+ }
+
+ public void RemoveMonitor(int ThreadId)
+ {
+ lock (Monitors)
+ {
+ if (Monitors.TryGetValue(ThreadId, out ExMonitor Monitor))
+ {
+ ExAddrs.Remove(Monitor.Position);
+ }
+
+ Monitors.Remove(ThreadId);
+ }
+ }
+
+ public void SetExclusive(AThreadState ThreadState, long Position)
+ {
+ Position &= ~ErgMask;
+
+ lock (Monitors)
+ {
+ if (Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor))
+ {
+ ExAddrs.Remove(Monitor.Position);
+ }
+
+ bool ExState = ExAddrs.Add(Position);
+
+ Monitor = new ExMonitor(Position, ExState);
+
+ if (!Monitors.TryAdd(ThreadState.ThreadId, Monitor))
+ {
+ Monitors[ThreadState.ThreadId] = Monitor;
+ }
+ }
+ }
+
+ public bool TestExclusive(AThreadState ThreadState, long Position)
+ {
+ Position &= ~ErgMask;
+
+ lock (Monitors)
+ {
+ if (!Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor))
+ {
+ return false;
+ }
+
+ return Monitor.HasExclusiveAccess(Position);
+ }
+ }
+
+ public void ClearExclusive(AThreadState ThreadState)
+ {
+ lock (Monitors)
+ {
+ if (Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor))
+ {
+ Monitor.Reset();
+ ExAddrs.Remove(Monitor.Position);
+ }
+ }
+ }
+
+ public bool AcquireAddress(long Position)
+ {
+ Position &= ~ErgMask;
+
+ lock (Monitors)
+ {
+ return ExAddrs.Add(Position);
+ }
+ }
+
+ public void ReleaseAddress(long Position)
+ {
+ Position &= ~ErgMask;
+
+ lock (Monitors)
+ {
+ ExAddrs.Remove(Position);
+ }
+ }
+
+ public sbyte ReadSByte(long Position) => (sbyte)ReadByte (Position);
+ public short ReadInt16(long Position) => (short)ReadUInt16(Position);
+ public int ReadInt32(long Position) => (int)ReadUInt32(Position);
+ public long ReadInt64(long Position) => (long)ReadUInt64(Position);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public byte ReadByte(long Position)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Read);
+#endif
+
+ return *((byte*)(RamPtr + (uint)Position));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ushort ReadUInt16(long Position)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Read);
+#endif
+
+ return *((ushort*)(RamPtr + (uint)Position));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public uint ReadUInt32(long Position)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Read);
+#endif
+
+ return *((uint*)(RamPtr + (uint)Position));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ulong ReadUInt64(long Position)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Read);
+#endif
+
+ return *((ulong*)(RamPtr + (uint)Position));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public AVec ReadVector8(long Position)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Read);
+#endif
+
+ return new AVec() { B0 = ReadByte(Position) };
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public AVec ReadVector16(long Position)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Read);
+#endif
+
+ return new AVec() { H0 = ReadUInt16(Position) };
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public AVec ReadVector32(long Position)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Read);
+#endif
+
+ return new AVec() { W0 = ReadUInt32(Position) };
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public AVec ReadVector64(long Position)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Read);
+#endif
+
+ return new AVec() { X0 = ReadUInt64(Position) };
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public AVec ReadVector128(long Position)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Read);
+#endif
+
+ return new AVec()
+ {
+ X0 = ReadUInt64(Position + 0),
+ X1 = ReadUInt64(Position + 8)
+ };
+ }
+
+ 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);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteByte(long Position, byte Value)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Write);
+#endif
+
+ *((byte*)(RamPtr + (uint)Position)) = Value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteUInt16(long Position, ushort Value)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Write);
+#endif
+
+ *((ushort*)(RamPtr + (uint)Position)) = Value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteUInt32(long Position, uint Value)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Write);
+#endif
+
+ *((uint*)(RamPtr + (uint)Position)) = Value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteUInt64(long Position, ulong Value)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Write);
+#endif
+
+ *((ulong*)(RamPtr + (uint)Position)) = Value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteVector8(long Position, AVec Value)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Write);
+#endif
+
+ WriteByte(Position, Value.B0);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteVector16(long Position, AVec Value)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Write);
+#endif
+
+ WriteUInt16(Position, Value.H0);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteVector32(long Position, AVec Value)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Write);
+#endif
+
+ WriteUInt32(Position, Value.W0);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteVector64(long Position, AVec Value)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Write);
+#endif
+
+ WriteUInt64(Position, Value.X0);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteVector128(long Position, AVec Value)
+ {
+#if DEBUG
+ EnsureAccessIsValid(Position, AMemoryPerm.Write);
+#endif
+
+ WriteUInt64(Position + 0, Value.X0);
+ WriteUInt64(Position + 8, Value.X1);
+ }
+
+ private void EnsureAccessIsValid(long Position, AMemoryPerm Perm)
+ {
+ if (!Manager.IsMapped(Position))
+ {
+ throw new VmmPageFaultException(Position);
+ }
+
+ if (!Manager.HasPermission(Position, Perm))
+ {
+ throw new VmmAccessViolationException(Position, Perm);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Memory/AMemoryAlloc.cs b/ChocolArm64/Memory/AMemoryAlloc.cs
new file mode 100644
index 00000000..b11e7793
--- /dev/null
+++ b/ChocolArm64/Memory/AMemoryAlloc.cs
@@ -0,0 +1,35 @@
+using ChocolArm64.Exceptions;
+
+namespace ChocolArm64.Memory
+{
+ public class AMemoryAlloc
+ {
+ private long PhysPos;
+
+ public long Alloc(long Size)
+ {
+ long Position = PhysPos;
+
+ Size = AMemoryHelper.PageRoundUp(Size);
+
+ PhysPos += Size;
+
+ if (PhysPos > AMemoryMgr.RamSize || PhysPos < 0)
+ {
+ throw new VmmOutOfMemoryException(Size);
+ }
+
+ return Position;
+ }
+
+ public void Free(long Position)
+ {
+ //TODO
+ }
+
+ public long GetFreeMem()
+ {
+ return AMemoryMgr.RamSize - PhysPos;
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Memory/AMemoryHelper.cs b/ChocolArm64/Memory/AMemoryHelper.cs
new file mode 100644
index 00000000..219aeebf
--- /dev/null
+++ b/ChocolArm64/Memory/AMemoryHelper.cs
@@ -0,0 +1,73 @@
+using System.IO;
+using System.Text;
+
+namespace ChocolArm64.Memory
+{
+ public static class AMemoryHelper
+ {
+ public static void FillWithZeros(AMemory Memory, long Position, int Size)
+ {
+ int Size8 = Size & ~(8 - 1);
+
+ for (int Offs = 0; Offs < Size8; Offs += 8)
+ {
+ Memory.WriteInt64(Position + Offs, 0);
+ }
+
+ for (int Offs = Size8; Offs < (Size - Size8); Offs++)
+ {
+ Memory.WriteByte(Position + Offs, 0);
+ }
+ }
+
+ public static byte[] ReadBytes(AMemory Memory, long Position, int Size)
+ {
+ byte[] Data = new byte[Size];
+
+ for (int Offs = 0; Offs < Size; Offs++)
+ {
+ Data[Offs] = (byte)Memory.ReadByte(Position + Offs);
+ }
+
+ return Data;
+ }
+
+ public static void WriteBytes(AMemory Memory, long Position, byte[] Data)
+ {
+ for (int Offs = 0; Offs < Data.Length; Offs++)
+ {
+ Memory.WriteByte(Position + Offs, Data[Offs]);
+ }
+ }
+
+ public static string ReadAsciiString(AMemory Memory, long Position, int MaxSize = -1)
+ {
+ using (MemoryStream MS = new MemoryStream())
+ {
+ for (int Offs = 0; Offs < MaxSize || MaxSize == -1; Offs++)
+ {
+ byte Value = (byte)Memory.ReadByte(Position + Offs);
+
+ if (Value == 0)
+ {
+ break;
+ }
+
+ MS.WriteByte(Value);
+ }
+
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Memory/AMemoryMapInfo.cs b/ChocolArm64/Memory/AMemoryMapInfo.cs
new file mode 100644
index 00000000..44b2cc07
--- /dev/null
+++ b/ChocolArm64/Memory/AMemoryMapInfo.cs
@@ -0,0 +1,21 @@
+namespace ChocolArm64.Memory
+{
+ public struct 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Memory/AMemoryMgr.cs b/ChocolArm64/Memory/AMemoryMgr.cs
new file mode 100644
index 00000000..05284059
--- /dev/null
+++ b/ChocolArm64/Memory/AMemoryMgr.cs
@@ -0,0 +1,286 @@
+namespace ChocolArm64.Memory
+{
+ public class AMemoryMgr
+ {
+ public const long AddrSize = RamSize;
+ public const long RamSize = 4L * 1024 * 1024 * 1024;
+
+ private const int PTLvl0Bits = 11;
+ private const int PTLvl1Bits = 13;
+ 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 + PTLvl0Bits;
+ private const int PTLvl1Bit = PTPageBits;
+
+ private AMemoryAlloc Allocator;
+
+ 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;
+
+ private bool IsHeapInitialized;
+
+ public long HeapAddr { get; private set; }
+ public long HeapSize { get; private set; }
+
+ public AMemoryMgr(AMemoryAlloc Allocator)
+ {
+ this.Allocator = Allocator;
+
+ PageTable = new PTEntry[PTLvl0Size][];
+ }
+
+ public long GetTotalMemorySize()
+ {
+ return Allocator.GetFreeMem() + GetUsedMemorySize();
+ }
+
+ public long GetUsedMemorySize()
+ {
+ long Size = 0;
+
+ for (int L0 = 0; L0 < PageTable.Length; L0++)
+ {
+ if (PageTable[L0] == null)
+ {
+ continue;
+ }
+
+ for (int L1 = 0; L1 < PageTable[L0].Length; L1++)
+ {
+ Size += PageTable[L0][L1].Map != PTMap.Unmapped ? PageSize : 0;
+ }
+ }
+
+ return Size;
+ }
+
+ public bool SetHeapAddr(long Position)
+ {
+ if (!IsHeapInitialized)
+ {
+ HeapAddr = Position;
+
+ IsHeapInitialized = true;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public void SetHeapSize(long Size, int Type)
+ {
+ //TODO: Return error when theres no enough space to allocate heap.
+ Size = AMemoryHelper.PageRoundUp(Size);
+
+ long Position = HeapAddr;
+
+ if ((ulong)Size < (ulong)HeapSize)
+ {
+ //Try to free now free area if size is smaller than old size.
+ Position += Size;
+
+ while ((ulong)Size < (ulong)HeapSize)
+ {
+ Allocator.Free(Position);
+
+ Position += PageSize;
+ }
+ }
+ else
+ {
+ //Allocate extra needed size.
+ Position += HeapSize;
+ Size -= HeapSize;
+
+ MapPhys(Position, Size, Type, AMemoryPerm.RW);
+ }
+
+ HeapSize = Size;
+ }
+
+ public void MapPhys(long Position, long Size, int Type, AMemoryPerm Perm)
+ {
+ while (Size > 0)
+ {
+ if (!IsMapped(Position))
+ {
+ SetPTEntry(Position, new PTEntry(PTMap.Mapped, Perm, Type, 0));
+ }
+
+ long CPgSize = PageSize - (Position & PageMask);
+
+ Position += CPgSize;
+ Size -= CPgSize;
+ }
+ }
+
+ public void MapMirror(long Src, long Dst, long Size, int Type)
+ {
+ Src = AMemoryHelper.PageRoundDown(Src);
+ Dst = AMemoryHelper.PageRoundDown(Dst);
+
+ Size = AMemoryHelper.PageRoundUp(Size);
+
+ long PagesCount = Size / PageSize;
+
+ while (PagesCount-- > 0)
+ {
+ PTEntry SrcEntry = GetPTEntry(Src);
+ PTEntry DstEntry = GetPTEntry(Dst);
+
+ DstEntry.Map = PTMap.Mapped;
+ DstEntry.Type = Type;
+ DstEntry.Perm = SrcEntry.Perm;
+
+ SrcEntry.Perm = AMemoryPerm.None;
+
+ SrcEntry.Attr |= 1;
+
+ SetPTEntry(Src, SrcEntry);
+ SetPTEntry(Dst, DstEntry);
+
+ Src += PageSize;
+ Dst += PageSize;
+ }
+ }
+
+ 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)
+ {
+ Position = AMemoryHelper.PageRoundDown(Position);
+
+ PTEntry BaseEntry = GetPTEntry(Position);
+
+ bool IsSameSegment(long Pos)
+ {
+ 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 bool HasPermission(long Position, AMemoryPerm Perm)
+ {
+ return GetPTEntry(Position).Perm.HasFlag(Perm);
+ }
+
+ 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, PTEntry Entry)
+ {
+ long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
+ long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
+
+ if (PageTable[L0] == null)
+ {
+ PageTable[L0] = new PTEntry[PTLvl1Size];
+ }
+
+ PageTable[L0][L1] = Entry;
+ }
+ }
+} \ No newline at end of file
diff --git a/ChocolArm64/Memory/AMemoryPerm.cs b/ChocolArm64/Memory/AMemoryPerm.cs
new file mode 100644
index 00000000..b425eb94
--- /dev/null
+++ b/ChocolArm64/Memory/AMemoryPerm.cs
@@ -0,0 +1,15 @@
+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
+ }
+} \ No newline at end of file