aboutsummaryrefslogtreecommitdiff
path: root/ChocolArm64/Memory/AMemoryMgr.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ChocolArm64/Memory/AMemoryMgr.cs')
-rw-r--r--ChocolArm64/Memory/AMemoryMgr.cs286
1 files changed, 286 insertions, 0 deletions
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