using ARMeilleure.Common;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Ryujinx.Cpu
{
///
/// Represents a table of guest address to a value.
///
/// Type of the value
public unsafe class AddressTable : IAddressTable where TEntry : unmanaged
{
private bool _disposed;
private TEntry** _table;
private readonly List _pages;
///
public AddressTableType TableType => AddressTableType.Default;
///
public ulong Mask { get; }
///
public AddressTableLevel[] Levels { get; }
///
public TEntry Fill { get; set; }
///
public nint Base
{
get
{
ObjectDisposedException.ThrowIf(_disposed, this);
lock (_pages)
{
return (nint)GetRootPage();
}
}
}
///
/// Constructs a new instance of the class with the specified list of
/// .
///
/// Levels for the address table
/// is null
/// Length of is less than 2
public AddressTable(AddressTableLevel[] levels)
{
ArgumentNullException.ThrowIfNull(levels);
_pages = new List(capacity: 16);
Levels = levels;
Mask = 0;
foreach (AddressTableLevel level in Levels)
{
Mask |= level.Mask;
}
}
///
/// Create an instance for an ARM function table.
/// Selects the best table structure for A32/A64, taking into account the selected memory manager type.
///
/// True if the guest is A64, false otherwise
/// An for ARM function lookup
public static AddressTable CreateForArm(bool for64Bits)
{
return new AddressTable(AddressTablePresets.GetArmPreset(for64Bits, false));
}
///
/// Signal that the given code range exists.
///
///
///
public void SignalCodeRange(ulong address, ulong size) { }
///
public bool IsValid(ulong address)
{
return (address & ~Mask) == 0;
}
///
public ref TEntry GetValue(ulong address)
{
ObjectDisposedException.ThrowIf(_disposed, this);
if (!IsValid(address))
{
throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address));
}
lock (_pages)
{
TEntry* page = GetPage(address);
long index = Levels[^1].GetValue(address);
return ref page[index];
}
}
///
/// Gets the leaf page for the specified guest .
///
/// Guest address
/// Leaf page for the specified guest
private TEntry* GetPage(ulong address)
{
TEntry** page = GetRootPage();
for (int i = 0; i < Levels.Length - 1; i++)
{
ref AddressTableLevel level = ref Levels[i];
if (page != null)
{
ref TEntry* nextPage = ref page![level.GetValue(address)];
if (nextPage == null)
{
ref AddressTableLevel nextLevel = ref Levels[i + 1];
if (i == Levels.Length - 2)
{
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, Fill);
}
else
{
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, nint.Zero);
}
}
page = (TEntry**)nextPage;
}
}
return (TEntry*)page;
}
///
/// Lazily initialize and get the root page of the .
///
/// Root page of the
private TEntry** GetRootPage()
{
if (_table == null)
{
if (Levels.Length == 1)
_table = (TEntry**)Allocate(1 << Levels[0].Length, Fill);
else
_table = (TEntry**)Allocate(1 << Levels[0].Length, nint.Zero);
}
return _table;
}
///
/// Allocates a block of memory of the specified type and length.
///
/// Type of elements
/// Number of elements
/// Fill value
/// Allocated block
private nint Allocate(int length, T fill) where T : unmanaged
{
int size = sizeof(T) * length;
nint address = (nint)NativeAllocator.Instance.Allocate((uint)size);
Span span = new((void*)address, length);
span.Fill(fill);
_pages.Add(address);
//TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
return address;
}
///
/// Releases all resources used by the instance.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Releases all unmanaged and optionally managed resources used by the
/// instance.
///
/// to dispose managed resources also; otherwise just unmanaged resouces
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
foreach (nint page in _pages)
{
Marshal.FreeHGlobal(page);
}
_disposed = true;
}
///
/// Frees resources used by the instance.
///
~AddressTable()
{
Dispose(false);
}
}
}