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