aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryBlock.cs
blob: d2c4aadf3fc346f90d96400df22e432170d8ab9b (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
152
153
154
155
156
using Ryujinx.Common.Collections;
using System;

namespace Ryujinx.HLE.HOS.Kernel.Memory
{
    class KMemoryBlock : IntrusiveRedBlackTreeNode<KMemoryBlock>, IComparable<KMemoryBlock>, IComparable<ulong>
    {
        public ulong BaseAddress { get; private set; }
        public ulong PagesCount { get; private set; }

        public MemoryState State { get; private set; }
        public KMemoryPermission Permission { get; private set; }
        public MemoryAttribute Attribute { get; private set; }
        public KMemoryPermission SourcePermission { get; private set; }

        public int IpcRefCount { get; private set; }
        public int DeviceRefCount { get; private set; }

        public KMemoryBlock(
            ulong baseAddress,
            ulong pagesCount,
            MemoryState state,
            KMemoryPermission permission,
            MemoryAttribute attribute,
            int ipcRefCount = 0,
            int deviceRefCount = 0)
        {
            BaseAddress = baseAddress;
            PagesCount = pagesCount;
            State = state;
            Attribute = attribute;
            Permission = permission;
            IpcRefCount = ipcRefCount;
            DeviceRefCount = deviceRefCount;
        }

        public void SetState(KMemoryPermission permission, MemoryState state, MemoryAttribute attribute)
        {
            Permission = permission;
            State = state;
            Attribute &= MemoryAttribute.IpcAndDeviceMapped;
            Attribute |= attribute;
        }

        public void SetIpcMappingPermission(KMemoryPermission newPermission)
        {
            int oldIpcRefCount = IpcRefCount++;

            if ((ushort)IpcRefCount == 0)
            {
                throw new InvalidOperationException("IPC reference count increment overflowed.");
            }

            if (oldIpcRefCount == 0)
            {
                SourcePermission = Permission;

                Permission &= ~KMemoryPermission.ReadAndWrite;
                Permission |= KMemoryPermission.ReadAndWrite & newPermission;
            }

            Attribute |= MemoryAttribute.IpcMapped;
        }

        public void RestoreIpcMappingPermission()
        {
            int oldIpcRefCount = IpcRefCount--;

            if (oldIpcRefCount == 0)
            {
                throw new InvalidOperationException("IPC reference count decrement underflowed.");
            }

            if (oldIpcRefCount == 1)
            {
                Permission = SourcePermission;

                SourcePermission = KMemoryPermission.None;

                Attribute &= ~MemoryAttribute.IpcMapped;
            }
        }

        public KMemoryBlock SplitRightAtAddress(ulong address)
        {
            ulong leftAddress = BaseAddress;

            ulong leftPagesCount = (address - leftAddress) / KPageTableBase.PageSize;

            BaseAddress = address;

            PagesCount -= leftPagesCount;

            return new KMemoryBlock(
                leftAddress,
                leftPagesCount,
                State,
                Permission,
                Attribute,
                IpcRefCount,
                DeviceRefCount);
        }

        public void AddPages(ulong pagesCount)
        {
            PagesCount += pagesCount;
        }

        public KMemoryInfo GetInfo()
        {
            ulong size = PagesCount * KPageTableBase.PageSize;

            return new KMemoryInfo(
                BaseAddress,
                size,
                State,
                Permission,
                Attribute,
                SourcePermission,
                IpcRefCount,
                DeviceRefCount);
        }

        public int CompareTo(KMemoryBlock other)
        {
            if (BaseAddress < other.BaseAddress)
            {
                return -1;
            }
            else if (BaseAddress <= other.BaseAddress + other.PagesCount * KPageTableBase.PageSize - 1UL)
            {
                return 0;
            }
            else
            {
                return 1;
            }
        }

        public int CompareTo(ulong address)
        {
            if (address < BaseAddress)
            {
                return 1;
            }
            else if (address <= BaseAddress + PagesCount * KPageTableBase.PageSize - 1UL)
            {
                return 0;
            }
            else
            {
                return -1;
            }
        }
    }
}