using System; using System.Runtime.Versioning; namespace Ryujinx.Memory.WindowsShared { /// /// Windows 4KB memory placeholder manager. /// [SupportedOSPlatform("windows")] class PlaceholderManager4KB { private const int PageSize = MemoryManagementWindows.PageSize; private readonly IntervalTree _mappings; /// /// Creates a new instance of the Windows 4KB memory placeholder manager. /// public PlaceholderManager4KB() { _mappings = new IntervalTree(); } /// /// Unmaps the specified range of memory and marks it as mapped internally. /// /// /// Since this marks the range as mapped, the expectation is that the range will be mapped after calling this method. /// /// Memory address to unmap and mark as mapped /// Size of the range in bytes public void UnmapAndMarkRangeAsMapped(IntPtr location, IntPtr size) { ulong startAddress = (ulong)location; ulong unmapSize = (ulong)size; ulong endAddress = startAddress + unmapSize; var overlaps = Array.Empty>(); int count = 0; lock (_mappings) { count = _mappings.Get(startAddress, endAddress, ref overlaps); } for (int index = 0; index < count; index++) { var overlap = overlaps[index]; // Tree operations might modify the node start/end values, so save a copy before we modify the tree. ulong overlapStart = overlap.Start; ulong overlapEnd = overlap.End; ulong overlapValue = overlap.Value; _mappings.Remove(overlap); ulong unmapStart = Math.Max(overlapStart, startAddress); ulong unmapEnd = Math.Min(overlapEnd, endAddress); if (overlapStart < startAddress) { startAddress = overlapStart; } if (overlapEnd > endAddress) { endAddress = overlapEnd; } ulong currentAddress = unmapStart; while (currentAddress < unmapEnd) { WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)currentAddress, 2); currentAddress += PageSize; } } _mappings.Add(startAddress, endAddress, 0); } /// /// Unmaps views at the specified memory range. /// /// Address of the range /// Size of the range in bytes public void UnmapView(IntPtr location, IntPtr size) { ulong startAddress = (ulong)location; ulong unmapSize = (ulong)size; ulong endAddress = startAddress + unmapSize; var overlaps = Array.Empty>(); int count = 0; lock (_mappings) { count = _mappings.Get(startAddress, endAddress, ref overlaps); } for (int index = 0; index < count; index++) { var overlap = overlaps[index]; // Tree operations might modify the node start/end values, so save a copy before we modify the tree. ulong overlapStart = overlap.Start; ulong overlapEnd = overlap.End; _mappings.Remove(overlap); if (overlapStart < startAddress) { _mappings.Add(overlapStart, startAddress, 0); } if (overlapEnd > endAddress) { _mappings.Add(endAddress, overlapEnd, 0); } ulong unmapStart = Math.Max(overlapStart, startAddress); ulong unmapEnd = Math.Min(overlapEnd, endAddress); ulong currentAddress = unmapStart; while (currentAddress < unmapEnd) { WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)currentAddress, 2); currentAddress += PageSize; } } } /// /// Unmaps mapped memory at a given range. /// /// Address of the range /// Size of the range in bytes public void UnmapRange(IntPtr location, IntPtr size) { ulong startAddress = (ulong)location; ulong unmapSize = (ulong)size; ulong endAddress = startAddress + unmapSize; var overlaps = Array.Empty>(); int count = 0; lock (_mappings) { count = _mappings.Get(startAddress, endAddress, ref overlaps); } for (int index = 0; index < count; index++) { var overlap = overlaps[index]; // Tree operations might modify the node start/end values, so save a copy before we modify the tree. ulong unmapStart = Math.Max(overlap.Start, startAddress); ulong unmapEnd = Math.Min(overlap.End, endAddress); _mappings.Remove(overlap); ulong currentAddress = unmapStart; while (currentAddress < unmapEnd) { WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)currentAddress, 2); currentAddress += PageSize; } } } } }