aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Memory/Tracking/VirtualRegion.cs
blob: bb087e9af07b842e14a2505d6b469876ddf98be8 (plain) (blame)
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
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();

        private readonly MemoryTracking _tracking;
        private MemoryPermission _lastPermission;

        public bool Guest { get; }

        public VirtualRegion(MemoryTracking tracking, ulong address, ulong size, bool guest, MemoryPermission lastPermission = MemoryPermission.Invalid) : base(address, size)
        {
            _lastPermission = lastPermission;
            _tracking = tracking;

            Guest = guest;
        }

        /// <inheritdoc/>
        public override void Signal(ulong address, ulong size, bool write, int? exemptId)
        {
            IList<RegionHandle> handles = Handles;

            for (int i = 0; i < handles.Count; i++)
            {
                if (exemptId == null || handles[i].Id != exemptId.Value)
                {
                    handles[i].Signal(address, size, write, ref handles);
                }
            }

            UpdateProtection();
        }

        /// <inheritdoc/>
        public override void SignalPrecise(ulong address, ulong size, bool write, int? exemptId)
        {
            IList<RegionHandle> handles = Handles;

            bool allPrecise = true;

            for (int i = 0; i < handles.Count; i++)
            {
                if (exemptId == null || handles[i].Id != exemptId.Value)
                {
                    allPrecise &= handles[i].SignalPrecise(address, size, write, ref handles);
                }
            }

            // Only update protection if a regular signal handler was called.
            // This allows precise actions to skip reprotection costs if they want (they can still do it manually).
            if (!allPrecise)
            {
                UpdateProtection();
            }
        }

        /// <summary>
        /// Signal that this region has been mapped or unmapped.
        /// </summary>
        /// <param name="mapped">True if the region has been mapped, false if unmapped</param>
        public void SignalMappingChanged(bool mapped)
        {
            _lastPermission = MemoryPermission.Invalid;

            foreach (RegionHandle handle in Handles)
            {
                handle.SignalMappingChanged(mapped);
            }
        }

        /// <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.
        /// </summary>
        public bool UpdateProtection()
        {
            MemoryPermission permission = GetRequiredPermission();

            if (_lastPermission != permission)
            {
                _tracking.ProtectVirtualRegion(this, permission, Guest);
                _lastPermission = permission;

                return true;
            }

            return false;
        }

        /// <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)
        {
            lock (_tracking.TrackingLock)
            {
                Handles.Remove(handle);
                UpdateProtection();
                if (Handles.Count == 0)
                {
                    _tracking.RemoveVirtual(this);
                }
            }
        }

        public override INonOverlappingRange Split(ulong splitAddress)
        {
            VirtualRegion newRegion = new(_tracking, splitAddress, EndAddress - splitAddress, Guest, _lastPermission);
            Size = splitAddress - Address;

            // The new region inherits all of our parents.
            newRegion.Handles = new List<RegionHandle>(Handles);
            foreach (var parent in Handles)
            {
                parent.AddChild(newRegion);
            }

            return newRegion;
        }
    }
}