using Ryujinx.Memory.Range; using System.Collections.Generic; using System.Threading; namespace Ryujinx.Memory.Tracking { /// /// A tracking handle for a given region of virtual memory. The Dirty flag is updated whenever any changes are made, /// and an action can be performed when the region is read to or written from. /// public class RegionHandle : IRegionHandle, IRange { public bool Dirty { get; private set; } public ulong Address { get; } public ulong Size { get; } public ulong EndAddress { get; } internal IMultiRegionHandle Parent { get; set; } internal int SequenceNumber { get; set; } private RegionSignal _preAction; // Action to perform before a read or write. This will block the memory access. private readonly List _regions; private readonly MemoryTracking _tracking; internal MemoryPermission RequiredPermission => _preAction != null ? MemoryPermission.None : (Dirty ? MemoryPermission.ReadAndWrite : MemoryPermission.Read); /// /// Create a new region handle. The handle is registered with the given tracking object, /// and will be notified of any changes to the specified region. /// /// Tracking object for the target memory block /// Virtual address of the region to track /// Size of the region to track /// Initial value of the dirty flag internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, bool dirty = true) { Dirty = dirty; Address = address; Size = size; EndAddress = address + size; _tracking = tracking; _regions = tracking.GetVirtualRegionsForHandle(address, size); foreach (var region in _regions) { region.Handles.Add(this); } } /// /// Signal that a memory action occurred within this handle's virtual regions. /// /// Whether the region was written to or read internal void Signal(ulong address, ulong size, bool write) { RegionSignal action = Interlocked.Exchange(ref _preAction, null); action?.Invoke(address, size); if (write) { Dirty = true; Parent?.SignalWrite(); } } /// /// Consume the dirty flag for this handle, and reprotect so it can be set on the next write. /// public void Reprotect() { Dirty = false; lock (_tracking.TrackingLock) { foreach (VirtualRegion region in _regions) { region.UpdateProtection(); } } } /// /// Register an action to perform when the tracked region is read or written. /// The action is automatically removed after it runs. /// /// Action to call on read or write public void RegisterAction(RegionSignal action) { RegionSignal lastAction = Interlocked.Exchange(ref _preAction, action); if (lastAction == null && action != lastAction) { lock (_tracking.TrackingLock) { foreach (VirtualRegion region in _regions) { region.UpdateProtection(); } } } } /// /// Add a child virtual region to this handle. /// /// Virtual region to add as a child internal void AddChild(VirtualRegion region) { _regions.Add(region); } /// /// Check if this region overlaps with another. /// /// Base address /// Size of the region /// True if overlapping, false otherwise public bool OverlapsWith(ulong address, ulong size) { return Address < address + size && address < EndAddress; } /// /// Dispose the handle. Within the tracking lock, this removes references from virtual and physical regions. /// public void Dispose() { lock (_tracking.TrackingLock) { foreach (VirtualRegion region in _regions) { region.RemoveHandle(this); } } } } }