aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Memory/Tracking/VirtualRegion.cs
blob: 15a11568e7d3886da913562816f6db603c3079b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
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;
        }
    }
}