/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#pragma once
#include
#include
#include
namespace ams::kern {
enum KMemoryState : u32 {
KMemoryState_None = 0,
KMemoryState_Mask = 0xFF,
KMemoryState_All = ~KMemoryState_None,
KMemoryState_FlagCanReprotect = (1 << 8),
KMemoryState_FlagCanDebug = (1 << 9),
KMemoryState_FlagCanUseIpc = (1 << 10),
KMemoryState_FlagCanUseNonDeviceIpc = (1 << 11),
KMemoryState_FlagCanUseNonSecureIpc = (1 << 12),
KMemoryState_FlagMapped = (1 << 13),
KMemoryState_FlagCode = (1 << 14),
KMemoryState_FlagCanAlias = (1 << 15),
KMemoryState_FlagCanCodeAlias = (1 << 16),
KMemoryState_FlagCanTransfer = (1 << 17),
KMemoryState_FlagCanQueryPhysical = (1 << 18),
KMemoryState_FlagCanDeviceMap = (1 << 19),
KMemoryState_FlagCanAlignedDeviceMap = (1 << 20),
KMemoryState_FlagCanIpcUserBuffer = (1 << 21),
KMemoryState_FlagReferenceCounted = (1 << 22),
KMemoryState_FlagCanMapProcess = (1 << 23),
KMemoryState_FlagCanChangeAttribute = (1 << 24),
KMemoryState_FlagCanCodeMemory = (1 << 25),
KMemoryState_FlagLinearMapped = (1 << 26),
KMemoryState_FlagCanPermissionLock = (1 << 27),
KMemoryState_FlagsData = KMemoryState_FlagCanReprotect | KMemoryState_FlagCanUseIpc |
KMemoryState_FlagCanUseNonDeviceIpc | KMemoryState_FlagCanUseNonSecureIpc |
KMemoryState_FlagMapped | KMemoryState_FlagCanAlias |
KMemoryState_FlagCanTransfer | KMemoryState_FlagCanQueryPhysical |
KMemoryState_FlagCanDeviceMap | KMemoryState_FlagCanAlignedDeviceMap |
KMemoryState_FlagCanIpcUserBuffer | KMemoryState_FlagReferenceCounted |
KMemoryState_FlagCanChangeAttribute | KMemoryState_FlagLinearMapped,
KMemoryState_FlagsCode = KMemoryState_FlagCanDebug | KMemoryState_FlagCanUseIpc |
KMemoryState_FlagCanUseNonDeviceIpc | KMemoryState_FlagCanUseNonSecureIpc |
KMemoryState_FlagMapped | KMemoryState_FlagCode |
KMemoryState_FlagCanQueryPhysical | KMemoryState_FlagCanDeviceMap |
KMemoryState_FlagCanAlignedDeviceMap | KMemoryState_FlagReferenceCounted |
KMemoryState_FlagLinearMapped,
KMemoryState_FlagsMisc = KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted |
KMemoryState_FlagCanQueryPhysical | KMemoryState_FlagCanDeviceMap |
KMemoryState_FlagLinearMapped,
KMemoryState_Free = ams::svc::MemoryState_Free,
KMemoryState_IoMemory = ams::svc::MemoryState_Io | KMemoryState_FlagMapped | KMemoryState_FlagCanDeviceMap | KMemoryState_FlagCanAlignedDeviceMap,
KMemoryState_IoRegister = ams::svc::MemoryState_Io | KMemoryState_FlagCanDeviceMap | KMemoryState_FlagCanAlignedDeviceMap,
KMemoryState_Static = ams::svc::MemoryState_Static | KMemoryState_FlagCanQueryPhysical,
KMemoryState_Code = ams::svc::MemoryState_Code | KMemoryState_FlagsCode | KMemoryState_FlagCanMapProcess,
KMemoryState_CodeData = ams::svc::MemoryState_CodeData | KMemoryState_FlagsData | KMemoryState_FlagCanMapProcess | KMemoryState_FlagCanCodeMemory | KMemoryState_FlagCanPermissionLock,
KMemoryState_Normal = ams::svc::MemoryState_Normal | KMemoryState_FlagsData | KMemoryState_FlagCanCodeMemory,
KMemoryState_Shared = ams::svc::MemoryState_Shared | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted | KMemoryState_FlagLinearMapped,
/* KMemoryState_Alias was removed after 1.0.0. */
KMemoryState_AliasCode = ams::svc::MemoryState_AliasCode | KMemoryState_FlagsCode | KMemoryState_FlagCanMapProcess | KMemoryState_FlagCanCodeAlias,
KMemoryState_AliasCodeData = ams::svc::MemoryState_AliasCodeData | KMemoryState_FlagsData | KMemoryState_FlagCanMapProcess | KMemoryState_FlagCanCodeAlias | KMemoryState_FlagCanCodeMemory
| KMemoryState_FlagCanPermissionLock,
KMemoryState_Ipc = ams::svc::MemoryState_Ipc | KMemoryState_FlagsMisc | KMemoryState_FlagCanAlignedDeviceMap
| KMemoryState_FlagCanUseIpc | KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
KMemoryState_Stack = ams::svc::MemoryState_Stack | KMemoryState_FlagsMisc | KMemoryState_FlagCanAlignedDeviceMap
| KMemoryState_FlagCanUseIpc | KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
KMemoryState_ThreadLocal = ams::svc::MemoryState_ThreadLocal | KMemoryState_FlagLinearMapped,
KMemoryState_Transfered = ams::svc::MemoryState_Transfered | KMemoryState_FlagsMisc | KMemoryState_FlagCanAlignedDeviceMap | KMemoryState_FlagCanChangeAttribute
| KMemoryState_FlagCanUseIpc | KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
KMemoryState_SharedTransfered = ams::svc::MemoryState_SharedTransfered | KMemoryState_FlagsMisc | KMemoryState_FlagCanAlignedDeviceMap
| KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
KMemoryState_SharedCode = ams::svc::MemoryState_SharedCode | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted | KMemoryState_FlagLinearMapped
| KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
KMemoryState_Inaccessible = ams::svc::MemoryState_Inaccessible,
KMemoryState_NonSecureIpc = ams::svc::MemoryState_NonSecureIpc | KMemoryState_FlagsMisc | KMemoryState_FlagCanAlignedDeviceMap
| KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
KMemoryState_NonDeviceIpc = ams::svc::MemoryState_NonDeviceIpc | KMemoryState_FlagsMisc | KMemoryState_FlagCanUseNonDeviceIpc,
KMemoryState_Kernel = ams::svc::MemoryState_Kernel,
KMemoryState_GeneratedCode = ams::svc::MemoryState_GeneratedCode | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted | KMemoryState_FlagCanDebug | KMemoryState_FlagLinearMapped,
KMemoryState_CodeOut = ams::svc::MemoryState_CodeOut | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted | KMemoryState_FlagLinearMapped,
KMemoryState_Coverage = ams::svc::MemoryState_Coverage | KMemoryState_FlagMapped,
KMemoryState_Insecure = ams::svc::MemoryState_Insecure | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted | KMemoryState_FlagLinearMapped | KMemoryState_FlagCanChangeAttribute
| KMemoryState_FlagCanDeviceMap | KMemoryState_FlagCanAlignedDeviceMap | KMemoryState_FlagCanQueryPhysical
| KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
};
#if 1
static_assert(KMemoryState_Free == 0x00000000);
static_assert(KMemoryState_IoMemory == 0x00182001);
static_assert(KMemoryState_IoRegister == 0x00180001);
static_assert(KMemoryState_Static == 0x00040002);
static_assert(KMemoryState_Code == 0x04DC7E03);
static_assert(KMemoryState_CodeData == 0x0FFEBD04);
static_assert(KMemoryState_Normal == 0x077EBD05);
static_assert(KMemoryState_Shared == 0x04402006);
static_assert(KMemoryState_AliasCode == 0x04DD7E08);
static_assert(KMemoryState_AliasCodeData == 0x0FFFBD09);
static_assert(KMemoryState_Ipc == 0x045C3C0A);
static_assert(KMemoryState_Stack == 0x045C3C0B);
static_assert(KMemoryState_ThreadLocal == 0x0400000C);
static_assert(KMemoryState_Transfered == 0x055C3C0D);
static_assert(KMemoryState_SharedTransfered == 0x045C380E);
static_assert(KMemoryState_SharedCode == 0x0440380F);
static_assert(KMemoryState_Inaccessible == 0x00000010);
static_assert(KMemoryState_NonSecureIpc == 0x045C3811);
static_assert(KMemoryState_NonDeviceIpc == 0x044C2812);
static_assert(KMemoryState_Kernel == 0x00000013);
static_assert(KMemoryState_GeneratedCode == 0x04402214);
static_assert(KMemoryState_CodeOut == 0x04402015);
static_assert(KMemoryState_Coverage == 0x00002016); /* TODO: Is this correct? */
static_assert(KMemoryState_Insecure == 0x055C3817);
#endif
enum KMemoryPermission : u8 {
KMemoryPermission_None = 0,
KMemoryPermission_All = static_cast(~KMemoryPermission_None),
KMemoryPermission_KernelShift = 3,
KMemoryPermission_KernelRead = ams::svc::MemoryPermission_Read << KMemoryPermission_KernelShift,
KMemoryPermission_KernelWrite = ams::svc::MemoryPermission_Write << KMemoryPermission_KernelShift,
KMemoryPermission_KernelExecute = ams::svc::MemoryPermission_Execute << KMemoryPermission_KernelShift,
KMemoryPermission_NotMapped = (1 << (2 * KMemoryPermission_KernelShift)),
KMemoryPermission_KernelReadWrite = KMemoryPermission_KernelRead | KMemoryPermission_KernelWrite,
KMemoryPermission_KernelReadExecute = KMemoryPermission_KernelRead | KMemoryPermission_KernelExecute,
KMemoryPermission_UserRead = ams::svc::MemoryPermission_Read | KMemoryPermission_KernelRead,
KMemoryPermission_UserWrite = ams::svc::MemoryPermission_Write | KMemoryPermission_KernelWrite,
KMemoryPermission_UserExecute = ams::svc::MemoryPermission_Execute,
KMemoryPermission_UserReadWrite = KMemoryPermission_UserRead | KMemoryPermission_UserWrite,
KMemoryPermission_UserReadExecute = KMemoryPermission_UserRead | KMemoryPermission_UserExecute,
KMemoryPermission_UserMask = ams::svc::MemoryPermission_Read | ams::svc::MemoryPermission_Write | ams::svc::MemoryPermission_Execute,
KMemoryPermission_IpcLockChangeMask = KMemoryPermission_NotMapped | KMemoryPermission_UserReadWrite,
};
constexpr KMemoryPermission ConvertToKMemoryPermission(ams::svc::MemoryPermission perm) {
return static_cast((util::ToUnderlying(perm) & KMemoryPermission_UserMask) | KMemoryPermission_KernelRead | ((util::ToUnderlying(perm) & ams::svc::MemoryPermission_Write) ? KMemoryPermission_KernelWrite : KMemoryPermission_None) | (perm == ams::svc::MemoryPermission_None ? KMemoryPermission_NotMapped : KMemoryPermission_None));
}
enum KMemoryAttribute : u8 {
KMemoryAttribute_None = 0x00,
KMemoryAttribute_All = 0xFF,
KMemoryAttribute_UserMask = KMemoryAttribute_All,
KMemoryAttribute_Locked = ams::svc::MemoryAttribute_Locked,
KMemoryAttribute_IpcLocked = ams::svc::MemoryAttribute_IpcLocked,
KMemoryAttribute_DeviceShared = ams::svc::MemoryAttribute_DeviceShared,
KMemoryAttribute_Uncached = ams::svc::MemoryAttribute_Uncached,
KMemoryAttribute_PermissionLocked = ams::svc::MemoryAttribute_PermissionLocked,
KMemoryAttribute_SetMask = KMemoryAttribute_Uncached | KMemoryAttribute_PermissionLocked,
};
enum KMemoryBlockDisableMergeAttribute : u8 {
KMemoryBlockDisableMergeAttribute_None = 0,
KMemoryBlockDisableMergeAttribute_Normal = (1u << 0),
KMemoryBlockDisableMergeAttribute_DeviceLeft = (1u << 1),
KMemoryBlockDisableMergeAttribute_IpcLeft = (1u << 2),
KMemoryBlockDisableMergeAttribute_Locked = (1u << 3),
/* ... */
KMemoryBlockDisableMergeAttribute_DeviceRight = (1u << 5),
KMemoryBlockDisableMergeAttribute_AllLeft = KMemoryBlockDisableMergeAttribute_Normal | KMemoryBlockDisableMergeAttribute_DeviceLeft | KMemoryBlockDisableMergeAttribute_IpcLeft | KMemoryBlockDisableMergeAttribute_Locked,
KMemoryBlockDisableMergeAttribute_AllRight = KMemoryBlockDisableMergeAttribute_DeviceRight,
};
struct KMemoryInfo {
uintptr_t m_address;
size_t m_size;
KMemoryState m_state;
u16 m_device_disable_merge_left_count;
u16 m_device_disable_merge_right_count;
u16 m_ipc_lock_count;
u16 m_device_use_count;
u16 m_ipc_disable_merge_count;
KMemoryPermission m_permission;
KMemoryAttribute m_attribute;
KMemoryPermission m_original_permission;
KMemoryBlockDisableMergeAttribute m_disable_merge_attribute;
constexpr ams::svc::MemoryInfo GetSvcMemoryInfo() const {
return {
.base_address = m_address,
.size = m_size,
.state = static_cast(m_state & KMemoryState_Mask),
.attribute = static_cast(m_attribute & KMemoryAttribute_UserMask),
.permission = static_cast(m_permission & KMemoryPermission_UserMask),
.ipc_count = m_ipc_lock_count,
.device_count = m_device_use_count,
.padding = {},
};
}
constexpr uintptr_t GetAddress() const {
return m_address;
}
constexpr size_t GetSize() const {
return m_size;
}
constexpr size_t GetNumPages() const {
return this->GetSize() / PageSize;
}
constexpr uintptr_t GetEndAddress() const {
return this->GetAddress() + this->GetSize();
}
constexpr uintptr_t GetLastAddress() const {
return this->GetEndAddress() - 1;
}
constexpr u16 GetIpcLockCount() const {
return m_ipc_lock_count;
}
constexpr u16 GetIpcDisableMergeCount() const {
return m_ipc_disable_merge_count;
}
constexpr KMemoryState GetState() const {
return m_state;
}
constexpr ams::svc::MemoryState GetSvcState() const {
return static_cast(m_state & KMemoryState_Mask);
}
constexpr KMemoryPermission GetPermission() const {
return m_permission;
}
constexpr KMemoryPermission GetOriginalPermission() const {
return m_original_permission;
}
constexpr KMemoryAttribute GetAttribute() const {
return m_attribute;
}
constexpr KMemoryBlockDisableMergeAttribute GetDisableMergeAttribute() const {
return m_disable_merge_attribute;
}
};
class KMemoryBlock : public util::IntrusiveRedBlackTreeBaseNode {
private:
KMemoryPermission m_permission;
KMemoryPermission m_original_permission;
KMemoryAttribute m_attribute;
KMemoryBlockDisableMergeAttribute m_disable_merge_attribute;
KProcessAddress m_address;
u32 m_num_pages;
KMemoryState m_memory_state;
u16 m_ipc_lock_count;
u16 m_ipc_disable_merge_count;
u16 m_device_use_count;
u16 m_device_disable_merge_left_count;
u16 m_device_disable_merge_right_count;
public:
static constexpr ALWAYS_INLINE int Compare(const KMemoryBlock &lhs, const KMemoryBlock &rhs) {
if (lhs.GetAddress() < rhs.GetAddress()) {
return -1;
} else if (lhs.GetAddress() <= rhs.GetLastAddress()) {
return 0;
} else {
return 1;
}
}
public:
constexpr KProcessAddress GetAddress() const {
return m_address;
}
constexpr size_t GetNumPages() const {
return m_num_pages;
}
constexpr size_t GetSize() const {
return this->GetNumPages() * PageSize;
}
constexpr KProcessAddress GetEndAddress() const {
return this->GetAddress() + this->GetSize();
}
constexpr KProcessAddress GetLastAddress() const {
return this->GetEndAddress() - 1;
}
constexpr KMemoryState GetState() const {
return m_memory_state;
}
constexpr u16 GetIpcLockCount() const {
return m_ipc_lock_count;
}
constexpr u16 GetIpcDisableMergeCount() const {
return m_ipc_disable_merge_count;
}
constexpr u16 GetDeviceUseCount() const {
return m_device_use_count;
}
constexpr KMemoryPermission GetPermission() const {
return m_permission;
}
constexpr KMemoryPermission GetOriginalPermission() const {
return m_original_permission;
}
constexpr KMemoryAttribute GetAttribute() const {
return m_attribute;
}
constexpr KMemoryInfo GetMemoryInfo() const {
return {
.m_address = GetInteger(this->GetAddress()),
.m_size = this->GetSize(),
.m_state = m_memory_state,
.m_device_disable_merge_left_count = m_device_disable_merge_left_count,
.m_device_disable_merge_right_count = m_device_disable_merge_right_count,
.m_ipc_lock_count = m_ipc_lock_count,
.m_device_use_count = m_device_use_count,
.m_ipc_disable_merge_count = m_ipc_disable_merge_count,
.m_permission = m_permission,
.m_attribute = m_attribute,
.m_original_permission = m_original_permission,
.m_disable_merge_attribute = m_disable_merge_attribute,
};
}
public:
explicit KMemoryBlock() { /* ... */ }
constexpr KMemoryBlock(util::ConstantInitializeTag, KProcessAddress addr, u32 np, KMemoryState ms, KMemoryPermission p, KMemoryAttribute attr)
: util::IntrusiveRedBlackTreeBaseNode(util::ConstantInitialize), m_permission(p), m_original_permission(KMemoryPermission_None),
m_attribute(attr), m_disable_merge_attribute(), m_address(addr), m_num_pages(np), m_memory_state(ms), m_ipc_lock_count(0),
m_ipc_disable_merge_count(), m_device_use_count(0), m_device_disable_merge_left_count(), m_device_disable_merge_right_count()
{
/* ... */
}
constexpr void Initialize(KProcessAddress addr, u32 np, KMemoryState ms, KMemoryPermission p, KMemoryAttribute attr) {
MESOSPHERE_ASSERT_THIS();
m_device_disable_merge_left_count = 0;
m_device_disable_merge_right_count = 0;
m_address = addr;
m_num_pages = np;
m_memory_state = ms;
m_ipc_lock_count = 0;
m_device_use_count = 0;
m_permission = p;
m_original_permission = KMemoryPermission_None;
m_attribute = attr;
m_disable_merge_attribute = KMemoryBlockDisableMergeAttribute_None;
}
constexpr bool HasProperties(KMemoryState s, KMemoryPermission p, KMemoryAttribute a) const {
MESOSPHERE_ASSERT_THIS();
constexpr auto AttributeIgnoreMask = KMemoryAttribute_IpcLocked | KMemoryAttribute_DeviceShared;
return m_memory_state == s && m_permission == p && (m_attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask);
}
constexpr bool HasSameProperties(const KMemoryBlock &rhs) const {
MESOSPHERE_ASSERT_THIS();
return m_memory_state == rhs.m_memory_state &&
m_permission == rhs.m_permission &&
m_original_permission == rhs.m_original_permission &&
m_attribute == rhs.m_attribute &&
m_ipc_lock_count == rhs.m_ipc_lock_count &&
m_device_use_count == rhs.m_device_use_count;
}
constexpr bool CanMergeWith(const KMemoryBlock &rhs) const {
return this->HasSameProperties(rhs) &&
(m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute_AllRight) == 0 &&
(rhs.m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute_AllLeft) == 0;
}
constexpr bool Contains(KProcessAddress addr) const {
MESOSPHERE_ASSERT_THIS();
return this->GetAddress() <= addr && addr <= this->GetEndAddress();
}
constexpr void Add(const KMemoryBlock &added_block) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(added_block.GetNumPages() > 0);
MESOSPHERE_ASSERT(this->GetAddress() + added_block.GetSize() - 1 < this->GetEndAddress() + added_block.GetSize() - 1);
m_num_pages += added_block.GetNumPages();
m_disable_merge_attribute = static_cast(m_disable_merge_attribute | added_block.m_disable_merge_attribute);
m_device_disable_merge_right_count = added_block.m_device_disable_merge_right_count;
}
constexpr void Update(KMemoryState s, KMemoryPermission p, KMemoryAttribute a, bool set_disable_merge_attr, u8 set_mask, u8 clear_mask) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(m_original_permission == KMemoryPermission_None);
MESOSPHERE_ASSERT((m_attribute & KMemoryAttribute_IpcLocked) == 0);
m_memory_state = s;
m_permission = p;
m_attribute = static_cast(a | (m_attribute & (KMemoryAttribute_IpcLocked | KMemoryAttribute_DeviceShared)));
if (set_disable_merge_attr && set_mask != 0) {
m_disable_merge_attribute = static_cast(m_disable_merge_attribute | set_mask);
}
if (clear_mask != 0) {
m_disable_merge_attribute = static_cast(m_disable_merge_attribute & ~clear_mask);
}
}
constexpr void UpdateAttribute(u32 mask, u32 attr) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT((mask & KMemoryAttribute_IpcLocked) == 0);
MESOSPHERE_ASSERT((mask & KMemoryAttribute_DeviceShared) == 0);
m_attribute = static_cast((m_attribute & ~mask) | attr);
}
constexpr void Split(KMemoryBlock *block, KProcessAddress addr) {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(this->GetAddress() < addr);
MESOSPHERE_ASSERT(this->Contains(addr));
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(addr), PageSize));
block->m_address = m_address;
block->m_num_pages = (addr - this->GetAddress()) / PageSize;
block->m_memory_state = m_memory_state;
block->m_ipc_lock_count = m_ipc_lock_count;
block->m_device_use_count = m_device_use_count;
block->m_permission = m_permission;
block->m_original_permission = m_original_permission;
block->m_attribute = m_attribute;
block->m_disable_merge_attribute = static_cast(m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute_AllLeft);
block->m_ipc_disable_merge_count = m_ipc_disable_merge_count;
block->m_device_disable_merge_left_count = m_device_disable_merge_left_count;
block->m_device_disable_merge_right_count = 0;
m_address = addr;
m_num_pages -= block->m_num_pages;
m_ipc_disable_merge_count = 0;
m_device_disable_merge_left_count = 0;
m_disable_merge_attribute = static_cast(m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute_AllRight);
}
constexpr void UpdateDeviceDisableMergeStateForShareLeft(KMemoryPermission new_perm, bool left, bool right) {
/* New permission/right aren't used. */
MESOSPHERE_UNUSED(new_perm, right);
if (left) {
m_disable_merge_attribute = static_cast(m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute_DeviceLeft);
const u16 new_device_disable_merge_left_count = ++m_device_disable_merge_left_count;
MESOSPHERE_ABORT_UNLESS(new_device_disable_merge_left_count > 0);
}
}
constexpr void UpdateDeviceDisableMergeStateForShareRight(KMemoryPermission new_perm, bool left, bool right) {
/* New permission/left aren't used. */
MESOSPHERE_UNUSED(new_perm, left);
if (right) {
m_disable_merge_attribute = static_cast(m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute_DeviceRight);
const u16 new_device_disable_merge_right_count = ++m_device_disable_merge_right_count;
MESOSPHERE_ABORT_UNLESS(new_device_disable_merge_right_count > 0);
}
}
constexpr void UpdateDeviceDisableMergeStateForShare(KMemoryPermission new_perm, bool left, bool right) {
this->UpdateDeviceDisableMergeStateForShareLeft(new_perm, left, right);
this->UpdateDeviceDisableMergeStateForShareRight(new_perm, left, right);
}
constexpr void ShareToDevice(KMemoryPermission new_perm, bool left, bool right) {
/* New permission isn't used. */
MESOSPHERE_UNUSED(new_perm);
/* We must either be shared or have a zero lock count. */
MESOSPHERE_ASSERT((m_attribute & KMemoryAttribute_DeviceShared) == KMemoryAttribute_DeviceShared || m_device_use_count == 0);
/* Share. */
const u16 new_count = ++m_device_use_count;
MESOSPHERE_ABORT_UNLESS(new_count > 0);
m_attribute = static_cast(m_attribute | KMemoryAttribute_DeviceShared);
this->UpdateDeviceDisableMergeStateForShare(new_perm, left, right);
}
constexpr void UpdateDeviceDisableMergeStateForUnshareLeft(KMemoryPermission new_perm, bool left, bool right) {
/* New permission/right aren't used. */
MESOSPHERE_UNUSED(new_perm, right);
if (left) {
if (!m_device_disable_merge_left_count) {
return;
}
--m_device_disable_merge_left_count;
}
m_device_disable_merge_left_count = std::min(m_device_disable_merge_left_count, m_device_use_count);
if (m_device_disable_merge_left_count == 0) {
m_disable_merge_attribute = static_cast(m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute_DeviceLeft);
}
}
constexpr void UpdateDeviceDisableMergeStateForUnshareRight(KMemoryPermission new_perm, bool left, bool right) {
/* New permission/left aren't used. */
MESOSPHERE_UNUSED(new_perm, left);
if (right) {
const u16 old_device_disable_merge_right_count = m_device_disable_merge_right_count--;
MESOSPHERE_ASSERT(old_device_disable_merge_right_count > 0);
if (old_device_disable_merge_right_count == 1) {
m_disable_merge_attribute = static_cast(m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute_DeviceRight);
}
}
}
constexpr void UpdateDeviceDisableMergeStateForUnshare(KMemoryPermission new_perm, bool left, bool right) {
this->UpdateDeviceDisableMergeStateForUnshareLeft(new_perm, left, right);
this->UpdateDeviceDisableMergeStateForUnshareRight(new_perm, left, right);
}
constexpr void UnshareToDevice(KMemoryPermission new_perm, bool left, bool right) {
/* New permission isn't used. */
MESOSPHERE_UNUSED(new_perm);
/* We must be shared. */
MESOSPHERE_ASSERT((m_attribute & KMemoryAttribute_DeviceShared) == KMemoryAttribute_DeviceShared);
/* Unhare. */
const u16 old_count = m_device_use_count--;
MESOSPHERE_ABORT_UNLESS(old_count > 0);
if (old_count == 1) {
m_attribute = static_cast(m_attribute & ~KMemoryAttribute_DeviceShared);
}
this->UpdateDeviceDisableMergeStateForUnshare(new_perm, left, right);
}
constexpr void UnshareToDeviceRight(KMemoryPermission new_perm, bool left, bool right) {
/* New permission isn't used. */
MESOSPHERE_UNUSED(new_perm);
/* We must be shared. */
MESOSPHERE_ASSERT((m_attribute & KMemoryAttribute_DeviceShared) == KMemoryAttribute_DeviceShared);
/* Unhare. */
const u16 old_count = m_device_use_count--;
MESOSPHERE_ABORT_UNLESS(old_count > 0);
if (old_count == 1) {
m_attribute = static_cast(m_attribute & ~KMemoryAttribute_DeviceShared);
}
this->UpdateDeviceDisableMergeStateForUnshareRight(new_perm, left, right);
}
constexpr void LockForIpc(KMemoryPermission new_perm, bool left, bool right) {
/* We must either be locked or have a zero lock count. */
MESOSPHERE_ASSERT((m_attribute & KMemoryAttribute_IpcLocked) == KMemoryAttribute_IpcLocked || m_ipc_lock_count == 0);
/* Lock. */
const u16 new_lock_count = ++m_ipc_lock_count;
MESOSPHERE_ABORT_UNLESS(new_lock_count > 0);
/* If this is our first lock, update our permissions. */
if (new_lock_count == 1) {
MESOSPHERE_ASSERT(m_original_permission == KMemoryPermission_None);
MESOSPHERE_ASSERT((m_permission | new_perm | KMemoryPermission_NotMapped) == (m_permission | KMemoryPermission_NotMapped));
MESOSPHERE_ASSERT((m_permission & KMemoryPermission_UserExecute) != KMemoryPermission_UserExecute || (new_perm == KMemoryPermission_UserRead));
m_original_permission = m_permission;
m_permission = static_cast((new_perm & KMemoryPermission_IpcLockChangeMask) | (m_original_permission & ~KMemoryPermission_IpcLockChangeMask));
}
m_attribute = static_cast(m_attribute | KMemoryAttribute_IpcLocked);
if (left) {
m_disable_merge_attribute = static_cast(m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute_IpcLeft);
const u16 new_ipc_disable_merge_count = ++m_ipc_disable_merge_count;
MESOSPHERE_ABORT_UNLESS(new_ipc_disable_merge_count > 0);
}
MESOSPHERE_UNUSED(right);
}
constexpr void UnlockForIpc(KMemoryPermission new_perm, bool left, bool right) {
/* New permission isn't used. */
MESOSPHERE_UNUSED(new_perm);
/* We must be locked. */
MESOSPHERE_ASSERT((m_attribute & KMemoryAttribute_IpcLocked) == KMemoryAttribute_IpcLocked);
/* Unlock. */
const u16 old_lock_count = m_ipc_lock_count--;
MESOSPHERE_ABORT_UNLESS(old_lock_count > 0);
/* If this is our last unlock, update our permissions. */
if (old_lock_count == 1) {
MESOSPHERE_ASSERT(m_original_permission != KMemoryPermission_None);
m_permission = m_original_permission;
m_original_permission = KMemoryPermission_None;
m_attribute = static_cast(m_attribute & ~KMemoryAttribute_IpcLocked);
}
if (left) {
const u16 old_ipc_disable_merge_count = m_ipc_disable_merge_count--;
MESOSPHERE_ASSERT(old_ipc_disable_merge_count > 0);
if (old_ipc_disable_merge_count == 1) {
m_disable_merge_attribute = static_cast(m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute_IpcLeft);
}
}
MESOSPHERE_UNUSED(right);
}
constexpr KMemoryBlockDisableMergeAttribute GetDisableMergeAttribute() const {
return m_disable_merge_attribute;
}
};
static_assert(std::is_trivially_destructible::value);
}