diff options
Diffstat (limited to 'src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitioned.cs')
| -rw-r--r-- | src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitioned.cs | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitioned.cs b/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitioned.cs new file mode 100644 index 00000000..2cf2c248 --- /dev/null +++ b/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitioned.cs @@ -0,0 +1,407 @@ +using Ryujinx.Common; +using Ryujinx.Memory; +using Ryujinx.Memory.Tracking; +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Ryujinx.Cpu.Jit.HostTracked +{ + class AddressSpacePartitioned : IDisposable + { + private const int PartitionBits = 25; + private const ulong PartitionSize = 1UL << PartitionBits; + + private readonly MemoryBlock _backingMemory; + private readonly List<AddressSpacePartition> _partitions; + private readonly AddressSpacePartitionAllocator _asAllocator; + private readonly Action<ulong, IntPtr, ulong> _updatePtCallback; + private readonly bool _useProtectionMirrors; + + public AddressSpacePartitioned(MemoryTracking tracking, MemoryBlock backingMemory, NativePageTable nativePageTable, bool useProtectionMirrors) + { + _backingMemory = backingMemory; + _partitions = new(); + _asAllocator = new(tracking, nativePageTable.Read, _partitions); + _updatePtCallback = nativePageTable.Update; + _useProtectionMirrors = useProtectionMirrors; + } + + public void Map(ulong va, ulong pa, ulong size) + { + ulong endVa = va + size; + + lock (_partitions) + { + EnsurePartitionsLocked(va, size); + + while (va < endVa) + { + int partitionIndex = FindPartitionIndexLocked(va); + AddressSpacePartition partition = _partitions[partitionIndex]; + + (ulong clampedVa, ulong clampedEndVa) = ClampRange(partition, va, endVa); + + partition.Map(clampedVa, pa, clampedEndVa - clampedVa); + + ulong currentSize = clampedEndVa - clampedVa; + + va += currentSize; + pa += currentSize; + + InsertOrRemoveBridgeIfNeeded(partitionIndex); + } + } + } + + public void Unmap(ulong va, ulong size) + { + ulong endVa = va + size; + + while (va < endVa) + { + AddressSpacePartition partition; + + lock (_partitions) + { + int partitionIndex = FindPartitionIndexLocked(va); + if (partitionIndex < 0) + { + va += PartitionSize - (va & (PartitionSize - 1)); + + continue; + } + + partition = _partitions[partitionIndex]; + + (ulong clampedVa, ulong clampedEndVa) = ClampRange(partition, va, endVa); + + partition.Unmap(clampedVa, clampedEndVa - clampedVa); + + va += clampedEndVa - clampedVa; + + InsertOrRemoveBridgeIfNeeded(partitionIndex); + + if (partition.IsEmpty()) + { + _partitions.Remove(partition); + partition.Dispose(); + } + } + } + } + + public void Reprotect(ulong va, ulong size, MemoryPermission protection) + { + ulong endVa = va + size; + + lock (_partitions) + { + while (va < endVa) + { + AddressSpacePartition partition = FindPartitionWithIndex(va, out int partitionIndex); + + if (partition == null) + { + va += PartitionSize - (va & (PartitionSize - 1)); + + continue; + } + + (ulong clampedVa, ulong clampedEndVa) = ClampRange(partition, va, endVa); + + if (_useProtectionMirrors) + { + partition.Reprotect(clampedVa, clampedEndVa - clampedVa, protection, this, _updatePtCallback); + } + else + { + partition.ReprotectAligned(clampedVa, clampedEndVa - clampedVa, protection); + + if (clampedVa == partition.Address && + partitionIndex > 0 && + _partitions[partitionIndex - 1].EndAddress == partition.Address) + { + _partitions[partitionIndex - 1].ReprotectBridge(protection); + } + } + + va += clampedEndVa - clampedVa; + } + } + } + + public PrivateRange GetPrivateAllocation(ulong va) + { + AddressSpacePartition partition = FindPartition(va); + + if (partition == null) + { + return PrivateRange.Empty; + } + + return partition.GetPrivateAllocation(va); + } + + public PrivateRange GetFirstPrivateAllocation(ulong va, ulong size, out ulong nextVa) + { + AddressSpacePartition partition = FindPartition(va); + + if (partition == null) + { + nextVa = (va & ~(PartitionSize - 1)) + PartitionSize; + + return PrivateRange.Empty; + } + + return partition.GetFirstPrivateAllocation(va, size, out nextVa); + } + + public bool HasAnyPrivateAllocation(ulong va, ulong size, out PrivateRange range) + { + range = PrivateRange.Empty; + + ulong startVa = va; + ulong endVa = va + size; + + while (va < endVa) + { + AddressSpacePartition partition = FindPartition(va); + + if (partition == null) + { + va += PartitionSize - (va & (PartitionSize - 1)); + + continue; + } + + (ulong clampedVa, ulong clampedEndVa) = ClampRange(partition, va, endVa); + + if (partition.HasPrivateAllocation(clampedVa, clampedEndVa - clampedVa, startVa, size, ref range)) + { + return true; + } + + va += clampedEndVa - clampedVa; + } + + return false; + } + + private void InsertOrRemoveBridgeIfNeeded(int partitionIndex) + { + if (partitionIndex > 0) + { + if (_partitions[partitionIndex - 1].EndAddress == _partitions[partitionIndex].Address) + { + _partitions[partitionIndex - 1].InsertBridgeAtEnd(_partitions[partitionIndex], _useProtectionMirrors); + } + else + { + _partitions[partitionIndex - 1].InsertBridgeAtEnd(null, _useProtectionMirrors); + } + } + + if (partitionIndex + 1 < _partitions.Count && _partitions[partitionIndex].EndAddress == _partitions[partitionIndex + 1].Address) + { + _partitions[partitionIndex].InsertBridgeAtEnd(_partitions[partitionIndex + 1], _useProtectionMirrors); + } + else + { + _partitions[partitionIndex].InsertBridgeAtEnd(null, _useProtectionMirrors); + } + } + + public IntPtr GetPointer(ulong va, ulong size) + { + AddressSpacePartition partition = FindPartition(va); + + return partition.GetPointer(va, size); + } + + private static (ulong, ulong) ClampRange(AddressSpacePartition partition, ulong va, ulong endVa) + { + if (va < partition.Address) + { + va = partition.Address; + } + + if (endVa > partition.EndAddress) + { + endVa = partition.EndAddress; + } + + return (va, endVa); + } + + private AddressSpacePartition FindPartition(ulong va) + { + lock (_partitions) + { + int index = FindPartitionIndexLocked(va); + if (index >= 0) + { + return _partitions[index]; + } + } + + return null; + } + + private AddressSpacePartition FindPartitionWithIndex(ulong va, out int index) + { + lock (_partitions) + { + index = FindPartitionIndexLocked(va); + if (index >= 0) + { + return _partitions[index]; + } + } + + return null; + } + + private int FindPartitionIndexLocked(ulong va) + { + int left = 0; + int middle; + int right = _partitions.Count - 1; + + while (left <= right) + { + middle = left + ((right - left) >> 1); + + AddressSpacePartition partition = _partitions[middle]; + + if (partition.Address <= va && partition.EndAddress > va) + { + return middle; + } + + if (partition.Address >= va) + { + right = middle - 1; + } + else + { + left = middle + 1; + } + } + + return -1; + } + + private void EnsurePartitionsLocked(ulong va, ulong size) + { + ulong endVa = BitUtils.AlignUp(va + size, PartitionSize); + va = BitUtils.AlignDown(va, PartitionSize); + + for (int i = 0; i < _partitions.Count && va < endVa; i++) + { + AddressSpacePartition partition = _partitions[i]; + + if (partition.Address <= va && partition.EndAddress > va) + { + if (partition.EndAddress >= endVa) + { + // Fully mapped already. + va = endVa; + + break; + } + + ulong gapSize; + + if (i + 1 < _partitions.Count) + { + AddressSpacePartition nextPartition = _partitions[i + 1]; + + if (partition.EndAddress == nextPartition.Address) + { + va = partition.EndAddress; + + continue; + } + + gapSize = Math.Min(endVa, nextPartition.Address) - partition.EndAddress; + } + else + { + gapSize = endVa - partition.EndAddress; + } + + _partitions.Insert(i + 1, CreateAsPartition(partition.EndAddress, gapSize)); + va = partition.EndAddress + gapSize; + i++; + } + else if (partition.EndAddress > va) + { + Debug.Assert(partition.Address > va); + + ulong gapSize; + + if (partition.Address < endVa) + { + gapSize = partition.Address - va; + } + else + { + gapSize = endVa - va; + } + + _partitions.Insert(i, CreateAsPartition(va, gapSize)); + va = Math.Min(partition.EndAddress, endVa); + i++; + } + } + + if (va < endVa) + { + _partitions.Add(CreateAsPartition(va, endVa - va)); + } + + ValidatePartitionList(); + } + + [Conditional("DEBUG")] + private void ValidatePartitionList() + { + for (int i = 1; i < _partitions.Count; i++) + { + Debug.Assert(_partitions[i].Address > _partitions[i - 1].Address); + Debug.Assert(_partitions[i].EndAddress > _partitions[i - 1].EndAddress); + } + } + + private AddressSpacePartition CreateAsPartition(ulong va, ulong size) + { + return new(CreateAsPartitionAllocation(va, size), _backingMemory, va, size); + } + + public AddressSpacePartitionAllocation CreateAsPartitionAllocation(ulong va, ulong size) + { + return _asAllocator.Allocate(va, size + MemoryBlock.GetPageSize()); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + foreach (AddressSpacePartition partition in _partitions) + { + partition.Dispose(); + } + + _partitions.Clear(); + _asAllocator.Dispose(); + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} |
