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); } }