92 lines
3.3 KiB
C#
92 lines
3.3 KiB
C#
|
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);
|
||
|
}
|
||
|
}
|