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<byte> data)` implementation.

* move property `AddressSpaceSize` up by the other properties
This commit is contained in:
jhorv 2024-03-09 19:01:51 -05:00 committed by GitHub
parent 3924bd1a43
commit a3a63d4394
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 161 additions and 295 deletions

View file

@ -0,0 +1,91 @@
using System;
using System.Numerics;
namespace Ryujinx.Memory
{
public abstract class VirtualMemoryManagerBase<TVirtual, TPhysical>
where TVirtual : IBinaryInteger<TVirtual>
where TPhysical : IBinaryInteger<TPhysical>
{
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<byte> 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));
}
}
/// <summary>
/// Ensures the combination of virtual address and size is part of the addressable space.
/// </summary>
/// <param name="va">Virtual address of the range</param>
/// <param name="size">Size of the range in bytes</param>
/// <exception cref="InvalidMemoryRegionException">Throw when the memory region specified outside the addressable space</exception>
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<byte> GetPhysicalAddressSpan(TPhysical pa, int size);
protected abstract TPhysical TranslateVirtualAddressForRead(TVirtual va);
/// <summary>
/// Checks if the virtual address is part of the addressable space.
/// </summary>
/// <param name="va">Virtual address</param>
/// <returns>True if the virtual address is part of the addressable space</returns>
protected bool ValidateAddress(TVirtual va)
{
return va < AddressSpaceSize;
}
/// <summary>
/// Checks if the combination of virtual address and size is part of the addressable space.
/// </summary>
/// <param name="va">Virtual address of the range</param>
/// <param name="size">Size of the range in bytes</param>
/// <returns>True if the combination of virtual address and size is part of the addressable space</returns>
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);
}
}