2018-02-28 00:45:07 +01:00
|
|
|
using System;
|
|
|
|
|
2018-02-05 00:08:20 +01:00
|
|
|
namespace ChocolArm64.Memory
|
|
|
|
{
|
|
|
|
public class AMemoryMgr
|
|
|
|
{
|
2018-02-07 17:44:48 +01:00
|
|
|
public const long RamSize = 4L * 1024 * 1024 * 1024;
|
2018-03-10 03:12:57 +01:00
|
|
|
public const long AddrSize = RamSize;
|
2018-02-05 00:08:20 +01:00
|
|
|
|
2018-02-28 00:45:07 +01:00
|
|
|
private const int PTLvl0Bits = 10;
|
|
|
|
private const int PTLvl1Bits = 10;
|
|
|
|
private const int PTPageBits = 12;
|
2018-02-05 00:08:20 +01:00
|
|
|
|
2018-02-28 00:45:07 +01:00
|
|
|
private const int PTLvl0Size = 1 << PTLvl0Bits;
|
|
|
|
private const int PTLvl1Size = 1 << PTLvl1Bits;
|
|
|
|
public const int PageSize = 1 << PTPageBits;
|
2018-02-05 00:08:20 +01:00
|
|
|
|
2018-02-28 00:45:07 +01:00
|
|
|
private const int PTLvl0Mask = PTLvl0Size - 1;
|
|
|
|
private const int PTLvl1Mask = PTLvl1Size - 1;
|
|
|
|
public const int PageMask = PageSize - 1;
|
2018-02-05 00:08:20 +01:00
|
|
|
|
2018-03-10 03:12:57 +01:00
|
|
|
private const int PTLvl0Bit = PTPageBits + PTLvl1Bits;
|
|
|
|
private const int PTLvl1Bit = PTPageBits;
|
2018-02-05 00:08:20 +01:00
|
|
|
|
|
|
|
private enum PTMap
|
|
|
|
{
|
|
|
|
Unmapped,
|
2018-02-10 01:13:18 +01:00
|
|
|
Mapped
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private struct PTEntry
|
|
|
|
{
|
|
|
|
public PTMap Map;
|
|
|
|
public AMemoryPerm Perm;
|
|
|
|
|
2018-02-10 01:13:18 +01:00
|
|
|
public int Type;
|
|
|
|
public int Attr;
|
|
|
|
|
|
|
|
public PTEntry(PTMap Map, AMemoryPerm Perm, int Type, int Attr)
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-02-10 01:13:18 +01:00
|
|
|
this.Map = Map;
|
|
|
|
this.Perm = Perm;
|
|
|
|
this.Type = Type;
|
|
|
|
this.Attr = Attr;
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private PTEntry[][] PageTable;
|
|
|
|
|
2018-02-28 00:45:07 +01:00
|
|
|
public AMemoryMgr()
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
|
|
|
PageTable = new PTEntry[PTLvl0Size][];
|
|
|
|
}
|
|
|
|
|
2018-02-28 00:45:07 +01:00
|
|
|
public void Map(long Position, long Size, int Type, AMemoryPerm Perm)
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-02-28 00:45:07 +01:00
|
|
|
SetPTEntry(Position, Size, new PTEntry(PTMap.Mapped, Perm, Type, 0));
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
2018-02-28 00:45:07 +01:00
|
|
|
public void Unmap(long Position, long Size)
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-02-28 00:45:07 +01:00
|
|
|
SetPTEntry(Position, Size, new PTEntry(PTMap.Unmapped, 0, 0, 0));
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
2018-02-28 00:45:07 +01:00
|
|
|
public void Unmap(long Position, long Size, int Type)
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-02-28 00:45:07 +01:00
|
|
|
SetPTEntry(Position, Size, Type, new PTEntry(PTMap.Unmapped, 0, 0, 0));
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2018-02-28 00:45:07 +01:00
|
|
|
if (!IsValidPosition(Position))
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2018-02-05 00:08:20 +01:00
|
|
|
Position = AMemoryHelper.PageRoundDown(Position);
|
|
|
|
|
|
|
|
PTEntry BaseEntry = GetPTEntry(Position);
|
|
|
|
|
|
|
|
bool IsSameSegment(long Pos)
|
|
|
|
{
|
2018-02-28 00:45:07 +01:00
|
|
|
if (!IsValidPosition(Pos))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-02-05 00:08:20 +01:00
|
|
|
PTEntry Entry = GetPTEntry(Pos);
|
|
|
|
|
2018-02-10 01:13:18 +01:00
|
|
|
return Entry.Map == BaseEntry.Map &&
|
|
|
|
Entry.Perm == BaseEntry.Perm &&
|
|
|
|
Entry.Type == BaseEntry.Type &&
|
|
|
|
Entry.Attr == BaseEntry.Attr;
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2018-02-10 01:13:18 +01:00
|
|
|
return new AMemoryMapInfo(
|
|
|
|
Start,
|
|
|
|
Size,
|
|
|
|
BaseEntry.Type,
|
|
|
|
BaseEntry.Attr,
|
|
|
|
BaseEntry.Perm);
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
2018-02-26 02:53:01 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-09 04:26:20 +01:00
|
|
|
public bool HasPermission(long Position, AMemoryPerm Perm)
|
|
|
|
{
|
|
|
|
return GetPTEntry(Position).Perm.HasFlag(Perm);
|
|
|
|
}
|
|
|
|
|
2018-02-28 00:45:07 +01:00
|
|
|
public bool IsValidPosition(long Position)
|
|
|
|
{
|
|
|
|
if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-02-09 04:26:20 +01:00
|
|
|
public bool IsMapped(long Position)
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2018-02-28 00:45:07 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-05 00:08:20 +01:00
|
|
|
private void SetPTEntry(long Position, PTEntry Entry)
|
|
|
|
{
|
2018-02-28 00:45:07 +01:00
|
|
|
if (!IsValidPosition(Position))
|
|
|
|
{
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(Position));
|
|
|
|
}
|
|
|
|
|
2018-02-05 00:08:20 +01:00
|
|
|
long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask;
|
|
|
|
long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask;
|
|
|
|
|
|
|
|
if (PageTable[L0] == null)
|
|
|
|
{
|
|
|
|
PageTable[L0] = new PTEntry[PTLvl1Size];
|
|
|
|
}
|
|
|
|
|
|
|
|
PageTable[L0][L1] = Entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|