From b4b7d4723eebc379a9bf044a6df90f08bf1cde3f Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 21 Jun 2021 17:23:21 -0300 Subject: [PATCH 1/9] Back to the origins: Make memory manager take guest PA rather than host address once again --- Ryujinx.Cpu/MemoryManager.cs | 109 +++++----- Ryujinx.Cpu/MemoryManagerHostMapped.cs | 13 +- Ryujinx.HLE/HOS/ArmProcessContextFactory.cs | 2 +- Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs | 76 ++----- .../HOS/Kernel/Memory/KPageTableBase.cs | 52 ++--- .../HOS/Kernel/Memory/KPageTableHostMapped.cs | 10 +- .../HOS/Kernel/Memory/KSharedMemory.cs | 10 +- .../HOS/Kernel/Memory/KTransferMemory.cs | 14 +- .../HOS/Kernel/Memory/SharedMemoryStorage.cs | 22 -- .../Kernel/Process/ProcessContextFactory.cs | 2 +- Ryujinx.Memory/AddressSpaceManager.cs | 193 ++++++------------ Ryujinx.Memory/IVirtualMemoryManager.cs | 6 +- 12 files changed, 180 insertions(+), 329 deletions(-) diff --git a/Ryujinx.Cpu/MemoryManager.cs b/Ryujinx.Cpu/MemoryManager.cs index 85ab763e4..a3135b9ec 100644 --- a/Ryujinx.Cpu/MemoryManager.cs +++ b/Ryujinx.Cpu/MemoryManager.cs @@ -25,6 +25,7 @@ namespace Ryujinx.Cpu private const int PointerTagBit = 62; + private readonly MemoryBlock _backingMemory; private readonly InvalidAccessHandler _invalidAccessHandler; /// @@ -50,10 +51,12 @@ namespace Ryujinx.Cpu /// /// Creates a new instance of the memory manager. /// + /// Physical backing memory where virtual memory will be mapped to /// Size of the address space /// Optional function to handle invalid memory accesses - public MemoryManager(ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler = null) + public MemoryManager(MemoryBlock backingMemory, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler = null) { + _backingMemory = backingMemory; _invalidAccessHandler = invalidAccessHandler; ulong asSize = PageSize; @@ -73,18 +76,19 @@ namespace Ryujinx.Cpu } /// - public void Map(ulong va, nuint hostAddress, ulong size) + public void Map(ulong va, ulong pa, ulong size) { AssertValidAddressAndSize(va, size); ulong remainingSize = size; ulong oVa = va; + ulong oPa = pa; while (remainingSize != 0) { - _pageTable.Write((va / PageSize) * PteSize, hostAddress); + _pageTable.Write((va / PageSize) * PteSize, PaToPte(pa)); va += PageSize; - hostAddress += PageSize; + pa += PageSize; remainingSize -= PageSize; } Tracking.Map(oVa, size); @@ -107,7 +111,7 @@ namespace Ryujinx.Cpu ulong remainingSize = size; while (remainingSize != 0) { - _pageTable.Write((va / PageSize) * PteSize, (nuint)0); + _pageTable.Write((va / PageSize) * PteSize, 0UL); va += PageSize; remainingSize -= PageSize; @@ -177,7 +181,7 @@ namespace Ryujinx.Cpu if (IsContiguousAndMapped(va, data.Length)) { - data.CopyTo(GetHostSpanContiguous(va, data.Length)); + data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length)); } else { @@ -185,18 +189,22 @@ namespace Ryujinx.Cpu if ((va & PageMask) != 0) { + ulong pa = GetPhysicalAddressInternal(va); + size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); - data.Slice(0, size).CopyTo(GetHostSpanContiguous(va, size)); + 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(GetHostSpanContiguous(va + (ulong)offset, size)); + data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size)); } } } @@ -224,7 +232,7 @@ namespace Ryujinx.Cpu if (IsContiguousAndMapped(va, size)) { - return GetHostSpanContiguous(va, size); + return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size); } else { @@ -251,7 +259,7 @@ namespace Ryujinx.Cpu SignalMemoryTracking(va, (ulong)size, true); } - return new WritableRegion(null, va, new NativeMemoryManager((byte*)GetHostAddress(va), size).Memory); + return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size)); } else { @@ -264,7 +272,7 @@ namespace Ryujinx.Cpu } /// - public unsafe ref T GetRef(ulong va) where T : unmanaged + public ref T GetRef(ulong va) where T : unmanaged { if (!IsContiguous(va, Unsafe.SizeOf())) { @@ -273,7 +281,7 @@ namespace Ryujinx.Cpu SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), true); - return ref *(T*)GetHostAddress(va); + return ref _backingMemory.GetRef(GetPhysicalAddressInternal(va)); } /// @@ -315,7 +323,7 @@ namespace Ryujinx.Cpu return false; } - if (GetHostAddress(va) + PageSize != GetHostAddress(va + PageSize)) + if (GetPhysicalAddressInternal(va) + PageSize != GetPhysicalAddressInternal(va + PageSize)) { return false; } @@ -327,11 +335,11 @@ namespace Ryujinx.Cpu } /// - public IEnumerable GetPhysicalRegions(ulong va, ulong size) + public IEnumerable GetPhysicalRegions(ulong va, ulong size) { if (size == 0) { - return Enumerable.Empty(); + return Enumerable.Empty(); } if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) @@ -341,9 +349,9 @@ namespace Ryujinx.Cpu int pages = GetPagesCount(va, (uint)size, out va); - var regions = new List(); + var regions = new List(); - nuint regionStart = GetHostAddress(va); + ulong regionStart = GetPhysicalAddressInternal(va); ulong regionSize = PageSize; for (int page = 0; page < pages - 1; page++) @@ -353,12 +361,12 @@ namespace Ryujinx.Cpu return null; } - nuint newHostAddress = GetHostAddress(va + PageSize); + ulong newPa = GetPhysicalAddressInternal(va + PageSize); - if (GetHostAddress(va) + PageSize != newHostAddress) + if (GetPhysicalAddressInternal(va) + PageSize != newPa) { - regions.Add(new HostMemoryRange(regionStart, regionSize)); - regionStart = newHostAddress; + regions.Add(new MemoryRange(regionStart, regionSize)); + regionStart = newPa; regionSize = 0; } @@ -366,7 +374,7 @@ namespace Ryujinx.Cpu regionSize += PageSize; } - regions.Add(new HostMemoryRange(regionStart, regionSize)); + regions.Add(new MemoryRange(regionStart, regionSize)); return regions; } @@ -386,18 +394,22 @@ namespace Ryujinx.Cpu if ((va & PageMask) != 0) { + ulong pa = GetPhysicalAddressInternal(va); + size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); - GetHostSpanContiguous(va, size).CopyTo(data.Slice(0, size)); + _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); - GetHostSpanContiguous(va + (ulong)offset, size).CopyTo(data.Slice(offset, size)); + _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(offset, size)); } } catch (InvalidMemoryRegionException) @@ -446,7 +458,7 @@ namespace Ryujinx.Cpu return false; } - return _pageTable.Read((va / PageSize) * PteSize) != 0; + return _pageTable.Read((va / PageSize) * PteSize) != 0; } private bool ValidateAddress(ulong va) @@ -480,37 +492,20 @@ namespace Ryujinx.Cpu } } - /// - /// Get a span representing the given virtual address and size range in host memory. - /// This function assumes that the requested virtual memory region is contiguous. - /// - /// Virtual address of the range - /// Size of the range in bytes - /// A span representing the given virtual range in host memory - /// Throw when the base virtual address is not mapped - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe Span GetHostSpanContiguous(ulong va, int size) + private ulong GetPhysicalAddress(ulong va) { - return new Span((void*)GetHostAddress(va), size); - } - - /// - /// Get the host address for a given virtual address, using the page table. - /// - /// Virtual address - /// The corresponding host address for the given virtual address - /// Throw when the virtual address is not mapped - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private nuint GetHostAddress(ulong va) - { - nuint pageBase = _pageTable.Read((va / PageSize) * PteSize) & unchecked((nuint)0xffff_ffff_ffffUL); - - if (pageBase == 0) + // We return -1L if the virtual address is invalid or unmapped. + if (!ValidateAddress(va) || !IsMapped(va)) { - ThrowInvalidMemoryRegionException($"Not mapped: va=0x{va:X16}"); + return ulong.MaxValue; } - return pageBase + (nuint)(va & PageMask); + return GetPhysicalAddressInternal(va); + } + + private ulong GetPhysicalAddressInternal(ulong va) + { + return PteToPa(_pageTable.Read((va / PageSize) * PteSize) & ~(0xffffUL << 48)) + (va & PageMask); } /// @@ -604,6 +599,16 @@ namespace Ryujinx.Cpu } } + private ulong PaToPte(ulong pa) + { + return (ulong)_backingMemory.GetPointer(pa, PageSize); + } + + private ulong PteToPa(ulong pte) + { + return (ulong)((long)pte - _backingMemory.Pointer.ToInt64()); + } + /// /// Disposes of resources used by the memory manager. /// diff --git a/Ryujinx.Cpu/MemoryManagerHostMapped.cs b/Ryujinx.Cpu/MemoryManagerHostMapped.cs index c37d23a51..e0f23048c 100644 --- a/Ryujinx.Cpu/MemoryManagerHostMapped.cs +++ b/Ryujinx.Cpu/MemoryManagerHostMapped.cs @@ -136,7 +136,7 @@ namespace Ryujinx.Cpu } /// - public void Map(ulong va, nuint hostAddress, ulong size) + public void Map(ulong va, ulong pa, ulong size) { AssertValidAddressAndSize(va, size); @@ -389,16 +389,9 @@ namespace Ryujinx.Cpu } /// - public IEnumerable GetPhysicalRegions(ulong va, ulong size) + public IEnumerable GetPhysicalRegions(ulong va, ulong size) { - if (size == 0) - { - return Enumerable.Empty(); - } - - AssertMapped(va, size); - - return new HostMemoryRange[] { new HostMemoryRange(_addressSpaceMirror.GetPointer(va, size), size) }; + throw new NotImplementedException(); } /// diff --git a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs index 50dbd8435..faa379f79 100644 --- a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs +++ b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs @@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS switch (mode) { case MemoryManagerMode.SoftwarePageTable: - return new ArmProcessContext(pid, _gpu, new MemoryManager(addressSpaceSize, invalidAccessHandler), for64Bit); + return new ArmProcessContext(pid, _gpu, new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler), for64Bit); case MemoryManagerMode.HostMapped: case MemoryManagerMode.HostMappedUnsafe: diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs index d7ee04e32..f1b60ba11 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs @@ -1,10 +1,7 @@ using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.Memory; -using Ryujinx.Memory.Range; using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Linq; namespace Ryujinx.HLE.HOS.Kernel.Memory { @@ -20,9 +17,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } /// - protected override IEnumerable GetPhysicalRegions(ulong va, ulong size) + protected override void GetPhysicalRegions(ulong va, ulong size, KPageList pageList) { - return _cpuMemory.GetPhysicalRegions(va, size); + var ranges = _cpuMemory.GetPhysicalRegions(va, size); + foreach (var range in ranges) + { + pageList.AddRange(range.Address + DramMemoryMap.DramBase, range.Size / PageSize); + } } /// @@ -34,7 +35,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// protected override KernelResult MapMemory(ulong src, ulong dst, ulong pagesCount, KMemoryPermission oldSrcPermission, KMemoryPermission newDstPermission) { - var srcRanges = GetPhysicalRegions(src, pagesCount * PageSize); + KPageList pageList = new KPageList(); + GetPhysicalRegions(src, pagesCount * PageSize, pageList); KernelResult result = Reprotect(src, pagesCount, KMemoryPermission.None); @@ -43,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return result; } - result = MapPages(dst, srcRanges, newDstPermission); + result = MapPages(dst, pageList, newDstPermission, false, 0); if (result != KernelResult.Success) { @@ -59,10 +61,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { ulong size = pagesCount * PageSize; - var srcRanges = GetPhysicalRegions(src, size); - var dstRanges = GetPhysicalRegions(dst, size); + KPageList srcPageList = new KPageList(); + KPageList dstPageList = new KPageList(); - if (!dstRanges.SequenceEqual(srcRanges)) + GetPhysicalRegions(src, size, srcPageList); + GetPhysicalRegions(dst, size, dstPageList); + + if (!dstPageList.IsEqual(srcPageList)) { return KernelResult.InvalidMemRange; } @@ -78,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory if (result != KernelResult.Success) { - KernelResult mapResult = MapPages(dst, dstRanges, oldDstPermission); + KernelResult mapResult = MapPages(dst, dstPageList, oldDstPermission, false, 0); Debug.Assert(mapResult == KernelResult.Success); } @@ -92,7 +97,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory Context.Memory.Commit(srcPa - DramMemoryMap.DramBase, size); - _cpuMemory.Map(dstVa, Context.Memory.GetPointer(srcPa - DramMemoryMap.DramBase, size), size); + _cpuMemory.Map(dstVa, srcPa - DramMemoryMap.DramBase, size); if (DramMemoryMap.IsHeapPhysicalAddress(srcPa)) { @@ -121,7 +126,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory Context.Memory.Commit(addr, size); - _cpuMemory.Map(currentVa, Context.Memory.GetPointer(addr, size), size); + _cpuMemory.Map(currentVa, addr, size); if (shouldFillPages) { @@ -136,33 +141,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.Success; } - /// - protected override KernelResult MapPages(ulong address, IEnumerable ranges, KMemoryPermission permission) - { - ulong currentVa = address; - - foreach (var range in ranges) - { - ulong size = range.Size; - - ulong pa = GetDramAddressFromHostAddress(range.Address); - if (pa != ulong.MaxValue) - { - pa += DramMemoryMap.DramBase; - if (DramMemoryMap.IsHeapPhysicalAddress(pa)) - { - Context.MemoryManager.IncrementPagesReferenceCount(pa, size / PageSize); - } - } - - _cpuMemory.Map(currentVa, range.Address, size); - - currentVa += size; - } - - return KernelResult.Success; - } - /// protected override KernelResult Unmap(ulong address, ulong pagesCount) { @@ -172,13 +150,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory foreach (var region in regions) { - ulong pa = GetDramAddressFromHostAddress(region.Address); - if (pa == ulong.MaxValue) - { - continue; - } - - pa += DramMemoryMap.DramBase; + ulong pa = region.Address + DramMemoryMap.DramBase; if (DramMemoryMap.IsHeapPhysicalAddress(pa)) { pagesToClose.AddRange(pa, region.Size / PageSize); @@ -217,15 +189,5 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { _cpuMemory.Write(va, data); } - - private ulong GetDramAddressFromHostAddress(nuint hostAddress) - { - if (hostAddress < (nuint)(ulong)Context.Memory.Pointer || hostAddress >= (nuint)((ulong)Context.Memory.Pointer + Context.Memory.Size)) - { - return ulong.MaxValue; - } - - return hostAddress - (ulong)Context.Memory.Pointer; - } } } diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs index ff87ecb79..509955aa7 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs @@ -380,7 +380,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } - public KernelResult UnmapPages(ulong address, ulong pagesCount, IEnumerable ranges, MemoryState stateExpected) + public KernelResult UnmapPages(ulong address, ulong pagesCount, KPageList pageList, MemoryState stateExpected) { ulong size = pagesCount * PageSize; @@ -405,9 +405,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory lock (_blockManager) { - var currentRanges = GetPhysicalRegions(address, size); + KPageList currentPageList = new KPageList(); - if (!currentRanges.SequenceEqual(ranges)) + GetPhysicalRegions(address, size, currentPageList); + + if (!currentPageList.IsEqual(pageList)) { return KernelResult.InvalidMemRange; } @@ -1828,7 +1830,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { ulong alignedSize = endAddrTruncated - addressRounded; - KernelResult result = MapPages(currentVa, srcPageTable.GetPhysicalRegions(addressRounded, alignedSize), permission); + KPageList pageList = new KPageList(); + srcPageTable.GetPhysicalRegions(addressRounded, alignedSize, pageList); + + KernelResult result = MapPages(currentVa, pageList, permission); if (result != KernelResult.Success) { @@ -2041,7 +2046,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory MemoryAttribute.Borrowed); } - public KernelResult BorrowTransferMemory(List ranges, ulong address, ulong size, KMemoryPermission permission) + public KernelResult BorrowTransferMemory(KPageList pageList, ulong address, ulong size, KMemoryPermission permission) { return SetAttributesAndChangePermission( address, @@ -2054,7 +2059,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory MemoryAttribute.None, permission, MemoryAttribute.Borrowed, - ranges); + pageList); } private KernelResult SetAttributesAndChangePermission( @@ -2068,7 +2073,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory MemoryAttribute attributeExpected, KMemoryPermission newPermission, MemoryAttribute attributeSetMask, - List ranges = null) + KPageList pageList = null) { if (address + size <= address || !InsideAddrSpace(address, size)) { @@ -2093,7 +2098,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { ulong pagesCount = size / PageSize; - ranges?.AddRange(GetPhysicalRegions(address, size)); + if (pageList != null) + { + GetPhysicalRegions(address, pagesCount * PageSize, pageList); + } if (!_slabManager.CanAllocate(MaxBlocksNeededForInsertion)) { @@ -2143,7 +2151,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory MemoryAttribute.Borrowed); } - public KernelResult UnborrowTransferMemory(ulong address, ulong size, List ranges) + public KernelResult UnborrowTransferMemory(ulong address, ulong size, KPageList pageList) { return ClearAttributesAndChangePermission( address, @@ -2156,7 +2164,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory MemoryAttribute.Borrowed, KMemoryPermission.ReadAndWrite, MemoryAttribute.Borrowed, - ranges); + pageList); } private KernelResult ClearAttributesAndChangePermission( @@ -2170,7 +2178,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory MemoryAttribute attributeExpected, KMemoryPermission newPermission, MemoryAttribute attributeClearMask, - List ranges = null) + KPageList pageList = null) { if (address + size <= address || !InsideAddrSpace(address, size)) { @@ -2195,11 +2203,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { ulong pagesCount = size / PageSize; - if (ranges != null) + if (pageList != null) { - var currentRanges = GetPhysicalRegions(address, size); + KPageList currentPageList = new KPageList(); - if (!currentRanges.SequenceEqual(ranges)) + GetPhysicalRegions(address, pagesCount * PageSize, currentPageList); + + if (!currentPageList.IsEqual(pageList)) { return KernelResult.InvalidMemRange; } @@ -2741,8 +2751,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// /// Virtual address of the range /// Size of the range - /// Array of physical regions - protected abstract IEnumerable GetPhysicalRegions(ulong va, ulong size); + /// Page list where the ranges will be added + protected abstract void GetPhysicalRegions(ulong va, ulong size, KPageList pageList); /// /// Gets a read-only span of data from CPU mapped memory. @@ -2803,16 +2813,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory /// Result of the mapping operation protected abstract KernelResult MapPages(ulong address, KPageList pageList, KMemoryPermission permission, bool shouldFillPages = false, byte fillValue = 0); - /// - /// Maps a region of memory into the specified host memory ranges. - /// - /// Destination virtual address that should be mapped - /// Ranges of host memory that should be mapped - /// Permission of the region to be mapped - /// Result of the mapping operation - /// The implementation does not support memory aliasing - protected abstract KernelResult MapPages(ulong address, IEnumerable ranges, KMemoryPermission permission); - /// /// Unmaps a region of memory that was previously mapped with one of the page mapping methods. /// diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableHostMapped.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableHostMapped.cs index 29a7b2ed6..0c7d77ada 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableHostMapped.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableHostMapped.cs @@ -21,9 +21,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } /// - protected override IEnumerable GetPhysicalRegions(ulong va, ulong size) + protected override void GetPhysicalRegions(ulong va, ulong size, KPageList pageList) { - return _cpuMemory.GetPhysicalRegions(va, size); + throw new NotImplementedException(); } /// @@ -97,12 +97,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.Success; } - /// - protected override KernelResult MapPages(ulong address, IEnumerable ranges, KMemoryPermission permission) - { - throw new NotSupportedException(); - } - /// protected override KernelResult Unmap(ulong address, ulong pagesCount) { diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs index 61c883d80..7aef27ad8 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs @@ -60,11 +60,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return result; } - public KernelResult UnmapFromProcess( - KPageTableBase memoryManager, - ulong address, - ulong size, - KProcess process) + public KernelResult UnmapFromProcess(KPageTableBase memoryManager, ulong address, ulong size, KProcess process) { ulong pagesCountRounded = BitUtils.DivRoundUp(size, KPageTableBase.PageSize); @@ -76,9 +72,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.InvalidSize; } - var ranges = _storage.GetRanges(); - - return memoryManager.UnmapPages(address, pagesCount, ranges, MemoryState.SharedMemory); + return memoryManager.UnmapPages(address, pagesCount, pageList, MemoryState.SharedMemory); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs index 3051d998c..381cacc57 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs @@ -1,9 +1,7 @@ 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; namespace Ryujinx.HLE.HOS.Kernel.Memory { @@ -14,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory // TODO: Remove when we no longer need to read it from the owner directly. public KProcess Creator => _creator; - private readonly List _ranges; + private readonly KPageList _pageList; private readonly SharedMemoryStorage _storage; @@ -28,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory public KTransferMemory(KernelContext context) : base(context) { - _ranges = new List(); + _pageList = new KPageList(); } public KTransferMemory(KernelContext context, SharedMemoryStorage storage) : base(context) @@ -46,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory _creator = creator; - KernelResult result = creator.MemoryManager.BorrowTransferMemory(_ranges, address, size, permission); + KernelResult result = creator.MemoryManager.BorrowTransferMemory(_pageList, address, size, permission); if (result != KernelResult.Success) { @@ -127,11 +125,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.InvalidSize; } - var ranges = _storage.GetRanges(); - MemoryState state = Permission == KMemoryPermission.None ? MemoryState.TransferMemoryIsolated : MemoryState.TransferMemory; - KernelResult result = memoryManager.UnmapPages(address, pagesCount, ranges, state); + KernelResult result = memoryManager.UnmapPages(address, pagesCount, pageList, state); if (result == KernelResult.Success) { @@ -145,7 +141,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { if (_hasBeenInitialized) { - if (!_isMapped && _creator.MemoryManager.UnborrowTransferMemory(Address, Size, _ranges) != KernelResult.Success) + if (!_isMapped && _creator.MemoryManager.UnborrowTransferMemory(Address, Size, _pageList) != KernelResult.Success) { throw new InvalidOperationException("Unexpected failure restoring transfer memory attributes."); } diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs b/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs index cd22b65f7..ea101411b 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/SharedMemoryStorage.cs @@ -73,28 +73,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } - public IEnumerable GetRanges() - { - if (_borrowerMemory == null) - { - var ranges = new List(); - - foreach (KPageNode pageNode in _pageList) - { - ulong address = pageNode.Address - DramMemoryMap.DramBase; - ulong size = pageNode.PagesCount * KPageTableBase.PageSize; - - ranges.Add(new HostMemoryRange(_context.Memory.GetPointer(address, size), size)); - } - - return ranges; - } - else - { - return _borrowerMemory.GetPhysicalRegions(_borrowerVa, _size); - } - } - public KPageList GetPageList() { return _pageList; diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs index ff250e88b..07d2e30f5 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { public IProcessContext Create(KernelContext context, long pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit) { - return new ProcessContext(new AddressSpaceManager(addressSpaceSize)); + return new ProcessContext(new AddressSpaceManager(context.Memory, addressSpaceSize)); } } } diff --git a/Ryujinx.Memory/AddressSpaceManager.cs b/Ryujinx.Memory/AddressSpaceManager.cs index 0195644d4..821fd5fd1 100644 --- a/Ryujinx.Memory/AddressSpaceManager.cs +++ b/Ryujinx.Memory/AddressSpaceManager.cs @@ -13,9 +13,9 @@ namespace Ryujinx.Memory /// public sealed class AddressSpaceManager : IVirtualMemoryManager, IWritableBlock { - public const int PageBits = PageTable.PageBits; - public const int PageSize = PageTable.PageSize; - public const int PageMask = PageTable.PageMask; + public const int PageBits = PageTable.PageBits; + public const int PageSize = PageTable.PageSize; + public const int PageMask = PageTable.PageMask; /// /// Address space width in bits. @@ -24,14 +24,15 @@ namespace Ryujinx.Memory private readonly ulong _addressSpaceSize; - private readonly PageTable _pageTable; + private readonly MemoryBlock _backingMemory; + private readonly PageTable _pageTable; /// /// Creates a new instance of the memory manager. /// /// Physical backing memory where virtual memory will be mapped to /// Size of the address space - public AddressSpaceManager(ulong addressSpaceSize) + public AddressSpaceManager(MemoryBlock backingMemory, ulong addressSpaceSize) { ulong asSize = PageSize; int asBits = PageBits; @@ -44,37 +45,26 @@ namespace Ryujinx.Memory AddressSpaceBits = asBits; _addressSpaceSize = asSize; - _pageTable = new PageTable(); + _backingMemory = backingMemory; + _pageTable = new PageTable(); } - /// - /// Maps a virtual memory range into a physical memory range. - /// - /// - /// Addresses and size must be page aligned. - /// - /// Virtual memory address - /// Physical memory address - /// Size to be mapped - public void Map(ulong va, nuint hostAddress, ulong size) + /// + public void Map(ulong va, ulong pa, ulong size) { AssertValidAddressAndSize(va, size); while (size != 0) { - _pageTable.Map(va, hostAddress); + _pageTable.Map(va, pa); va += PageSize; - hostAddress += PageSize; + pa += PageSize; size -= PageSize; } } - /// - /// Unmaps a previously mapped range of virtual memory. - /// - /// Virtual address of the range to be unmapped - /// Size of the range to be unmapped + /// public void Unmap(ulong va, ulong size) { AssertValidAddressAndSize(va, size); @@ -88,47 +78,25 @@ namespace Ryujinx.Memory } } - /// - /// Reads data from mapped memory. - /// - /// Type of the data being read - /// Virtual address of the data in memory - /// The data - /// Throw for unhandled invalid or unmapped memory accesses + /// public T Read(ulong va) where T : unmanaged { return MemoryMarshal.Cast(GetSpan(va, Unsafe.SizeOf()))[0]; } - /// - /// Reads data from mapped memory. - /// - /// Virtual address of the data in memory - /// Span to store the data being read into - /// Throw for unhandled invalid or unmapped memory accesses + /// public void Read(ulong va, Span data) { ReadImpl(va, data); } - /// - /// Writes data to mapped memory. - /// - /// Type of the data being written - /// Virtual address to write the data into - /// Data to be written - /// Throw for unhandled invalid or unmapped memory accesses + /// public void Write(ulong va, T value) where T : unmanaged { Write(va, MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref value, 1))); } - /// - /// Writes data to mapped memory. - /// - /// Virtual address to write the data into - /// Data to be written - /// Throw for unhandled invalid or unmapped memory accesses + /// public void Write(ulong va, ReadOnlySpan data) { if (data.Length == 0) @@ -140,7 +108,7 @@ namespace Ryujinx.Memory if (IsContiguousAndMapped(va, data.Length)) { - data.CopyTo(GetHostSpanContiguous(va, data.Length)); + data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length)); } else { @@ -148,34 +116,27 @@ namespace Ryujinx.Memory if ((va & PageMask) != 0) { + ulong pa = GetPhysicalAddressInternal(va); + size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); - data.Slice(0, size).CopyTo(GetHostSpanContiguous(va, size)); + 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(GetHostSpanContiguous(va + (ulong)offset, size)); + data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size)); } } } - /// - /// Gets a read-only span of data from mapped memory. - /// - /// - /// This may perform a allocation if the data is not contiguous in memory. - /// For this reason, the span is read-only, you can't modify the data. - /// - /// Virtual address of the data - /// Size of the data - /// True if read tracking is triggered on the span - /// A read-only span of the data - /// Throw for unhandled invalid or unmapped memory accesses + /// public ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false) { if (size == 0) @@ -185,7 +146,7 @@ namespace Ryujinx.Memory if (IsContiguousAndMapped(va, size)) { - return GetHostSpanContiguous(va, size); + return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size); } else { @@ -197,19 +158,7 @@ namespace Ryujinx.Memory } } - /// - /// Gets a region of memory that can be written to. - /// - /// - /// If the requested region is not contiguous in physical memory, - /// this will perform an allocation, and flush the data (writing it - /// back to the backing memory) on disposal. - /// - /// Virtual address of the data - /// Size of the data - /// True if write tracking is triggered on the span - /// A writable region of memory containing the data - /// Throw for unhandled invalid or unmapped memory accesses + /// public unsafe WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false) { if (size == 0) @@ -219,7 +168,7 @@ namespace Ryujinx.Memory if (IsContiguousAndMapped(va, size)) { - return new WritableRegion(null, va, new NativeMemoryManager((byte*)GetHostAddress(va), size).Memory); + return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size)); } else { @@ -231,33 +180,18 @@ namespace Ryujinx.Memory } } - /// - /// Gets a reference for the given type at the specified virtual memory address. - /// - /// - /// The data must be located at a contiguous memory region. - /// - /// Type of the data to get the reference - /// Virtual address of the data - /// A reference to the data in memory - /// Throw if the specified memory region is not contiguous in physical memory - public unsafe ref T GetRef(ulong va) where T : unmanaged + /// + public ref T GetRef(ulong va) where T : unmanaged { if (!IsContiguous(va, Unsafe.SizeOf())) { ThrowMemoryNotContiguous(); } - return ref *(T*)GetHostAddress(va); + return ref _backingMemory.GetRef(GetPhysicalAddressInternal(va)); } - /// - /// Computes the number of pages in a virtual address range. - /// - /// Virtual address of the range - /// Size of the range - /// The virtual address of the beginning of the first page - /// This function does not differentiate between allocated and unallocated pages. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] private int GetPagesCount(ulong va, uint size, out ulong startVa) { @@ -290,7 +224,7 @@ namespace Ryujinx.Memory return false; } - if (GetHostAddress(va) + PageSize != GetHostAddress(va + PageSize)) + if (GetPhysicalAddressInternal(va) + PageSize != GetPhysicalAddressInternal(va + PageSize)) { return false; } @@ -301,18 +235,12 @@ namespace Ryujinx.Memory return true; } - /// - /// Gets the physical regions that make up the given virtual address region. - /// If any part of the virtual region is unmapped, null is returned. - /// - /// Virtual address of the range - /// Size of the range - /// Array of physical regions - public IEnumerable GetPhysicalRegions(ulong va, ulong size) + /// + public IEnumerable GetPhysicalRegions(ulong va, ulong size) { if (size == 0) { - return Enumerable.Empty(); + return Enumerable.Empty(); } if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) @@ -322,9 +250,9 @@ namespace Ryujinx.Memory int pages = GetPagesCount(va, (uint)size, out va); - var regions = new List(); + var regions = new List(); - nuint regionStart = GetHostAddress(va); + ulong regionStart = GetPhysicalAddressInternal(va); ulong regionSize = PageSize; for (int page = 0; page < pages - 1; page++) @@ -334,12 +262,12 @@ namespace Ryujinx.Memory return null; } - nuint newHostAddress = GetHostAddress(va + PageSize); + ulong newPa = GetPhysicalAddressInternal(va + PageSize); - if (GetHostAddress(va) + PageSize != newHostAddress) + if (GetPhysicalAddressInternal(va) + PageSize != newPa) { - regions.Add(new HostMemoryRange(regionStart, regionSize)); - regionStart = newHostAddress; + regions.Add(new MemoryRange(regionStart, regionSize)); + regionStart = newPa; regionSize = 0; } @@ -347,7 +275,7 @@ namespace Ryujinx.Memory regionSize += PageSize; } - regions.Add(new HostMemoryRange(regionStart, regionSize)); + regions.Add(new MemoryRange(regionStart, regionSize)); return regions; } @@ -365,26 +293,26 @@ namespace Ryujinx.Memory if ((va & PageMask) != 0) { + ulong pa = GetPhysicalAddressInternal(va); + size = Math.Min(data.Length, PageSize - (int)(va & PageMask)); - GetHostSpanContiguous(va, size).CopyTo(data.Slice(0, size)); + _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); - GetHostSpanContiguous(va + (ulong)offset, size).CopyTo(data.Slice(offset, size)); + _backingMemory.GetSpan(pa, size).CopyTo(data.Slice(offset, size)); } } - /// - /// Checks if the page at a given virtual address is mapped. - /// - /// Virtual address to check - /// True if the address is mapped, false otherwise + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsMapped(ulong va) { @@ -396,12 +324,7 @@ namespace Ryujinx.Memory return _pageTable.Read(va) != 0; } - /// - /// Checks if a memory range is mapped. - /// - /// Virtual address of the range - /// Size of the range in bytes - /// True if the entire range is mapped, false otherwise + /// public bool IsRangeMapped(ulong va, ulong size) { if (size == 0UL) @@ -460,14 +383,20 @@ namespace Ryujinx.Memory } } - private unsafe Span GetHostSpanContiguous(ulong va, int size) + private ulong GetPhysicalAddress(ulong va) { - return new Span((void*)GetHostAddress(va), size); + // We return -1L if the virtual address is invalid or unmapped. + if (!ValidateAddress(va) || !IsMapped(va)) + { + return ulong.MaxValue; + } + + return GetPhysicalAddressInternal(va); } - private nuint GetHostAddress(ulong va) + private ulong GetPhysicalAddressInternal(ulong va) { - return _pageTable.Read(va) + (nuint)(va & PageMask); + return _pageTable.Read(va) + (va & PageMask); } /// diff --git a/Ryujinx.Memory/IVirtualMemoryManager.cs b/Ryujinx.Memory/IVirtualMemoryManager.cs index 8bccfbad4..f97cb0b57 100644 --- a/Ryujinx.Memory/IVirtualMemoryManager.cs +++ b/Ryujinx.Memory/IVirtualMemoryManager.cs @@ -13,9 +13,9 @@ namespace Ryujinx.Memory /// Addresses and size must be page aligned. /// /// Virtual memory address - /// Pointer where the region should be mapped to + /// Physical memory address where the region should be mapped to /// Size to be mapped - void Map(ulong va, nuint hostAddress, ulong size); + void Map(ulong va, ulong pa, ulong size); /// /// Unmaps a previously mapped range of virtual memory. @@ -111,7 +111,7 @@ namespace Ryujinx.Memory /// Virtual address of the range /// Size of the range /// Array of physical regions - IEnumerable GetPhysicalRegions(ulong va, ulong size); + IEnumerable GetPhysicalRegions(ulong va, ulong size); /// /// Checks if the page at a given CPU virtual address is mapped. From 335d91a367074e1af3ba5b20d81d519cd2d5689c Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 21 Jun 2021 21:31:16 -0300 Subject: [PATCH 2/9] 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; From 02718079a089c7d343156e2c2345fc9078fe3c60 Mon Sep 17 00:00:00 2001 From: gdk Date: Tue, 28 Dec 2021 09:25:58 -0300 Subject: [PATCH 3/9] Fixes and remove more of the emulated shared memory --- Ryujinx.Cpu/MemoryManagerHostMapped.cs | 25 ++++-------- Ryujinx.HLE/HOS/ApplicationLoader.cs | 10 ++++- Ryujinx.HLE/HOS/ArmProcessContextFactory.cs | 5 +++ Ryujinx.Memory/MemoryBlock.cs | 30 ++++++++++++-- Ryujinx.Memory/MemoryManagement.cs | 4 +- Ryujinx.Memory/MemoryManagementWindows.cs | 45 +++------------------ 6 files changed, 54 insertions(+), 65 deletions(-) diff --git a/Ryujinx.Cpu/MemoryManagerHostMapped.cs b/Ryujinx.Cpu/MemoryManagerHostMapped.cs index 8bd3092e2..91fcec327 100644 --- a/Ryujinx.Cpu/MemoryManagerHostMapped.cs +++ b/Ryujinx.Cpu/MemoryManagerHostMapped.cs @@ -324,16 +324,16 @@ namespace Ryujinx.Cpu /// public WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false) { - if (tracked) - { - SignalMemoryTracking(va, (ulong)size, true); - } - if (size == 0) { return new WritableRegion(null, va, Memory.Empty); } + if (tracked) + { + SignalMemoryTracking(va, (ulong)size, true); + } + if (IsContiguousAndMapped(va, size)) { return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size)); @@ -342,7 +342,7 @@ namespace Ryujinx.Cpu { Memory memory = new byte[size]; - GetSpan(va, size).CopyTo(memory.Span); + ReadImpl(va, memory.Span); return new WritableRegion(this, va, memory); } @@ -849,17 +849,6 @@ 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); @@ -874,6 +863,6 @@ namespace Ryujinx.Cpu _memoryEh.Dispose(); } - private void ThrowInvalidMemoryRegionException(string message) => throw new InvalidMemoryRegionException(message); + private static void ThrowInvalidMemoryRegionException(string message) => throw new InvalidMemoryRegionException(message); } } diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs index 8ceed5ea6..bb79c5bf4 100644 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs @@ -17,6 +17,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.Memory; using System; using System.Collections.Generic; using System.Globalization; @@ -555,7 +556,14 @@ namespace Ryujinx.HLE.HOS Graphics.Gpu.GraphicsConfig.TitleId = TitleIdText; _device.Gpu.HostInitalized.Set(); - Ptc.Initialize(TitleIdText, DisplayVersion, usePtc, _device.Configuration.MemoryManagerMode); + MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode; + + if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible)) + { + memoryManagerMode = MemoryManagerMode.SoftwarePageTable; + } + + Ptc.Initialize(TitleIdText, DisplayVersion, usePtc, memoryManagerMode); metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, new ProgramInfo(in npdm), executables: programs); diff --git a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs index a951b3a82..2cd905a75 100644 --- a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs +++ b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs @@ -21,6 +21,11 @@ namespace Ryujinx.HLE.HOS { MemoryManagerMode mode = context.Device.Configuration.MemoryManagerMode; + if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible)) + { + mode = MemoryManagerMode.SoftwarePageTable; + } + switch (mode) { case MemoryManagerMode.SoftwarePageTable: diff --git a/Ryujinx.Memory/MemoryBlock.cs b/Ryujinx.Memory/MemoryBlock.cs index fdc84068d..63719c8c7 100644 --- a/Ryujinx.Memory/MemoryBlock.cs +++ b/Ryujinx.Memory/MemoryBlock.cs @@ -11,6 +11,7 @@ namespace Ryujinx.Memory { private readonly bool _usesSharedMemory; private readonly bool _isMirror; + private readonly bool _viewCompatible; private IntPtr _sharedMemory; private IntPtr _pointer; @@ -41,7 +42,8 @@ namespace Ryujinx.Memory } else if (flags.HasFlag(MemoryAllocationFlags.Reserve)) { - _pointer = MemoryManagement.Reserve(size, flags.HasFlag(MemoryAllocationFlags.ViewCompatible)); + _viewCompatible = flags.HasFlag(MemoryAllocationFlags.ViewCompatible); + _pointer = MemoryManagement.Reserve(size, _viewCompatible); } else { @@ -154,7 +156,7 @@ namespace Ryujinx.Memory /// Throw when is invalid public void Reprotect(ulong offset, ulong size, MemoryPermission permission, bool throwOnFail = true) { - MemoryManagement.Reprotect(GetPointerInternal(offset, size), size, permission, throwOnFail); + MemoryManagement.Reprotect(GetPointerInternal(offset, size), size, permission, _viewCompatible, throwOnFail); } /// @@ -412,7 +414,27 @@ namespace Ryujinx.Memory } } - private void ThrowObjectDisposed() => throw new ObjectDisposedException(nameof(MemoryBlock)); - private void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException(); + /// + /// Checks if the specified memory allocation flags are supported on the current platform. + /// + /// Flags to be checked + /// True if the platform supports all the flags, false otherwise + public static bool SupportsFlags(MemoryAllocationFlags flags) + { + if (flags.HasFlag(MemoryAllocationFlags.ViewCompatible)) + { + if (OperatingSystem.IsWindows()) + { + return OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134); + } + + return OperatingSystem.IsLinux() || OperatingSystem.IsMacOS(); + } + + return true; + } + + private static void ThrowObjectDisposed() => throw new ObjectDisposedException(nameof(MemoryBlock)); + private static void ThrowInvalidMemoryRegionException() => throw new InvalidMemoryRegionException(); } } diff --git a/Ryujinx.Memory/MemoryManagement.cs b/Ryujinx.Memory/MemoryManagement.cs index a325532f2..fa21f9d31 100644 --- a/Ryujinx.Memory/MemoryManagement.cs +++ b/Ryujinx.Memory/MemoryManagement.cs @@ -108,7 +108,7 @@ namespace Ryujinx.Memory } } - public static void Reprotect(IntPtr address, ulong size, MemoryPermission permission, bool throwOnFail) + public static void Reprotect(IntPtr address, ulong size, MemoryPermission permission, bool forView, bool throwOnFail) { bool result; @@ -116,7 +116,7 @@ namespace Ryujinx.Memory { IntPtr sizeNint = new IntPtr((long)size); - result = MemoryManagementWindows.Reprotect(address, sizeNint, permission); + result = MemoryManagementWindows.Reprotect(address, sizeNint, permission, forView); } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) diff --git a/Ryujinx.Memory/MemoryManagementWindows.cs b/Ryujinx.Memory/MemoryManagementWindows.cs index d0700518e..32effbc8e 100644 --- a/Ryujinx.Memory/MemoryManagementWindows.cs +++ b/Ryujinx.Memory/MemoryManagementWindows.cs @@ -1,6 +1,5 @@ using Ryujinx.Memory.WindowsShared; using System; -using System.Collections.Generic; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -13,11 +12,6 @@ namespace Ryujinx.Memory 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(); - private static EmulatedSharedMemoryWindows[] _emulatedShared = new EmulatedSharedMemoryWindows[64]; - private static List _emulatedSharedList = new List(); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr VirtualAlloc( @@ -87,11 +81,6 @@ namespace Ryujinx.Memory [DllImport("kernel32.dll")] private static extern uint GetLastError(); - static MemoryManagementWindows() - { - UseWin10Placeholders = OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134); - } - public static IntPtr Allocate(IntPtr size) { return AllocateInternal(size, AllocationType.Reserve | AllocationType.Commit); @@ -186,24 +175,21 @@ namespace Ryujinx.Memory } } - public static bool Reprotect(IntPtr address, IntPtr size, MemoryPermission permission) + public static bool Reprotect(IntPtr address, IntPtr size, MemoryPermission permission, bool forView) { - if (UseWin10Placeholders) + if (forView) { ulong uaddress = (ulong)address; ulong usize = (ulong)size; while (usize > 0) { - ulong nextGranular = (uaddress & ~EmulatedSharedMemoryWindows.MappingMask) + EmulatedSharedMemoryWindows.MappingGranularity; - ulong mapSize = Math.Min(usize, nextGranular - uaddress); - - if (!VirtualProtect((IntPtr)uaddress, (IntPtr)mapSize, GetProtection(permission), out _)) + if (!VirtualProtect((IntPtr)uaddress, (IntPtr)PageSize, GetProtection(permission), out _)) { return false; } - uaddress = nextGranular; - usize -= mapSize; + uaddress += PageSize; + usize -= PageSize; } return true; @@ -233,27 +219,6 @@ namespace Ryujinx.Memory return VirtualFree(address, IntPtr.Zero, AllocationType.Release); } - private static int GetEmulatedHandle() - { - // Assumes we have the handle lock. - - for (int i = 0; i < _emulatedShared.Length; i++) - { - if (_emulatedShared[i] == null) - { - return i + 1; - } - } - - throw new InvalidProgramException("Too many shared memory handles were created."); - } - - public static bool EmulatedHandleValid(ref int handle) - { - handle--; - return handle >= 0 && handle < _emulatedShared.Length && _emulatedShared[handle] != null; - } - public static IntPtr CreateSharedMemory(IntPtr size, bool reserve) { var prot = reserve ? FileMapProtection.SectionReserve : FileMapProtection.SectionCommit; From 5834b9aab63a50052bf5543ac0aa2bc997c0a137 Mon Sep 17 00:00:00 2001 From: gdk Date: Tue, 28 Dec 2021 17:55:45 -0300 Subject: [PATCH 4/9] Linux support --- Ryujinx.Memory/MemoryManagement.cs | 41 +++++++------- Ryujinx.Memory/MemoryManagementUnix.cs | 5 ++ Ryujinx.Memory/Range/HostMemoryRange.cs | 71 +++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 22 deletions(-) create mode 100644 Ryujinx.Memory/Range/HostMemoryRange.cs diff --git a/Ryujinx.Memory/MemoryManagement.cs b/Ryujinx.Memory/MemoryManagement.cs index fa21f9d31..5dbfcc332 100644 --- a/Ryujinx.Memory/MemoryManagement.cs +++ b/Ryujinx.Memory/MemoryManagement.cs @@ -12,8 +12,7 @@ namespace Ryujinx.Memory return MemoryManagementWindows.Allocate(sizeNint); } - else if (OperatingSystem.IsLinux() || - OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { return MemoryManagementUnix.Allocate(size); } @@ -31,8 +30,7 @@ namespace Ryujinx.Memory return MemoryManagementWindows.Reserve(sizeNint, viewCompatible); } - else if (OperatingSystem.IsLinux() || - OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { return MemoryManagementUnix.Reserve(size); } @@ -50,8 +48,7 @@ namespace Ryujinx.Memory return MemoryManagementWindows.Commit(address, sizeNint); } - else if (OperatingSystem.IsLinux() || - OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { return MemoryManagementUnix.Commit(address, size); } @@ -69,8 +66,7 @@ namespace Ryujinx.Memory return MemoryManagementWindows.Decommit(address, sizeNint); } - else if (OperatingSystem.IsLinux() || - OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { return MemoryManagementUnix.Decommit(address, size); } @@ -88,6 +84,10 @@ namespace Ryujinx.Memory MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, sizeNint); } + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + { + MemoryManagementUnix.Remap(address, (IntPtr)((ulong)sharedMemory + srcOffset), size); + } else { throw new PlatformNotSupportedException(); @@ -102,6 +102,10 @@ namespace Ryujinx.Memory MemoryManagementWindows.UnmapView(address, sizeNint); } + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + { + MemoryManagementUnix.Unmap(address, size); + } else { throw new PlatformNotSupportedException(); @@ -118,8 +122,7 @@ namespace Ryujinx.Memory result = MemoryManagementWindows.Reprotect(address, sizeNint, permission, forView); } - else if (OperatingSystem.IsLinux() || - OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { result = MemoryManagementUnix.Reprotect(address, size, permission); } @@ -140,8 +143,7 @@ namespace Ryujinx.Memory { return MemoryManagementWindows.Free(address); } - else if (OperatingSystem.IsLinux() || - OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { return MemoryManagementUnix.Free(address); } @@ -159,8 +161,7 @@ namespace Ryujinx.Memory return MemoryManagementWindows.CreateSharedMemory(sizeNint, reserve); } - else if (OperatingSystem.IsLinux() || - OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { return MemoryManagementUnix.CreateSharedMemory(size, reserve); } @@ -176,8 +177,7 @@ namespace Ryujinx.Memory { MemoryManagementWindows.DestroySharedMemory(handle); } - else if (OperatingSystem.IsLinux() || - OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { MemoryManagementUnix.DestroySharedMemory(handle); } @@ -193,8 +193,7 @@ namespace Ryujinx.Memory { return MemoryManagementWindows.MapSharedMemory(handle); } - else if (OperatingSystem.IsLinux() || - OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { return MemoryManagementUnix.MapSharedMemory(handle); } @@ -210,8 +209,7 @@ namespace Ryujinx.Memory { MemoryManagementWindows.UnmapSharedMemory(address); } - else if (OperatingSystem.IsLinux() || - OperatingSystem.IsMacOS()) + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { MemoryManagementUnix.UnmapSharedMemory(address); } @@ -223,8 +221,7 @@ namespace Ryujinx.Memory public static IntPtr Remap(IntPtr target, IntPtr source, ulong size) { - if (OperatingSystem.IsLinux() || - OperatingSystem.IsMacOS()) + if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { return MemoryManagementUnix.Remap(target, source, size); } diff --git a/Ryujinx.Memory/MemoryManagementUnix.cs b/Ryujinx.Memory/MemoryManagementUnix.cs index 9e3ec48af..08b69ea78 100644 --- a/Ryujinx.Memory/MemoryManagementUnix.cs +++ b/Ryujinx.Memory/MemoryManagementUnix.cs @@ -136,6 +136,11 @@ namespace Ryujinx.Memory return false; } + public static bool Unmap(IntPtr address, ulong size) + { + return munmap(address, size) == 0; + } + public static IntPtr Remap(IntPtr target, IntPtr source, ulong size) { int flags = 1; diff --git a/Ryujinx.Memory/Range/HostMemoryRange.cs b/Ryujinx.Memory/Range/HostMemoryRange.cs new file mode 100644 index 000000000..c6d8689c5 --- /dev/null +++ b/Ryujinx.Memory/Range/HostMemoryRange.cs @@ -0,0 +1,71 @@ +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); + } + } +} From 616b3568e458e16c50df637e7806f71b08158f71 Mon Sep 17 00:00:00 2001 From: gdk Date: Thu, 30 Dec 2021 14:31:16 -0300 Subject: [PATCH 5/9] Make shared and transfer memory not depend on SharedMemoryStorage --- .../HOS/Kernel/Memory/KPageTableBase.cs | 3 +- .../HOS/Kernel/Memory/KSharedMemory.cs | 20 ++---- .../HOS/Kernel/Memory/KTransferMemory.cs | 30 ++------ Ryujinx.Memory/Range/HostMemoryRange.cs | 71 ------------------- 4 files changed, 13 insertions(+), 111 deletions(-) delete mode 100644 Ryujinx.Memory/Range/HostMemoryRange.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs index aca43f431..b7d8c28c2 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs @@ -376,8 +376,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } - public KernelResult UnmapPages(ulong address, ulong pagesCount, KPageList pageList, MemoryState stateExpected) + public KernelResult UnmapPages(ulong address, KPageList pageList, MemoryState stateExpected) { + ulong pagesCount = pageList.GetPagesCount(); ulong size = pagesCount * PageSize; ulong endAddr = address + size; diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs index 72f4b6caf..8921034c7 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KSharedMemory.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory { class KSharedMemory : KAutoObject { - private readonly SharedMemoryStorage _storage; + private readonly KPageList _pageList; private readonly long _ownerPid; @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory KMemoryPermission ownerPermission, KMemoryPermission userPermission) : base(context) { - _storage = storage; + _pageList = storage.GetPageList(); _ownerPid = ownerPid; _ownerPermission = ownerPermission; _userPermission = userPermission; @@ -33,10 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory KProcess process, KMemoryPermission permission) { - ulong pagesCountRounded = BitUtils.DivRoundUp(size, KPageTableBase.PageSize); - - var pageList = _storage.GetPageList(); - if (pageList.GetPagesCount() != pagesCountRounded) + if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize)) { return KernelResult.InvalidSize; } @@ -50,22 +47,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory return KernelResult.InvalidPermission; } - return memoryManager.MapPages(address, pageList, MemoryState.SharedMemory, permission); + return memoryManager.MapPages(address, _pageList, MemoryState.SharedMemory, permission); } public KernelResult UnmapFromProcess(KPageTableBase memoryManager, ulong address, ulong size, KProcess process) { - ulong pagesCountRounded = BitUtils.DivRoundUp(size, KPageTableBase.PageSize); - - var pageList = _storage.GetPageList(); - ulong pagesCount = pageList.GetPagesCount(); - - if (pagesCount != pagesCountRounded) + if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize)) { return KernelResult.InvalidSize; } - return memoryManager.UnmapPages(address, pagesCount, pageList, MemoryState.SharedMemory); + return memoryManager.UnmapPages(address, _pageList, MemoryState.SharedMemory); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs index 3b9b70176..107f04c91 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KTransferMemory.cs @@ -14,8 +14,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory private readonly KPageList _pageList; - private readonly SharedMemoryStorage _storage; - public ulong Address { get; private set; } public ulong Size { get; private set; } @@ -31,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory public KTransferMemory(KernelContext context, SharedMemoryStorage storage) : base(context) { - _storage = storage; + _pageList = storage.GetPageList(); Permission = KMemoryPermission.ReadAndWrite; _hasBeenInitialized = true; @@ -69,15 +67,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory KProcess process, KMemoryPermission permission) { - if (_storage == null) - { - throw new NotImplementedException(); - } - - ulong pagesCountRounded = BitUtils.DivRoundUp(size, KPageTableBase.PageSize); - - var pageList = _storage.GetPageList(); - if (pageList.GetPagesCount() != pagesCountRounded) + if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize)) { return KernelResult.InvalidSize; } @@ -89,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory MemoryState state = Permission == KMemoryPermission.None ? MemoryState.TransferMemoryIsolated : MemoryState.TransferMemory; - KernelResult result = memoryManager.MapPages(address, pageList, state, KMemoryPermission.ReadAndWrite); + KernelResult result = memoryManager.MapPages(address, _pageList, state, KMemoryPermission.ReadAndWrite); if (result == KernelResult.Success) { @@ -105,24 +95,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory ulong size, KProcess process) { - if (_storage == null) - { - throw new NotImplementedException(); - } - - ulong pagesCountRounded = BitUtils.DivRoundUp(size, KPageTableBase.PageSize); - - var pageList = _storage.GetPageList(); - ulong pagesCount = pageList.GetPagesCount(); - - if (pagesCount != pagesCountRounded) + if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize)) { return KernelResult.InvalidSize; } MemoryState state = Permission == KMemoryPermission.None ? MemoryState.TransferMemoryIsolated : MemoryState.TransferMemory; - KernelResult result = memoryManager.UnmapPages(address, pagesCount, pageList, state); + KernelResult result = memoryManager.UnmapPages(address, _pageList, state); if (result == KernelResult.Success) { 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); - } - } -} From e6b4491d0956fcd5eb38a2ad686f393796cd99a0 Mon Sep 17 00:00:00 2001 From: gdk Date: Thu, 30 Dec 2021 14:52:19 -0300 Subject: [PATCH 6/9] Implement code memory syscalls --- Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs | 170 +++++++++++ .../HOS/Kernel/Memory/KPageTableBase.cs | 146 +++++++++ .../SupervisorCall/CodeMemoryOperation.cs | 10 + .../HOS/Kernel/SupervisorCall/Syscall.cs | 276 +++++++++++++++--- .../HOS/Kernel/SupervisorCall/Syscall64.cs | 30 +- .../HOS/Kernel/SupervisorCall/SyscallTable.cs | 4 + 6 files changed, 594 insertions(+), 42 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs new file mode 100644 index 000000000..47d737399 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs @@ -0,0 +1,170 @@ +using Ryujinx.Common; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Process; +using System; +using System.Diagnostics; + +namespace Ryujinx.HLE.HOS.Kernel.Memory +{ + class KCodeMemory : KAutoObject + { + public KProcess Owner { get; private set; } + private readonly KPageList _pageList; + private readonly object _lock; + private ulong _address; + private bool _isInitialized; + private bool _isOwnerMapped; + private bool _isMapped; + + public KCodeMemory(KernelContext context) : base(context) + { + _pageList = new KPageList(); + _lock = new object(); + } + + public KernelResult Initialize(ulong address, ulong size) + { + Owner = KernelStatic.GetCurrentProcess(); + + KernelResult result = Owner.MemoryManager.BorrowCodeMemory(_pageList, address, size); + + if (result != KernelResult.Success) + { + return result; + } + + Owner.CpuMemory.Fill(address, size, 0xff); + Owner.IncrementReferenceCount(); + + _address = address; + _isInitialized = true; + _isMapped = false; + _isOwnerMapped = false; + + return KernelResult.Success; + } + + public KernelResult Map(ulong address, ulong size, KMemoryPermission perm) + { + if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize)) + { + return KernelResult.InvalidSize; + } + + lock (_lock) + { + if (_isMapped) + { + return KernelResult.InvalidState; + } + + KProcess process = KernelStatic.GetCurrentProcess(); + + KernelResult result = process.MemoryManager.MapPages(address, _pageList, MemoryState.CodeWritable, KMemoryPermission.ReadAndWrite); + + if (result != KernelResult.Success) + { + return result; + } + + _isMapped = true; + } + + return KernelResult.Success; + } + + public KernelResult MapToOwner(ulong address, ulong size, KMemoryPermission permission) + { + if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize)) + { + return KernelResult.InvalidSize; + } + + lock (_lock) + { + if (_isOwnerMapped) + { + return KernelResult.InvalidState; + } + + Debug.Assert(permission == KMemoryPermission.Read || permission == KMemoryPermission.ReadAndExecute); + + KernelResult result = Owner.MemoryManager.MapPages(address, _pageList, MemoryState.CodeReadOnly, permission); + + if (result != KernelResult.Success) + { + return result; + } + + _isOwnerMapped = true; + } + + return KernelResult.Success; + } + + public KernelResult Unmap(ulong address, ulong size) + { + if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize)) + { + return KernelResult.InvalidSize; + } + + lock (_lock) + { + KProcess process = KernelStatic.GetCurrentProcess(); + + KernelResult result = process.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeWritable); + + if (result != KernelResult.Success) + { + return result; + } + + Debug.Assert(_isMapped); + + _isMapped = false; + } + + return KernelResult.Success; + } + + public KernelResult UnmapFromOwner(ulong address, ulong size) + { + if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize)) + { + return KernelResult.InvalidSize; + } + + lock (_lock) + { + KernelResult result = Owner.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeReadOnly); + + if (result != KernelResult.Success) + { + return result; + } + + Debug.Assert(_isOwnerMapped); + + _isOwnerMapped = false; + } + + return KernelResult.Success; + } + + protected override void Destroy() + { + if (!_isMapped && !_isOwnerMapped) + { + ulong size = _pageList.GetPagesCount() * KPageTableBase.PageSize; + + if (Owner.MemoryManager.UnborrowCodeMemory(_address, size, _pageList) != KernelResult.Success) + { + throw new InvalidOperationException("Unexpected failure restoring transfer memory attributes."); + } + } + + Owner.DecrementReferenceCount(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs index b7d8c28c2..e55bd79b0 100644 --- a/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs +++ b/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs @@ -1095,6 +1095,77 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory } } + public KernelResult UnmapProcessMemory(ulong dst, ulong size, KPageTableBase srcPageTable, ulong src) + { + lock (_blockManager) + { + lock (srcPageTable._blockManager) + { + bool success = CheckRange( + dst, + size, + MemoryState.Mask, + MemoryState.ProcessMemory, + KMemoryPermission.ReadAndWrite, + KMemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + success &= srcPageTable.CheckRange( + src, + size, + MemoryState.MapProcessAllowed, + MemoryState.MapProcessAllowed, + KMemoryPermission.None, + KMemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + if (!success) + { + return KernelResult.InvalidMemState; + } + + KPageList srcPageList = new KPageList(); + KPageList dstPageList = new KPageList(); + + srcPageTable.GetPhysicalRegions(src, size, srcPageList); + GetPhysicalRegions(dst, size, dstPageList); + + if (!dstPageList.IsEqual(srcPageList)) + { + return KernelResult.InvalidMemRange; + } + } + + if (!_slabManager.CanAllocate(MaxBlocksNeededForInsertion)) + { + return KernelResult.OutOfResource; + } + + ulong pagesCount = size / PageSize; + + KernelResult result = Unmap(dst, pagesCount); + + if (result != KernelResult.Success) + { + return result; + } + + _blockManager.InsertBlock(dst, pagesCount, MemoryState.Unmapped); + + return KernelResult.Success; + } + } + public KernelResult SetProcessMemoryPermission(ulong address, ulong size, KMemoryPermission permission) { lock (_blockManager) @@ -2023,6 +2094,49 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory block.RestoreIpcMappingPermission(); } + public KernelResult GetPagesIfStateEquals( + ulong address, + ulong size, + MemoryState stateMask, + MemoryState stateExpected, + KMemoryPermission permissionMask, + KMemoryPermission permissionExpected, + MemoryAttribute attributeMask, + MemoryAttribute attributeExpected, + KPageList pageList) + { + if (!InsideAddrSpace(address, size)) + { + return KernelResult.InvalidMemState; + } + + lock (_blockManager) + { + if (CheckRange( + address, + size, + stateMask | MemoryState.IsPoolAllocated, + stateExpected | MemoryState.IsPoolAllocated, + permissionMask, + permissionExpected, + attributeMask, + attributeExpected, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _)) + { + GetPhysicalRegions(address, size, pageList); + + return KernelResult.Success; + } + else + { + return KernelResult.InvalidMemState; + } + } + } + public KernelResult BorrowIpcBuffer(ulong address, ulong size) { return SetAttributesAndChangePermission( @@ -2054,6 +2168,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory pageList); } + public KernelResult BorrowCodeMemory(KPageList pageList, ulong address, ulong size) + { + return SetAttributesAndChangePermission( + address, + size, + MemoryState.CodeMemoryAllowed, + MemoryState.CodeMemoryAllowed, + KMemoryPermission.Mask, + KMemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + KMemoryPermission.None, + MemoryAttribute.Borrowed, + pageList); + } + private KernelResult SetAttributesAndChangePermission( ulong address, ulong size, @@ -2159,6 +2289,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory pageList); } + public KernelResult UnborrowCodeMemory(ulong address, ulong size, KPageList pageList) + { + return ClearAttributesAndChangePermission( + address, + size, + MemoryState.CodeMemoryAllowed, + MemoryState.CodeMemoryAllowed, + KMemoryPermission.None, + KMemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.Borrowed, + KMemoryPermission.ReadAndWrite, + MemoryAttribute.Borrowed, + pageList); + } + private KernelResult ClearAttributesAndChangePermission( ulong address, ulong size, diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs new file mode 100644 index 000000000..511ee99af --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/CodeMemoryOperation.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall +{ + enum CodeMemoryOperation : uint + { + Map, + MapToOwner, + Unmap, + UnmapFromOwner + }; +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index 2dd9d8070..fadf1007c 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -1303,6 +1303,245 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return process.MemoryManager.UnmapPhysicalMemory(address, size); } + public KernelResult CreateCodeMemory(ulong address, ulong size, out int handle) + { + handle = 0; + + if (!PageAligned(address)) + { + return KernelResult.InvalidAddress; + } + + if (!PageAligned(size) || size == 0) + { + return KernelResult.InvalidSize; + } + + if (size + address <= address) + { + return KernelResult.InvalidMemState; + } + + KCodeMemory codeMemory = new KCodeMemory(_context); + + using var _ = new OnScopeExit(codeMemory.DecrementReferenceCount); + + KProcess currentProcess = KernelStatic.GetCurrentProcess(); + + if (!currentProcess.MemoryManager.InsideAddrSpace(address, size)) + { + return KernelResult.InvalidMemState; + } + + KernelResult result = codeMemory.Initialize(address, size); + + if (result != KernelResult.Success) + { + return result; + } + + return currentProcess.HandleTable.GenerateHandle(codeMemory, out handle); + } + + public KernelResult ControlCodeMemory(int handle, CodeMemoryOperation op, ulong address, ulong size, KMemoryPermission permission) + { + KProcess currentProcess = KernelStatic.GetCurrentProcess(); + + KCodeMemory codeMemory = currentProcess.HandleTable.GetObject(handle); + + if (codeMemory == null || codeMemory.Owner == currentProcess) + { + return KernelResult.InvalidHandle; + } + + switch (op) + { + case CodeMemoryOperation.Map: + if (!currentProcess.MemoryManager.CanContain(address, size, MemoryState.CodeWritable)) + { + return KernelResult.InvalidMemRange; + } + + if (permission != KMemoryPermission.ReadAndWrite) + { + return KernelResult.InvalidPermission; + } + + return codeMemory.Map(address, size, permission); + + case CodeMemoryOperation.MapToOwner: + if (!currentProcess.MemoryManager.CanContain(address, size, MemoryState.CodeReadOnly)) + { + return KernelResult.InvalidMemRange; + } + + if (permission != KMemoryPermission.Read && permission != KMemoryPermission.ReadAndExecute) + { + return KernelResult.InvalidPermission; + } + + return codeMemory.MapToOwner(address, size, permission); + + case CodeMemoryOperation.Unmap: + if (!currentProcess.MemoryManager.CanContain(address, size, MemoryState.CodeWritable)) + { + return KernelResult.InvalidMemRange; + } + + if (permission != KMemoryPermission.None) + { + return KernelResult.InvalidPermission; + } + + return codeMemory.Unmap(address, size); + + case CodeMemoryOperation.UnmapFromOwner: + if (!currentProcess.MemoryManager.CanContain(address, size, MemoryState.CodeReadOnly)) + { + return KernelResult.InvalidMemRange; + } + + if (permission != KMemoryPermission.None) + { + return KernelResult.InvalidPermission; + } + + return codeMemory.UnmapFromOwner(address, size); + + default: return KernelResult.InvalidEnumValue; + } + } + + public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, KMemoryPermission permission) + { + if (!PageAligned(src)) + { + return KernelResult.InvalidAddress; + } + + if (!PageAligned(size) || size == 0) + { + return KernelResult.InvalidSize; + } + + if (permission != KMemoryPermission.None && + permission != KMemoryPermission.Read && + permission != KMemoryPermission.ReadAndWrite && + permission != KMemoryPermission.ReadAndExecute) + { + return KernelResult.InvalidPermission; + } + + KProcess currentProcess = KernelStatic.GetCurrentProcess(); + + KProcess targetProcess = currentProcess.HandleTable.GetObject(handle); + + if (targetProcess == null) + { + return KernelResult.InvalidHandle; + } + + if (targetProcess.MemoryManager.OutsideAddrSpace(src, size)) + { + return KernelResult.InvalidMemState; + } + + return targetProcess.MemoryManager.SetProcessMemoryPermission(src, size, permission); + } + + public KernelResult MapProcessMemory(ulong dst, int handle, ulong src, ulong size) + { + if (!PageAligned(src) || !PageAligned(dst)) + { + return KernelResult.InvalidAddress; + } + + if (!PageAligned(size) || size == 0) + { + return KernelResult.InvalidSize; + } + + if (dst + size <= dst || src + size <= src) + { + return KernelResult.InvalidMemRange; + } + + KProcess dstProcess = KernelStatic.GetCurrentProcess(); + KProcess srcProcess = dstProcess.HandleTable.GetObject(handle); + + if (srcProcess == null) + { + return KernelResult.InvalidHandle; + } + + if (!srcProcess.MemoryManager.InsideAddrSpace(src, size) || + !dstProcess.MemoryManager.CanContain(dst, size, MemoryState.ProcessMemory)) + { + return KernelResult.InvalidMemRange; + } + + KPageList pageList = new KPageList(); + + KernelResult result = srcProcess.MemoryManager.GetPagesIfStateEquals( + src, + size, + MemoryState.MapProcessAllowed, + MemoryState.MapProcessAllowed, + KMemoryPermission.None, + KMemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + pageList); + + if (result != KernelResult.Success) + { + return result; + } + + return dstProcess.MemoryManager.MapPages(dst, pageList, MemoryState.ProcessMemory, KMemoryPermission.ReadAndWrite); + } + + public KernelResult UnmapProcessMemory(ulong dst, int handle, ulong src, ulong size) + { + if (!PageAligned(src) || !PageAligned(dst)) + { + return KernelResult.InvalidAddress; + } + + if (!PageAligned(size) || size == 0) + { + return KernelResult.InvalidSize; + } + + if (dst + size <= dst || src + size <= src) + { + return KernelResult.InvalidMemRange; + } + + KProcess dstProcess = KernelStatic.GetCurrentProcess(); + KProcess srcProcess = dstProcess.HandleTable.GetObject(handle); + + if (srcProcess == null) + { + return KernelResult.InvalidHandle; + } + + if (!srcProcess.MemoryManager.InsideAddrSpace(src, size) || + !dstProcess.MemoryManager.CanContain(dst, size, MemoryState.ProcessMemory)) + { + return KernelResult.InvalidMemRange; + } + + KernelResult result = dstProcess.MemoryManager.UnmapProcessMemory(dst, size, srcProcess.MemoryManager, src); + + if (result != KernelResult.Success) + { + return result; + } + + return KernelResult.Success; + } + public KernelResult MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size) { if (!PageAligned(dst) || !PageAligned(src)) @@ -1377,43 +1616,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return targetProcess.MemoryManager.UnmapProcessCodeMemory(dst, src, size); } - public KernelResult SetProcessMemoryPermission(int handle, ulong src, ulong size, KMemoryPermission permission) - { - if (!PageAligned(src)) - { - return KernelResult.InvalidAddress; - } - - if (!PageAligned(size) || size == 0) - { - return KernelResult.InvalidSize; - } - - if (permission != KMemoryPermission.None && - permission != KMemoryPermission.Read && - permission != KMemoryPermission.ReadAndWrite && - permission != KMemoryPermission.ReadAndExecute) - { - return KernelResult.InvalidPermission; - } - - KProcess currentProcess = KernelStatic.GetCurrentProcess(); - - KProcess targetProcess = currentProcess.HandleTable.GetObject(handle); - - if (targetProcess == null) - { - return KernelResult.InvalidHandle; - } - - if (targetProcess.MemoryManager.OutsideAddrSpace(src, size)) - { - return KernelResult.InvalidMemState; - } - - return targetProcess.MemoryManager.SetProcessMemoryPermission(src, size, permission); - } - private static bool PageAligned(ulong position) { return (position & (KPageTableBase.PageSize - 1)) == 0; diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs index fc826552b..6e2d3e4c8 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall64.cs @@ -160,6 +160,16 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return _syscall.CreateTransferMemory(address, size, permission, out handle); } + public KernelResult CreateCodeMemory64([R(1)] ulong address, [R(2)] ulong size, [R(1)] out int handle) + { + return _syscall.CreateCodeMemory(address, size, out handle); + } + + public KernelResult ControlCodeMemory64([R(0)] int handle, [R(1)] CodeMemoryOperation op, [R(2)] ulong address, [R(3)] ulong size, [R(4)] KMemoryPermission permission) + { + return _syscall.ControlCodeMemory(handle, op, address, size, permission); + } + public KernelResult MapTransferMemory64([R(0)] int handle, [R(1)] ulong address, [R(2)] ulong size, [R(3)] KMemoryPermission permission) { return _syscall.MapTransferMemory(handle, address, size, permission); @@ -180,6 +190,21 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return _syscall.UnmapPhysicalMemory(address, size); } + public KernelResult SetProcessMemoryPermission64([R(0)] int handle, [R(1)] ulong src, [R(2)] ulong size, [R(3)] KMemoryPermission permission) + { + return _syscall.SetProcessMemoryPermission(handle, src, size, permission); + } + + public KernelResult MapProcessMemory64([R(0)] ulong dst, [R(1)] int handle, [R(2)] ulong src, [R(3)] ulong size) + { + return _syscall.MapProcessMemory(dst, handle, src, size); + } + + public KernelResult UnmapProcessMemory64([R(0)] ulong dst, [R(1)] int handle, [R(2)] ulong src, [R(3)] ulong size) + { + return _syscall.UnmapProcessMemory(dst, handle, src, size); + } + public KernelResult MapProcessCodeMemory64([R(0)] int handle, [R(1)] ulong dst, [R(2)] ulong src, [R(3)] ulong size) { return _syscall.MapProcessCodeMemory(handle, dst, src, size); @@ -190,11 +215,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return _syscall.UnmapProcessCodeMemory(handle, dst, src, size); } - public KernelResult SetProcessMemoryPermission64([R(0)] int handle, [R(1)] ulong src, [R(2)] ulong size, [R(3)] KMemoryPermission permission) - { - return _syscall.SetProcessMemoryPermission(handle, src, size, permission); - } - // System public void ExitProcess64() diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallTable.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallTable.cs index 178dc0298..01208968d 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SyscallTable.cs @@ -78,6 +78,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { 0x43, nameof(Syscall64.ReplyAndReceive64) }, { 0x44, nameof(Syscall64.ReplyAndReceiveWithUserBuffer64) }, { 0x45, nameof(Syscall64.CreateEvent64) }, + { 0x4b, nameof(Syscall64.CreateCodeMemory64) }, + { 0x4c, nameof(Syscall64.ControlCodeMemory64) }, { 0x51, nameof(Syscall64.MapTransferMemory64) }, { 0x52, nameof(Syscall64.UnmapTransferMemory64) }, { 0x65, nameof(Syscall64.GetProcessList64) }, @@ -86,6 +88,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { 0x71, nameof(Syscall64.ManageNamedPort64) }, { 0x72, nameof(Syscall64.ConnectToPort64) }, { 0x73, nameof(Syscall64.SetProcessMemoryPermission64) }, + { 0x74, nameof(Syscall64.MapProcessMemory64) }, + { 0x75, nameof(Syscall64.UnmapProcessMemory64) }, { 0x77, nameof(Syscall64.MapProcessCodeMemory64) }, { 0x78, nameof(Syscall64.UnmapProcessCodeMemory64) }, { 0x7B, nameof(Syscall64.TerminateProcess64) }, From f8c6a6edf4f888fc9d90b3a1ff776e2ac3b7a1f9 Mon Sep 17 00:00:00 2001 From: gdk Date: Sat, 1 Jan 2022 21:27:36 -0300 Subject: [PATCH 7/9] Enable CPU JIT cache invalidation --- ARMeilleure/Translation/IntervalTree.cs | 764 ++++++++++++++++++ ARMeilleure/Translation/PTC/Ptc.cs | 4 +- ARMeilleure/Translation/PTC/PtcProfiler.cs | 2 +- ARMeilleure/Translation/Translator.cs | 35 +- ARMeilleure/Translation/TranslatorCache.cs | 95 +++ Ryujinx.Cpu/CpuContext.cs | 5 + Ryujinx.HLE/HOS/ArmProcessContext.cs | 5 + .../HOS/Kernel/Process/IProcessContext.cs | 1 + .../HOS/Kernel/Process/ProcessContext.cs | 4 + 9 files changed, 904 insertions(+), 11 deletions(-) create mode 100644 ARMeilleure/Translation/IntervalTree.cs create mode 100644 ARMeilleure/Translation/TranslatorCache.cs diff --git a/ARMeilleure/Translation/IntervalTree.cs b/ARMeilleure/Translation/IntervalTree.cs new file mode 100644 index 000000000..df28e64b2 --- /dev/null +++ b/ARMeilleure/Translation/IntervalTree.cs @@ -0,0 +1,764 @@ +using System; +using System.Collections.Generic; + +namespace ARMeilleure.Translation +{ + /// + /// An Augmented Interval Tree based off of the "TreeDictionary"'s Red-Black Tree. Allows fast overlap checking of ranges. + /// + /// Key + /// Value + public class IntervalTree where K : IComparable + { + private const int ArrayGrowthSize = 32; + + private const bool Black = true; + private const bool Red = false; + private IntervalTreeNode _root = null; + private int _count = 0; + + public int Count => _count; + + public IntervalTree() { } + + #region Public Methods + + /// + /// Gets the values of the interval whose key is . + /// + /// Key of the node value to get + /// Value with the given + /// True if the key is on the dictionary, false otherwise + public bool TryGet(K key, out V value) + { + IntervalTreeNode node = GetNode(key); + + if (node == null) + { + value = default; + return false; + } + + value = node.Value; + return true; + } + + /// + /// Returns the start addresses of the intervals whose start and end keys overlap the given range. + /// + /// Start of the range + /// End of the range + /// Overlaps array to place results in + /// Index to start writing results into the array. Defaults to 0 + /// Number of intervals found + public int Get(K start, K end, ref K[] overlaps, int overlapCount = 0) + { + GetValues(_root, start, end, ref overlaps, ref overlapCount); + + return overlapCount; + } + + /// + /// Adds a new interval into the tree whose start is , end is and value is . + /// + /// Start of the range to add + /// End of the range to insert + /// Value to add + /// Optional factory used to create a new value if is already on the tree + /// is null + /// True if the value was added, false if the start key was already in the dictionary + public bool AddOrUpdate(K start, K end, V value, Func updateFactoryCallback) + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + bool added = BSTInsert(start, end, value, updateFactoryCallback, out IntervalTreeNode node); + if (!added) + { + return false; + } + RestoreBalanceAfterInsertion(node); + return true; + } + + /// + /// Gets an existing or adds a new interval into the tree whose start is , end is and value is . + /// + /// Start of the range to add + /// End of the range to insert + /// Value to add + /// is null + /// if is not yet on the tree, or the existing value otherwise + public V GetOrAdd(K start, K end, V value) + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + bool added = BSTInsert(start, end, value, null, out IntervalTreeNode node); + if (added) + { + RestoreBalanceAfterInsertion(node); + } + return node.Value; + } + + /// + /// Removes a value from the tree, searching for it with . + /// + /// Key of the node to remove + /// Number of deleted values + public int Remove(K key) + { + int removed = Delete(key); + + _count -= removed; + + return removed; + } + + /// + /// Adds all the nodes in the dictionary into . + /// + /// A list of all values sorted by Key Order + public List AsList() + { + List list = new List(); + + AddToList(_root, list); + + return list; + } + + #endregion + + #region Private Methods (BST) + + /// + /// Adds all values that are children of or contained within into , in Key Order. + /// + /// The node to search for values within + /// The list to add values to + private void AddToList(IntervalTreeNode node, List list) + { + if (node == null) + { + return; + } + + AddToList(node.Left, list); + + list.Add(node.Value); + + AddToList(node.Right, list); + } + + /// + /// Retrieve the node reference whose key is , or null if no such node exists. + /// + /// Key of the node to get + /// is null + /// Node reference in the tree + private IntervalTreeNode GetNode(K key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + IntervalTreeNode node = _root; + while (node != null) + { + int cmp = key.CompareTo(node.Start); + if (cmp < 0) + { + node = node.Left; + } + else if (cmp > 0) + { + node = node.Right; + } + else + { + return node; + } + } + return null; + } + + /// + /// Retrieve all values that overlap the given start and end keys. + /// + /// Start of the range + /// End of the range + /// Overlaps array to place results in + /// Overlaps count to update + private void GetValues(IntervalTreeNode node, K start, K end, ref K[] overlaps, ref int overlapCount) + { + if (node == null || start.CompareTo(node.Max) >= 0) + { + return; + } + + GetValues(node.Left, start, end, ref overlaps, ref overlapCount); + + bool endsOnRight = end.CompareTo(node.Start) > 0; + if (endsOnRight) + { + if (start.CompareTo(node.End) < 0) + { + if (overlaps.Length >= overlapCount) + { + Array.Resize(ref overlaps, overlapCount + ArrayGrowthSize); + } + + overlaps[overlapCount++] = node.Start; + } + + GetValues(node.Right, start, end, ref overlaps, ref overlapCount); + } + } + + /// + /// Propagate an increase in max value starting at the given node, heading up the tree. + /// This should only be called if the max increases - not for rebalancing or removals. + /// + /// The node to start propagating from + private void PropagateIncrease(IntervalTreeNode node) + { + K max = node.Max; + IntervalTreeNode ptr = node; + + while ((ptr = ptr.Parent) != null) + { + if (max.CompareTo(ptr.Max) > 0) + { + ptr.Max = max; + } + else + { + break; + } + } + } + + /// + /// Propagate recalculating max value starting at the given node, heading up the tree. + /// This fully recalculates the max value from all children when there is potential for it to decrease. + /// + /// The node to start propagating from + private void PropagateFull(IntervalTreeNode node) + { + IntervalTreeNode ptr = node; + + do + { + K max = ptr.End; + + if (ptr.Left != null && ptr.Left.Max.CompareTo(max) > 0) + { + max = ptr.Left.Max; + } + + if (ptr.Right != null && ptr.Right.Max.CompareTo(max) > 0) + { + max = ptr.Right.Max; + } + + ptr.Max = max; + } while ((ptr = ptr.Parent) != null); + } + + /// + /// Insertion Mechanism for the interval tree. Similar to a BST insert, with the start of the range as the key. + /// Iterates the tree starting from the root and inserts a new node where all children in the left subtree are less than , and all children in the right subtree are greater than . + /// Each node can contain multiple values, and has an end address which is the maximum of all those values. + /// Post insertion, the "max" value of the node and all parents are updated. + /// + /// Start of the range to insert + /// End of the range to insert + /// Value to insert + /// Optional factory used to create a new value if is already on the tree + /// Node that was inserted or modified + /// True if was not yet on the tree, false otherwise + private bool BSTInsert(K start, K end, V value, Func updateFactoryCallback, out IntervalTreeNode outNode) + { + IntervalTreeNode parent = null; + IntervalTreeNode node = _root; + + while (node != null) + { + parent = node; + int cmp = start.CompareTo(node.Start); + if (cmp < 0) + { + node = node.Left; + } + else if (cmp > 0) + { + node = node.Right; + } + else + { + outNode = node; + + if (updateFactoryCallback != null) + { + // Replace + node.Value = updateFactoryCallback(start, node.Value); + + int endCmp = end.CompareTo(node.End); + + if (endCmp > 0) + { + node.End = end; + if (end.CompareTo(node.Max) > 0) + { + node.Max = end; + PropagateIncrease(node); + } + } + else if (endCmp < 0) + { + node.End = end; + PropagateFull(node); + } + } + + return false; + } + } + IntervalTreeNode newNode = new IntervalTreeNode(start, end, value, parent); + if (newNode.Parent == null) + { + _root = newNode; + } + else if (start.CompareTo(parent.Start) < 0) + { + parent.Left = newNode; + } + else + { + parent.Right = newNode; + } + + PropagateIncrease(newNode); + _count++; + outNode = newNode; + return true; + } + + /// + /// Removes the value from the dictionary after searching for it with . + /// + /// Key to search for + /// Number of deleted values + private int Delete(K key) + { + IntervalTreeNode nodeToDelete = GetNode(key); + + if (nodeToDelete == null) + { + return 0; + } + + IntervalTreeNode replacementNode; + + if (LeftOf(nodeToDelete) == null || RightOf(nodeToDelete) == null) + { + replacementNode = nodeToDelete; + } + else + { + replacementNode = PredecessorOf(nodeToDelete); + } + + IntervalTreeNode tmp = LeftOf(replacementNode) ?? RightOf(replacementNode); + + if (tmp != null) + { + tmp.Parent = ParentOf(replacementNode); + } + + if (ParentOf(replacementNode) == null) + { + _root = tmp; + } + else if (replacementNode == LeftOf(ParentOf(replacementNode))) + { + ParentOf(replacementNode).Left = tmp; + } + else + { + ParentOf(replacementNode).Right = tmp; + } + + if (replacementNode != nodeToDelete) + { + nodeToDelete.Start = replacementNode.Start; + nodeToDelete.Value = replacementNode.Value; + nodeToDelete.End = replacementNode.End; + nodeToDelete.Max = replacementNode.Max; + } + + PropagateFull(replacementNode); + + if (tmp != null && ColorOf(replacementNode) == Black) + { + RestoreBalanceAfterRemoval(tmp); + } + + return 1; + } + + /// + /// Returns the node with the largest key where is considered the root node. + /// + /// Root Node + /// Node with the maximum key in the tree of + private static IntervalTreeNode Maximum(IntervalTreeNode node) + { + IntervalTreeNode tmp = node; + while (tmp.Right != null) + { + tmp = tmp.Right; + } + + return tmp; + } + + /// + /// Finds the node whose key is immediately less than . + /// + /// Node to find the predecessor of + /// Predecessor of + private static IntervalTreeNode PredecessorOf(IntervalTreeNode node) + { + if (node.Left != null) + { + return Maximum(node.Left); + } + IntervalTreeNode parent = node.Parent; + while (parent != null && node == parent.Left) + { + node = parent; + parent = parent.Parent; + } + return parent; + } + + #endregion + + #region Private Methods (RBL) + + private void RestoreBalanceAfterRemoval(IntervalTreeNode balanceNode) + { + IntervalTreeNode ptr = balanceNode; + + while (ptr != _root && ColorOf(ptr) == Black) + { + if (ptr == LeftOf(ParentOf(ptr))) + { + IntervalTreeNode sibling = RightOf(ParentOf(ptr)); + + if (ColorOf(sibling) == Red) + { + SetColor(sibling, Black); + SetColor(ParentOf(ptr), Red); + RotateLeft(ParentOf(ptr)); + sibling = RightOf(ParentOf(ptr)); + } + if (ColorOf(LeftOf(sibling)) == Black && ColorOf(RightOf(sibling)) == Black) + { + SetColor(sibling, Red); + ptr = ParentOf(ptr); + } + else + { + if (ColorOf(RightOf(sibling)) == Black) + { + SetColor(LeftOf(sibling), Black); + SetColor(sibling, Red); + RotateRight(sibling); + sibling = RightOf(ParentOf(ptr)); + } + SetColor(sibling, ColorOf(ParentOf(ptr))); + SetColor(ParentOf(ptr), Black); + SetColor(RightOf(sibling), Black); + RotateLeft(ParentOf(ptr)); + ptr = _root; + } + } + else + { + IntervalTreeNode sibling = LeftOf(ParentOf(ptr)); + + if (ColorOf(sibling) == Red) + { + SetColor(sibling, Black); + SetColor(ParentOf(ptr), Red); + RotateRight(ParentOf(ptr)); + sibling = LeftOf(ParentOf(ptr)); + } + if (ColorOf(RightOf(sibling)) == Black && ColorOf(LeftOf(sibling)) == Black) + { + SetColor(sibling, Red); + ptr = ParentOf(ptr); + } + else + { + if (ColorOf(LeftOf(sibling)) == Black) + { + SetColor(RightOf(sibling), Black); + SetColor(sibling, Red); + RotateLeft(sibling); + sibling = LeftOf(ParentOf(ptr)); + } + SetColor(sibling, ColorOf(ParentOf(ptr))); + SetColor(ParentOf(ptr), Black); + SetColor(LeftOf(sibling), Black); + RotateRight(ParentOf(ptr)); + ptr = _root; + } + } + } + SetColor(ptr, Black); + } + + private void RestoreBalanceAfterInsertion(IntervalTreeNode balanceNode) + { + SetColor(balanceNode, Red); + while (balanceNode != null && balanceNode != _root && ColorOf(ParentOf(balanceNode)) == Red) + { + if (ParentOf(balanceNode) == LeftOf(ParentOf(ParentOf(balanceNode)))) + { + IntervalTreeNode sibling = RightOf(ParentOf(ParentOf(balanceNode))); + + if (ColorOf(sibling) == Red) + { + SetColor(ParentOf(balanceNode), Black); + SetColor(sibling, Black); + SetColor(ParentOf(ParentOf(balanceNode)), Red); + balanceNode = ParentOf(ParentOf(balanceNode)); + } + else + { + if (balanceNode == RightOf(ParentOf(balanceNode))) + { + balanceNode = ParentOf(balanceNode); + RotateLeft(balanceNode); + } + SetColor(ParentOf(balanceNode), Black); + SetColor(ParentOf(ParentOf(balanceNode)), Red); + RotateRight(ParentOf(ParentOf(balanceNode))); + } + } + else + { + IntervalTreeNode sibling = LeftOf(ParentOf(ParentOf(balanceNode))); + + if (ColorOf(sibling) == Red) + { + SetColor(ParentOf(balanceNode), Black); + SetColor(sibling, Black); + SetColor(ParentOf(ParentOf(balanceNode)), Red); + balanceNode = ParentOf(ParentOf(balanceNode)); + } + else + { + if (balanceNode == LeftOf(ParentOf(balanceNode))) + { + balanceNode = ParentOf(balanceNode); + RotateRight(balanceNode); + } + SetColor(ParentOf(balanceNode), Black); + SetColor(ParentOf(ParentOf(balanceNode)), Red); + RotateLeft(ParentOf(ParentOf(balanceNode))); + } + } + } + SetColor(_root, Black); + } + + private void RotateLeft(IntervalTreeNode node) + { + if (node != null) + { + IntervalTreeNode right = RightOf(node); + node.Right = LeftOf(right); + if (node.Right != null) + { + node.Right.Parent = node; + } + IntervalTreeNode nodeParent = ParentOf(node); + right.Parent = nodeParent; + if (nodeParent == null) + { + _root = right; + } + else if (node == LeftOf(nodeParent)) + { + nodeParent.Left = right; + } + else + { + nodeParent.Right = right; + } + right.Left = node; + node.Parent = right; + + PropagateFull(node); + } + } + + private void RotateRight(IntervalTreeNode node) + { + if (node != null) + { + IntervalTreeNode left = LeftOf(node); + node.Left = RightOf(left); + if (node.Left != null) + { + node.Left.Parent = node; + } + IntervalTreeNode nodeParent = ParentOf(node); + left.Parent = nodeParent; + if (nodeParent == null) + { + _root = left; + } + else if (node == RightOf(nodeParent)) + { + nodeParent.Right = left; + } + else + { + nodeParent.Left = left; + } + left.Right = node; + node.Parent = left; + + PropagateFull(node); + } + } + + #endregion + + #region Safety-Methods + + // These methods save memory by allowing us to forego sentinel nil nodes, as well as serve as protection against NullReferenceExceptions. + + /// + /// Returns the color of , or Black if it is null. + /// + /// Node + /// The boolean color of , or black if null + private static bool ColorOf(IntervalTreeNode node) + { + return node == null || node.Color; + } + + /// + /// Sets the color of node to . + ///

