From a3a63d43948b79450d1a0ee963ea4796cb3532a0 Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Sat, 9 Mar 2024 19:01:51 -0500 Subject: Refactor memory managers to a common base class, consolidate Read() method logic (#6360) * - add new abstract class `VirtualMemoryManagerBase` - rename `MemoryManagerBase` to `VirtualMemoryManagerRefCountedBase` and derive from `VirtualMemoryManagerBase` - change `AddressSpaceManager`, `HvMemoryManager`, `MemoryManager`, and `MemoryManagerHostMapped` to implement abstract members and use the inherited `void VirtualMemoryManagerBase.Read(TVirtual va, Span data)` implementation. * move property `AddressSpaceSize` up by the other properties --- src/Ryujinx.Memory/VirtualMemoryManagerBase.cs | 91 ++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/Ryujinx.Memory/VirtualMemoryManagerBase.cs (limited to 'src/Ryujinx.Memory/VirtualMemoryManagerBase.cs') diff --git a/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs b/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs new file mode 100644 index 00000000..cbec88cc --- /dev/null +++ b/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs @@ -0,0 +1,91 @@ +using System; +using System.Numerics; + +namespace Ryujinx.Memory +{ + public abstract class VirtualMemoryManagerBase + where TVirtual : IBinaryInteger + where TPhysical : IBinaryInteger + { + public const int PageBits = 12; + public const int PageSize = 1 << PageBits; + public const int PageMask = PageSize - 1; + + protected abstract TVirtual AddressSpaceSize { get; } + + public virtual void Read(TVirtual va, Span data) + { + if (data.Length == 0) + { + return; + } + + AssertValidAddressAndSize(va, TVirtual.CreateChecked(data.Length)); + + int offset = 0, size; + + if ((int.CreateTruncating(va) & PageMask) != 0) + { + TPhysical pa = TranslateVirtualAddressForRead(va); + + size = Math.Min(data.Length, PageSize - ((int.CreateTruncating(va) & PageMask))); + + GetPhysicalAddressSpan(pa, size).CopyTo(data[..size]); + + offset += size; + } + + for (; offset < data.Length; offset += size) + { + TPhysical pa = TranslateVirtualAddressForRead(va + TVirtual.CreateChecked(offset)); + + size = Math.Min(data.Length - offset, PageSize); + + GetPhysicalAddressSpan(pa, size).CopyTo(data.Slice(offset, size)); + } + } + + /// + /// Ensures the combination of virtual address and size is part of the addressable space. + /// + /// Virtual address of the range + /// Size of the range in bytes + /// Throw when the memory region specified outside the addressable space + protected void AssertValidAddressAndSize(TVirtual va, TVirtual size) + { + if (!ValidateAddressAndSize(va, size)) + { + throw new InvalidMemoryRegionException($"va=0x{va:X16}, size=0x{size:X16}"); + } + } + + protected abstract Span GetPhysicalAddressSpan(TPhysical pa, int size); + + protected abstract TPhysical TranslateVirtualAddressForRead(TVirtual va); + + /// + /// Checks if the virtual address is part of the addressable space. + /// + /// Virtual address + /// True if the virtual address is part of the addressable space + protected bool ValidateAddress(TVirtual va) + { + return va < AddressSpaceSize; + } + + /// + /// Checks if the combination of virtual address and size is part of the addressable space. + /// + /// Virtual address of the range + /// Size of the range in bytes + /// True if the combination of virtual address and size is part of the addressable space + protected bool ValidateAddressAndSize(TVirtual va, TVirtual size) + { + TVirtual endVa = va + size; + return endVa >= va && endVa >= size && endVa <= AddressSpaceSize; + } + + protected static void ThrowInvalidMemoryRegionException(string message) + => throw new InvalidMemoryRegionException(message); + } +} -- cgit v1.2.3