From 335d91a367074e1af3ba5b20d81d519cd2d5689c Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 21 Jun 2021 21:31:16 -0300 Subject: [PATCH] Direct mapping with alias support on Windows --- Ryujinx.Cpu/MemoryManager.cs | 19 +- Ryujinx.Cpu/MemoryManagerHostMapped.cs | 361 +++++++++++++----- Ryujinx.HLE/HOS/ArmProcessContextFactory.cs | 2 +- Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs | 2 - .../HOS/Kernel/Memory/KPageTableBase.cs | 11 +- .../HOS/Kernel/Memory/KPageTableHostMapped.cs | 133 ------- .../HOS/Kernel/Memory/KSharedMemory.cs | 9 +- .../HOS/Kernel/Memory/KTransferMemory.cs | 5 - .../HOS/Kernel/Memory/SharedMemoryStorage.cs | 44 +-- Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs | 9 +- Ryujinx.HLE/Switch.cs | 2 +- .../MockVirtualMemoryManager.cs | 6 +- Ryujinx.Memory/AddressSpaceManager.cs | 2 +- Ryujinx.Memory/MemoryAllocationFlags.cs | 8 +- Ryujinx.Memory/MemoryBlock.cs | 32 +- Ryujinx.Memory/MemoryManagement.cs | 32 +- Ryujinx.Memory/MemoryManagementWindows.cs | 202 +++++----- Ryujinx.Memory/PageTable.cs | 2 +- Ryujinx.Memory/Range/HostMemoryRange.cs | 71 ---- Ryujinx.Tests/Cpu/CpuTest.cs | 4 +- Ryujinx.Tests/Cpu/CpuTest32.cs | 4 +- 21 files changed, 478 insertions(+), 482 deletions(-) delete mode 100644 Ryujinx.HLE/HOS/Kernel/Memory/KPageTableHostMapped.cs delete mode 100644 Ryujinx.Memory/Range/HostMemoryRange.cs diff --git a/Ryujinx.Cpu/MemoryManager.cs b/Ryujinx.Cpu/MemoryManager.cs index a3135b9ec..b9769fd40 100644 --- a/Ryujinx.Cpu/MemoryManager.cs +++ b/Ryujinx.Cpu/MemoryManager.cs @@ -127,8 +127,21 @@ namespace Ryujinx.Cpu /// public T ReadTracked(ulong va) where T : unmanaged { - SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), false); - return MemoryMarshal.Cast(GetSpan(va, Unsafe.SizeOf()))[0]; + try + { + SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), false); + + return Read(va); + } + catch (InvalidMemoryRegionException) + { + if (_invalidAccessHandler == null || !_invalidAccessHandler(va)) + { + throw; + } + + return default; + } } /// @@ -301,7 +314,7 @@ namespace Ryujinx.Cpu return (int)(vaSpan / PageSize); } - private void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException(); + private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException(); [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va); diff --git a/Ryujinx.Cpu/MemoryManagerHostMapped.cs b/Ryujinx.Cpu/MemoryManagerHostMapped.cs index e0f23048c..8bd3092e2 100644 --- a/Ryujinx.Cpu/MemoryManagerHostMapped.cs +++ b/Ryujinx.Cpu/MemoryManagerHostMapped.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; namespace Ryujinx.Cpu @@ -14,7 +15,7 @@ namespace Ryujinx.Cpu /// /// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region. /// - public class MemoryManagerHostMapped : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked + public class MemoryManagerHostMapped : MemoryManagerBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock { public const int PageBits = 12; public const int PageSize = 1 << PageBits; @@ -39,12 +40,14 @@ namespace Ryujinx.Cpu private readonly bool _unsafeMode; private readonly MemoryBlock _addressSpace; - private readonly MemoryBlock _addressSpaceMirror; private readonly ulong _addressSpaceSize; + private readonly MemoryBlock _backingMemory; + private readonly PageTable _pageTable; + private readonly MemoryEhMeilleure _memoryEh; - private ulong[] _pageTable; + private readonly ulong[] _pageBitmap; public int AddressSpaceBits { get; } @@ -59,11 +62,14 @@ namespace Ryujinx.Cpu /// /// Creates a new instance of the host mapped memory manager. /// + /// Physical backing memory where virtual memory will be mapped to /// Size of the address space /// True if unmanaged access should not be masked (unsafe), false otherwise. /// Optional function to handle invalid memory accesses - public MemoryManagerHostMapped(ulong addressSpaceSize, bool unsafeMode, InvalidAccessHandler invalidAccessHandler = null) + public MemoryManagerHostMapped(MemoryBlock backingMemory, ulong addressSpaceSize, bool unsafeMode, InvalidAccessHandler invalidAccessHandler = null) { + _backingMemory = backingMemory; + _pageTable = new PageTable(); _invalidAccessHandler = invalidAccessHandler; _unsafeMode = unsafeMode; _addressSpaceSize = addressSpaceSize; @@ -79,9 +85,8 @@ namespace Ryujinx.Cpu AddressSpaceBits = asBits; - _pageTable = new ulong[1 << (AddressSpaceBits - (PageBits + PageToPteShift))]; - _addressSpace = new MemoryBlock(asSize, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Mirrorable); - _addressSpaceMirror = _addressSpace.CreateMirror(); + _pageBitmap = new ulong[1 << (AddressSpaceBits - (PageBits + PageToPteShift))]; + _addressSpace = new MemoryBlock(asSize, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); Tracking = new MemoryTracking(this, PageSize, invalidAccessHandler); _memoryEh = new MemoryEhMeilleure(_addressSpace, Tracking); } @@ -140,12 +145,25 @@ namespace Ryujinx.Cpu { AssertValidAddressAndSize(va, size); - _addressSpace.Commit(va, size); + PtMap(va, pa, size); + _addressSpace.MapView(_backingMemory, pa, va, size); AddMapping(va, size); Tracking.Map(va, size); } + private void PtMap(ulong va, ulong pa, ulong size) + { + while (size != 0) + { + _pageTable.Map(va, pa); + + va += PageSize; + pa += PageSize; + size -= PageSize; + } + } + /// public void Unmap(ulong va, ulong size) { @@ -155,27 +173,25 @@ namespace Ryujinx.Cpu Tracking.Unmap(va, size); RemoveMapping(va, size); - _addressSpace.Decommit(va, size); + _addressSpace.UnmapView(va, size); + PtUnmap(va, size); + } + + private void PtUnmap(ulong va, ulong size) + { + while (size != 0) + { + _pageTable.Unmap(va); + + va += PageSize; + size -= PageSize; + } } /// public T Read(ulong va) where T : unmanaged { - try - { - AssertMapped(va, (ulong)Unsafe.SizeOf()); - - return _addressSpaceMirror.Read(va); - } - catch (InvalidMemoryRegionException) - { - if (_invalidAccessHandler == null || !_invalidAccessHandler(va)) - { - throw; - } - - return default; - } + return MemoryMarshal.Cast(GetSpan(va, Unsafe.SizeOf()))[0]; } /// @@ -201,46 +217,73 @@ namespace Ryujinx.Cpu /// public void Read(ulong va, Span data) { - try - { - AssertMapped(va, (ulong)data.Length); - - _addressSpaceMirror.Read(va, data); - } - catch (InvalidMemoryRegionException) - { - if (_invalidAccessHandler == null || !_invalidAccessHandler(va)) - { - throw; - } - } + ReadImpl(va, data); } /// public void Write(ulong va, T value) where T : unmanaged { - try - { - SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), write: true); - - _addressSpaceMirror.Write(va, value); - } - catch (InvalidMemoryRegionException) - { - if (_invalidAccessHandler == null || !_invalidAccessHandler(va)) - { - throw; - } - } + Write(va, MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref value, 1))); } /// public void Write(ulong va, ReadOnlySpan data) { - try { - SignalMemoryTracking(va, (ulong)data.Length, write: true); + if (data.Length == 0) + { + return; + } - _addressSpaceMirror.Write(va, data); + SignalMemoryTracking(va, (ulong)data.Length, true); + + WriteImpl(va, data); + } + + /// + public void WriteUntracked(ulong va, ReadOnlySpan data) + { + if (data.Length == 0) + { + return; + } + + WriteImpl(va, data); + } + + private void WriteImpl(ulong va, ReadOnlySpan data) + { + try + { + AssertValidAddressAndSize(va, (ulong)data.Length); + + if (IsContiguousAndMapped(va, data.Length)) + { + data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length)); + } + else + { + int offset = 0, size; + + if ((va & PageMask) != 0) + { + ulong pa = GetPhysicalAddressInternal(va); + + size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); + + data.Slice(0, size).CopyTo(_backingMemory.GetSpan(pa, size)); + + offset += size; + } + + for (; offset < data.Length; offset += size) + { + ulong pa = GetPhysicalAddressInternal(va + (ulong)offset); + + size = Math.Min(data.Length - offset, PageSize); + + data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size)); + } + } } catch (InvalidMemoryRegionException) { @@ -251,37 +294,31 @@ namespace Ryujinx.Cpu } } - /// - public void WriteUntracked(ulong va, ReadOnlySpan data) - { - try - { - AssertMapped(va, (ulong)data.Length); - - _addressSpaceMirror.Write(va, data); - } - catch (InvalidMemoryRegionException) - { - if (_invalidAccessHandler == null || !_invalidAccessHandler(va)) - { - throw; - } - } -} - /// public ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false) { + if (size == 0) + { + return ReadOnlySpan.Empty; + } + if (tracked) { - SignalMemoryTracking(va, (ulong)size, write: false); + SignalMemoryTracking(va, (ulong)size, false); + } + + if (IsContiguousAndMapped(va, size)) + { + return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size); } else { - AssertMapped(va, (ulong)size); - } + Span data = new byte[size]; - return _addressSpaceMirror.GetSpan(va, size); + ReadImpl(va, data); + + return data; + } } /// @@ -291,20 +328,35 @@ namespace Ryujinx.Cpu { SignalMemoryTracking(va, (ulong)size, true); } - else + + if (size == 0) { - AssertMapped(va, (ulong)size); + return new WritableRegion(null, va, Memory.Empty); } - return _addressSpaceMirror.GetWritableRegion(va, size); + if (IsContiguousAndMapped(va, size)) + { + return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size)); + } + else + { + Memory memory = new byte[size]; + + GetSpan(va, size).CopyTo(memory.Span); + + return new WritableRegion(this, va, memory); + } } /// public ref T GetRef(ulong va) where T : unmanaged { - SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), true); + if (!IsContiguous(va, Unsafe.SizeOf())) + { + ThrowMemoryNotContiguous(); + } - return ref _addressSpaceMirror.GetRef(va); + return ref _backingMemory.GetRef(GetPhysicalAddressInternal(va)); } /// @@ -322,7 +374,7 @@ namespace Ryujinx.Cpu int bit = (int)((page & 31) << 1); int pageIndex = (int)(page >> PageToPteShift); - ref ulong pageRef = ref _pageTable[pageIndex]; + ref ulong pageRef = ref _pageBitmap[pageIndex]; ulong pte = Volatile.Read(ref pageRef); @@ -373,7 +425,7 @@ namespace Ryujinx.Cpu mask &= endMask; } - ref ulong pageRef = ref _pageTable[pageIndex++]; + ref ulong pageRef = ref _pageBitmap[pageIndex++]; ulong pte = Volatile.Read(ref pageRef); pte |= pte >> 1; @@ -388,10 +440,124 @@ namespace Ryujinx.Cpu return true; } + private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsContiguous(ulong va, int size) + { + if (!ValidateAddress(va) || !ValidateAddressAndSize(va, (ulong)size)) + { + return false; + } + + int pages = GetPagesCount(va, (uint)size, out va); + + for (int page = 0; page < pages - 1; page++) + { + if (!ValidateAddress(va + PageSize)) + { + return false; + } + + if (GetPhysicalAddressInternal(va) + PageSize != GetPhysicalAddressInternal(va + PageSize)) + { + return false; + } + + va += PageSize; + } + + return true; + } + /// public IEnumerable GetPhysicalRegions(ulong va, ulong size) { - throw new NotImplementedException(); + if (size == 0) + { + return Enumerable.Empty(); + } + + if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) + { + return null; + } + + int pages = GetPagesCount(va, (uint)size, out va); + + var regions = new List(); + + ulong regionStart = GetPhysicalAddressInternal(va); + ulong regionSize = PageSize; + + for (int page = 0; page < pages - 1; page++) + { + if (!ValidateAddress(va + PageSize)) + { + return null; + } + + ulong newPa = GetPhysicalAddressInternal(va + PageSize); + + if (GetPhysicalAddressInternal(va) + PageSize != newPa) + { + regions.Add(new MemoryRange(regionStart, regionSize)); + regionStart = newPa; + regionSize = 0; + } + + va += PageSize; + regionSize += PageSize; + } + + regions.Add(new MemoryRange(regionStart, regionSize)); + + return regions; + } + + private void ReadImpl(ulong va, Span data) + { + if (data.Length == 0) + { + return; + } + + try + { + AssertValidAddressAndSize(va, (ulong)data.Length); + + int offset = 0, size; + + if ((va & PageMask) != 0) + { + ulong pa = GetPhysicalAddressInternal(va); + + size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); + + _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(0, size)); + + offset += size; + } + + for (; offset < data.Length; offset += size) + { + ulong pa = GetPhysicalAddressInternal(va + (ulong)offset); + + size = Math.Min(data.Length - offset, PageSize); + + _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(offset, size)); + } + } + catch (InvalidMemoryRegionException) + { + if (_invalidAccessHandler == null || !_invalidAccessHandler(va)) + { + throw; + } + } } /// @@ -420,7 +586,7 @@ namespace Ryujinx.Cpu int bit = (int)((pageStart & 31) << 1); int pageIndex = (int)(pageStart >> PageToPteShift); - ref ulong pageRef = ref _pageTable[pageIndex]; + ref ulong pageRef = ref _pageBitmap[pageIndex]; ulong pte = Volatile.Read(ref pageRef); ulong state = ((pte >> bit) & 3); @@ -452,7 +618,7 @@ namespace Ryujinx.Cpu mask &= endMask; } - ref ulong pageRef = ref _pageTable[pageIndex++]; + ref ulong pageRef = ref _pageBitmap[pageIndex++]; ulong pte = Volatile.Read(ref pageRef); ulong mappedMask = mask & BlockMappedMask; @@ -523,7 +689,7 @@ namespace Ryujinx.Cpu ulong tag = protTag << bit; int pageIndex = (int)(pageStart >> PageToPteShift); - ref ulong pageRef = ref _pageTable[pageIndex]; + ref ulong pageRef = ref _pageBitmap[pageIndex]; ulong pte; @@ -555,7 +721,7 @@ namespace Ryujinx.Cpu mask &= endMask; } - ref ulong pageRef = ref _pageTable[pageIndex++]; + ref ulong pageRef = ref _pageBitmap[pageIndex++]; ulong pte; ulong mappedMask; @@ -625,7 +791,7 @@ namespace Ryujinx.Cpu mask &= endMask; } - ref ulong pageRef = ref _pageTable[pageIndex++]; + ref ulong pageRef = ref _pageBitmap[pageIndex++]; ulong pte; ulong mappedMask; @@ -670,7 +836,7 @@ namespace Ryujinx.Cpu mask |= endMask; } - ref ulong pageRef = ref _pageTable[pageIndex++]; + ref ulong pageRef = ref _pageBitmap[pageIndex++]; ulong pte; do @@ -683,12 +849,27 @@ namespace Ryujinx.Cpu } } + private ulong GetPhysicalAddress(ulong va) + { + // We return -1L if the virtual address is invalid or unmapped. + if (!ValidateAddress(va) || !IsMapped(va)) + { + return ulong.MaxValue; + } + + return GetPhysicalAddressInternal(va); + } + + private ulong GetPhysicalAddressInternal(ulong va) + { + return _pageTable.Read(va) + (va & PageMask); + } + /// /// Disposes of resources used by the memory manager. /// protected override void Destroy() { - _addressSpaceMirror.Dispose(); _addressSpace.Dispose(); _memoryEh.Dispose(); } diff --git a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs index faa379f79..a951b3a82 100644 --- a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs +++ b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS case MemoryManagerMode.HostMapped: case MemoryManagerMode.HostMappedUnsafe: bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe; - return new ArmProcessContext(pid, _gpu, new MemoryManagerHostMapped(addressSpaceSize, unsafeMode, invalidAccessHandler), for64Bit); + return new ArmProcessContext(pid, _gpu, new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler), for64Bit); default: throw new ArgumentOutOfRangeException(); diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs index f1b60ba11..9d5212315 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs @@ -9,8 +9,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { private readonly IVirtualMemoryManager _cpuMemory; - public override bool SupportsMemoryAliasing => true; - public KPageTable(KernelContext context, IVirtualMemoryManager cpuMemory) : base(context) { _cpuMemory = cpuMemory; diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs index 509955aa7..aca43f431 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs @@ -1,11 +1,9 @@ using Ryujinx.Common; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; -using Ryujinx.Memory.Range; using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; namespace Ryujinx.HLE.HOS.Kernel.Memory { @@ -73,8 +71,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory private MersenneTwister _randomNumberGenerator; - public abstract bool SupportsMemoryAliasing { get; } - private MemoryFillValue _heapFillValue; private MemoryFillValue _ipcFillValue; @@ -305,7 +301,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory TlsIoRegionStart = tlsIoRegion.Start; TlsIoRegionEnd = tlsIoRegion.End; - // TODO: Check kernel configuration via secure monitor call when implemented to set memory fill values. + // TODO: Check kernel configuration via secure monitor call when implemented to set memory fill values. _currentHeapAddr = HeapRegionStart; _heapCapacity = 0; @@ -1692,11 +1688,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory bool send, out ulong dst) { - if (!SupportsMemoryAliasing) - { - throw new NotSupportedException("Memory aliasing not supported, can't map IPC buffers."); - } - dst = 0; if (!_slabManager.CanAllocate(MaxBlocksNeededForInsertion)) diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableHostMapped.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableHostMapped.cs deleted file mode 100644 index 0c7d77ada..000000000 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableHostMapped.cs +++ /dev/null @@ -1,133 +0,0 @@ -using Ryujinx.HLE.HOS.Kernel.Common; -using Ryujinx.Memory; -using Ryujinx.Memory.Range; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Ryujinx.HLE.HOS.Kernel.Memory -{ - class KPageTableHostMapped : KPageTableBase - { - private const int CopyChunckSize = 0x100000; - - private readonly IVirtualMemoryManager _cpuMemory; - - public override bool SupportsMemoryAliasing => false; - - public KPageTableHostMapped(KernelContext context, IVirtualMemoryManager cpuMemory) : base(context) - { - _cpuMemory = cpuMemory; - } - - /// - protected override void GetPhysicalRegions(ulong va, ulong size, KPageList pageList) - { - throw new NotImplementedException(); - } - - /// - protected override ReadOnlySpan GetSpan(ulong va, int size) - { - return _cpuMemory.GetSpan(va, size); - } - - /// - protected override KernelResult MapMemory(ulong src, ulong dst, ulong pagesCount, KMemoryPermission oldSrcPermission, KMemoryPermission newDstPermission) - { - ulong size = pagesCount * PageSize; - - _cpuMemory.Map(dst, 0, size); - - ulong currentSize = size; - while (currentSize > 0) - { - ulong copySize = Math.Min(currentSize, CopyChunckSize); - _cpuMemory.Write(dst, _cpuMemory.GetSpan(src, (int)copySize)); - currentSize -= copySize; - } - - return KernelResult.Success; - } - - /// - protected override KernelResult UnmapMemory(ulong dst, ulong src, ulong pagesCount, KMemoryPermission oldDstPermission, KMemoryPermission newSrcPermission) - { - ulong size = pagesCount * PageSize; - - // TODO: Validation. - - ulong currentSize = size; - while (currentSize > 0) - { - ulong copySize = Math.Min(currentSize, CopyChunckSize); - _cpuMemory.Write(src, _cpuMemory.GetSpan(dst, (int)copySize)); - currentSize -= copySize; - } - - _cpuMemory.Unmap(dst, size); - return KernelResult.Success; - } - - /// - protected override KernelResult MapPages(ulong dstVa, ulong pagesCount, ulong srcPa, KMemoryPermission permission, bool shouldFillPages, byte fillValue) - { - _cpuMemory.Map(dstVa, 0, pagesCount * PageSize); - - if (shouldFillPages) - { - _cpuMemory.Fill(dstVa, pagesCount * PageSize, fillValue); - } - - return KernelResult.Success; - } - - /// - protected override KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission, bool shouldFillPages, byte fillValue) - { - ulong pagesCount = pageList.GetPagesCount(); - - _cpuMemory.Map(address, 0, pagesCount * PageSize); - - if (shouldFillPages) - { - _cpuMemory.Fill(address, pagesCount * PageSize, fillValue); - } - - return KernelResult.Success; - } - - /// - protected override KernelResult Unmap(ulong address, ulong pagesCount) - { - _cpuMemory.Unmap(address, pagesCount * PageSize); - return KernelResult.Success; - } - - /// - protected override KernelResult Reprotect(ulong address, ulong pagesCount, KMemoryPermission permission) - { - // TODO. - return KernelResult.Success; - } - - /// - protected override KernelResult ReprotectWithAttributes(ulong address, ulong pagesCount, KMemoryPermission permission) - { - // TODO. - return KernelResult.Success; - } - - /// - protected override void SignalMemoryTracking(ulong va, ulong size, bool write) - { - _cpuMemory.SignalMemoryTracking(va, size, write); - } - - /// - protected override void Write(ulong va, ReadOnlySpan data) - { - _cpuMemory.Write(va, data); - } - } -} diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs index 7aef27ad8..72f4b6caf 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs @@ -50,14 +50,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.InvalidPermission; } - KernelResult result = memoryManager.MapPages(address, pageList, MemoryState.SharedMemory, permission); - - if (result == KernelResult.Success && !memoryManager.SupportsMemoryAliasing) - { - _storage.Borrow(process, address); - } - - return result; + return memoryManager.MapPages(address, pageList, MemoryState.SharedMemory, permission); } public KernelResult UnmapFromProcess(KPageTableBase memoryManager, ulong address, ulong size, KProcess process) diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs index 381cacc57..3b9b70176 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs @@ -94,11 +94,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory if (result == KernelResult.Success) { _isMapped = true; - - if (!memoryManager.SupportsMemoryAliasing) - { - _storage.Borrow(process, address); - } } return result; diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs b/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs index ea101411b..167e0aa90 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs @@ -1,8 +1,4 @@ -using Ryujinx.HLE.HOS.Kernel.Process; -using Ryujinx.Memory; -using Ryujinx.Memory.Range; -using System; -using System.Collections.Generic; +using System; namespace Ryujinx.HLE.HOS.Kernel.Memory { @@ -12,9 +8,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory private readonly KPageList _pageList; private readonly ulong _size; - private IVirtualMemoryManager _borrowerMemory; - private ulong _borrowerVa; - public SharedMemoryStorage(KernelContext context, KPageList pageList) { _context = context; @@ -29,24 +22,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } - public void Borrow(KProcess dstProcess, ulong va) - { - ulong currentOffset = 0; - - foreach (KPageNode pageNode in _pageList) - { - ulong address = pageNode.Address - DramMemoryMap.DramBase; - ulong size = pageNode.PagesCount * KPageTableBase.PageSize; - - dstProcess.CpuMemory.Write(va + currentOffset, _context.Memory.GetSpan(address + currentOffset, (int)size)); - - currentOffset += size; - } - - _borrowerMemory = dstProcess.CpuMemory; - _borrowerVa = va; - } - public void ZeroFill() { for (ulong offset = 0; offset < _size; offset += sizeof(ulong)) @@ -57,20 +32,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory public ref T GetRef(ulong offset) where T : unmanaged { - if (_borrowerMemory == null) + if (_pageList.Nodes.Count == 1) { - if (_pageList.Nodes.Count == 1) - { - ulong address = _pageList.Nodes.First.Value.Address - DramMemoryMap.DramBase; - return ref _context.Memory.GetRef(address + offset); - } + ulong address = _pageList.Nodes.First.Value.Address - DramMemoryMap.DramBase; + return ref _context.Memory.GetRef(address + offset); + } - throw new NotImplementedException("Non-contiguous shared memory is not yet supported."); - } - else - { - return ref _borrowerMemory.GetRef(_borrowerVa + offset); - } + throw new NotImplementedException("Non-contiguous shared memory is not yet supported."); } public KPageList GetPageList() diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index be45f02aa..00f17db89 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -1075,14 +1075,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process Context = _contextFactory.Create(KernelContext, Pid, 1UL << addrSpaceBits, InvalidAccessHandler, for64Bit); - if (Context.AddressSpace is MemoryManagerHostMapped) - { - MemoryManager = new KPageTableHostMapped(KernelContext, CpuMemory); - } - else - { - MemoryManager = new KPageTable(KernelContext, CpuMemory); - } + MemoryManager = new KPageTable(KernelContext, CpuMemory); } private bool InvalidAccessHandler(ulong va) diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 0dcbc7ec5..b51cc3080 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -60,7 +60,7 @@ namespace Ryujinx.HLE AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(configuration.AudioDeviceDriver); - Memory = new MemoryBlock(configuration.MemoryConfiguration.ToDramSize(), MemoryAllocationFlags.Reserve); + Memory = new MemoryBlock(configuration.MemoryConfiguration.ToDramSize(), MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Mirrorable); Gpu = new GpuContext(configuration.GpuRenderer); diff --git a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs index 05e157b66..cad0c2b53 100644 --- a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs +++ b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Memory.Tests { } - public void Map(ulong va, nuint hostAddress, ulong size) + public void Map(ulong va, ulong pa, ulong size) { throw new NotImplementedException(); } @@ -59,9 +59,9 @@ namespace Ryujinx.Memory.Tests throw new NotImplementedException(); } - IEnumerable IVirtualMemoryManager.GetPhysicalRegions(ulong va, ulong size) + IEnumerable IVirtualMemoryManager.GetPhysicalRegions(ulong va, ulong size) { - return NoMappings ? new HostMemoryRange[0] : new HostMemoryRange[] { new HostMemoryRange((nuint)va, size) }; + return NoMappings ? new MemoryRange[0] : new MemoryRange[] { new MemoryRange(va, size) }; } public bool IsMapped(ulong va) diff --git a/Ryujinx.Memory/AddressSpaceManager.cs b/Ryujinx.Memory/AddressSpaceManager.cs index 821fd5fd1..da4aa047f 100644 --- a/Ryujinx.Memory/AddressSpaceManager.cs +++ b/Ryujinx.Memory/AddressSpaceManager.cs @@ -202,7 +202,7 @@ namespace Ryujinx.Memory return (int)(vaSpan / PageSize); } - private void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException(); + private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException(); [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va); diff --git a/Ryujinx.Memory/MemoryAllocationFlags.cs b/Ryujinx.Memory/MemoryAllocationFlags.cs index d9420dd37..313f33e5f 100644 --- a/Ryujinx.Memory/MemoryAllocationFlags.cs +++ b/Ryujinx.Memory/MemoryAllocationFlags.cs @@ -29,6 +29,12 @@ namespace Ryujinx.Memory /// Enables mirroring of the memory block through aliasing of memory pages. /// When enabled, this allows creating more memory blocks sharing the same backing storage. /// - Mirrorable = 1 << 2 + Mirrorable = 1 << 2, + + /// + /// Indicates that the memory block should support mapping views of a mirrorable memory block. + /// The block that is to have their views mapped should be created with the flag. + /// + ViewCompatible = 1 << 3 } } diff --git a/Ryujinx.Memory/MemoryBlock.cs b/Ryujinx.Memory/MemoryBlock.cs index 3561b81a6..fdc84068d 100644 --- a/Ryujinx.Memory/MemoryBlock.cs +++ b/Ryujinx.Memory/MemoryBlock.cs @@ -41,7 +41,7 @@ namespace Ryujinx.Memory } else if (flags.HasFlag(MemoryAllocationFlags.Reserve)) { - _pointer = MemoryManagement.Reserve(size); + _pointer = MemoryManagement.Reserve(size, flags.HasFlag(MemoryAllocationFlags.ViewCompatible)); } else { @@ -112,6 +112,36 @@ namespace Ryujinx.Memory return MemoryManagement.Decommit(GetPointerInternal(offset, size), size); } + /// + /// Maps a view of memory from another memory block. + /// + /// Memory block from where the backing memory will be taken + /// Offset on of the region that should be mapped + /// Offset to map the view into on this block + /// Size of the range to be mapped + /// Throw when the source memory block does not support mirroring + /// Throw when the memory block has already been disposed + /// Throw when either or are out of range + public void MapView(MemoryBlock srcBlock, ulong srcOffset, ulong dstOffset, ulong size) + { + if (srcBlock._sharedMemory == IntPtr.Zero) + { + throw new ArgumentException("The source memory block is not mirrorable, and thus cannot be mapped on the current block."); + } + + MemoryManagement.MapView(srcBlock._sharedMemory, srcOffset, GetPointerInternal(dstOffset, size), size); + } + + /// + /// Unmaps a view of memory from another memory block. + /// + /// Offset of the view previously mapped with + /// Size of the range to be unmapped + public void UnmapView(ulong offset, ulong size) + { + MemoryManagement.UnmapView(GetPointerInternal(offset, size), size); + } + /// /// Reprotects a region of memory. /// diff --git a/Ryujinx.Memory/MemoryManagement.cs b/Ryujinx.Memory/MemoryManagement.cs index 680969b6d..a325532f2 100644 --- a/Ryujinx.Memory/MemoryManagement.cs +++ b/Ryujinx.Memory/MemoryManagement.cs @@ -23,13 +23,13 @@ namespace Ryujinx.Memory } } - public static IntPtr Reserve(ulong size) + public static IntPtr Reserve(ulong size, bool viewCompatible) { if (OperatingSystem.IsWindows()) { IntPtr sizeNint = new IntPtr((long)size); - return MemoryManagementWindows.Reserve(sizeNint); + return MemoryManagementWindows.Reserve(sizeNint, viewCompatible); } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) @@ -80,6 +80,34 @@ namespace Ryujinx.Memory } } + public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr address, ulong size) + { + if (OperatingSystem.IsWindows()) + { + IntPtr sizeNint = new IntPtr((long)size); + + MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, sizeNint); + } + else + { + throw new PlatformNotSupportedException(); + } + } + + public static void UnmapView(IntPtr address, ulong size) + { + if (OperatingSystem.IsWindows()) + { + IntPtr sizeNint = new IntPtr((long)size); + + MemoryManagementWindows.UnmapView(address, sizeNint); + } + else + { + throw new PlatformNotSupportedException(); + } + } + public static void Reprotect(IntPtr address, ulong size, MemoryPermission permission, bool throwOnFail) { bool result; diff --git a/Ryujinx.Memory/MemoryManagementWindows.cs b/Ryujinx.Memory/MemoryManagementWindows.cs index 48616ec30..d0700518e 100644 --- a/Ryujinx.Memory/MemoryManagementWindows.cs +++ b/Ryujinx.Memory/MemoryManagementWindows.cs @@ -9,7 +9,10 @@ namespace Ryujinx.Memory [SupportedOSPlatform("windows")] static class MemoryManagementWindows { + private const int PageSize = 0x1000; + private static readonly IntPtr InvalidHandleValue = new IntPtr(-1); + private static readonly IntPtr CurrentProcessHandle = new IntPtr(-1); private static bool UseWin10Placeholders; private static object _emulatedHandleLock = new object(); @@ -23,6 +26,16 @@ namespace Ryujinx.Memory AllocationType flAllocationType, MemoryProtection flProtect); + [DllImport("KernelBase.dll", SetLastError = true)] + private static extern IntPtr VirtualAlloc2( + IntPtr process, + IntPtr lpAddress, + IntPtr dwSize, + AllocationType flAllocationType, + MemoryProtection flProtect, + IntPtr extendedParameters, + ulong parameterCount); + [DllImport("kernel32.dll", SetLastError = true)] private static extern bool VirtualProtect( IntPtr lpAddress, @@ -53,10 +66,25 @@ namespace Ryujinx.Memory uint dwFileOffsetLow, IntPtr dwNumberOfBytesToMap); + [DllImport("KernelBase.dll", SetLastError = true)] + private static extern IntPtr MapViewOfFile3( + IntPtr hFileMappingObject, + IntPtr process, + IntPtr baseAddress, + ulong offset, + IntPtr dwNumberOfBytesToMap, + ulong allocationType, + MemoryProtection dwDesiredAccess, + IntPtr extendedParameters, + ulong parameterCount); + [DllImport("kernel32.dll", SetLastError = true)] private static extern bool UnmapViewOfFile(IntPtr lpBaseAddress); - [DllImport("kernel32.dll", SetLastError = true)] + [DllImport("KernelBase.dll", SetLastError = true)] + private static extern bool UnmapViewOfFile2(IntPtr process, IntPtr lpBaseAddress, ulong unmapFlags); + + [DllImport("kernel32.dll")] private static extern uint GetLastError(); static MemoryManagementWindows() @@ -69,8 +97,13 @@ namespace Ryujinx.Memory return AllocateInternal(size, AllocationType.Reserve | AllocationType.Commit); } - public static IntPtr Reserve(IntPtr size) + public static IntPtr Reserve(IntPtr size, bool viewCompatible) { + if (viewCompatible) + { + return AllocateInternal2(size, AllocationType.Reserve | AllocationType.ReservePlaceholder); + } + return AllocateInternal(size, AllocationType.Reserve); } @@ -86,44 +119,73 @@ namespace Ryujinx.Memory return ptr; } - public static bool Commit(IntPtr location, IntPtr size) + private static IntPtr AllocateInternal2(IntPtr size, AllocationType flags = 0) { - if (UseWin10Placeholders) + IntPtr ptr = VirtualAlloc2(CurrentProcessHandle, IntPtr.Zero, size, flags, MemoryProtection.NoAccess, IntPtr.Zero, 0); + + if (ptr == IntPtr.Zero) { - lock (_emulatedSharedList) - { - foreach (var shared in _emulatedSharedList) - { - if (shared.CommitMap(location, size)) - { - return true; - } - } - } + throw new OutOfMemoryException(); } + return ptr; + } + + public static bool Commit(IntPtr location, IntPtr size) + { return VirtualAlloc(location, size, AllocationType.Commit, MemoryProtection.ReadWrite) != IntPtr.Zero; } public static bool Decommit(IntPtr location, IntPtr size) { - if (UseWin10Placeholders) - { - lock (_emulatedSharedList) - { - foreach (var shared in _emulatedSharedList) - { - if (shared.DecommitMap(location, size)) - { - return true; - } - } - } - } - return VirtualFree(location, size, AllocationType.Decommit); } + public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size) + { + IntPtr endLocation = (nint)location + (nint)size; + + while (location != endLocation) + { + VirtualFree(location, (IntPtr)PageSize, AllocationType.Release | AllocationType.PreservePlaceholder); + + var ptr = MapViewOfFile3( + sharedMemory, + CurrentProcessHandle, + location, + srcOffset, + (IntPtr)PageSize, + 0x4000, + MemoryProtection.ReadWrite, + IntPtr.Zero, + 0); + + if (ptr == IntPtr.Zero) + { + throw new Exception($"MapViewOfFile3 failed with error code 0x{GetLastError():X}."); + } + + location += PageSize; + srcOffset += PageSize; + } + } + + public static void UnmapView(IntPtr location, IntPtr size) + { + IntPtr endLocation = (nint)location + (int)size; + + while (location != endLocation) + { + bool result = UnmapViewOfFile2(CurrentProcessHandle, location, 2); + if (!result) + { + throw new Exception($"UnmapViewOfFile2 failed with error code 0x{GetLastError():X}."); + } + + location += PageSize; + } + } + public static bool Reprotect(IntPtr address, IntPtr size, MemoryPermission permission) { if (UseWin10Placeholders) @@ -194,57 +256,26 @@ namespace Ryujinx.Memory public static IntPtr CreateSharedMemory(IntPtr size, bool reserve) { - if (UseWin10Placeholders && reserve) + var prot = reserve ? FileMapProtection.SectionReserve : FileMapProtection.SectionCommit; + + IntPtr handle = CreateFileMapping( + InvalidHandleValue, + IntPtr.Zero, + FileMapProtection.PageReadWrite | prot, + (uint)(size.ToInt64() >> 32), + (uint)size.ToInt64(), + null); + + if (handle == IntPtr.Zero) { - lock (_emulatedHandleLock) - { - int handle = GetEmulatedHandle(); - _emulatedShared[handle - 1] = new EmulatedSharedMemoryWindows((ulong)size); - _emulatedSharedList.Add(_emulatedShared[handle - 1]); - - return (IntPtr)handle; - } + throw new OutOfMemoryException(); } - else - { - var prot = reserve ? FileMapProtection.SectionReserve : FileMapProtection.SectionCommit; - IntPtr handle = CreateFileMapping( - InvalidHandleValue, - IntPtr.Zero, - FileMapProtection.PageReadWrite | prot, - (uint)(size.ToInt64() >> 32), - (uint)size.ToInt64(), - null); - - if (handle == IntPtr.Zero) - { - throw new OutOfMemoryException(); - } - - return handle; - } + return handle; } public static void DestroySharedMemory(IntPtr handle) { - if (UseWin10Placeholders) - { - lock (_emulatedHandleLock) - { - int iHandle = (int)(ulong)handle; - - if (EmulatedHandleValid(ref iHandle)) - { - _emulatedSharedList.Remove(_emulatedShared[iHandle]); - _emulatedShared[iHandle].Dispose(); - _emulatedShared[iHandle] = null; - - return; - } - } - } - if (!CloseHandle(handle)) { throw new ArgumentException("Invalid handle.", nameof(handle)); @@ -253,19 +284,6 @@ namespace Ryujinx.Memory public static IntPtr MapSharedMemory(IntPtr handle) { - if (UseWin10Placeholders) - { - lock (_emulatedHandleLock) - { - int iHandle = (int)(ulong)handle; - - if (EmulatedHandleValid(ref iHandle)) - { - return _emulatedShared[iHandle].Map(); - } - } - } - IntPtr ptr = MapViewOfFile(handle, 4 | 2, 0, 0, IntPtr.Zero); if (ptr == IntPtr.Zero) @@ -278,20 +296,6 @@ namespace Ryujinx.Memory public static void UnmapSharedMemory(IntPtr address) { - if (UseWin10Placeholders) - { - lock (_emulatedHandleLock) - { - foreach (EmulatedSharedMemoryWindows shared in _emulatedSharedList) - { - if (shared.Unmap((ulong)address)) - { - return; - } - } - } - } - if (!UnmapViewOfFile(address)) { throw new ArgumentException("Invalid address.", nameof(address)); diff --git a/Ryujinx.Memory/PageTable.cs b/Ryujinx.Memory/PageTable.cs index 71db1e762..8fdedd4f8 100644 --- a/Ryujinx.Memory/PageTable.cs +++ b/Ryujinx.Memory/PageTable.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Memory { - class PageTable where T : unmanaged + public class PageTable where T : unmanaged { public const int PageBits = 12; public const int PageSize = 1 << PageBits; diff --git a/Ryujinx.Memory/Range/HostMemoryRange.cs b/Ryujinx.Memory/Range/HostMemoryRange.cs deleted file mode 100644 index c6d8689c5..000000000 --- a/Ryujinx.Memory/Range/HostMemoryRange.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; - -namespace Ryujinx.Memory.Range -{ - /// - /// Range of memory composed of an address and size. - /// - public struct HostMemoryRange : IEquatable - { - /// - /// An empty memory range, with a null address and zero size. - /// - public static HostMemoryRange Empty => new HostMemoryRange(0, 0); - - /// - /// Start address of the range. - /// - public nuint Address { get; } - - /// - /// Size of the range in bytes. - /// - public ulong Size { get; } - - /// - /// Address where the range ends (exclusive). - /// - public nuint EndAddress => Address + (nuint)Size; - - /// - /// Creates a new memory range with the specified address and size. - /// - /// Start address - /// Size in bytes - public HostMemoryRange(nuint address, ulong size) - { - Address = address; - Size = size; - } - - /// - /// Checks if the range overlaps with another. - /// - /// The other range to check for overlap - /// True if the ranges overlap, false otherwise - public bool OverlapsWith(HostMemoryRange other) - { - nuint thisAddress = Address; - nuint thisEndAddress = EndAddress; - nuint otherAddress = other.Address; - nuint otherEndAddress = other.EndAddress; - - return thisAddress < otherEndAddress && otherAddress < thisEndAddress; - } - - public override bool Equals(object obj) - { - return obj is HostMemoryRange other && Equals(other); - } - - public bool Equals(HostMemoryRange other) - { - return Address == other.Address && Size == other.Size; - } - - public override int GetHashCode() - { - return HashCode.Combine(Address, Size); - } - } -} diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index 26f0d286c..9cee54970 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -54,9 +54,9 @@ namespace Ryujinx.Tests.Cpu _currAddress = CodeBaseAddress; _ram = new MemoryBlock(Size * 2); - _memory = new MemoryManager(1ul << 16); + _memory = new MemoryManager(_ram, 1ul << 16); _memory.IncrementReferenceCount(); - _memory.Map(CodeBaseAddress, _ram.GetPointer(0, Size * 2), Size * 2); + _memory.Map(CodeBaseAddress, 0, Size * 2); _context = CpuContext.CreateExecutionContext(); Translator.IsReadyForTranslation.Set(); diff --git a/Ryujinx.Tests/Cpu/CpuTest32.cs b/Ryujinx.Tests/Cpu/CpuTest32.cs index 5d24af39c..9be3c38a7 100644 --- a/Ryujinx.Tests/Cpu/CpuTest32.cs +++ b/Ryujinx.Tests/Cpu/CpuTest32.cs @@ -49,9 +49,9 @@ namespace Ryujinx.Tests.Cpu _currAddress = CodeBaseAddress; _ram = new MemoryBlock(Size * 2); - _memory = new MemoryManager(1ul << 16); + _memory = new MemoryManager(_ram, 1ul << 16); _memory.IncrementReferenceCount(); - _memory.Map(CodeBaseAddress, _ram.GetPointer(0, Size * 2), Size * 2); + _memory.Map(CodeBaseAddress, 0, Size * 2); _context = CpuContext.CreateExecutionContext(); _context.IsAarch32 = true;