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) { foreach (var handle in Handles) { handle.Signal(address, size, write); } UpdateProtection(); } /// <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; } } }