forked from Mirror/Ryujinx
166 lines
5.5 KiB
C#
166 lines
5.5 KiB
C#
|
using Ryujinx.Memory.Range;
|
|||
|
using System.Collections.Generic;
|
|||
|
|
|||
|
namespace Ryujinx.Memory.Tracking
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// A region of virtual memory.
|
|||
|
/// </summary>
|
|||
|
class VirtualRegion : AbstractRegion
|
|||
|
{
|
|||
|
public List<RegionHandle> Handles = new List<RegionHandle>();
|
|||
|
private List<PhysicalRegion> _physicalChildren;
|
|||
|
|
|||
|
private readonly MemoryTracking _tracking;
|
|||
|
|
|||
|
public VirtualRegion(MemoryTracking tracking, ulong address, ulong size) : base(address, size)
|
|||
|
{
|
|||
|
_tracking = tracking;
|
|||
|
|
|||
|
UpdatePhysicalChildren();
|
|||
|
}
|
|||
|
|
|||
|
public override void Signal(ulong address, ulong size, bool write)
|
|||
|
{
|
|||
|
_tracking.ProtectVirtualRegion(this, MemoryPermission.ReadAndWrite); // Remove our protection immedately.
|
|||
|
|
|||
|
foreach (var handle in Handles)
|
|||
|
{
|
|||
|
handle.Signal(address, size, write);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Clears all physical children of this region. Assumes that the tracking lock has been obtained.
|
|||
|
/// </summary>
|
|||
|
private void ClearPhysicalChildren()
|
|||
|
{
|
|||
|
if (_physicalChildren != null)
|
|||
|
{
|
|||
|
foreach (PhysicalRegion child in _physicalChildren)
|
|||
|
{
|
|||
|
child.RemoveParent(this);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Updates the physical children of this region, assuming that they are clear and that the tracking lock has been obtained.
|
|||
|
/// </summary>
|
|||
|
private void UpdatePhysicalChildren()
|
|||
|
{
|
|||
|
_physicalChildren = _tracking.GetPhysicalRegionsForVirtual(Address, Size);
|
|||
|
|
|||
|
foreach (PhysicalRegion child in _physicalChildren)
|
|||
|
{
|
|||
|
child.VirtualParents.Add(this);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Recalculates the physical children for this virtual region. Assumes that the tracking lock has been obtained.
|
|||
|
/// </summary>
|
|||
|
public void RecalculatePhysicalChildren()
|
|||
|
{
|
|||
|
ClearPhysicalChildren();
|
|||
|
UpdatePhysicalChildren();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets the strictest permission that the child handles demand. Assumes that the tracking lock has been obtained.
|
|||
|
/// </summary>
|
|||
|
/// <returns>Protection level that this region demands</returns>
|
|||
|
public MemoryPermission GetRequiredPermission()
|
|||
|
{
|
|||
|
// Start with Read/Write, each handle can strip off permissions as necessary.
|
|||
|
// Assumes the tracking lock has already been obtained.
|
|||
|
|
|||
|
MemoryPermission result = MemoryPermission.ReadAndWrite;
|
|||
|
|
|||
|
foreach (var handle in Handles)
|
|||
|
{
|
|||
|
result &= handle.RequiredPermission;
|
|||
|
if (result == 0) return result;
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Updates the protection for this virtual region, and all child physical regions.
|
|||
|
/// </summary>
|
|||
|
public void UpdateProtection()
|
|||
|
{
|
|||
|
// Re-evaluate protection for all physical children.
|
|||
|
|
|||
|
_tracking.ProtectVirtualRegion(this, GetRequiredPermission());
|
|||
|
lock (_tracking.TrackingLock)
|
|||
|
{
|
|||
|
foreach (var child in _physicalChildren)
|
|||
|
{
|
|||
|
child.UpdateProtection();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Removes a handle from this virtual region. If there are no handles left, this virtual region is removed.
|
|||
|
/// </summary>
|
|||
|
/// <param name="handle">Handle to remove</param>
|
|||
|
public void RemoveHandle(RegionHandle handle)
|
|||
|
{
|
|||
|
bool removedRegions = false;
|
|||
|
lock (_tracking.TrackingLock)
|
|||
|
{
|
|||
|
Handles.Remove(handle);
|
|||
|
UpdateProtection();
|
|||
|
if (Handles.Count == 0)
|
|||
|
{
|
|||
|
_tracking.RemoveVirtual(this);
|
|||
|
foreach (var child in _physicalChildren)
|
|||
|
{
|
|||
|
removedRegions |= child.RemoveParent(this);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (removedRegions)
|
|||
|
{
|
|||
|
// The first lock will unprotect any regions that have been removed. This second lock will remove them.
|
|||
|
lock (_tracking.TrackingLock)
|
|||
|
{
|
|||
|
foreach (var child in _physicalChildren)
|
|||
|
{
|
|||
|
child.TryDelete();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Add a child physical region to this virtual region. Assumes that the tracking lock has been obtained.
|
|||
|
/// </summary>
|
|||
|
/// <param name="region">Physical region to add as a child</param>
|
|||
|
public void AddChild(PhysicalRegion region)
|
|||
|
{
|
|||
|
_physicalChildren.Add(region);
|
|||
|
}
|
|||
|
|
|||
|
public override INonOverlappingRange Split(ulong splitAddress)
|
|||
|
{
|
|||
|
ClearPhysicalChildren();
|
|||
|
VirtualRegion newRegion = new VirtualRegion(_tracking, splitAddress, EndAddress - splitAddress);
|
|||
|
Size = splitAddress - Address;
|
|||
|
UpdatePhysicalChildren();
|
|||
|
|
|||
|
// The new region inherits all of our parents.
|
|||
|
newRegion.Handles = new List<RegionHandle>(Handles);
|
|||
|
foreach (var parent in Handles)
|
|||
|
{
|
|||
|
parent.AddChild(newRegion);
|
|||
|
}
|
|||
|
|
|||
|
return newRegion;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|