diff options
Diffstat (limited to 'ChocolArm64/Memory')
| -rw-r--r-- | ChocolArm64/Memory/AMemory.cs | 345 | ||||
| -rw-r--r-- | ChocolArm64/Memory/AMemoryAlloc.cs | 35 | ||||
| -rw-r--r-- | ChocolArm64/Memory/AMemoryHelper.cs | 73 | ||||
| -rw-r--r-- | ChocolArm64/Memory/AMemoryMapInfo.cs | 21 | ||||
| -rw-r--r-- | ChocolArm64/Memory/AMemoryMgr.cs | 286 | ||||
| -rw-r--r-- | ChocolArm64/Memory/AMemoryPerm.cs | 15 |
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 |
