| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470 |
- using Ryujinx.Common;
- using Ryujinx.Common.Collections;
- using Ryujinx.Memory;
- using System;
- namespace Ryujinx.Cpu
- {
- class AddressSpace : IDisposable
- {
- private const ulong PageSize = 0x1000;
- private const int DefaultBlockAlignment = 1 << 20;
- private enum MappingType : byte
- {
- None,
- Private,
- Shared
- }
- private class Mapping : IntrusiveRedBlackTreeNode<Mapping>, IComparable<Mapping>
- {
- public ulong Address { get; private set; }
- public ulong Size { get; private set; }
- public ulong EndAddress => Address + Size;
- public MappingType Type { get; private set; }
- public Mapping(ulong address, ulong size, MappingType type)
- {
- Address = address;
- Size = size;
- Type = type;
- }
- public Mapping Split(ulong splitAddress)
- {
- ulong leftSize = splitAddress - Address;
- ulong rightSize = EndAddress - splitAddress;
- Mapping left = new Mapping(Address, leftSize, Type);
- Address = splitAddress;
- Size = rightSize;
- return left;
- }
- public void UpdateState(MappingType newType)
- {
- Type = newType;
- }
- public void Extend(ulong sizeDelta)
- {
- Size += sizeDelta;
- }
- public int CompareTo(Mapping other)
- {
- if (Address < other.Address)
- {
- return -1;
- }
- else if (Address <= other.EndAddress - 1UL)
- {
- return 0;
- }
- else
- {
- return 1;
- }
- }
- }
- private class PrivateMapping : IntrusiveRedBlackTreeNode<PrivateMapping>, IComparable<PrivateMapping>
- {
- public ulong Address { get; private set; }
- public ulong Size { get; private set; }
- public ulong EndAddress => Address + Size;
- public PrivateMemoryAllocation PrivateAllocation { get; private set; }
- public PrivateMapping(ulong address, ulong size, PrivateMemoryAllocation privateAllocation)
- {
- Address = address;
- Size = size;
- PrivateAllocation = privateAllocation;
- }
- public PrivateMapping Split(ulong splitAddress)
- {
- ulong leftSize = splitAddress - Address;
- ulong rightSize = EndAddress - splitAddress;
- (var leftAllocation, PrivateAllocation) = PrivateAllocation.Split(leftSize);
- PrivateMapping left = new PrivateMapping(Address, leftSize, leftAllocation);
- Address = splitAddress;
- Size = rightSize;
- return left;
- }
- public void Map(MemoryBlock baseBlock, MemoryBlock mirrorBlock, PrivateMemoryAllocation newAllocation)
- {
- baseBlock.MapView(newAllocation.Memory, newAllocation.Offset, Address, Size);
- mirrorBlock.MapView(newAllocation.Memory, newAllocation.Offset, Address, Size);
- PrivateAllocation = newAllocation;
- }
- public void Unmap(MemoryBlock baseBlock, MemoryBlock mirrorBlock)
- {
- if (PrivateAllocation.IsValid)
- {
- baseBlock.UnmapView(PrivateAllocation.Memory, Address, Size);
- mirrorBlock.UnmapView(PrivateAllocation.Memory, Address, Size);
- PrivateAllocation.Dispose();
- }
- PrivateAllocation = default;
- }
- public void Extend(ulong sizeDelta)
- {
- Size += sizeDelta;
- }
- public int CompareTo(PrivateMapping other)
- {
- if (Address < other.Address)
- {
- return -1;
- }
- else if (Address <= other.EndAddress - 1UL)
- {
- return 0;
- }
- else
- {
- return 1;
- }
- }
- }
- private readonly MemoryBlock _backingMemory;
- private readonly PrivateMemoryAllocator _privateMemoryAllocator;
- private readonly IntrusiveRedBlackTree<Mapping> _mappingTree;
- private readonly IntrusiveRedBlackTree<PrivateMapping> _privateTree;
- private readonly object _treeLock;
- private readonly bool _supports4KBPages;
- public MemoryBlock Base { get; }
- public MemoryBlock Mirror { get; }
- public AddressSpace(MemoryBlock backingMemory, ulong asSize, bool supports4KBPages)
- {
- if (!supports4KBPages)
- {
- _privateMemoryAllocator = new PrivateMemoryAllocator(DefaultBlockAlignment, MemoryAllocationFlags.Mirrorable | MemoryAllocationFlags.NoMap);
- _mappingTree = new IntrusiveRedBlackTree<Mapping>();
- _privateTree = new IntrusiveRedBlackTree<PrivateMapping>();
- _treeLock = new object();
- _mappingTree.Add(new Mapping(0UL, asSize, MappingType.None));
- _privateTree.Add(new PrivateMapping(0UL, asSize, default));
- }
- _backingMemory = backingMemory;
- _supports4KBPages = supports4KBPages;
- MemoryAllocationFlags asFlags = MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible;
- Base = new MemoryBlock(asSize, asFlags);
- Mirror = new MemoryBlock(asSize, asFlags);
- }
- public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
- {
- if (_supports4KBPages)
- {
- Base.MapView(_backingMemory, pa, va, size);
- Mirror.MapView(_backingMemory, pa, va, size);
- return;
- }
- lock (_treeLock)
- {
- ulong alignment = MemoryBlock.GetPageSize();
- bool isAligned = ((va | pa | size) & (alignment - 1)) == 0;
- if (flags.HasFlag(MemoryMapFlags.Private) && !isAligned)
- {
- Update(va, pa, size, MappingType.Private);
- }
- else
- {
- // The update method assumes that shared mappings are already aligned.
- if (!flags.HasFlag(MemoryMapFlags.Private))
- {
- if ((va & (alignment - 1)) != (pa & (alignment - 1)))
- {
- throw new InvalidMemoryRegionException($"Virtual address 0x{va:X} and physical address 0x{pa:X} are misaligned and can't be aligned.");
- }
- ulong endAddress = va + size;
- va = BitUtils.AlignDown(va, alignment);
- pa = BitUtils.AlignDown(pa, alignment);
- size = BitUtils.AlignUp(endAddress, alignment) - va;
- }
- Update(va, pa, size, MappingType.Shared);
- }
- }
- }
- public void Unmap(ulong va, ulong size)
- {
- if (_supports4KBPages)
- {
- Base.UnmapView(_backingMemory, va, size);
- Mirror.UnmapView(_backingMemory, va, size);
- return;
- }
- lock (_treeLock)
- {
- Update(va, 0UL, size, MappingType.None);
- }
- }
- private void Update(ulong va, ulong pa, ulong size, MappingType type)
- {
- Mapping map = _mappingTree.GetNode(new Mapping(va, 1UL, MappingType.None));
- Update(map, va, pa, size, type);
- }
- private Mapping Update(Mapping map, ulong va, ulong pa, ulong size, MappingType type)
- {
- ulong endAddress = va + size;
- for (; map != null; map = map.Successor)
- {
- if (map.Address < va)
- {
- _mappingTree.Add(map.Split(va));
- }
- if (map.EndAddress > endAddress)
- {
- Mapping newMap = map.Split(endAddress);
- _mappingTree.Add(newMap);
- map = newMap;
- }
- switch (type)
- {
- case MappingType.None:
- if (map.Type == MappingType.Shared)
- {
- ulong startOffset = map.Address - va;
- ulong mapVa = va + startOffset;
- ulong mapSize = Math.Min(size - startOffset, map.Size);
- ulong mapEndAddress = mapVa + mapSize;
- ulong alignment = MemoryBlock.GetPageSize();
- mapVa = BitUtils.AlignDown(mapVa, alignment);
- mapEndAddress = BitUtils.AlignUp(mapEndAddress, alignment);
- mapSize = mapEndAddress - mapVa;
- Base.UnmapView(_backingMemory, mapVa, mapSize);
- Mirror.UnmapView(_backingMemory, mapVa, mapSize);
- }
- else
- {
- UnmapPrivate(va, size);
- }
- break;
- case MappingType.Private:
- if (map.Type == MappingType.Shared)
- {
- 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}.");
- }
- else
- {
- MapPrivate(va, size);
- }
- break;
- case MappingType.Shared:
- if (map.Type != MappingType.None)
- {
- 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}.");
- }
- else
- {
- ulong startOffset = map.Address - va;
- ulong mapPa = pa + startOffset;
- ulong mapVa = va + startOffset;
- ulong mapSize = Math.Min(size - startOffset, map.Size);
- Base.MapView(_backingMemory, mapPa, mapVa, mapSize);
- Mirror.MapView(_backingMemory, mapPa, mapVa, mapSize);
- }
- break;
- }
- map.UpdateState(type);
- map = TryCoalesce(map);
- if (map.EndAddress >= endAddress)
- {
- break;
- }
- }
- return map;
- }
- private Mapping TryCoalesce(Mapping map)
- {
- Mapping previousMap = map.Predecessor;
- Mapping nextMap = map.Successor;
- if (previousMap != null && CanCoalesce(previousMap, map))
- {
- previousMap.Extend(map.Size);
- _mappingTree.Remove(map);
- map = previousMap;
- }
- if (nextMap != null && CanCoalesce(map, nextMap))
- {
- map.Extend(nextMap.Size);
- _mappingTree.Remove(nextMap);
- }
- return map;
- }
- private static bool CanCoalesce(Mapping left, Mapping right)
- {
- return left.Type == right.Type;
- }
- private void MapPrivate(ulong va, ulong size)
- {
- ulong endAddress = va + size;
- ulong alignment = MemoryBlock.GetPageSize();
- // Expand the range outwards based on page size to ensure that at least the requested region is mapped.
- ulong vaAligned = BitUtils.AlignDown(va, alignment);
- ulong endAddressAligned = BitUtils.AlignUp(endAddress, alignment);
- ulong sizeAligned = endAddressAligned - vaAligned;
- PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default));
- for (; map != null; map = map.Successor)
- {
- if (!map.PrivateAllocation.IsValid)
- {
- if (map.Address < vaAligned)
- {
- _privateTree.Add(map.Split(vaAligned));
- }
- if (map.EndAddress > endAddressAligned)
- {
- PrivateMapping newMap = map.Split(endAddressAligned);
- _privateTree.Add(newMap);
- map = newMap;
- }
- map.Map(Base, Mirror, _privateMemoryAllocator.Allocate(map.Size, MemoryBlock.GetPageSize()));
- }
- if (map.EndAddress >= endAddressAligned)
- {
- break;
- }
- }
- }
- private void UnmapPrivate(ulong va, ulong size)
- {
- ulong endAddress = va + size;
- ulong alignment = MemoryBlock.GetPageSize();
- // Shrink the range inwards based on page size to ensure we won't unmap memory that might be still in use.
- ulong vaAligned = BitUtils.AlignUp(va, alignment);
- ulong endAddressAligned = BitUtils.AlignDown(endAddress, alignment);
- if (endAddressAligned <= vaAligned)
- {
- return;
- }
- ulong alignedSize = endAddressAligned - vaAligned;
- PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default));
- for (; map != null; map = map.Successor)
- {
- if (map.PrivateAllocation.IsValid)
- {
- if (map.Address < vaAligned)
- {
- _privateTree.Add(map.Split(vaAligned));
- }
- if (map.EndAddress > endAddressAligned)
- {
- PrivateMapping newMap = map.Split(endAddressAligned);
- _privateTree.Add(newMap);
- map = newMap;
- }
- map.Unmap(Base, Mirror);
- map = TryCoalesce(map);
- }
- if (map.EndAddress >= endAddressAligned)
- {
- break;
- }
- }
- }
- private PrivateMapping TryCoalesce(PrivateMapping map)
- {
- PrivateMapping previousMap = map.Predecessor;
- PrivateMapping nextMap = map.Successor;
- if (previousMap != null && CanCoalesce(previousMap, map))
- {
- previousMap.Extend(map.Size);
- _privateTree.Remove(map);
- map = previousMap;
- }
- if (nextMap != null && CanCoalesce(map, nextMap))
- {
- map.Extend(nextMap.Size);
- _privateTree.Remove(nextMap);
- }
- return map;
- }
- private static bool CanCoalesce(PrivateMapping left, PrivateMapping right)
- {
- return !left.PrivateAllocation.IsValid && !right.PrivateAllocation.IsValid;
- }
- public void Dispose()
- {
- _privateMemoryAllocator?.Dispose();
- Base.Dispose();
- Mirror.Dispose();
- }
- }
- }
|