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