1 // Copyright 2020 yuzu Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4 
5 // This file references various implementation details from Atmosphere, an open-source firmware for
6 // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
7 
8 #pragma once
9 
10 #include "common/alignment.h"
11 #include "common/assert.h"
12 #include "common/common_types.h"
13 #include "core/hle/kernel/memory/memory_types.h"
14 #include "core/hle/kernel/svc_types.h"
15 
16 namespace Kernel::Memory {
17 
18 enum class MemoryState : u32 {
19     None = 0,
20     Mask = 0xFF,
21     All = ~None,
22 
23     FlagCanReprotect = (1 << 8),
24     FlagCanDebug = (1 << 9),
25     FlagCanUseIpc = (1 << 10),
26     FlagCanUseNonDeviceIpc = (1 << 11),
27     FlagCanUseNonSecureIpc = (1 << 12),
28     FlagMapped = (1 << 13),
29     FlagCode = (1 << 14),
30     FlagCanAlias = (1 << 15),
31     FlagCanCodeAlias = (1 << 16),
32     FlagCanTransfer = (1 << 17),
33     FlagCanQueryPhysical = (1 << 18),
34     FlagCanDeviceMap = (1 << 19),
35     FlagCanAlignedDeviceMap = (1 << 20),
36     FlagCanIpcUserBuffer = (1 << 21),
37     FlagReferenceCounted = (1 << 22),
38     FlagCanMapProcess = (1 << 23),
39     FlagCanChangeAttribute = (1 << 24),
40     FlagCanCodeMemory = (1 << 25),
41 
42     FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
43                 FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
44                 FlagCanDeviceMap | FlagCanAlignedDeviceMap | FlagCanIpcUserBuffer |
45                 FlagReferenceCounted | FlagCanChangeAttribute,
46 
47     FlagsCode = FlagCanDebug | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
48                 FlagMapped | FlagCode | FlagCanQueryPhysical | FlagCanDeviceMap |
49                 FlagCanAlignedDeviceMap | FlagReferenceCounted,
50 
51     FlagsMisc = FlagMapped | FlagReferenceCounted | FlagCanQueryPhysical | FlagCanDeviceMap,
52 
53     Free = static_cast<u32>(Svc::MemoryState::Free),
54     Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped,
55     Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
56     Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
57     CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
58                FlagCanCodeMemory,
59     Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted,
60     Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
61 
62     AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
63                 FlagCanCodeAlias,
64     AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData |
65                     FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory,
66 
67     Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap |
68           FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
69 
70     Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
71             FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
72 
73     ThreadLocal =
74         static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted,
75 
76     Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
77                  FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
78                  FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
79 
80     SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc |
81                        FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
82 
83     SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
84                  FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
85 
86     Inaccessible = static_cast<u32>(Svc::MemoryState::Inaccessible),
87 
88     NonSecureIpc = static_cast<u32>(Svc::MemoryState::NonSecureIpc) | FlagsMisc |
89                    FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
90 
91     NonDeviceIpc =
92         static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc,
93 
94     Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped,
95 
96     GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
97                     FlagReferenceCounted | FlagCanDebug,
98     CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted,
99 };
100 DECLARE_ENUM_FLAG_OPERATORS(MemoryState);
101 
102 static_assert(static_cast<u32>(MemoryState::Free) == 0x00000000);
103 static_assert(static_cast<u32>(MemoryState::Io) == 0x00002001);
104 static_assert(static_cast<u32>(MemoryState::Static) == 0x00042002);
105 static_assert(static_cast<u32>(MemoryState::Code) == 0x00DC7E03);
106 static_assert(static_cast<u32>(MemoryState::CodeData) == 0x03FEBD04);
107 static_assert(static_cast<u32>(MemoryState::Normal) == 0x037EBD05);
108 static_assert(static_cast<u32>(MemoryState::Shared) == 0x00402006);
109 static_assert(static_cast<u32>(MemoryState::AliasCode) == 0x00DD7E08);
110 static_assert(static_cast<u32>(MemoryState::AliasCodeData) == 0x03FFBD09);
111 static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A);
112 static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B);
113 static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C);
114 static_assert(static_cast<u32>(MemoryState::Transfered) == 0x015C3C0D);
115 static_assert(static_cast<u32>(MemoryState::SharedTransfered) == 0x005C380E);
116 static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F);
117 static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010);
118 static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811);
119 static_assert(static_cast<u32>(MemoryState::NonDeviceIpc) == 0x004C2812);
120 static_assert(static_cast<u32>(MemoryState::Kernel) == 0x00002013);
121 static_assert(static_cast<u32>(MemoryState::GeneratedCode) == 0x00402214);
122 static_assert(static_cast<u32>(MemoryState::CodeOut) == 0x00402015);
123 
124 enum class MemoryPermission : u8 {
125     None = 0,
126     Mask = static_cast<u8>(~None),
127 
128     Read = 1 << 0,
129     Write = 1 << 1,
130     Execute = 1 << 2,
131 
132     ReadAndWrite = Read | Write,
133     ReadAndExecute = Read | Execute,
134 
135     UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
136                                Svc::MemoryPermission::Execute),
137 };
138 DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission);
139 
140 enum class MemoryAttribute : u8 {
141     None = 0x00,
142     Mask = 0x7F,
143     All = Mask,
144     DontCareMask = 0x80,
145 
146     Locked = static_cast<u8>(Svc::MemoryAttribute::Locked),
147     IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked),
148     DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
149     Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
150 
151     IpcAndDeviceMapped = IpcLocked | DeviceShared,
152     LockedAndIpcLocked = Locked | IpcLocked,
153     DeviceSharedAndUncached = DeviceShared | Uncached
154 };
155 DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute);
156 
157 static_assert((static_cast<u8>(MemoryAttribute::Mask) &
158                static_cast<u8>(MemoryAttribute::DontCareMask)) == 0);
159 
160 struct MemoryInfo {
161     VAddr addr{};
162     std::size_t size{};
163     MemoryState state{};
164     MemoryPermission perm{};
165     MemoryAttribute attribute{};
166     MemoryPermission original_perm{};
167     u16 ipc_lock_count{};
168     u16 device_use_count{};
169 
GetSvcMemoryInfoMemoryInfo170     constexpr Svc::MemoryInfo GetSvcMemoryInfo() const {
171         return {
172             addr,
173             size,
174             static_cast<Svc::MemoryState>(state & MemoryState::Mask),
175             static_cast<Svc::MemoryAttribute>(attribute & MemoryAttribute::Mask),
176             static_cast<Svc::MemoryPermission>(perm & MemoryPermission::UserMask),
177             ipc_lock_count,
178             device_use_count,
179         };
180     }
181 
GetAddressMemoryInfo182     constexpr VAddr GetAddress() const {
183         return addr;
184     }
GetSizeMemoryInfo185     constexpr std::size_t GetSize() const {
186         return size;
187     }
GetNumPagesMemoryInfo188     constexpr std::size_t GetNumPages() const {
189         return GetSize() / PageSize;
190     }
GetEndAddressMemoryInfo191     constexpr VAddr GetEndAddress() const {
192         return GetAddress() + GetSize();
193     }
GetLastAddressMemoryInfo194     constexpr VAddr GetLastAddress() const {
195         return GetEndAddress() - 1;
196     }
197 };
198 
199 class MemoryBlock final {
200     friend class MemoryBlockManager;
201 
202 private:
203     VAddr addr{};
204     std::size_t num_pages{};
205     MemoryState state{MemoryState::None};
206     u16 ipc_lock_count{};
207     u16 device_use_count{};
208     MemoryPermission perm{MemoryPermission::None};
209     MemoryPermission original_perm{MemoryPermission::None};
210     MemoryAttribute attribute{MemoryAttribute::None};
211 
212 public:
Compare(const MemoryBlock & lhs,const MemoryBlock & rhs)213     static constexpr int Compare(const MemoryBlock& lhs, const MemoryBlock& rhs) {
214         if (lhs.GetAddress() < rhs.GetAddress()) {
215             return -1;
216         } else if (lhs.GetAddress() <= rhs.GetLastAddress()) {
217             return 0;
218         } else {
219             return 1;
220         }
221     }
222 
223 public:
224     constexpr MemoryBlock() = default;
MemoryBlock(VAddr addr_,std::size_t num_pages_,MemoryState state_,MemoryPermission perm_,MemoryAttribute attribute_)225     constexpr MemoryBlock(VAddr addr_, std::size_t num_pages_, MemoryState state_,
226                           MemoryPermission perm_, MemoryAttribute attribute_)
227         : addr{addr_}, num_pages(num_pages_), state{state_}, perm{perm_}, attribute{attribute_} {}
228 
GetAddress()229     constexpr VAddr GetAddress() const {
230         return addr;
231     }
232 
GetNumPages()233     constexpr std::size_t GetNumPages() const {
234         return num_pages;
235     }
236 
GetSize()237     constexpr std::size_t GetSize() const {
238         return GetNumPages() * PageSize;
239     }
240 
GetEndAddress()241     constexpr VAddr GetEndAddress() const {
242         return GetAddress() + GetSize();
243     }
244 
GetLastAddress()245     constexpr VAddr GetLastAddress() const {
246         return GetEndAddress() - 1;
247     }
248 
GetMemoryInfo()249     constexpr MemoryInfo GetMemoryInfo() const {
250         return {
251             GetAddress(), GetSize(),     state,          perm,
252             attribute,    original_perm, ipc_lock_count, device_use_count,
253         };
254     }
255 
ShareToDevice(MemoryPermission)256     void ShareToDevice(MemoryPermission /*new_perm*/) {
257         ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared ||
258                device_use_count == 0);
259         attribute |= MemoryAttribute::DeviceShared;
260         const u16 new_use_count{++device_use_count};
261         ASSERT(new_use_count > 0);
262     }
263 
UnshareToDevice(MemoryPermission)264     void UnshareToDevice(MemoryPermission /*new_perm*/) {
265         ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared);
266         const u16 prev_use_count{device_use_count--};
267         ASSERT(prev_use_count > 0);
268         if (prev_use_count == 1) {
269             attribute &= ~MemoryAttribute::DeviceShared;
270         }
271     }
272 
273 private:
HasProperties(MemoryState s,MemoryPermission p,MemoryAttribute a)274     constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const {
275         constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask |
276                                                       MemoryAttribute::IpcLocked |
277                                                       MemoryAttribute::DeviceShared};
278         return state == s && perm == p &&
279                (attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask);
280     }
281 
HasSameProperties(const MemoryBlock & rhs)282     constexpr bool HasSameProperties(const MemoryBlock& rhs) const {
283         return state == rhs.state && perm == rhs.perm && original_perm == rhs.original_perm &&
284                attribute == rhs.attribute && ipc_lock_count == rhs.ipc_lock_count &&
285                device_use_count == rhs.device_use_count;
286     }
287 
Contains(VAddr start)288     constexpr bool Contains(VAddr start) const {
289         return GetAddress() <= start && start <= GetEndAddress();
290     }
291 
Add(std::size_t count)292     constexpr void Add(std::size_t count) {
293         ASSERT(count > 0);
294         ASSERT(GetAddress() + count * PageSize - 1 < GetEndAddress() + count * PageSize - 1);
295 
296         num_pages += count;
297     }
298 
Update(MemoryState new_state,MemoryPermission new_perm,MemoryAttribute new_attribute)299     constexpr void Update(MemoryState new_state, MemoryPermission new_perm,
300                           MemoryAttribute new_attribute) {
301         ASSERT(original_perm == MemoryPermission::None);
302         ASSERT((attribute & MemoryAttribute::IpcLocked) == MemoryAttribute::None);
303 
304         state = new_state;
305         perm = new_perm;
306 
307         attribute = static_cast<MemoryAttribute>(
308             new_attribute |
309             (attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared)));
310     }
311 
Split(VAddr split_addr)312     constexpr MemoryBlock Split(VAddr split_addr) {
313         ASSERT(GetAddress() < split_addr);
314         ASSERT(Contains(split_addr));
315         ASSERT(Common::IsAligned(split_addr, PageSize));
316 
317         MemoryBlock block;
318         block.addr = addr;
319         block.num_pages = (split_addr - GetAddress()) / PageSize;
320         block.state = state;
321         block.ipc_lock_count = ipc_lock_count;
322         block.device_use_count = device_use_count;
323         block.perm = perm;
324         block.original_perm = original_perm;
325         block.attribute = attribute;
326 
327         addr = split_addr;
328         num_pages -= block.num_pages;
329 
330         return block;
331     }
332 };
333 static_assert(std::is_trivially_destructible<MemoryBlock>::value);
334 
335 } // namespace Kernel::Memory
336