AddressSpace.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. using Ryujinx.Common;
  2. using Ryujinx.Common.Collections;
  3. using Ryujinx.Memory;
  4. using System;
  5. namespace Ryujinx.Cpu
  6. {
  7. class AddressSpace : IDisposable
  8. {
  9. private const ulong PageSize = 0x1000;
  10. private const int DefaultBlockAlignment = 1 << 20;
  11. private enum MappingType : byte
  12. {
  13. None,
  14. Private,
  15. Shared
  16. }
  17. private class Mapping : IntrusiveRedBlackTreeNode<Mapping>, IComparable<Mapping>
  18. {
  19. public ulong Address { get; private set; }
  20. public ulong Size { get; private set; }
  21. public ulong EndAddress => Address + Size;
  22. public MappingType Type { get; private set; }
  23. public Mapping(ulong address, ulong size, MappingType type)
  24. {
  25. Address = address;
  26. Size = size;
  27. Type = type;
  28. }
  29. public Mapping Split(ulong splitAddress)
  30. {
  31. ulong leftSize = splitAddress - Address;
  32. ulong rightSize = EndAddress - splitAddress;
  33. Mapping left = new Mapping(Address, leftSize, Type);
  34. Address = splitAddress;
  35. Size = rightSize;
  36. return left;
  37. }
  38. public void UpdateState(MappingType newType)
  39. {
  40. Type = newType;
  41. }
  42. public void Extend(ulong sizeDelta)
  43. {
  44. Size += sizeDelta;
  45. }
  46. public int CompareTo(Mapping other)
  47. {
  48. if (Address < other.Address)
  49. {
  50. return -1;
  51. }
  52. else if (Address <= other.EndAddress - 1UL)
  53. {
  54. return 0;
  55. }
  56. else
  57. {
  58. return 1;
  59. }
  60. }
  61. }
  62. private class PrivateMapping : IntrusiveRedBlackTreeNode<PrivateMapping>, IComparable<PrivateMapping>
  63. {
  64. public ulong Address { get; private set; }
  65. public ulong Size { get; private set; }
  66. public ulong EndAddress => Address + Size;
  67. public PrivateMemoryAllocation PrivateAllocation { get; private set; }
  68. public PrivateMapping(ulong address, ulong size, PrivateMemoryAllocation privateAllocation)
  69. {
  70. Address = address;
  71. Size = size;
  72. PrivateAllocation = privateAllocation;
  73. }
  74. public PrivateMapping Split(ulong splitAddress)
  75. {
  76. ulong leftSize = splitAddress - Address;
  77. ulong rightSize = EndAddress - splitAddress;
  78. (var leftAllocation, PrivateAllocation) = PrivateAllocation.Split(leftSize);
  79. PrivateMapping left = new PrivateMapping(Address, leftSize, leftAllocation);
  80. Address = splitAddress;
  81. Size = rightSize;
  82. return left;
  83. }
  84. public void Map(MemoryBlock baseBlock, MemoryBlock mirrorBlock, PrivateMemoryAllocation newAllocation)
  85. {
  86. baseBlock.MapView(newAllocation.Memory, newAllocation.Offset, Address, Size);
  87. mirrorBlock.MapView(newAllocation.Memory, newAllocation.Offset, Address, Size);
  88. PrivateAllocation = newAllocation;
  89. }
  90. public void Unmap(MemoryBlock baseBlock, MemoryBlock mirrorBlock)
  91. {
  92. if (PrivateAllocation.IsValid)
  93. {
  94. baseBlock.UnmapView(PrivateAllocation.Memory, Address, Size);
  95. mirrorBlock.UnmapView(PrivateAllocation.Memory, Address, Size);
  96. PrivateAllocation.Dispose();
  97. }
  98. PrivateAllocation = default;
  99. }
  100. public void Extend(ulong sizeDelta)
  101. {
  102. Size += sizeDelta;
  103. }
  104. public int CompareTo(PrivateMapping other)
  105. {
  106. if (Address < other.Address)
  107. {
  108. return -1;
  109. }
  110. else if (Address <= other.EndAddress - 1UL)
  111. {
  112. return 0;
  113. }
  114. else
  115. {
  116. return 1;
  117. }
  118. }
  119. }
  120. private readonly MemoryBlock _backingMemory;
  121. private readonly PrivateMemoryAllocator _privateMemoryAllocator;
  122. private readonly IntrusiveRedBlackTree<Mapping> _mappingTree;
  123. private readonly IntrusiveRedBlackTree<PrivateMapping> _privateTree;
  124. private readonly object _treeLock;
  125. private readonly bool _supports4KBPages;
  126. public MemoryBlock Base { get; }
  127. public MemoryBlock Mirror { get; }
  128. public AddressSpace(MemoryBlock backingMemory, ulong asSize, bool supports4KBPages)
  129. {
  130. if (!supports4KBPages)
  131. {
  132. _privateMemoryAllocator = new PrivateMemoryAllocator(DefaultBlockAlignment, MemoryAllocationFlags.Mirrorable | MemoryAllocationFlags.NoMap);
  133. _mappingTree = new IntrusiveRedBlackTree<Mapping>();
  134. _privateTree = new IntrusiveRedBlackTree<PrivateMapping>();
  135. _treeLock = new object();
  136. _mappingTree.Add(new Mapping(0UL, asSize, MappingType.None));
  137. _privateTree.Add(new PrivateMapping(0UL, asSize, default));
  138. }
  139. _backingMemory = backingMemory;
  140. _supports4KBPages = supports4KBPages;
  141. MemoryAllocationFlags asFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible;
  142. Base = new MemoryBlock(asSize, asFlags);
  143. Mirror = new MemoryBlock(asSize, asFlags);
  144. }
  145. public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
  146. {
  147. if (_supports4KBPages)
  148. {
  149. Base.MapView(_backingMemory, pa, va, size);
  150. Mirror.MapView(_backingMemory, pa, va, size);
  151. return;
  152. }
  153. lock (_treeLock)
  154. {
  155. ulong alignment = MemoryBlock.GetPageSize();
  156. bool isAligned = ((va | pa | size) & (alignment - 1)) == 0;
  157. if (flags.HasFlag(MemoryMapFlags.Private) && !isAligned)
  158. {
  159. Update(va, pa, size, MappingType.Private);
  160. }
  161. else
  162. {
  163. // The update method assumes that shared mappings are already aligned.
  164. if (!flags.HasFlag(MemoryMapFlags.Private))
  165. {
  166. if ((va & (alignment - 1)) != (pa & (alignment - 1)))
  167. {
  168. throw new InvalidMemoryRegionException($"Virtual address 0x{va:X} and physical address 0x{pa:X} are misaligned and can't be aligned.");
  169. }
  170. ulong endAddress = va + size;
  171. va = BitUtils.AlignDown(va, alignment);
  172. pa = BitUtils.AlignDown(pa, alignment);
  173. size = BitUtils.AlignUp(endAddress, alignment) - va;
  174. }
  175. Update(va, pa, size, MappingType.Shared);
  176. }
  177. }
  178. }
  179. public void Unmap(ulong va, ulong size)
  180. {
  181. if (_supports4KBPages)
  182. {
  183. Base.UnmapView(_backingMemory, va, size);
  184. Mirror.UnmapView(_backingMemory, va, size);
  185. return;
  186. }
  187. lock (_treeLock)
  188. {
  189. Update(va, 0UL, size, MappingType.None);
  190. }
  191. }
  192. private void Update(ulong va, ulong pa, ulong size, MappingType type)
  193. {
  194. Mapping map = _mappingTree.GetNode(new Mapping(va, 1UL, MappingType.None));
  195. Update(map, va, pa, size, type);
  196. }
  197. private Mapping Update(Mapping map, ulong va, ulong pa, ulong size, MappingType type)
  198. {
  199. ulong endAddress = va + size;
  200. for (; map != null; map = map.Successor)
  201. {
  202. if (map.Address < va)
  203. {
  204. _mappingTree.Add(map.Split(va));
  205. }
  206. if (map.EndAddress > endAddress)
  207. {
  208. Mapping newMap = map.Split(endAddress);
  209. _mappingTree.Add(newMap);
  210. map = newMap;
  211. }
  212. switch (type)
  213. {
  214. case MappingType.None:
  215. if (map.Type == MappingType.Shared)
  216. {
  217. ulong startOffset = map.Address - va;
  218. ulong mapVa = va + startOffset;
  219. ulong mapSize = Math.Min(size - startOffset, map.Size);
  220. ulong mapEndAddress = mapVa + mapSize;
  221. ulong alignment = MemoryBlock.GetPageSize();
  222. mapVa = BitUtils.AlignDown(mapVa, alignment);
  223. mapEndAddress = BitUtils.AlignUp(mapEndAddress, alignment);
  224. mapSize = mapEndAddress - mapVa;
  225. Base.UnmapView(_backingMemory, mapVa, mapSize);
  226. Mirror.UnmapView(_backingMemory, mapVa, mapSize);
  227. }
  228. else
  229. {
  230. UnmapPrivate(va, size);
  231. }
  232. break;
  233. case MappingType.Private:
  234. if (map.Type == MappingType.Shared)
  235. {
  236. throw new InvalidMemoryRegionException($"Private mapping request at 0x{va:X} with size 0x{size:X} overlaps shared mapping at 0x{map.Address:X} with size 0x{map.Size:X}.");
  237. }
  238. else
  239. {
  240. MapPrivate(va, size);
  241. }
  242. break;
  243. case MappingType.Shared:
  244. if (map.Type != MappingType.None)
  245. {
  246. throw new InvalidMemoryRegionException($"Shared mapping request at 0x{va:X} with size 0x{size:X} overlaps mapping at 0x{map.Address:X} with size 0x{map.Size:X}.");
  247. }
  248. else
  249. {
  250. ulong startOffset = map.Address - va;
  251. ulong mapPa = pa + startOffset;
  252. ulong mapVa = va + startOffset;
  253. ulong mapSize = Math.Min(size - startOffset, map.Size);
  254. Base.MapView(_backingMemory, mapPa, mapVa, mapSize);
  255. Mirror.MapView(_backingMemory, mapPa, mapVa, mapSize);
  256. }
  257. break;
  258. }
  259. map.UpdateState(type);
  260. map = TryCoalesce(map);
  261. if (map.EndAddress >= endAddress)
  262. {
  263. break;
  264. }
  265. }
  266. return map;
  267. }
  268. private Mapping TryCoalesce(Mapping map)
  269. {
  270. Mapping previousMap = map.Predecessor;
  271. Mapping nextMap = map.Successor;
  272. if (previousMap != null && CanCoalesce(previousMap, map))
  273. {
  274. previousMap.Extend(map.Size);
  275. _mappingTree.Remove(map);
  276. map = previousMap;
  277. }
  278. if (nextMap != null && CanCoalesce(map, nextMap))
  279. {
  280. map.Extend(nextMap.Size);
  281. _mappingTree.Remove(nextMap);
  282. }
  283. return map;
  284. }
  285. private static bool CanCoalesce(Mapping left, Mapping right)
  286. {
  287. return left.Type == right.Type;
  288. }
  289. private void MapPrivate(ulong va, ulong size)
  290. {
  291. ulong endAddress = va + size;
  292. ulong alignment = MemoryBlock.GetPageSize();
  293. // Expand the range outwards based on page size to ensure that at least the requested region is mapped.
  294. ulong vaAligned = BitUtils.AlignDown(va, alignment);
  295. ulong endAddressAligned = BitUtils.AlignUp(endAddress, alignment);
  296. ulong sizeAligned = endAddressAligned - vaAligned;
  297. PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default));
  298. for (; map != null; map = map.Successor)
  299. {
  300. if (!map.PrivateAllocation.IsValid)
  301. {
  302. if (map.Address < vaAligned)
  303. {
  304. _privateTree.Add(map.Split(vaAligned));
  305. }
  306. if (map.EndAddress > endAddressAligned)
  307. {
  308. PrivateMapping newMap = map.Split(endAddressAligned);
  309. _privateTree.Add(newMap);
  310. map = newMap;
  311. }
  312. map.Map(Base, Mirror, _privateMemoryAllocator.Allocate(map.Size, MemoryBlock.GetPageSize()));
  313. }
  314. if (map.EndAddress >= endAddressAligned)
  315. {
  316. break;
  317. }
  318. }
  319. }
  320. private void UnmapPrivate(ulong va, ulong size)
  321. {
  322. ulong endAddress = va + size;
  323. ulong alignment = MemoryBlock.GetPageSize();
  324. // Shrink the range inwards based on page size to ensure we won't unmap memory that might be still in use.
  325. ulong vaAligned = BitUtils.AlignUp(va, alignment);
  326. ulong endAddressAligned = BitUtils.AlignDown(endAddress, alignment);
  327. if (endAddressAligned <= vaAligned)
  328. {
  329. return;
  330. }
  331. ulong alignedSize = endAddressAligned - vaAligned;
  332. PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default));
  333. for (; map != null; map = map.Successor)
  334. {
  335. if (map.PrivateAllocation.IsValid)
  336. {
  337. if (map.Address < vaAligned)
  338. {
  339. _privateTree.Add(map.Split(vaAligned));
  340. }
  341. if (map.EndAddress > endAddressAligned)
  342. {
  343. PrivateMapping newMap = map.Split(endAddressAligned);
  344. _privateTree.Add(newMap);
  345. map = newMap;
  346. }
  347. map.Unmap(Base, Mirror);
  348. map = TryCoalesce(map);
  349. }
  350. if (map.EndAddress >= endAddressAligned)
  351. {
  352. break;
  353. }
  354. }
  355. }
  356. private PrivateMapping TryCoalesce(PrivateMapping map)
  357. {
  358. PrivateMapping previousMap = map.Predecessor;
  359. PrivateMapping nextMap = map.Successor;
  360. if (previousMap != null && CanCoalesce(previousMap, map))
  361. {
  362. previousMap.Extend(map.Size);
  363. _privateTree.Remove(map);
  364. map = previousMap;
  365. }
  366. if (nextMap != null && CanCoalesce(map, nextMap))
  367. {
  368. map.Extend(nextMap.Size);
  369. _privateTree.Remove(nextMap);
  370. }
  371. return map;
  372. }
  373. private static bool CanCoalesce(PrivateMapping left, PrivateMapping right)
  374. {
  375. return !left.PrivateAllocation.IsValid && !right.PrivateAllocation.IsValid;
  376. }
  377. public void Dispose()
  378. {
  379. _privateMemoryAllocator?.Dispose();
  380. Base.Dispose();
  381. Mirror.Dispose();
  382. }
  383. }
  384. }