aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
diff options
context:
space:
mode:
authorgdk <gab.dark.100@gmail.com>2019-10-13 03:02:07 -0300
committerThog <thog@protonmail.com>2020-01-09 02:13:00 +0100
commit1876b346fea647e8284a66bb6d62c38801035cff (patch)
tree6eeff094298cda84d1613dc5ec0691e51d7b35f1 /Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
parentf617fb542a0e3d36012d77a4b5acbde7b08902f2 (diff)
Initial work
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs')
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs265
1 files changed, 265 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
new file mode 100644
index 00000000..d1a3e69c
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
@@ -0,0 +1,265 @@
+namespace Ryujinx.Graphics.Gpu.Memory
+{
+ public class MemoryManager
+ {
+ private const ulong AddressSpaceSize = 1UL << 40;
+
+ public const ulong BadAddress = ulong.MaxValue;
+
+ private const int PtLvl0Bits = 14;
+ private const int PtLvl1Bits = 14;
+ private const int PtPageBits = 12;
+
+ private const ulong PtLvl0Size = 1UL << PtLvl0Bits;
+ private const ulong PtLvl1Size = 1UL << PtLvl1Bits;
+ public const ulong PageSize = 1UL << PtPageBits;
+
+ private const ulong PtLvl0Mask = PtLvl0Size - 1;
+ private const ulong PtLvl1Mask = PtLvl1Size - 1;
+ public const ulong PageMask = PageSize - 1;
+
+ private const int PtLvl0Bit = PtPageBits + PtLvl1Bits;
+ private const int PtLvl1Bit = PtPageBits;
+
+ private const ulong PteUnmapped = 0xffffffff_ffffffff;
+ private const ulong PteReserved = 0xffffffff_fffffffe;
+
+ private ulong[][] _pageTable;
+
+ public MemoryManager()
+ {
+ _pageTable = new ulong[PtLvl0Size][];
+ }
+
+ public ulong Map(ulong pa, ulong va, ulong size)
+ {
+ lock (_pageTable)
+ {
+ for (ulong offset = 0; offset < size; offset += PageSize)
+ {
+ SetPte(va + offset, pa + offset);
+ }
+ }
+
+ return va;
+ }
+
+ public ulong Map(ulong pa, ulong size)
+ {
+ lock (_pageTable)
+ {
+ ulong va = GetFreePosition(size);
+
+ if (va != PteUnmapped)
+ {
+ for (ulong offset = 0; offset < size; offset += PageSize)
+ {
+ SetPte(va + offset, pa + offset);
+ }
+ }
+
+ return va;
+ }
+ }
+
+ public ulong MapLow(ulong pa, ulong size)
+ {
+ lock (_pageTable)
+ {
+ ulong va = GetFreePosition(size, 1, PageSize);
+
+ if (va != PteUnmapped && va <= uint.MaxValue && (va + size) <= uint.MaxValue)
+ {
+ for (ulong offset = 0; offset < size; offset += PageSize)
+ {
+ SetPte(va + offset, pa + offset);
+ }
+ }
+ else
+ {
+ va = PteUnmapped;
+ }
+
+ return va;
+ }
+ }
+
+ public ulong ReserveFixed(ulong va, ulong size)
+ {
+ lock (_pageTable)
+ {
+ for (ulong offset = 0; offset < size; offset += PageSize)
+ {
+ if (IsPageInUse(va + offset))
+ {
+ return PteUnmapped;
+ }
+ }
+
+ for (ulong offset = 0; offset < size; offset += PageSize)
+ {
+ SetPte(va + offset, PteReserved);
+ }
+ }
+
+ return va;
+ }
+
+ public ulong Reserve(ulong size, ulong alignment)
+ {
+ lock (_pageTable)
+ {
+ ulong address = GetFreePosition(size, alignment);
+
+ if (address != PteUnmapped)
+ {
+ for (ulong offset = 0; offset < size; offset += PageSize)
+ {
+ SetPte(address + offset, PteReserved);
+ }
+ }
+
+ return address;
+ }
+ }
+
+ public void Free(ulong va, ulong size)
+ {
+ lock (_pageTable)
+ {
+ for (ulong offset = 0; offset < size; offset += PageSize)
+ {
+ SetPte(va + offset, PteUnmapped);
+ }
+ }
+ }
+
+ private ulong GetFreePosition(ulong size, ulong alignment = 1, ulong start = 1UL << 32)
+ {
+ // Note: Address 0 is not considered valid by the driver,
+ // when 0 is returned it's considered a mapping error.
+ ulong address = start;
+ ulong freeSize = 0;
+
+ if (alignment == 0)
+ {
+ alignment = 1;
+ }
+
+ alignment = (alignment + PageMask) & ~PageMask;
+
+ while (address + freeSize < AddressSpaceSize)
+ {
+ if (!IsPageInUse(address + freeSize))
+ {
+ freeSize += PageSize;
+
+ if (freeSize >= size)
+ {
+ return address;
+ }
+ }
+ else
+ {
+ address += freeSize + PageSize;
+ freeSize = 0;
+
+ ulong remainder = address % alignment;
+
+ if (remainder != 0)
+ {
+ address = (address - remainder) + alignment;
+ }
+ }
+ }
+
+ return PteUnmapped;
+ }
+
+ internal ulong GetSubSize(ulong gpuVa)
+ {
+ ulong size = 0;
+
+ while (GetPte(gpuVa + size) != PteUnmapped)
+ {
+ size += PageSize;
+ }
+
+ return size;
+ }
+
+ internal ulong Translate(ulong gpuVa)
+ {
+ ulong baseAddress = GetPte(gpuVa);
+
+ if (baseAddress == PteUnmapped || baseAddress == PteReserved)
+ {
+ return PteUnmapped;
+ }
+
+ return baseAddress + (gpuVa & PageMask);
+ }
+
+ public bool IsRegionFree(ulong va, ulong size)
+ {
+ for (ulong offset = 0; offset < size; offset += PageSize)
+ {
+ if (IsPageInUse(va + offset))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private bool IsPageInUse(ulong va)
+ {
+ if (va >> PtLvl0Bits + PtLvl1Bits + PtPageBits != 0)
+ {
+ return false;
+ }
+
+ ulong l0 = (va >> PtLvl0Bit) & PtLvl0Mask;
+ ulong l1 = (va >> PtLvl1Bit) & PtLvl1Mask;
+
+ if (_pageTable[l0] == null)
+ {
+ return false;
+ }
+
+ return _pageTable[l0][l1] != PteUnmapped;
+ }
+
+ private ulong GetPte(ulong address)
+ {
+ ulong l0 = (address >> PtLvl0Bit) & PtLvl0Mask;
+ ulong l1 = (address >> PtLvl1Bit) & PtLvl1Mask;
+
+ if (_pageTable[l0] == null)
+ {
+ return PteUnmapped;
+ }
+
+ return _pageTable[l0][l1];
+ }
+
+ private void SetPte(ulong address, ulong tgtAddr)
+ {
+ ulong l0 = (address >> PtLvl0Bit) & PtLvl0Mask;
+ ulong l1 = (address >> PtLvl1Bit) & PtLvl1Mask;
+
+ if (_pageTable[l0] == null)
+ {
+ _pageTable[l0] = new ulong[PtLvl1Size];
+
+ for (ulong index = 0; index < PtLvl1Size; index++)
+ {
+ _pageTable[l0][index] = PteUnmapped;
+ }
+ }
+
+ _pageTable[l0][l1] = tgtAddr;
+ }
+ }
+} \ No newline at end of file