AddressSpaceManager.cs
1 using Ryujinx.Memory.Range; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Runtime.CompilerServices; 6 7 namespace Ryujinx.Memory 8 { 9 /// <summary> 10 /// Represents a address space manager. 11 /// Supports virtual memory region mapping, address translation and read/write access to mapped regions. 12 /// </summary> 13 public sealed class AddressSpaceManager : VirtualMemoryManagerBase, IVirtualMemoryManager 14 { 15 /// <inheritdoc/> 16 public bool UsesPrivateAllocations => false; 17 18 /// <summary> 19 /// Address space width in bits. 20 /// </summary> 21 public int AddressSpaceBits { get; } 22 23 private readonly MemoryBlock _backingMemory; 24 private readonly PageTable<nuint> _pageTable; 25 26 protected override ulong AddressSpaceSize { get; } 27 28 /// <summary> 29 /// Creates a new instance of the memory manager. 30 /// </summary> 31 /// <param name="backingMemory">Physical backing memory where virtual memory will be mapped to</param> 32 /// <param name="addressSpaceSize">Size of the address space</param> 33 public AddressSpaceManager(MemoryBlock backingMemory, ulong addressSpaceSize) 34 { 35 ulong asSize = PageSize; 36 int asBits = PageBits; 37 38 while (asSize < addressSpaceSize) 39 { 40 asSize <<= 1; 41 asBits++; 42 } 43 44 AddressSpaceBits = asBits; 45 AddressSpaceSize = asSize; 46 _backingMemory = backingMemory; 47 _pageTable = new PageTable<nuint>(); 48 } 49 50 /// <inheritdoc/> 51 public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags) 52 { 53 AssertValidAddressAndSize(va, size); 54 55 while (size != 0) 56 { 57 _pageTable.Map(va, (nuint)(ulong)_backingMemory.GetPointer(pa, PageSize)); 58 59 va += PageSize; 60 pa += PageSize; 61 size -= PageSize; 62 } 63 } 64 65 public override void MapForeign(ulong va, nuint hostPointer, ulong size) 66 { 67 AssertValidAddressAndSize(va, size); 68 69 while (size != 0) 70 { 71 _pageTable.Map(va, hostPointer); 72 73 va += PageSize; 74 hostPointer += PageSize; 75 size -= PageSize; 76 } 77 } 78 79 /// <inheritdoc/> 80 public void Unmap(ulong va, ulong size) 81 { 82 AssertValidAddressAndSize(va, size); 83 84 while (size != 0) 85 { 86 _pageTable.Unmap(va); 87 88 va += PageSize; 89 size -= PageSize; 90 } 91 } 92 93 /// <inheritdoc/> 94 public unsafe ref T GetRef<T>(ulong va) where T : unmanaged 95 { 96 if (!IsContiguous(va, Unsafe.SizeOf<T>())) 97 { 98 ThrowMemoryNotContiguous(); 99 } 100 101 return ref *(T*)GetHostAddress(va); 102 } 103 104 /// <inheritdoc/> 105 public IEnumerable<HostMemoryRange> GetHostRegions(ulong va, ulong size) 106 { 107 if (size == 0) 108 { 109 return Enumerable.Empty<HostMemoryRange>(); 110 } 111 112 return GetHostRegionsImpl(va, size); 113 } 114 115 /// <inheritdoc/> 116 public IEnumerable<MemoryRange> GetPhysicalRegions(ulong va, ulong size) 117 { 118 if (size == 0) 119 { 120 return Enumerable.Empty<MemoryRange>(); 121 } 122 123 var hostRegions = GetHostRegionsImpl(va, size); 124 if (hostRegions == null) 125 { 126 return null; 127 } 128 129 var regions = new MemoryRange[hostRegions.Count]; 130 131 ulong backingStart = (ulong)_backingMemory.Pointer; 132 ulong backingEnd = backingStart + _backingMemory.Size; 133 134 int count = 0; 135 136 for (int i = 0; i < regions.Length; i++) 137 { 138 var hostRegion = hostRegions[i]; 139 140 if (hostRegion.Address >= backingStart && hostRegion.Address < backingEnd) 141 { 142 regions[count++] = new MemoryRange(hostRegion.Address - backingStart, hostRegion.Size); 143 } 144 } 145 146 if (count != regions.Length) 147 { 148 return new ArraySegment<MemoryRange>(regions, 0, count); 149 } 150 151 return regions; 152 } 153 154 private List<HostMemoryRange> GetHostRegionsImpl(ulong va, ulong size) 155 { 156 if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) 157 { 158 return null; 159 } 160 161 int pages = GetPagesCount(va, size, out va); 162 163 var regions = new List<HostMemoryRange>(); 164 165 nuint regionStart = GetHostAddress(va); 166 ulong regionSize = PageSize; 167 168 for (int page = 0; page < pages - 1; page++) 169 { 170 if (!ValidateAddress(va + PageSize)) 171 { 172 return null; 173 } 174 175 nuint newHostAddress = GetHostAddress(va + PageSize); 176 177 if (GetHostAddress(va) + PageSize != newHostAddress) 178 { 179 regions.Add(new HostMemoryRange(regionStart, regionSize)); 180 regionStart = newHostAddress; 181 regionSize = 0; 182 } 183 184 va += PageSize; 185 regionSize += PageSize; 186 } 187 188 regions.Add(new HostMemoryRange(regionStart, regionSize)); 189 190 return regions; 191 } 192 193 [MethodImpl(MethodImplOptions.AggressiveInlining)] 194 public override bool IsMapped(ulong va) 195 { 196 if (!ValidateAddress(va)) 197 { 198 return false; 199 } 200 201 return _pageTable.Read(va) != 0; 202 } 203 204 /// <inheritdoc/> 205 public bool IsRangeMapped(ulong va, ulong size) 206 { 207 if (size == 0) 208 { 209 return true; 210 } 211 212 if (!ValidateAddressAndSize(va, size)) 213 { 214 return false; 215 } 216 217 int pages = GetPagesCount(va, (uint)size, out va); 218 219 for (int page = 0; page < pages; page++) 220 { 221 if (!IsMapped(va)) 222 { 223 return false; 224 } 225 226 va += PageSize; 227 } 228 229 return true; 230 } 231 232 private nuint GetHostAddress(ulong va) 233 { 234 return _pageTable.Read(va) + (nuint)(va & PageMask); 235 } 236 237 /// <inheritdoc/> 238 public void Reprotect(ulong va, ulong size, MemoryPermission protection) 239 { 240 } 241 242 /// <inheritdoc/> 243 public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection, bool guest = false) 244 { 245 throw new NotImplementedException(); 246 } 247 248 protected unsafe override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size) 249 => new NativeMemoryManager<byte>((byte*)pa, size).Memory; 250 251 protected override unsafe Span<byte> GetPhysicalAddressSpan(nuint pa, int size) 252 => new Span<byte>((void*)pa, size); 253 254 protected override nuint TranslateVirtualAddressChecked(ulong va) 255 => GetHostAddress(va); 256 257 protected override nuint TranslateVirtualAddressUnchecked(ulong va) 258 => GetHostAddress(va); 259 } 260 }