AddressTable.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. using ARMeilleure.Diagnostics;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Runtime.InteropServices;
  5. namespace ARMeilleure.Common
  6. {
  7. /// <summary>
  8. /// Represents a table of guest address to a value.
  9. /// </summary>
  10. /// <typeparam name="TEntry">Type of the value</typeparam>
  11. unsafe class AddressTable<TEntry> : IDisposable where TEntry : unmanaged
  12. {
  13. /// <summary>
  14. /// Represents a level in an <see cref="AddressTable{TEntry}"/>.
  15. /// </summary>
  16. public readonly struct Level
  17. {
  18. /// <summary>
  19. /// Gets the index of the <see cref="Level"/> in the guest address.
  20. /// </summary>
  21. public int Index { get; }
  22. /// <summary>
  23. /// Gets the length of the <see cref="Level"/> in the guest address.
  24. /// </summary>
  25. public int Length { get; }
  26. /// <summary>
  27. /// Gets the mask which masks the bits used by the <see cref="Level"/>.
  28. /// </summary>
  29. public ulong Mask => ((1ul << Length) - 1) << Index;
  30. /// <summary>
  31. /// Initializes a new instance of the <see cref="Level"/> structure with the specified
  32. /// <paramref name="index"/> and <paramref name="length"/>.
  33. /// </summary>
  34. /// <param name="index">Index of the <see cref="Level"/></param>
  35. /// <param name="length">Length of the <see cref="Level"/></param>
  36. public Level(int index, int length)
  37. {
  38. (Index, Length) = (index, length);
  39. }
  40. /// <summary>
  41. /// Gets the value of the <see cref="Level"/> from the specified guest <paramref name="address"/>.
  42. /// </summary>
  43. /// <param name="address">Guest address</param>
  44. /// <returns>Value of the <see cref="Level"/> from the specified guest <paramref name="address"/></returns>
  45. public int GetValue(ulong address)
  46. {
  47. return (int)((address & Mask) >> Index);
  48. }
  49. }
  50. private bool _disposed;
  51. private TEntry** _table;
  52. private readonly List<IntPtr> _pages;
  53. /// <summary>
  54. /// Gets the bits used by the <see cref="Levels"/> of the <see cref="AddressTable{TEntry}"/> instance.
  55. /// </summary>
  56. public ulong Mask { get; }
  57. /// <summary>
  58. /// Gets the <see cref="Level"/>s used by the <see cref="AddressTable{TEntry}"/> instance.
  59. /// </summary>
  60. public Level[] Levels { get; }
  61. /// <summary>
  62. /// Gets or sets the default fill value of newly created leaf pages.
  63. /// </summary>
  64. public TEntry Fill { get; set; }
  65. /// <summary>
  66. /// Gets the base address of the <see cref="EntryTable{TEntry}"/>.
  67. /// </summary>
  68. /// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
  69. public IntPtr Base
  70. {
  71. get
  72. {
  73. ObjectDisposedException.ThrowIf(_disposed, this);
  74. lock (_pages)
  75. {
  76. return (IntPtr)GetRootPage();
  77. }
  78. }
  79. }
  80. /// <summary>
  81. /// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
  82. /// <see cref="Level"/>.
  83. /// </summary>
  84. /// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
  85. /// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
  86. public AddressTable(Level[] levels)
  87. {
  88. ArgumentNullException.ThrowIfNull(levels);
  89. if (levels.Length < 2)
  90. {
  91. throw new ArgumentException("Table must be at least 2 levels deep.", nameof(levels));
  92. }
  93. _pages = new List<IntPtr>(capacity: 16);
  94. Levels = levels;
  95. Mask = 0;
  96. foreach (var level in Levels)
  97. {
  98. Mask |= level.Mask;
  99. }
  100. }
  101. /// <summary>
  102. /// Determines if the specified <paramref name="address"/> is in the range of the
  103. /// <see cref="AddressTable{TEntry}"/>.
  104. /// </summary>
  105. /// <param name="address">Guest address</param>
  106. /// <returns><see langword="true"/> if is valid; otherwise <see langword="false"/></returns>
  107. public bool IsValid(ulong address)
  108. {
  109. return (address & ~Mask) == 0;
  110. }
  111. /// <summary>
  112. /// Gets a reference to the value at the specified guest <paramref name="address"/>.
  113. /// </summary>
  114. /// <param name="address">Guest address</param>
  115. /// <returns>Reference to the value at the specified guest <paramref name="address"/></returns>
  116. /// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
  117. /// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
  118. public ref TEntry GetValue(ulong address)
  119. {
  120. ObjectDisposedException.ThrowIf(_disposed, this);
  121. if (!IsValid(address))
  122. {
  123. throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address));
  124. }
  125. lock (_pages)
  126. {
  127. return ref GetPage(address)[Levels[^1].GetValue(address)];
  128. }
  129. }
  130. /// <summary>
  131. /// Gets the leaf page for the specified guest <paramref name="address"/>.
  132. /// </summary>
  133. /// <param name="address">Guest address</param>
  134. /// <returns>Leaf page for the specified guest <paramref name="address"/></returns>
  135. private TEntry* GetPage(ulong address)
  136. {
  137. TEntry** page = GetRootPage();
  138. for (int i = 0; i < Levels.Length - 1; i++)
  139. {
  140. ref Level level = ref Levels[i];
  141. ref TEntry* nextPage = ref page[level.GetValue(address)];
  142. if (nextPage == null)
  143. {
  144. ref Level nextLevel = ref Levels[i + 1];
  145. nextPage = i == Levels.Length - 2 ?
  146. (TEntry*)Allocate(1 << nextLevel.Length, Fill, leaf: true) :
  147. (TEntry*)Allocate(1 << nextLevel.Length, IntPtr.Zero, leaf: false);
  148. }
  149. page = (TEntry**)nextPage;
  150. }
  151. return (TEntry*)page;
  152. }
  153. /// <summary>
  154. /// Lazily initialize and get the root page of the <see cref="AddressTable{TEntry}"/>.
  155. /// </summary>
  156. /// <returns>Root page of the <see cref="AddressTable{TEntry}"/></returns>
  157. private TEntry** GetRootPage()
  158. {
  159. if (_table == null)
  160. {
  161. _table = (TEntry**)Allocate(1 << Levels[0].Length, fill: IntPtr.Zero, leaf: false);
  162. }
  163. return _table;
  164. }
  165. /// <summary>
  166. /// Allocates a block of memory of the specified type and length.
  167. /// </summary>
  168. /// <typeparam name="T">Type of elements</typeparam>
  169. /// <param name="length">Number of elements</param>
  170. /// <param name="fill">Fill value</param>
  171. /// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param>
  172. /// <returns>Allocated block</returns>
  173. private IntPtr Allocate<T>(int length, T fill, bool leaf) where T : unmanaged
  174. {
  175. var size = sizeof(T) * length;
  176. var page = (IntPtr)NativeAllocator.Instance.Allocate((uint)size);
  177. var span = new Span<T>((void*)page, length);
  178. span.Fill(fill);
  179. _pages.Add(page);
  180. TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
  181. return page;
  182. }
  183. /// <summary>
  184. /// Releases all resources used by the <see cref="AddressTable{TEntry}"/> instance.
  185. /// </summary>
  186. public void Dispose()
  187. {
  188. Dispose(true);
  189. GC.SuppressFinalize(this);
  190. }
  191. /// <summary>
  192. /// Releases all unmanaged and optionally managed resources used by the <see cref="AddressTable{TEntry}"/>
  193. /// instance.
  194. /// </summary>
  195. /// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
  196. protected virtual void Dispose(bool disposing)
  197. {
  198. if (!_disposed)
  199. {
  200. foreach (var page in _pages)
  201. {
  202. Marshal.FreeHGlobal(page);
  203. }
  204. _disposed = true;
  205. }
  206. }
  207. /// <summary>
  208. /// Frees resources used by the <see cref="AddressTable{TEntry}"/> instance.
  209. /// </summary>
  210. ~AddressTable()
  211. {
  212. Dispose(false);
  213. }
  214. }
  215. }