+ /// This method does nothing if is null. + ///
+ /// Node to set the color of + /// Color (Boolean) + private static void SetColor(IntervalTreeNode node, bool color) + { + if (node != null) + { + node.Color = color; + } + } + + /// + /// This method returns the left node of , or null if is null. + /// + /// Node to retrieve the left child from + /// Left child of + private static IntervalTreeNode LeftOf(IntervalTreeNode node) + { + return node?.Left; + } + + /// + /// This method returns the right node of , or null if is null. + /// + /// Node to retrieve the right child from + /// Right child of + private static IntervalTreeNode RightOf(IntervalTreeNode node) + { + return node?.Right; + } + + /// + /// Returns the parent node of , or null if is null. + /// + /// Node to retrieve the parent from + /// Parent of + private static IntervalTreeNode ParentOf(IntervalTreeNode node) + { + return node?.Parent; + } + + #endregion + + public bool ContainsKey(K key) + { + return GetNode(key) != null; + } + + public void Clear() + { + _root = null; + _count = 0; + } + } + + /// + /// Represents a node in the IntervalTree which contains start and end keys of type K, and a value of generic type V. + /// + /// Key type of the node + /// Value type of the node + internal class IntervalTreeNode + { + internal bool Color = true; + internal IntervalTreeNode Left = null; + internal IntervalTreeNode Right = null; + internal IntervalTreeNode Parent = null; + + /// + /// The start of the range. + /// + internal K Start; + + /// + /// The end of the range. + /// + internal K End; + + /// + /// The maximum end value of this node and all its children. + /// + internal K Max; + + /// + /// Value stored on this node. + /// + internal V Value; + + public IntervalTreeNode(K start, K end, V value, IntervalTreeNode parent) + { + this.Start = start; + this.End = end; + this.Max = end; + this.Value = value; + this.Parent = parent; + } + } +} diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 85e56e491..c6df8b1ef 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -585,7 +585,7 @@ namespace ARMeilleure.Translation.PTC translator.RegisterFunction(infoEntry.Address, func); - bool isAddressUnique = translator.Functions.TryAdd(infoEntry.Address, func); + bool isAddressUnique = translator.Functions.TryAdd(infoEntry.Address, infoEntry.GuestSize, func); Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique."); } @@ -815,7 +815,7 @@ namespace ARMeilleure.Translation.PTC TranslatedFunction func = translator.Translate(address, item.funcProfile.Mode, item.funcProfile.HighCq); - bool isAddressUnique = translator.Functions.TryAdd(address, func); + bool isAddressUnique = translator.Functions.TryAdd(address, func.GuestSize, func); Debug.Assert(isAddressUnique, $"The address 0x{address:X16} is not unique."); diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs index e31ff230d..bb70da8d0 100644 --- a/ARMeilleure/Translation/PTC/PtcProfiler.cs +++ b/ARMeilleure/Translation/PTC/PtcProfiler.cs @@ -96,7 +96,7 @@ namespace ARMeilleure.Translation.PTC return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize; } - internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(ConcurrentDictionary funcs) + internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache funcs) { var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>(); diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index 9974fb2d8..cd078cd2b 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -49,7 +49,7 @@ namespace ARMeilleure.Translation private readonly AutoResetEvent _backgroundTranslatorEvent; private readonly ReaderWriterLock _backgroundTranslatorLock; - internal ConcurrentDictionary Functions { get; } + internal TranslatorCache Functions { get; } internal AddressTable FunctionTable { get; } internal EntryTable CountTable { get; } internal TranslatorStubs Stubs { get; } @@ -75,7 +75,7 @@ namespace ARMeilleure.Translation JitCache.Initialize(allocator); CountTable = new EntryTable(); - Functions = new ConcurrentDictionary(); + Functions = new TranslatorCache(); FunctionTable = new AddressTable(for64Bits ? Levels64Bit : Levels32Bit); Stubs = new TranslatorStubs(this); @@ -93,12 +93,12 @@ namespace ARMeilleure.Translation { _backgroundTranslatorLock.AcquireReaderLock(Timeout.Infinite); - if (_backgroundStack.TryPop(out RejitRequest request) && + if (_backgroundStack.TryPop(out RejitRequest request) && _backgroundSet.TryRemove(request.Address, out _)) { TranslatedFunction func = Translate(request.Address, request.Mode, highCq: true); - Functions.AddOrUpdate(request.Address, func, (key, oldFunc) => + Functions.AddOrUpdate(request.Address, func.GuestSize, func, (key, oldFunc) => { EnqueueForDeletion(key, oldFunc); return func; @@ -196,7 +196,7 @@ namespace ARMeilleure.Translation } } - public ulong ExecuteSingle(State.ExecutionContext context, ulong address) + private ulong ExecuteSingle(State.ExecutionContext context, ulong address) { TranslatedFunction func = GetOrTranslate(address, context.ExecutionMode); @@ -215,7 +215,7 @@ namespace ARMeilleure.Translation { func = Translate(address, mode, highCq: false); - TranslatedFunction oldFunc = Functions.GetOrAdd(address, func); + TranslatedFunction oldFunc = Functions.GetOrAdd(address, func.GuestSize, func); if (oldFunc != func) { @@ -459,7 +459,24 @@ namespace ARMeilleure.Translation // If rejit is running, stop it as it may be trying to rejit a function on the invalidated region. ClearRejitQueue(allowRequeue: true); - // TODO: Completely remove functions overlapping the specified range from the cache. + ulong[] overlapAddresses = Array.Empty(); + + int overlapsCount = Functions.GetOverlaps(address, size, ref overlapAddresses); + + for (int index = 0; index < overlapsCount; index++) + { + ulong overlapAddress = overlapAddresses[index]; + + if (Functions.TryGetValue(overlapAddress, out TranslatedFunction overlap)) + { + Functions.Remove(overlapAddress); + Volatile.Write(ref FunctionTable.GetValue(overlapAddress), FunctionTable.Fill); + EnqueueForDeletion(overlapAddress, overlap); + } + } + + // TODO: Remove overlapping functions from the JitCache aswell. + // This should be done safely, with a mechanism to ensure the function is not being executed. } internal void EnqueueForRejit(ulong guestAddress, ExecutionMode mode) @@ -481,7 +498,9 @@ namespace ARMeilleure.Translation // Ensure no attempt will be made to compile new functions due to rejit. ClearRejitQueue(allowRequeue: false); - foreach (var func in Functions.Values) + List functions = Functions.AsList(); + + foreach (var func in functions) { JitCache.Unmap(func.FuncPtr); diff --git a/ARMeilleure/Translation/TranslatorCache.cs b/ARMeilleure/Translation/TranslatorCache.cs new file mode 100644 index 000000000..11286381b --- /dev/null +++ b/ARMeilleure/Translation/TranslatorCache.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Threading; + +namespace ARMeilleure.Translation +{ + internal class TranslatorCache + { + private readonly IntervalTree _tree; + private readonly ReaderWriterLock _treeLock; + + public int Count => _tree.Count; + + public TranslatorCache() + { + _tree = new IntervalTree(); + _treeLock = new ReaderWriterLock(); + } + + public bool TryAdd(ulong address, ulong size, T value) + { + return AddOrUpdate(address, size, value, null); + } + + public bool AddOrUpdate(ulong address, ulong size, T value, Func updateFactoryCallback) + { + _treeLock.AcquireWriterLock(Timeout.Infinite); + bool result = _tree.AddOrUpdate(address, address + size, value, updateFactoryCallback); + _treeLock.ReleaseWriterLock(); + + return result; + } + + public T GetOrAdd(ulong address, ulong size, T value) + { + _treeLock.AcquireWriterLock(Timeout.Infinite); + value = _tree.GetOrAdd(address, address + size, value); + _treeLock.ReleaseWriterLock(); + + return value; + } + + public bool Remove(ulong address) + { + _treeLock.AcquireWriterLock(Timeout.Infinite); + bool removed = _tree.Remove(address) != 0; + _treeLock.ReleaseWriterLock(); + + return removed; + } + + public void Clear() + { + _treeLock.AcquireWriterLock(Timeout.Infinite); + _tree.Clear(); + _treeLock.ReleaseWriterLock(); + } + + public bool ContainsKey(ulong address) + { + _treeLock.AcquireReaderLock(Timeout.Infinite); + bool result = _tree.ContainsKey(address); + _treeLock.ReleaseReaderLock(); + + return result; + } + + public bool TryGetValue(ulong address, out T value) + { + _treeLock.AcquireReaderLock(Timeout.Infinite); + bool result = _tree.TryGet(address, out value); + _treeLock.ReleaseReaderLock(); + + return result; + } + + public int GetOverlaps(ulong address, ulong size, ref ulong[] overlaps) + { + _treeLock.AcquireReaderLock(Timeout.Infinite); + int count = _tree.Get(address, address + size, ref overlaps); + _treeLock.ReleaseReaderLock(); + + return count; + } + + public List AsList() + { + _treeLock.AcquireReaderLock(Timeout.Infinite); + List list = _tree.AsList(); + _treeLock.ReleaseReaderLock(); + + return list; + } + } +} diff --git a/Ryujinx.Cpu/CpuContext.cs b/Ryujinx.Cpu/CpuContext.cs index 3e422546a..9c09746bf 100644 --- a/Ryujinx.Cpu/CpuContext.cs +++ b/Ryujinx.Cpu/CpuContext.cs @@ -28,5 +28,10 @@ namespace Ryujinx.Cpu { _translator.Execute(context, address); } + + public void InvalidateCacheRegion(ulong address, ulong size) + { + _translator.InvalidateJitCacheRegion(address, size); + } } } diff --git a/Ryujinx.HLE/HOS/ArmProcessContext.cs b/Ryujinx.HLE/HOS/ArmProcessContext.cs index 89ce0eb34..8c9d17a55 100644 --- a/Ryujinx.HLE/HOS/ArmProcessContext.cs +++ b/Ryujinx.HLE/HOS/ArmProcessContext.cs @@ -36,6 +36,11 @@ namespace Ryujinx.HLE.HOS _cpuContext.Execute(context, codeAddress); } + public void InvalidateCacheRegion(ulong address, ulong size) + { + _cpuContext.InvalidateCacheRegion(address, size); + } + public void Dispose() { if (_memoryManager is IRefCounted rc) diff --git a/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs b/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs index 758ac6b10..707e6d981 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs @@ -9,5 +9,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process IVirtualMemoryManager AddressSpace { get; } void Execute(ExecutionContext context, ulong codeAddress); + void InvalidateCacheRegion(ulong address, ulong size); } } diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs index 54997cb7c..bb3a15570 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs @@ -18,6 +18,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process throw new NotSupportedException(); } + public void InvalidateCacheRegion(ulong address, ulong size) + { + } + public void Dispose() { } From a23274cb3cbe12e944b53bd9dfe7d874f26be834 Mon Sep 17 00:00:00 2001 From: gdk Date: Sat, 1 Jan 2022 22:04:13 -0300 Subject: [PATCH 8/9] Implement GetApplicationPid atmosphere extension --- Ryujinx.HLE/HOS/Kernel/KernelStatic.cs | 10 ++++++++ .../HOS/Kernel/SupervisorCall/Syscall.cs | 2 ++ .../HOS/Services/Pm/IDebugMonitorInterface.cs | 25 ++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs b/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs index 2446ace4b..cce7eb249 100644 --- a/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs +++ b/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs @@ -59,5 +59,15 @@ namespace Ryujinx.HLE.HOS.Kernel { return GetCurrentThread().Owner; } + + internal static KProcess GetProcessByPid(long pid) + { + if (Context.Processes.TryGetValue(pid, out KProcess process)) + { + return process; + } + + return null; + } } } diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index fadf1007c..46d611a65 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -1539,6 +1539,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return result; } + srcProcess.Context.InvalidateCacheRegion(src, size); + return KernelResult.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs b/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs index 06c119436..630acd556 100644 --- a/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs @@ -1,8 +1,31 @@ -namespace Ryujinx.HLE.HOS.Services.Pm +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Process; + +namespace Ryujinx.HLE.HOS.Services.Pm { [Service("pm:dmnt")] class IDebugMonitorInterface : IpcService { public IDebugMonitorInterface(ServiceCtx context) { } + + [CommandHipc(65000)] + // AtmosphereGetProcessInfo(os::ProcessId process_id) -> sf::OutCopyHandle out_process_handle, sf::Out out_loc, sf::Out out_status + public ResultCode GetApplicationPid(ServiceCtx context) + { + long pid = context.RequestData.ReadInt64(); + + KProcess process = KernelStatic.GetProcessByPid(pid); + + if (context.Process.HandleTable.GenerateHandle(process, out int processHandle) != KernelResult.Success) + { + throw new System.Exception("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(processHandle); + + return ResultCode.Success; + } } } \ No newline at end of file From 1e60711d4e8b09aba1557cc2681f1f82452163b8 Mon Sep 17 00:00:00 2001 From: gdk Date: Sat, 1 Jan 2022 22:15:16 -0300 Subject: [PATCH 9/9] Correct name --- Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs b/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs index 630acd556..e88bd3e88 100644 --- a/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Pm/IDebugMonitorInterface.cs @@ -12,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Pm [CommandHipc(65000)] // AtmosphereGetProcessInfo(os::ProcessId process_id) -> sf::OutCopyHandle out_process_handle, sf::Out out_loc, sf::Out out_status - public ResultCode GetApplicationPid(ServiceCtx context) + public ResultCode GetProcessInfo(ServiceCtx context) { long pid = context.RequestData.ReadInt64();