diff options
Diffstat (limited to 'Ryujinx.Memory/MemoryManagementUnix.cs')
-rw-r--r-- | Ryujinx.Memory/MemoryManagementUnix.cs | 210 |
1 files changed, 207 insertions, 3 deletions
diff --git a/Ryujinx.Memory/MemoryManagementUnix.cs b/Ryujinx.Memory/MemoryManagementUnix.cs index 81096867..69852787 100644 --- a/Ryujinx.Memory/MemoryManagementUnix.cs +++ b/Ryujinx.Memory/MemoryManagementUnix.cs @@ -1,11 +1,31 @@ using Mono.Unix.Native; using System; using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Runtime.InteropServices; namespace Ryujinx.Memory { static class MemoryManagementUnix { + private struct UnixSharedMemory + { + public IntPtr Pointer; + public ulong Size; + public IntPtr SourcePointer; + } + + [DllImport("libc", SetLastError = true)] + public static extern IntPtr mremap(IntPtr old_address, ulong old_size, ulong new_size, MremapFlags flags, IntPtr new_address); + + [DllImport("libc", SetLastError = true)] + public static extern int madvise(IntPtr address, ulong size, int advice); + + private const int MADV_DONTNEED = 4; + private const int MADV_REMOVE = 9; + + private static readonly List<UnixSharedMemory> _sharedMemory = new List<UnixSharedMemory>(); + private static readonly ConcurrentDictionary<IntPtr, ulong> _sharedMemorySource = new ConcurrentDictionary<IntPtr, ulong>(); private static readonly ConcurrentDictionary<IntPtr, ulong> _allocations = new ConcurrentDictionary<IntPtr, ulong>(); public static IntPtr Allocate(ulong size) @@ -18,9 +38,23 @@ namespace Ryujinx.Memory return AllocateInternal(size, MmapProts.PROT_NONE); } - private static IntPtr AllocateInternal(ulong size, MmapProts prot) + private static IntPtr AllocateInternal(ulong size, MmapProts prot, bool shared = false) { - const MmapFlags flags = MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS; + MmapFlags flags = MmapFlags.MAP_ANONYMOUS; + + if (shared) + { + flags |= MmapFlags.MAP_SHARED | (MmapFlags)0x80000; + } + else + { + flags |= MmapFlags.MAP_PRIVATE; + } + + if (prot == MmapProts.PROT_NONE) + { + flags |= MmapFlags.MAP_NORESERVE; + } IntPtr ptr = Syscall.mmap(IntPtr.Zero, size, prot, flags, -1, 0); @@ -40,7 +74,42 @@ namespace Ryujinx.Memory public static bool Commit(IntPtr address, ulong size) { - return Syscall.mprotect(address, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE) == 0; + bool success = Syscall.mprotect(address, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE) == 0; + + if (success) + { + foreach (var shared in _sharedMemory) + { + if ((ulong)address + size > (ulong)shared.SourcePointer && (ulong)address < (ulong)shared.SourcePointer + shared.Size) + { + ulong sharedAddress = ((ulong)address - (ulong)shared.SourcePointer) + (ulong)shared.Pointer; + + if (Syscall.mprotect((IntPtr)sharedAddress, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE) != 0) + { + return false; + } + } + } + } + + return success; + } + + public static bool Decommit(IntPtr address, ulong size) + { + bool isShared; + + lock (_sharedMemory) + { + isShared = _sharedMemory.Exists(x => (ulong)address >= (ulong)x.Pointer && (ulong)address + size <= (ulong)x.Pointer + x.Size); + } + + // Must be writable for madvise to work properly. + Syscall.mprotect(address, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE); + + madvise(address, size, isShared ? MADV_REMOVE : MADV_DONTNEED); + + return Syscall.mprotect(address, size, MmapProts.PROT_NONE) == 0; } public static bool Reprotect(IntPtr address, ulong size, MemoryPermission permission) @@ -71,5 +140,140 @@ namespace Ryujinx.Memory return false; } + + public static IntPtr Remap(IntPtr target, IntPtr source, ulong size) + { + int flags = (int)MremapFlags.MREMAP_MAYMOVE; + + if (target != IntPtr.Zero) + { + flags |= 2; + } + + IntPtr result = mremap(source, 0, size, (MremapFlags)(flags), target); + + if (result == IntPtr.Zero) + { + throw new InvalidOperationException(); + } + + return result; + } + + public static IntPtr CreateSharedMemory(ulong size, bool reserve) + { + IntPtr result = AllocateInternal( + size, + reserve ? MmapProts.PROT_NONE : MmapProts.PROT_READ | MmapProts.PROT_WRITE, + true); + + if (result == IntPtr.Zero) + { + throw new OutOfMemoryException(); + } + + _sharedMemorySource[result] = (ulong)size; + + return result; + } + + public static void DestroySharedMemory(IntPtr handle) + { + lock (_sharedMemory) + { + foreach (var memory in _sharedMemory) + { + if (memory.SourcePointer == handle) + { + throw new InvalidOperationException("Shared memory cannot be destroyed unless fully unmapped."); + } + } + } + + _sharedMemorySource.Remove(handle, out ulong _); + } + + public static IntPtr MapSharedMemory(IntPtr handle) + { + // Try find the handle for this shared memory. If it is mapped, then we want to map + // it a second time in another location. + // If it is not mapped, then its handle is the mapping. + + ulong size = _sharedMemorySource[handle]; + + if (size == 0) + { + throw new InvalidOperationException("Shared memory cannot be mapped after its source is unmapped."); + } + + lock (_sharedMemory) + { + foreach (var memory in _sharedMemory) + { + if (memory.Pointer == handle) + { + IntPtr result = AllocateInternal( + memory.Size, + MmapProts.PROT_NONE + ); + + if (result == IntPtr.Zero) + { + throw new OutOfMemoryException(); + } + + Remap(result, handle, memory.Size); + + _sharedMemory.Add(new UnixSharedMemory + { + Pointer = result, + Size = memory.Size, + + SourcePointer = handle + }); + + return result; + } + } + + _sharedMemory.Add(new UnixSharedMemory + { + Pointer = handle, + Size = size, + + SourcePointer = handle + }); + } + + return handle; + } + + public static void UnmapSharedMemory(IntPtr address) + { + lock (_sharedMemory) + { + int removed = _sharedMemory.RemoveAll(memory => + { + if (memory.Pointer == address) + { + if (memory.Pointer == memory.SourcePointer) + { + // After removing the original mapping, it cannot be mapped again. + _sharedMemorySource[memory.SourcePointer] = 0; + } + + Free(address); + return true; + } + + return false; + }); + + if (removed == 0) + { + throw new InvalidOperationException("Shared memory mapping could not be found."); + } + } + } } }
\ No newline at end of file |