// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. // This file references various implementation details from Atmosphere, an open-source firmware for // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. #pragma once #include "common/alignment.h" #include "common/assert.h" #include "common/common_types.h" #include "core/hle/kernel/memory/memory_types.h" #include "core/hle/kernel/svc_types.h" namespace Kernel::Memory { enum class MemoryState : u32 { None = 0, Mask = 0xFF, All = ~None, FlagCanReprotect = (1 << 8), FlagCanDebug = (1 << 9), FlagCanUseIpc = (1 << 10), FlagCanUseNonDeviceIpc = (1 << 11), FlagCanUseNonSecureIpc = (1 << 12), FlagMapped = (1 << 13), FlagCode = (1 << 14), FlagCanAlias = (1 << 15), FlagCanCodeAlias = (1 << 16), FlagCanTransfer = (1 << 17), FlagCanQueryPhysical = (1 << 18), FlagCanDeviceMap = (1 << 19), FlagCanAlignedDeviceMap = (1 << 20), FlagCanIpcUserBuffer = (1 << 21), FlagReferenceCounted = (1 << 22), FlagCanMapProcess = (1 << 23), FlagCanChangeAttribute = (1 << 24), FlagCanCodeMemory = (1 << 25), FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc | FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical | FlagCanDeviceMap | FlagCanAlignedDeviceMap | FlagCanIpcUserBuffer | FlagReferenceCounted | FlagCanChangeAttribute, FlagsCode = FlagCanDebug | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc | FlagMapped | FlagCode | FlagCanQueryPhysical | FlagCanDeviceMap | FlagCanAlignedDeviceMap | FlagReferenceCounted, FlagsMisc = FlagMapped | FlagReferenceCounted | FlagCanQueryPhysical | FlagCanDeviceMap, Free = static_cast(Svc::MemoryState::Free), Io = static_cast(Svc::MemoryState::Io) | FlagMapped, Static = static_cast(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical, Code = static_cast(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess, CodeData = static_cast(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess | FlagCanCodeMemory, Shared = static_cast(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted, Normal = static_cast(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory, AliasCode = static_cast(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess | FlagCanCodeAlias, AliasCodeData = static_cast(Svc::MemoryState::AliasCodeData) | FlagsData | FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory, Ipc = static_cast(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap | FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, Stack = static_cast(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap | FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, ThreadLocal = static_cast(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted, Transferred = static_cast(Svc::MemoryState::Transferred) | FlagsMisc | FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, SharedTransferred = static_cast(Svc::MemoryState::SharedTransferred) | FlagsMisc | FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, SharedCode = static_cast(Svc::MemoryState::SharedCode) | FlagMapped | FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, Inaccessible = static_cast(Svc::MemoryState::Inaccessible), NonSecureIpc = static_cast(Svc::MemoryState::NonSecureIpc) | FlagsMisc | FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, NonDeviceIpc = static_cast(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc, Kernel = static_cast(Svc::MemoryState::Kernel) | FlagMapped, GeneratedCode = static_cast(Svc::MemoryState::GeneratedCode) | FlagMapped | FlagReferenceCounted | FlagCanDebug, CodeOut = static_cast(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted, }; DECLARE_ENUM_FLAG_OPERATORS(MemoryState); static_assert(static_cast(MemoryState::Free) == 0x00000000); static_assert(static_cast(MemoryState::Io) == 0x00002001); static_assert(static_cast(MemoryState::Static) == 0x00042002); static_assert(static_cast(MemoryState::Code) == 0x00DC7E03); static_assert(static_cast(MemoryState::CodeData) == 0x03FEBD04); static_assert(static_cast(MemoryState::Normal) == 0x037EBD05); static_assert(static_cast(MemoryState::Shared) == 0x00402006); static_assert(static_cast(MemoryState::AliasCode) == 0x00DD7E08); static_assert(static_cast(MemoryState::AliasCodeData) == 0x03FFBD09); static_assert(static_cast(MemoryState::Ipc) == 0x005C3C0A); static_assert(static_cast(MemoryState::Stack) == 0x005C3C0B); static_assert(static_cast(MemoryState::ThreadLocal) == 0x0040200C); static_assert(static_cast(MemoryState::Transferred) == 0x015C3C0D); static_assert(static_cast(MemoryState::SharedTransferred) == 0x005C380E); static_assert(static_cast(MemoryState::SharedCode) == 0x0040380F); static_assert(static_cast(MemoryState::Inaccessible) == 0x00000010); static_assert(static_cast(MemoryState::NonSecureIpc) == 0x005C3811); static_assert(static_cast(MemoryState::NonDeviceIpc) == 0x004C2812); static_assert(static_cast(MemoryState::Kernel) == 0x00002013); static_assert(static_cast(MemoryState::GeneratedCode) == 0x00402214); static_assert(static_cast(MemoryState::CodeOut) == 0x00402015); enum class MemoryPermission : u8 { None = 0, Mask = static_cast(~None), Read = 1 << 0, Write = 1 << 1, Execute = 1 << 2, ReadAndWrite = Read | Write, ReadAndExecute = Read | Execute, UserMask = static_cast(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write | Svc::MemoryPermission::Execute), }; DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission); enum class MemoryAttribute : u8 { None = 0x00, Mask = 0x7F, All = Mask, DontCareMask = 0x80, Locked = static_cast(Svc::MemoryAttribute::Locked), IpcLocked = static_cast(Svc::MemoryAttribute::IpcLocked), DeviceShared = static_cast(Svc::MemoryAttribute::DeviceShared), Uncached = static_cast(Svc::MemoryAttribute::Uncached), IpcAndDeviceMapped = IpcLocked | DeviceShared, LockedAndIpcLocked = Locked | IpcLocked, DeviceSharedAndUncached = DeviceShared | Uncached }; DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute); static_assert((static_cast(MemoryAttribute::Mask) & static_cast(MemoryAttribute::DontCareMask)) == 0); struct MemoryInfo { VAddr addr{}; std::size_t size{}; MemoryState state{}; MemoryPermission perm{}; MemoryAttribute attribute{}; MemoryPermission original_perm{}; u16 ipc_lock_count{}; u16 device_use_count{}; constexpr Svc::MemoryInfo GetSvcMemoryInfo() const { return { addr, size, static_cast(state & MemoryState::Mask), static_cast(attribute & MemoryAttribute::Mask), static_cast(perm & MemoryPermission::UserMask), ipc_lock_count, device_use_count, }; } constexpr VAddr GetAddress() const { return addr; } constexpr std::size_t GetSize() const { return size; } constexpr std::size_t GetNumPages() const { return GetSize() / PageSize; } constexpr VAddr GetEndAddress() const { return GetAddress() + GetSize(); } constexpr VAddr GetLastAddress() const { return GetEndAddress() - 1; } }; class MemoryBlock final { friend class MemoryBlockManager; private: VAddr addr{}; std::size_t num_pages{}; MemoryState state{MemoryState::None}; u16 ipc_lock_count{}; u16 device_use_count{}; MemoryPermission perm{MemoryPermission::None}; MemoryPermission original_perm{MemoryPermission::None}; MemoryAttribute attribute{MemoryAttribute::None}; public: static constexpr int Compare(const MemoryBlock& lhs, const MemoryBlock& rhs) { if (lhs.GetAddress() < rhs.GetAddress()) { return -1; } else if (lhs.GetAddress() <= rhs.GetLastAddress()) { return 0; } else { return 1; } } public: constexpr MemoryBlock() = default; constexpr MemoryBlock(VAddr addr_, std::size_t num_pages_, MemoryState state_, MemoryPermission perm_, MemoryAttribute attribute_) : addr{addr_}, num_pages(num_pages_), state{state_}, perm{perm_}, attribute{attribute_} {} constexpr VAddr GetAddress() const { return addr; } constexpr std::size_t GetNumPages() const { return num_pages; } constexpr std::size_t GetSize() const { return GetNumPages() * PageSize; } constexpr VAddr GetEndAddress() const { return GetAddress() + GetSize(); } constexpr VAddr GetLastAddress() const { return GetEndAddress() - 1; } constexpr MemoryInfo GetMemoryInfo() const { return { GetAddress(), GetSize(), state, perm, attribute, original_perm, ipc_lock_count, device_use_count, }; } void ShareToDevice(MemoryPermission /*new_perm*/) { ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared || device_use_count == 0); attribute |= MemoryAttribute::DeviceShared; const u16 new_use_count{++device_use_count}; ASSERT(new_use_count > 0); } void UnshareToDevice(MemoryPermission /*new_perm*/) { ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared); const u16 prev_use_count{device_use_count--}; ASSERT(prev_use_count > 0); if (prev_use_count == 1) { attribute &= ~MemoryAttribute::DeviceShared; } } private: constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const { constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask | MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared}; return state == s && perm == p && (attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask); } constexpr bool HasSameProperties(const MemoryBlock& rhs) const { return state == rhs.state && perm == rhs.perm && original_perm == rhs.original_perm && attribute == rhs.attribute && ipc_lock_count == rhs.ipc_lock_count && device_use_count == rhs.device_use_count; } constexpr bool Contains(VAddr start) const { return GetAddress() <= start && start <= GetEndAddress(); } constexpr void Add(std::size_t count) { ASSERT(count > 0); ASSERT(GetAddress() + count * PageSize - 1 < GetEndAddress() + count * PageSize - 1); num_pages += count; } constexpr void Update(MemoryState new_state, MemoryPermission new_perm, MemoryAttribute new_attribute) { ASSERT(original_perm == MemoryPermission::None); ASSERT((attribute & MemoryAttribute::IpcLocked) == MemoryAttribute::None); state = new_state; perm = new_perm; attribute = static_cast( new_attribute | (attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared))); } constexpr MemoryBlock Split(VAddr split_addr) { ASSERT(GetAddress() < split_addr); ASSERT(Contains(split_addr)); ASSERT(Common::IsAligned(split_addr, PageSize)); MemoryBlock block; block.addr = addr; block.num_pages = (split_addr - GetAddress()) / PageSize; block.state = state; block.ipc_lock_count = ipc_lock_count; block.device_use_count = device_use_count; block.perm = perm; block.original_perm = original_perm; block.attribute = attribute; addr = split_addr; num_pages -= block.num_pages; return block; } }; static_assert(std::is_trivially_destructible::value); } // namespace Kernel::Memory