/ src / Ryujinx.Memory / Tracking / VirtualRegion.cs
VirtualRegion.cs
  1  using Ryujinx.Memory.Range;
  2  using System.Collections.Generic;
  3  
  4  namespace Ryujinx.Memory.Tracking
  5  {
  6      /// <summary>
  7      /// A region of virtual memory.
  8      /// </summary>
  9      class VirtualRegion : AbstractRegion
 10      {
 11          public List<RegionHandle> Handles = new();
 12  
 13          private readonly MemoryTracking _tracking;
 14          private MemoryPermission _lastPermission;
 15  
 16          public bool Guest { get; }
 17  
 18          public VirtualRegion(MemoryTracking tracking, ulong address, ulong size, bool guest, MemoryPermission lastPermission = MemoryPermission.Invalid) : base(address, size)
 19          {
 20              _lastPermission = lastPermission;
 21              _tracking = tracking;
 22  
 23              Guest = guest;
 24          }
 25  
 26          /// <inheritdoc/>
 27          public override void Signal(ulong address, ulong size, bool write, int? exemptId)
 28          {
 29              IList<RegionHandle> handles = Handles;
 30  
 31              for (int i = 0; i < handles.Count; i++)
 32              {
 33                  if (exemptId == null || handles[i].Id != exemptId.Value)
 34                  {
 35                      handles[i].Signal(address, size, write, ref handles);
 36                  }
 37              }
 38  
 39              UpdateProtection();
 40          }
 41  
 42          /// <inheritdoc/>
 43          public override void SignalPrecise(ulong address, ulong size, bool write, int? exemptId)
 44          {
 45              IList<RegionHandle> handles = Handles;
 46  
 47              bool allPrecise = true;
 48  
 49              for (int i = 0; i < handles.Count; i++)
 50              {
 51                  if (exemptId == null || handles[i].Id != exemptId.Value)
 52                  {
 53                      allPrecise &= handles[i].SignalPrecise(address, size, write, ref handles);
 54                  }
 55              }
 56  
 57              // Only update protection if a regular signal handler was called.
 58              // This allows precise actions to skip reprotection costs if they want (they can still do it manually).
 59              if (!allPrecise)
 60              {
 61                  UpdateProtection();
 62              }
 63          }
 64  
 65          /// <summary>
 66          /// Signal that this region has been mapped or unmapped.
 67          /// </summary>
 68          /// <param name="mapped">True if the region has been mapped, false if unmapped</param>
 69          public void SignalMappingChanged(bool mapped)
 70          {
 71              _lastPermission = MemoryPermission.Invalid;
 72  
 73              if (!Guest)
 74              {
 75                  foreach (RegionHandle handle in Handles)
 76                  {
 77                      handle.SignalMappingChanged(mapped);
 78                  }
 79              }
 80          }
 81  
 82          /// <summary>
 83          /// Gets the strictest permission that the child handles demand. Assumes that the tracking lock has been obtained.
 84          /// </summary>
 85          /// <returns>Protection level that this region demands</returns>
 86          public MemoryPermission GetRequiredPermission()
 87          {
 88              // Start with Read/Write, each handle can strip off permissions as necessary.
 89              // Assumes the tracking lock has already been obtained.
 90  
 91              MemoryPermission result = MemoryPermission.ReadAndWrite;
 92  
 93              foreach (var handle in Handles)
 94              {
 95                  result &= handle.RequiredPermission;
 96                  if (result == 0)
 97                  {
 98                      return result;
 99                  }
100              }
101              return result;
102          }
103  
104          /// <summary>
105          /// Updates the protection for this virtual region.
106          /// </summary>
107          public bool UpdateProtection()
108          {
109              MemoryPermission permission = GetRequiredPermission();
110  
111              if (_lastPermission != permission)
112              {
113                  _tracking.ProtectVirtualRegion(this, permission, Guest);
114                  _lastPermission = permission;
115  
116                  return true;
117              }
118  
119              return false;
120          }
121  
122          /// <summary>
123          /// Removes a handle from this virtual region. If there are no handles left, this virtual region is removed.
124          /// </summary>
125          /// <param name="handle">Handle to remove</param>
126          public void RemoveHandle(RegionHandle handle)
127          {
128              lock (_tracking.TrackingLock)
129              {
130                  Handles.Remove(handle);
131                  UpdateProtection();
132                  if (Handles.Count == 0)
133                  {
134                      _tracking.RemoveVirtual(this);
135                  }
136              }
137          }
138  
139          public override INonOverlappingRange Split(ulong splitAddress)
140          {
141              VirtualRegion newRegion = new(_tracking, splitAddress, EndAddress - splitAddress, Guest, _lastPermission);
142              Size = splitAddress - Address;
143  
144              // The new region inherits all of our parents.
145              newRegion.Handles = new List<RegionHandle>(Handles);
146              foreach (var parent in Handles)
147              {
148                  parent.AddChild(newRegion);
149              }
150  
151              return newRegion;
152          }
153      }
154  }