MemoryManager.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915
  1. using ChocolArm64.Events;
  2. using ChocolArm64.Exceptions;
  3. using ChocolArm64.Instructions;
  4. using System;
  5. using System.Collections.Concurrent;
  6. using System.Runtime.CompilerServices;
  7. using System.Runtime.InteropServices;
  8. using System.Runtime.Intrinsics;
  9. using System.Runtime.Intrinsics.X86;
  10. using System.Threading;
  11. using static ChocolArm64.Memory.CompareExchange128;
  12. namespace ChocolArm64.Memory
  13. {
  14. public unsafe class MemoryManager : IMemory, IDisposable
  15. {
  16. private const int PtLvl0Bits = 13;
  17. private const int PtLvl1Bits = 14;
  18. public const int PageBits = 12;
  19. private const int PtLvl0Size = 1 << PtLvl0Bits;
  20. private const int PtLvl1Size = 1 << PtLvl1Bits;
  21. public const int PageSize = 1 << PageBits;
  22. private const int PtLvl0Mask = PtLvl0Size - 1;
  23. private const int PtLvl1Mask = PtLvl1Size - 1;
  24. public const int PageMask = PageSize - 1;
  25. private const int PtLvl0Bit = PageBits + PtLvl1Bits;
  26. private const int PtLvl1Bit = PageBits;
  27. private ConcurrentDictionary<long, IntPtr> _observedPages;
  28. public IntPtr Ram { get; private set; }
  29. private byte* _ramPtr;
  30. private byte*** _pageTable;
  31. public event EventHandler<MemoryAccessEventArgs> InvalidAccess;
  32. public event EventHandler<MemoryAccessEventArgs> ObservedAccess;
  33. public MemoryManager(IntPtr ram)
  34. {
  35. _observedPages = new ConcurrentDictionary<long, IntPtr>();
  36. Ram = ram;
  37. _ramPtr = (byte*)ram;
  38. _pageTable = (byte***)Marshal.AllocHGlobal(PtLvl0Size * IntPtr.Size);
  39. for (int l0 = 0; l0 < PtLvl0Size; l0++)
  40. {
  41. _pageTable[l0] = null;
  42. }
  43. }
  44. internal bool AtomicCompareExchange2xInt32(
  45. long position,
  46. int expectedLow,
  47. int expectedHigh,
  48. int desiredLow,
  49. int desiredHigh)
  50. {
  51. long expected = (uint)expectedLow;
  52. long desired = (uint)desiredLow;
  53. expected |= (long)expectedHigh << 32;
  54. desired |= (long)desiredHigh << 32;
  55. return AtomicCompareExchangeInt64(position, expected, desired);
  56. }
  57. internal bool AtomicCompareExchangeInt128(
  58. long position,
  59. ulong expectedLow,
  60. ulong expectedHigh,
  61. ulong desiredLow,
  62. ulong desiredHigh)
  63. {
  64. if ((position & 0xf) != 0)
  65. {
  66. AbortWithAlignmentFault(position);
  67. }
  68. IntPtr ptr = new IntPtr(TranslateWrite(position));
  69. return InterlockedCompareExchange128(ptr, expectedLow, expectedHigh, desiredLow, desiredHigh);
  70. }
  71. internal Vector128<float> AtomicReadInt128(long position)
  72. {
  73. if ((position & 0xf) != 0)
  74. {
  75. AbortWithAlignmentFault(position);
  76. }
  77. IntPtr ptr = new IntPtr(Translate(position));
  78. InterlockedRead128(ptr, out ulong low, out ulong high);
  79. Vector128<float> vector = default(Vector128<float>);
  80. vector = VectorHelper.VectorInsertInt(low, vector, 0, 3);
  81. vector = VectorHelper.VectorInsertInt(high, vector, 1, 3);
  82. return vector;
  83. }
  84. public bool AtomicCompareExchangeByte(long position, byte expected, byte desired)
  85. {
  86. int* ptr = (int*)Translate(position);
  87. int currentValue = *ptr;
  88. int expected32 = (currentValue & ~byte.MaxValue) | expected;
  89. int desired32 = (currentValue & ~byte.MaxValue) | desired;
  90. return Interlocked.CompareExchange(ref *ptr, desired32, expected32) == expected32;
  91. }
  92. public bool AtomicCompareExchangeInt16(long position, short expected, short desired)
  93. {
  94. if ((position & 1) != 0)
  95. {
  96. AbortWithAlignmentFault(position);
  97. }
  98. int* ptr = (int*)Translate(position);
  99. int currentValue = *ptr;
  100. int expected32 = (currentValue & ~ushort.MaxValue) | (ushort)expected;
  101. int desired32 = (currentValue & ~ushort.MaxValue) | (ushort)desired;
  102. return Interlocked.CompareExchange(ref *ptr, desired32, expected32) == expected32;
  103. }
  104. public bool AtomicCompareExchangeInt32(long position, int expected, int desired)
  105. {
  106. if ((position & 3) != 0)
  107. {
  108. AbortWithAlignmentFault(position);
  109. }
  110. int* ptr = (int*)TranslateWrite(position);
  111. return Interlocked.CompareExchange(ref *ptr, desired, expected) == expected;
  112. }
  113. public bool AtomicCompareExchangeInt64(long position, long expected, long desired)
  114. {
  115. if ((position & 7) != 0)
  116. {
  117. AbortWithAlignmentFault(position);
  118. }
  119. long* ptr = (long*)TranslateWrite(position);
  120. return Interlocked.CompareExchange(ref *ptr, desired, expected) == expected;
  121. }
  122. public int AtomicIncrementInt32(long position)
  123. {
  124. if ((position & 3) != 0)
  125. {
  126. AbortWithAlignmentFault(position);
  127. }
  128. int* ptr = (int*)TranslateWrite(position);
  129. return Interlocked.Increment(ref *ptr);
  130. }
  131. public int AtomicDecrementInt32(long position)
  132. {
  133. if ((position & 3) != 0)
  134. {
  135. AbortWithAlignmentFault(position);
  136. }
  137. int* ptr = (int*)TranslateWrite(position);
  138. return Interlocked.Decrement(ref *ptr);
  139. }
  140. private void AbortWithAlignmentFault(long position)
  141. {
  142. //TODO: Abort mode and exception support on the CPU.
  143. throw new InvalidOperationException($"Tried to compare exchange a misaligned address 0x{position:X16}.");
  144. }
  145. public sbyte ReadSByte(long position)
  146. {
  147. return (sbyte)ReadByte(position);
  148. }
  149. public short ReadInt16(long position)
  150. {
  151. return (short)ReadUInt16(position);
  152. }
  153. public int ReadInt32(long position)
  154. {
  155. return (int)ReadUInt32(position);
  156. }
  157. public long ReadInt64(long position)
  158. {
  159. return (long)ReadUInt64(position);
  160. }
  161. public byte ReadByte(long position)
  162. {
  163. return *((byte*)Translate(position));
  164. }
  165. public ushort ReadUInt16(long position)
  166. {
  167. if ((position & 1) == 0)
  168. {
  169. return *((ushort*)Translate(position));
  170. }
  171. else
  172. {
  173. return (ushort)(ReadByte(position + 0) << 0 |
  174. ReadByte(position + 1) << 8);
  175. }
  176. }
  177. public uint ReadUInt32(long position)
  178. {
  179. if ((position & 3) == 0)
  180. {
  181. return *((uint*)Translate(position));
  182. }
  183. else
  184. {
  185. return (uint)(ReadUInt16(position + 0) << 0 |
  186. ReadUInt16(position + 2) << 16);
  187. }
  188. }
  189. public ulong ReadUInt64(long position)
  190. {
  191. if ((position & 7) == 0)
  192. {
  193. return *((ulong*)Translate(position));
  194. }
  195. else
  196. {
  197. return (ulong)ReadUInt32(position + 0) << 0 |
  198. (ulong)ReadUInt32(position + 4) << 32;
  199. }
  200. }
  201. public Vector128<float> ReadVector8(long position)
  202. {
  203. if (Sse2.IsSupported)
  204. {
  205. return Sse.StaticCast<byte, float>(Sse2.SetVector128(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ReadByte(position)));
  206. }
  207. else
  208. {
  209. Vector128<float> value = VectorHelper.VectorSingleZero();
  210. value = VectorHelper.VectorInsertInt(ReadByte(position), value, 0, 0);
  211. return value;
  212. }
  213. }
  214. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  215. public Vector128<float> ReadVector16(long position)
  216. {
  217. if (Sse2.IsSupported && (position & 1) == 0)
  218. {
  219. return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse2.SetZeroVector128<ushort>(), ReadUInt16(position), 0));
  220. }
  221. else
  222. {
  223. Vector128<float> value = VectorHelper.VectorSingleZero();
  224. value = VectorHelper.VectorInsertInt(ReadUInt16(position), value, 0, 1);
  225. return value;
  226. }
  227. }
  228. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  229. public Vector128<float> ReadVector32(long position)
  230. {
  231. if (Sse.IsSupported && (position & 3) == 0)
  232. {
  233. return Sse.LoadScalarVector128((float*)Translate(position));
  234. }
  235. else
  236. {
  237. Vector128<float> value = VectorHelper.VectorSingleZero();
  238. value = VectorHelper.VectorInsertInt(ReadUInt32(position), value, 0, 2);
  239. return value;
  240. }
  241. }
  242. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  243. public Vector128<float> ReadVector64(long position)
  244. {
  245. if (Sse2.IsSupported && (position & 7) == 0)
  246. {
  247. return Sse.StaticCast<double, float>(Sse2.LoadScalarVector128((double*)Translate(position)));
  248. }
  249. else
  250. {
  251. Vector128<float> value = VectorHelper.VectorSingleZero();
  252. value = VectorHelper.VectorInsertInt(ReadUInt64(position), value, 0, 3);
  253. return value;
  254. }
  255. }
  256. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  257. public Vector128<float> ReadVector128(long position)
  258. {
  259. if (Sse.IsSupported && (position & 15) == 0)
  260. {
  261. return Sse.LoadVector128((float*)Translate(position));
  262. }
  263. else
  264. {
  265. Vector128<float> value = VectorHelper.VectorSingleZero();
  266. value = VectorHelper.VectorInsertInt(ReadUInt64(position + 0), value, 0, 3);
  267. value = VectorHelper.VectorInsertInt(ReadUInt64(position + 8), value, 1, 3);
  268. return value;
  269. }
  270. }
  271. public byte[] ReadBytes(long position, long size)
  272. {
  273. long endAddr = position + size;
  274. if ((ulong)size > int.MaxValue)
  275. {
  276. throw new ArgumentOutOfRangeException(nameof(size));
  277. }
  278. if ((ulong)endAddr < (ulong)position)
  279. {
  280. throw new ArgumentOutOfRangeException(nameof(position));
  281. }
  282. byte[] data = new byte[size];
  283. int offset = 0;
  284. while ((ulong)position < (ulong)endAddr)
  285. {
  286. long pageLimit = (position + PageSize) & ~(long)PageMask;
  287. if ((ulong)pageLimit > (ulong)endAddr)
  288. {
  289. pageLimit = endAddr;
  290. }
  291. int copySize = (int)(pageLimit - position);
  292. Marshal.Copy((IntPtr)Translate(position), data, offset, copySize);
  293. position += copySize;
  294. offset += copySize;
  295. }
  296. return data;
  297. }
  298. public void ReadBytes(long position, byte[] data, int startIndex, int size)
  299. {
  300. //Note: This will be moved later.
  301. long endAddr = position + size;
  302. if ((ulong)size > int.MaxValue)
  303. {
  304. throw new ArgumentOutOfRangeException(nameof(size));
  305. }
  306. if ((ulong)endAddr < (ulong)position)
  307. {
  308. throw new ArgumentOutOfRangeException(nameof(position));
  309. }
  310. int offset = startIndex;
  311. while ((ulong)position < (ulong)endAddr)
  312. {
  313. long pageLimit = (position + PageSize) & ~(long)PageMask;
  314. if ((ulong)pageLimit > (ulong)endAddr)
  315. {
  316. pageLimit = endAddr;
  317. }
  318. int copySize = (int)(pageLimit - position);
  319. Marshal.Copy((IntPtr)Translate(position), data, offset, copySize);
  320. position += copySize;
  321. offset += copySize;
  322. }
  323. }
  324. public void WriteSByte(long position, sbyte value)
  325. {
  326. WriteByte(position, (byte)value);
  327. }
  328. public void WriteInt16(long position, short value)
  329. {
  330. WriteUInt16(position, (ushort)value);
  331. }
  332. public void WriteInt32(long position, int value)
  333. {
  334. WriteUInt32(position, (uint)value);
  335. }
  336. public void WriteInt64(long position, long value)
  337. {
  338. WriteUInt64(position, (ulong)value);
  339. }
  340. public void WriteByte(long position, byte value)
  341. {
  342. *((byte*)TranslateWrite(position)) = value;
  343. }
  344. public void WriteUInt16(long position, ushort value)
  345. {
  346. if ((position & 1) == 0)
  347. {
  348. *((ushort*)TranslateWrite(position)) = value;
  349. }
  350. else
  351. {
  352. WriteByte(position + 0, (byte)(value >> 0));
  353. WriteByte(position + 1, (byte)(value >> 8));
  354. }
  355. }
  356. public void WriteUInt32(long position, uint value)
  357. {
  358. if ((position & 3) == 0)
  359. {
  360. *((uint*)TranslateWrite(position)) = value;
  361. }
  362. else
  363. {
  364. WriteUInt16(position + 0, (ushort)(value >> 0));
  365. WriteUInt16(position + 2, (ushort)(value >> 16));
  366. }
  367. }
  368. public void WriteUInt64(long position, ulong value)
  369. {
  370. if ((position & 7) == 0)
  371. {
  372. *((ulong*)TranslateWrite(position)) = value;
  373. }
  374. else
  375. {
  376. WriteUInt32(position + 0, (uint)(value >> 0));
  377. WriteUInt32(position + 4, (uint)(value >> 32));
  378. }
  379. }
  380. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  381. public void WriteVector8(long position, Vector128<float> value)
  382. {
  383. if (Sse41.IsSupported)
  384. {
  385. WriteByte(position, Sse41.Extract(Sse.StaticCast<float, byte>(value), 0));
  386. }
  387. else if (Sse2.IsSupported)
  388. {
  389. WriteByte(position, (byte)Sse2.Extract(Sse.StaticCast<float, ushort>(value), 0));
  390. }
  391. else
  392. {
  393. WriteByte(position, (byte)VectorHelper.VectorExtractIntZx(value, 0, 0));
  394. }
  395. }
  396. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  397. public void WriteVector16(long position, Vector128<float> value)
  398. {
  399. if (Sse2.IsSupported)
  400. {
  401. WriteUInt16(position, Sse2.Extract(Sse.StaticCast<float, ushort>(value), 0));
  402. }
  403. else
  404. {
  405. WriteUInt16(position, (ushort)VectorHelper.VectorExtractIntZx(value, 0, 1));
  406. }
  407. }
  408. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  409. public void WriteVector32(long position, Vector128<float> value)
  410. {
  411. if (Sse.IsSupported && (position & 3) == 0)
  412. {
  413. Sse.StoreScalar((float*)TranslateWrite(position), value);
  414. }
  415. else
  416. {
  417. WriteUInt32(position, (uint)VectorHelper.VectorExtractIntZx(value, 0, 2));
  418. }
  419. }
  420. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  421. public void WriteVector64(long position, Vector128<float> value)
  422. {
  423. if (Sse2.IsSupported && (position & 7) == 0)
  424. {
  425. Sse2.StoreScalar((double*)TranslateWrite(position), Sse.StaticCast<float, double>(value));
  426. }
  427. else
  428. {
  429. WriteUInt64(position, VectorHelper.VectorExtractIntZx(value, 0, 3));
  430. }
  431. }
  432. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  433. public void WriteVector128(long position, Vector128<float> value)
  434. {
  435. if (Sse.IsSupported && (position & 15) == 0)
  436. {
  437. Sse.Store((float*)TranslateWrite(position), value);
  438. }
  439. else
  440. {
  441. WriteUInt64(position + 0, VectorHelper.VectorExtractIntZx(value, 0, 3));
  442. WriteUInt64(position + 8, VectorHelper.VectorExtractIntZx(value, 1, 3));
  443. }
  444. }
  445. public void WriteBytes(long position, byte[] data)
  446. {
  447. long endAddr = position + data.Length;
  448. if ((ulong)endAddr < (ulong)position)
  449. {
  450. throw new ArgumentOutOfRangeException(nameof(position));
  451. }
  452. int offset = 0;
  453. while ((ulong)position < (ulong)endAddr)
  454. {
  455. long pageLimit = (position + PageSize) & ~(long)PageMask;
  456. if ((ulong)pageLimit > (ulong)endAddr)
  457. {
  458. pageLimit = endAddr;
  459. }
  460. int copySize = (int)(pageLimit - position);
  461. Marshal.Copy(data, offset, (IntPtr)TranslateWrite(position), copySize);
  462. position += copySize;
  463. offset += copySize;
  464. }
  465. }
  466. public void WriteBytes(long position, byte[] data, int startIndex, int size)
  467. {
  468. //Note: This will be moved later.
  469. long endAddr = position + size;
  470. if ((ulong)endAddr < (ulong)position)
  471. {
  472. throw new ArgumentOutOfRangeException(nameof(position));
  473. }
  474. int offset = startIndex;
  475. while ((ulong)position < (ulong)endAddr)
  476. {
  477. long pageLimit = (position + PageSize) & ~(long)PageMask;
  478. if ((ulong)pageLimit > (ulong)endAddr)
  479. {
  480. pageLimit = endAddr;
  481. }
  482. int copySize = (int)(pageLimit - position);
  483. Marshal.Copy(data, offset, (IntPtr)TranslateWrite(position), copySize);
  484. position += copySize;
  485. offset += copySize;
  486. }
  487. }
  488. public void CopyBytes(long src, long dst, long size)
  489. {
  490. //Note: This will be moved later.
  491. if (IsContiguous(src, size) &&
  492. IsContiguous(dst, size))
  493. {
  494. byte* srcPtr = Translate(src);
  495. byte* dstPtr = TranslateWrite(dst);
  496. Buffer.MemoryCopy(srcPtr, dstPtr, size, size);
  497. }
  498. else
  499. {
  500. WriteBytes(dst, ReadBytes(src, size));
  501. }
  502. }
  503. public void Map(long va, long pa, long size)
  504. {
  505. SetPtEntries(va, _ramPtr + pa, size);
  506. }
  507. public void Unmap(long position, long size)
  508. {
  509. SetPtEntries(position, null, size);
  510. StopObservingRegion(position, size);
  511. }
  512. public bool IsMapped(long position)
  513. {
  514. if (!(IsValidPosition(position)))
  515. {
  516. return false;
  517. }
  518. long l0 = (position >> PtLvl0Bit) & PtLvl0Mask;
  519. long l1 = (position >> PtLvl1Bit) & PtLvl1Mask;
  520. if (_pageTable[l0] == null)
  521. {
  522. return false;
  523. }
  524. return _pageTable[l0][l1] != null || _observedPages.ContainsKey(position >> PageBits);
  525. }
  526. public long GetPhysicalAddress(long virtualAddress)
  527. {
  528. byte* ptr = Translate(virtualAddress);
  529. return (long)(ptr - _ramPtr);
  530. }
  531. internal byte* Translate(long position)
  532. {
  533. long l0 = (position >> PtLvl0Bit) & PtLvl0Mask;
  534. long l1 = (position >> PtLvl1Bit) & PtLvl1Mask;
  535. long old = position;
  536. byte** lvl1 = _pageTable[l0];
  537. if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0)
  538. {
  539. goto Unmapped;
  540. }
  541. if (lvl1 == null)
  542. {
  543. goto Unmapped;
  544. }
  545. position &= PageMask;
  546. byte* ptr = lvl1[l1];
  547. if (ptr == null)
  548. {
  549. goto Unmapped;
  550. }
  551. return ptr + position;
  552. Unmapped:
  553. return HandleNullPte(old);
  554. }
  555. private byte* HandleNullPte(long position)
  556. {
  557. long key = position >> PageBits;
  558. if (_observedPages.TryGetValue(key, out IntPtr ptr))
  559. {
  560. return (byte*)ptr + (position & PageMask);
  561. }
  562. InvalidAccess?.Invoke(this, new MemoryAccessEventArgs(position));
  563. throw new VmmPageFaultException(position);
  564. }
  565. internal byte* TranslateWrite(long position)
  566. {
  567. long l0 = (position >> PtLvl0Bit) & PtLvl0Mask;
  568. long l1 = (position >> PtLvl1Bit) & PtLvl1Mask;
  569. long old = position;
  570. byte** lvl1 = _pageTable[l0];
  571. if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0)
  572. {
  573. goto Unmapped;
  574. }
  575. if (lvl1 == null)
  576. {
  577. goto Unmapped;
  578. }
  579. position &= PageMask;
  580. byte* ptr = lvl1[l1];
  581. if (ptr == null)
  582. {
  583. goto Unmapped;
  584. }
  585. return ptr + position;
  586. Unmapped:
  587. return HandleNullPteWrite(old);
  588. }
  589. private byte* HandleNullPteWrite(long position)
  590. {
  591. long key = position >> PageBits;
  592. MemoryAccessEventArgs e = new MemoryAccessEventArgs(position);
  593. if (_observedPages.TryGetValue(key, out IntPtr ptr))
  594. {
  595. SetPtEntry(position, (byte*)ptr);
  596. ObservedAccess?.Invoke(this, e);
  597. return (byte*)ptr + (position & PageMask);
  598. }
  599. InvalidAccess?.Invoke(this, e);
  600. throw new VmmPageFaultException(position);
  601. }
  602. private void SetPtEntries(long va, byte* ptr, long size)
  603. {
  604. long endPosition = (va + size + PageMask) & ~PageMask;
  605. while ((ulong)va < (ulong)endPosition)
  606. {
  607. SetPtEntry(va, ptr);
  608. va += PageSize;
  609. if (ptr != null)
  610. {
  611. ptr += PageSize;
  612. }
  613. }
  614. }
  615. private void SetPtEntry(long position, byte* ptr)
  616. {
  617. if (!IsValidPosition(position))
  618. {
  619. throw new ArgumentOutOfRangeException(nameof(position));
  620. }
  621. long l0 = (position >> PtLvl0Bit) & PtLvl0Mask;
  622. long l1 = (position >> PtLvl1Bit) & PtLvl1Mask;
  623. if (_pageTable[l0] == null)
  624. {
  625. byte** lvl1 = (byte**)Marshal.AllocHGlobal(PtLvl1Size * IntPtr.Size);
  626. for (int zl1 = 0; zl1 < PtLvl1Size; zl1++)
  627. {
  628. lvl1[zl1] = null;
  629. }
  630. Thread.MemoryBarrier();
  631. _pageTable[l0] = lvl1;
  632. }
  633. _pageTable[l0][l1] = ptr;
  634. }
  635. public void StartObservingRegion(long position, long size)
  636. {
  637. long endPosition = (position + size + PageMask) & ~PageMask;
  638. position &= ~PageMask;
  639. while ((ulong)position < (ulong)endPosition)
  640. {
  641. _observedPages[position >> PageBits] = (IntPtr)Translate(position);
  642. SetPtEntry(position, null);
  643. position += PageSize;
  644. }
  645. }
  646. public void StopObservingRegion(long position, long size)
  647. {
  648. long endPosition = (position + size + PageMask) & ~PageMask;
  649. while (position < endPosition)
  650. {
  651. lock (_observedPages)
  652. {
  653. if (_observedPages.TryRemove(position >> PageBits, out IntPtr ptr))
  654. {
  655. SetPtEntry(position, (byte*)ptr);
  656. }
  657. }
  658. position += PageSize;
  659. }
  660. }
  661. public bool TryGetHostAddress(long position, long size, out IntPtr ptr)
  662. {
  663. if (IsContiguous(position, size))
  664. {
  665. ptr = (IntPtr)Translate(position);
  666. return true;
  667. }
  668. ptr = IntPtr.Zero;
  669. return false;
  670. }
  671. private bool IsContiguous(long position, long size)
  672. {
  673. long endPos = position + size;
  674. position &= ~PageMask;
  675. long expectedPa = GetPhysicalAddress(position);
  676. while ((ulong)position < (ulong)endPos)
  677. {
  678. long pa = GetPhysicalAddress(position);
  679. if (pa != expectedPa)
  680. {
  681. return false;
  682. }
  683. position += PageSize;
  684. expectedPa += PageSize;
  685. }
  686. return true;
  687. }
  688. public bool IsValidPosition(long position)
  689. {
  690. return position >> (PtLvl0Bits + PtLvl1Bits + PageBits) == 0;
  691. }
  692. public void Dispose()
  693. {
  694. Dispose(true);
  695. }
  696. protected virtual void Dispose(bool disposing)
  697. {
  698. if (_pageTable == null)
  699. {
  700. return;
  701. }
  702. for (int l0 = 0; l0 < PtLvl0Size; l0++)
  703. {
  704. if (_pageTable[l0] != null)
  705. {
  706. Marshal.FreeHGlobal((IntPtr)_pageTable[l0]);
  707. }
  708. _pageTable[l0] = null;
  709. }
  710. Marshal.FreeHGlobal((IntPtr)_pageTable);
  711. _pageTable = null;
  712. }
  713. }
  714. }