MemoryManager.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835
  1. using ARMeilleure.State;
  2. using System;
  3. using System.Runtime.InteropServices;
  4. using System.Threading;
  5. using static ARMeilleure.Memory.MemoryManagement;
  6. namespace ARMeilleure.Memory
  7. {
  8. public unsafe class MemoryManager : IMemoryManager
  9. {
  10. public const int PageBits = 12;
  11. public const int PageSize = 1 << PageBits;
  12. public const int PageMask = PageSize - 1;
  13. private const long PteFlagNotModified = 1;
  14. internal const long PteFlagsMask = 7;
  15. public IntPtr Ram { get; private set; }
  16. private byte* _ramPtr;
  17. private IntPtr _pageTable;
  18. internal IntPtr PageTable => _pageTable;
  19. internal int PtLevelBits { get; }
  20. internal int PtLevelSize { get; }
  21. internal int PtLevelMask { get; }
  22. public bool HasWriteWatchSupport => MemoryManagement.HasWriteWatchSupport;
  23. public int AddressSpaceBits { get; }
  24. public long AddressSpaceSize { get; }
  25. public MemoryManager(
  26. IntPtr ram,
  27. int addressSpaceBits = 48,
  28. bool useFlatPageTable = false)
  29. {
  30. Ram = ram;
  31. _ramPtr = (byte*)ram;
  32. AddressSpaceBits = addressSpaceBits;
  33. AddressSpaceSize = 1L << addressSpaceBits;
  34. // When flat page table is requested, we use a single
  35. // array for the mappings of the entire address space.
  36. // This has better performance, but also high memory usage.
  37. // The multi level page table uses 9 bits per level, so
  38. // the memory usage is lower, but the performance is also
  39. // lower, since each address translation requires multiple reads.
  40. if (useFlatPageTable)
  41. {
  42. PtLevelBits = addressSpaceBits - PageBits;
  43. }
  44. else
  45. {
  46. PtLevelBits = 9;
  47. }
  48. PtLevelSize = 1 << PtLevelBits;
  49. PtLevelMask = PtLevelSize - 1;
  50. _pageTable = Allocate((ulong)(PtLevelSize * IntPtr.Size));
  51. }
  52. public void Map(long va, long pa, long size)
  53. {
  54. SetPtEntries(va, _ramPtr + pa, size);
  55. }
  56. public void Unmap(long position, long size)
  57. {
  58. SetPtEntries(position, null, size);
  59. }
  60. public bool IsMapped(long position)
  61. {
  62. return Translate(position) != IntPtr.Zero;
  63. }
  64. public long GetPhysicalAddress(long virtualAddress)
  65. {
  66. byte* ptr = (byte*)Translate(virtualAddress);
  67. return (long)(ptr - _ramPtr);
  68. }
  69. private IntPtr Translate(long position)
  70. {
  71. if (!IsValidPosition(position))
  72. {
  73. return IntPtr.Zero;
  74. }
  75. byte* ptr = GetPtEntry(position);
  76. ulong ptrUlong = (ulong)ptr;
  77. if ((ptrUlong & PteFlagsMask) != 0)
  78. {
  79. ptrUlong &= ~(ulong)PteFlagsMask;
  80. ptr = (byte*)ptrUlong;
  81. }
  82. return new IntPtr(ptr + (position & PageMask));
  83. }
  84. private IntPtr TranslateWrite(long position)
  85. {
  86. if (!IsValidPosition(position))
  87. {
  88. return IntPtr.Zero;
  89. }
  90. byte* ptr = GetPtEntry(position);
  91. ulong ptrUlong = (ulong)ptr;
  92. if ((ptrUlong & PteFlagsMask) != 0)
  93. {
  94. if ((ptrUlong & PteFlagNotModified) != 0)
  95. {
  96. ClearPtEntryFlag(position, PteFlagNotModified);
  97. }
  98. ptrUlong &= ~(ulong)PteFlagsMask;
  99. ptr = (byte*)ptrUlong;
  100. }
  101. return new IntPtr(ptr + (position & PageMask));
  102. }
  103. private byte* GetPtEntry(long position)
  104. {
  105. return *(byte**)GetPtPtr(position);
  106. }
  107. private void SetPtEntries(long va, byte* ptr, long size)
  108. {
  109. long endPosition = (va + size + PageMask) & ~PageMask;
  110. while ((ulong)va < (ulong)endPosition)
  111. {
  112. SetPtEntry(va, ptr);
  113. va += PageSize;
  114. if (ptr != null)
  115. {
  116. ptr += PageSize;
  117. }
  118. }
  119. }
  120. private void SetPtEntry(long position, byte* ptr)
  121. {
  122. *(byte**)GetPtPtr(position) = ptr;
  123. }
  124. private void SetPtEntryFlag(long position, long flag)
  125. {
  126. ModifyPtEntryFlag(position, flag, setFlag: true);
  127. }
  128. private void ClearPtEntryFlag(long position, long flag)
  129. {
  130. ModifyPtEntryFlag(position, flag, setFlag: false);
  131. }
  132. private void ModifyPtEntryFlag(long position, long flag, bool setFlag)
  133. {
  134. IntPtr* pt = (IntPtr*)_pageTable;
  135. while (true)
  136. {
  137. IntPtr* ptPtr = GetPtPtr(position);
  138. IntPtr old = *ptPtr;
  139. long modified = old.ToInt64();
  140. if (setFlag)
  141. {
  142. modified |= flag;
  143. }
  144. else
  145. {
  146. modified &= ~flag;
  147. }
  148. IntPtr origValue = Interlocked.CompareExchange(ref *ptPtr, new IntPtr(modified), old);
  149. if (origValue == old)
  150. {
  151. break;
  152. }
  153. }
  154. }
  155. private IntPtr* GetPtPtr(long position)
  156. {
  157. if (!IsValidPosition(position))
  158. {
  159. throw new ArgumentOutOfRangeException(nameof(position));
  160. }
  161. IntPtr nextPtr = _pageTable;
  162. IntPtr* ptePtr = null;
  163. int bit = PageBits;
  164. while (true)
  165. {
  166. long index = (position >> bit) & PtLevelMask;
  167. ptePtr = &((IntPtr*)nextPtr)[index];
  168. bit += PtLevelBits;
  169. if (bit >= AddressSpaceBits)
  170. {
  171. break;
  172. }
  173. nextPtr = *ptePtr;
  174. if (nextPtr == IntPtr.Zero)
  175. {
  176. // Entry does not yet exist, allocate a new one.
  177. IntPtr newPtr = Allocate((ulong)(PtLevelSize * IntPtr.Size));
  178. // Try to swap the current pointer (should be zero), with the allocated one.
  179. nextPtr = Interlocked.CompareExchange(ref *ptePtr, newPtr, IntPtr.Zero);
  180. // If the old pointer is not null, then another thread already has set it.
  181. if (nextPtr != IntPtr.Zero)
  182. {
  183. Free(newPtr);
  184. }
  185. else
  186. {
  187. nextPtr = newPtr;
  188. }
  189. }
  190. }
  191. return ptePtr;
  192. }
  193. public bool IsRegionModified(long position, long size)
  194. {
  195. if (!HasWriteWatchSupport)
  196. {
  197. return IsRegionModifiedFallback(position, size);
  198. }
  199. IntPtr address = Translate(position);
  200. IntPtr baseAddr = address;
  201. IntPtr expectedAddr = address;
  202. long pendingPages = 0;
  203. long pages = size / PageSize;
  204. bool modified = false;
  205. bool IsAnyPageModified()
  206. {
  207. IntPtr pendingSize = new IntPtr(pendingPages * PageSize);
  208. IntPtr[] addresses = new IntPtr[pendingPages];
  209. bool result = GetModifiedPages(baseAddr, pendingSize, addresses, out ulong count);
  210. if (result)
  211. {
  212. return count != 0;
  213. }
  214. else
  215. {
  216. return true;
  217. }
  218. }
  219. while (pages-- > 0)
  220. {
  221. if (address != expectedAddr)
  222. {
  223. modified |= IsAnyPageModified();
  224. baseAddr = address;
  225. pendingPages = 0;
  226. }
  227. expectedAddr = address + PageSize;
  228. pendingPages++;
  229. if (pages == 0)
  230. {
  231. break;
  232. }
  233. position += PageSize;
  234. address = Translate(position);
  235. }
  236. if (pendingPages != 0)
  237. {
  238. modified |= IsAnyPageModified();
  239. }
  240. return modified;
  241. }
  242. private unsafe bool IsRegionModifiedFallback(long position, long size)
  243. {
  244. long endAddr = (position + size + PageMask) & ~PageMask;
  245. bool modified = false;
  246. while ((ulong)position < (ulong)endAddr)
  247. {
  248. if (IsValidPosition(position))
  249. {
  250. byte* ptr = ((byte**)_pageTable)[position >> PageBits];
  251. ulong ptrUlong = (ulong)ptr;
  252. if ((ptrUlong & PteFlagNotModified) == 0)
  253. {
  254. modified = true;
  255. SetPtEntryFlag(position, PteFlagNotModified);
  256. }
  257. }
  258. else
  259. {
  260. modified = true;
  261. }
  262. position += PageSize;
  263. }
  264. return modified;
  265. }
  266. public bool TryGetHostAddress(long position, long size, out IntPtr ptr)
  267. {
  268. if (IsContiguous(position, size))
  269. {
  270. ptr = (IntPtr)Translate(position);
  271. return true;
  272. }
  273. ptr = IntPtr.Zero;
  274. return false;
  275. }
  276. private bool IsContiguous(long position, long size)
  277. {
  278. long endPos = position + size;
  279. position &= ~PageMask;
  280. long expectedPa = GetPhysicalAddress(position);
  281. while ((ulong)position < (ulong)endPos)
  282. {
  283. long pa = GetPhysicalAddress(position);
  284. if (pa != expectedPa)
  285. {
  286. return false;
  287. }
  288. position += PageSize;
  289. expectedPa += PageSize;
  290. }
  291. return true;
  292. }
  293. public bool IsValidPosition(long position)
  294. {
  295. return (ulong)position < (ulong)AddressSpaceSize;
  296. }
  297. internal V128 AtomicLoadInt128(long position)
  298. {
  299. if ((position & 0xf) != 0)
  300. {
  301. AbortWithAlignmentFault(position);
  302. }
  303. IntPtr ptr = TranslateWrite(position);
  304. return MemoryManagerPal.AtomicLoad128(ptr);
  305. }
  306. internal bool AtomicCompareExchangeByte(long position, byte expected, byte desired)
  307. {
  308. int* ptr = (int*)Translate(position);
  309. int currentValue = *ptr;
  310. int expected32 = (currentValue & ~byte.MaxValue) | expected;
  311. int desired32 = (currentValue & ~byte.MaxValue) | desired;
  312. return Interlocked.CompareExchange(ref *ptr, desired32, expected32) == expected32;
  313. }
  314. internal bool AtomicCompareExchangeInt16(long position, short expected, short desired)
  315. {
  316. if ((position & 1) != 0)
  317. {
  318. AbortWithAlignmentFault(position);
  319. }
  320. int* ptr = (int*)Translate(position);
  321. int currentValue = *ptr;
  322. int expected32 = (currentValue & ~ushort.MaxValue) | (ushort)expected;
  323. int desired32 = (currentValue & ~ushort.MaxValue) | (ushort)desired;
  324. return Interlocked.CompareExchange(ref *ptr, desired32, expected32) == expected32;
  325. }
  326. public bool AtomicCompareExchangeInt32(long position, int expected, int desired)
  327. {
  328. if ((position & 3) != 0)
  329. {
  330. AbortWithAlignmentFault(position);
  331. }
  332. int* ptr = (int*)TranslateWrite(position);
  333. return Interlocked.CompareExchange(ref *ptr, desired, expected) == expected;
  334. }
  335. internal bool AtomicCompareExchangeInt64(long position, long expected, long desired)
  336. {
  337. if ((position & 7) != 0)
  338. {
  339. AbortWithAlignmentFault(position);
  340. }
  341. long* ptr = (long*)TranslateWrite(position);
  342. return Interlocked.CompareExchange(ref *ptr, desired, expected) == expected;
  343. }
  344. internal bool AtomicCompareExchangeInt128(long position, V128 expected, V128 desired)
  345. {
  346. if ((position & 0xf) != 0)
  347. {
  348. AbortWithAlignmentFault(position);
  349. }
  350. IntPtr ptr = TranslateWrite(position);
  351. return MemoryManagerPal.CompareAndSwap128(ptr, expected, desired) == expected;
  352. }
  353. public int AtomicIncrementInt32(long position)
  354. {
  355. if ((position & 3) != 0)
  356. {
  357. AbortWithAlignmentFault(position);
  358. }
  359. int* ptr = (int*)TranslateWrite(position);
  360. return Interlocked.Increment(ref *ptr);
  361. }
  362. public int AtomicDecrementInt32(long position)
  363. {
  364. if ((position & 3) != 0)
  365. {
  366. AbortWithAlignmentFault(position);
  367. }
  368. int* ptr = (int*)TranslateWrite(position);
  369. return Interlocked.Decrement(ref *ptr);
  370. }
  371. private void AbortWithAlignmentFault(long position)
  372. {
  373. // TODO: Abort mode and exception support on the CPU.
  374. throw new InvalidOperationException($"Tried to compare exchange a misaligned address 0x{position:X16}.");
  375. }
  376. public sbyte ReadSByte(long position)
  377. {
  378. return (sbyte)ReadByte(position);
  379. }
  380. public short ReadInt16(long position)
  381. {
  382. return (short)ReadUInt16(position);
  383. }
  384. public int ReadInt32(long position)
  385. {
  386. return (int)ReadUInt32(position);
  387. }
  388. public long ReadInt64(long position)
  389. {
  390. return (long)ReadUInt64(position);
  391. }
  392. public byte ReadByte(long position)
  393. {
  394. return *((byte*)Translate(position));
  395. }
  396. public ushort ReadUInt16(long position)
  397. {
  398. if ((position & 1) == 0)
  399. {
  400. return *((ushort*)Translate(position));
  401. }
  402. else
  403. {
  404. return (ushort)(ReadByte(position + 0) << 0 |
  405. ReadByte(position + 1) << 8);
  406. }
  407. }
  408. public uint ReadUInt32(long position)
  409. {
  410. if ((position & 3) == 0)
  411. {
  412. return *((uint*)Translate(position));
  413. }
  414. else
  415. {
  416. return (uint)(ReadUInt16(position + 0) << 0 |
  417. ReadUInt16(position + 2) << 16);
  418. }
  419. }
  420. public ulong ReadUInt64(long position)
  421. {
  422. if ((position & 7) == 0)
  423. {
  424. return *((ulong*)Translate(position));
  425. }
  426. else
  427. {
  428. return (ulong)ReadUInt32(position + 0) << 0 |
  429. (ulong)ReadUInt32(position + 4) << 32;
  430. }
  431. }
  432. public V128 ReadVector128(long position)
  433. {
  434. return new V128(ReadUInt64(position), ReadUInt64(position + 8));
  435. }
  436. public byte[] ReadBytes(long position, long size)
  437. {
  438. long endAddr = position + size;
  439. if ((ulong)size > int.MaxValue)
  440. {
  441. throw new ArgumentOutOfRangeException(nameof(size));
  442. }
  443. if ((ulong)endAddr < (ulong)position)
  444. {
  445. throw new ArgumentOutOfRangeException(nameof(position));
  446. }
  447. byte[] data = new byte[size];
  448. int offset = 0;
  449. while ((ulong)position < (ulong)endAddr)
  450. {
  451. long pageLimit = (position + PageSize) & ~(long)PageMask;
  452. if ((ulong)pageLimit > (ulong)endAddr)
  453. {
  454. pageLimit = endAddr;
  455. }
  456. int copySize = (int)(pageLimit - position);
  457. Marshal.Copy(Translate(position), data, offset, copySize);
  458. position += copySize;
  459. offset += copySize;
  460. }
  461. return data;
  462. }
  463. public void ReadBytes(long position, byte[] data, int startIndex, int size)
  464. {
  465. // Note: This will be moved later.
  466. long endAddr = position + size;
  467. if ((ulong)size > int.MaxValue)
  468. {
  469. throw new ArgumentOutOfRangeException(nameof(size));
  470. }
  471. if ((ulong)endAddr < (ulong)position)
  472. {
  473. throw new ArgumentOutOfRangeException(nameof(position));
  474. }
  475. int offset = startIndex;
  476. while ((ulong)position < (ulong)endAddr)
  477. {
  478. long pageLimit = (position + PageSize) & ~(long)PageMask;
  479. if ((ulong)pageLimit > (ulong)endAddr)
  480. {
  481. pageLimit = endAddr;
  482. }
  483. int copySize = (int)(pageLimit - position);
  484. Marshal.Copy(Translate(position), data, offset, copySize);
  485. position += copySize;
  486. offset += copySize;
  487. }
  488. }
  489. public void WriteSByte(long position, sbyte value)
  490. {
  491. WriteByte(position, (byte)value);
  492. }
  493. public void WriteInt16(long position, short value)
  494. {
  495. WriteUInt16(position, (ushort)value);
  496. }
  497. public void WriteInt32(long position, int value)
  498. {
  499. WriteUInt32(position, (uint)value);
  500. }
  501. public void WriteInt64(long position, long value)
  502. {
  503. WriteUInt64(position, (ulong)value);
  504. }
  505. public void WriteByte(long position, byte value)
  506. {
  507. *((byte*)TranslateWrite(position)) = value;
  508. }
  509. public void WriteUInt16(long position, ushort value)
  510. {
  511. if ((position & 1) == 0)
  512. {
  513. *((ushort*)TranslateWrite(position)) = value;
  514. }
  515. else
  516. {
  517. WriteByte(position + 0, (byte)(value >> 0));
  518. WriteByte(position + 1, (byte)(value >> 8));
  519. }
  520. }
  521. public void WriteUInt32(long position, uint value)
  522. {
  523. if ((position & 3) == 0)
  524. {
  525. *((uint*)TranslateWrite(position)) = value;
  526. }
  527. else
  528. {
  529. WriteUInt16(position + 0, (ushort)(value >> 0));
  530. WriteUInt16(position + 2, (ushort)(value >> 16));
  531. }
  532. }
  533. public void WriteUInt64(long position, ulong value)
  534. {
  535. if ((position & 7) == 0)
  536. {
  537. *((ulong*)TranslateWrite(position)) = value;
  538. }
  539. else
  540. {
  541. WriteUInt32(position + 0, (uint)(value >> 0));
  542. WriteUInt32(position + 4, (uint)(value >> 32));
  543. }
  544. }
  545. public void WriteVector128(long position, V128 value)
  546. {
  547. WriteUInt64(position + 0, value.GetUInt64(0));
  548. WriteUInt64(position + 8, value.GetUInt64(1));
  549. }
  550. public void WriteBytes(long position, byte[] data)
  551. {
  552. long endAddr = position + data.Length;
  553. if ((ulong)endAddr < (ulong)position)
  554. {
  555. throw new ArgumentOutOfRangeException(nameof(position));
  556. }
  557. int offset = 0;
  558. while ((ulong)position < (ulong)endAddr)
  559. {
  560. long pageLimit = (position + PageSize) & ~(long)PageMask;
  561. if ((ulong)pageLimit > (ulong)endAddr)
  562. {
  563. pageLimit = endAddr;
  564. }
  565. int copySize = (int)(pageLimit - position);
  566. Marshal.Copy(data, offset, TranslateWrite(position), copySize);
  567. position += copySize;
  568. offset += copySize;
  569. }
  570. }
  571. public void WriteBytes(long position, byte[] data, int startIndex, int size)
  572. {
  573. // Note: This will be moved later.
  574. long endAddr = position + size;
  575. if ((ulong)endAddr < (ulong)position)
  576. {
  577. throw new ArgumentOutOfRangeException(nameof(position));
  578. }
  579. int offset = startIndex;
  580. while ((ulong)position < (ulong)endAddr)
  581. {
  582. long pageLimit = (position + PageSize) & ~(long)PageMask;
  583. if ((ulong)pageLimit > (ulong)endAddr)
  584. {
  585. pageLimit = endAddr;
  586. }
  587. int copySize = (int)(pageLimit - position);
  588. Marshal.Copy(data, offset, Translate(position), copySize);
  589. position += copySize;
  590. offset += copySize;
  591. }
  592. }
  593. public void CopyBytes(long src, long dst, long size)
  594. {
  595. // Note: This will be moved later.
  596. if (IsContiguous(src, size) &&
  597. IsContiguous(dst, size))
  598. {
  599. byte* srcPtr = (byte*)Translate(src);
  600. byte* dstPtr = (byte*)Translate(dst);
  601. Buffer.MemoryCopy(srcPtr, dstPtr, size, size);
  602. }
  603. else
  604. {
  605. WriteBytes(dst, ReadBytes(src, size));
  606. }
  607. }
  608. public void Dispose()
  609. {
  610. Dispose(true);
  611. }
  612. protected virtual void Dispose(bool disposing)
  613. {
  614. IntPtr ptr = Interlocked.Exchange(ref _pageTable, IntPtr.Zero);
  615. if (ptr != IntPtr.Zero)
  616. {
  617. FreePageTableEntry(ptr, PageBits);
  618. }
  619. }
  620. private void FreePageTableEntry(IntPtr ptr, int levelBitEnd)
  621. {
  622. levelBitEnd += PtLevelBits;
  623. if (levelBitEnd >= AddressSpaceBits)
  624. {
  625. Free(ptr);
  626. return;
  627. }
  628. for (int index = 0; index < PtLevelSize; index++)
  629. {
  630. IntPtr ptePtr = ((IntPtr*)ptr)[index];
  631. if (ptePtr != IntPtr.Zero)
  632. {
  633. FreePageTableEntry(ptePtr, levelBitEnd);
  634. }
  635. }
  636. Free(ptr);
  637. }
  638. }
  639. }