mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 04:11:18 +00:00
kern: implement PermissionLock, update KPageTableBase attribute/alignment checks
This commit is contained in:
parent
777b6d285c
commit
ad5bd81d3f
5 changed files with 114 additions and 20 deletions
|
@ -191,7 +191,7 @@ namespace ams::kern {
|
|||
KMemoryAttribute_Uncached = ams::svc::MemoryAttribute_Uncached,
|
||||
KMemoryAttribute_PermissionLocked = ams::svc::MemoryAttribute_PermissionLocked,
|
||||
|
||||
KMemoryAttribute_SetMask = KMemoryAttribute_Uncached,
|
||||
KMemoryAttribute_SetMask = KMemoryAttribute_Uncached | KMemoryAttribute_PermissionLocked,
|
||||
};
|
||||
|
||||
enum KMemoryBlockDisableMergeAttribute : u8 {
|
||||
|
@ -331,6 +331,10 @@ namespace ams::kern {
|
|||
return this->GetEndAddress() - 1;
|
||||
}
|
||||
|
||||
constexpr KMemoryState GetState() const {
|
||||
return m_memory_state;
|
||||
}
|
||||
|
||||
constexpr u16 GetIpcLockCount() const {
|
||||
return m_ipc_lock_count;
|
||||
}
|
||||
|
@ -450,6 +454,14 @@ namespace ams::kern {
|
|||
}
|
||||
}
|
||||
|
||||
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<KMemoryAttribute>((m_attribute & ~mask) | attr);
|
||||
}
|
||||
|
||||
constexpr void Split(KMemoryBlock *block, KProcessAddress addr) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
MESOSPHERE_ASSERT(this->GetAddress() < addr);
|
||||
|
|
|
@ -104,7 +104,9 @@ namespace ams::kern {
|
|||
void Update(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr, KMemoryBlockDisableMergeAttribute set_disable_attr, KMemoryBlockDisableMergeAttribute clear_disable_attr);
|
||||
void UpdateLock(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, MemoryBlockLockFunction lock_func, KMemoryPermission perm);
|
||||
|
||||
void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm, KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr);
|
||||
void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm, KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr, KMemoryBlockDisableMergeAttribute set_disable_attr, KMemoryBlockDisableMergeAttribute clear_disable_attr);
|
||||
|
||||
void UpdateAttribute(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, u32 mask, u32 attr);
|
||||
|
||||
iterator FindIterator(KProcessAddress address) const {
|
||||
return m_memory_block_tree.find(KMemoryBlock(util::ConstantInitialize, address, 1, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None));
|
||||
|
|
|
@ -223,7 +223,7 @@ namespace ams::kern {
|
|||
}
|
||||
|
||||
/* Update block state. */
|
||||
it->Update(state, perm, attr, cur_address == address, set_disable_attr, clear_disable_attr);
|
||||
it->Update(state, perm, attr, it->GetAddress() == address, set_disable_attr, clear_disable_attr);
|
||||
cur_address += cur_info.GetSize();
|
||||
remaining_pages -= cur_info.GetNumPages();
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ namespace ams::kern {
|
|||
this->CoalesceForUpdate(allocator, address, num_pages);
|
||||
}
|
||||
|
||||
void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm, KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr) {
|
||||
void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm, KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm, KMemoryAttribute attr, KMemoryBlockDisableMergeAttribute set_disable_attr, KMemoryBlockDisableMergeAttribute clear_disable_attr) {
|
||||
/* Ensure for auditing that we never end up with an invalid tree. */
|
||||
KScopedMemoryBlockManagerAuditor auditor(this);
|
||||
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(address), PageSize));
|
||||
|
@ -270,7 +270,7 @@ namespace ams::kern {
|
|||
}
|
||||
|
||||
/* Update block state. */
|
||||
it->Update(state, perm, attr, false, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None);
|
||||
it->Update(state, perm, attr, it->GetAddress() == address, set_disable_attr, clear_disable_attr);
|
||||
cur_address += cur_info.GetSize();
|
||||
remaining_pages -= cur_info.GetNumPages();
|
||||
} else {
|
||||
|
@ -336,6 +336,62 @@ namespace ams::kern {
|
|||
this->CoalesceForUpdate(allocator, address, num_pages);
|
||||
}
|
||||
|
||||
void KMemoryBlockManager::UpdateAttribute(KMemoryBlockManagerUpdateAllocator *allocator, KProcessAddress address, size_t num_pages, u32 mask, u32 attr) {
|
||||
/* Ensure for auditing that we never end up with an invalid tree. */
|
||||
KScopedMemoryBlockManagerAuditor auditor(this);
|
||||
MESOSPHERE_ASSERT(util::IsAligned(GetInteger(address), PageSize));
|
||||
|
||||
KProcessAddress cur_address = address;
|
||||
size_t remaining_pages = num_pages;
|
||||
iterator it = this->FindIterator(address);
|
||||
|
||||
while (remaining_pages > 0) {
|
||||
const size_t remaining_size = remaining_pages * PageSize;
|
||||
KMemoryInfo cur_info = it->GetMemoryInfo();
|
||||
|
||||
if ((it->GetAttribute() & mask) != attr) {
|
||||
/* If we need to, create a new block before and insert it. */
|
||||
if (cur_info.GetAddress() != GetInteger(cur_address)) {
|
||||
KMemoryBlock *new_block = allocator->Allocate();
|
||||
|
||||
it->Split(new_block, cur_address);
|
||||
it = m_memory_block_tree.insert(*new_block);
|
||||
it++;
|
||||
|
||||
cur_info = it->GetMemoryInfo();
|
||||
cur_address = cur_info.GetAddress();
|
||||
}
|
||||
|
||||
/* If we need to, create a new block after and insert it. */
|
||||
if (cur_info.GetSize() > remaining_size) {
|
||||
KMemoryBlock *new_block = allocator->Allocate();
|
||||
|
||||
it->Split(new_block, cur_address + remaining_size);
|
||||
it = m_memory_block_tree.insert(*new_block);
|
||||
|
||||
cur_info = it->GetMemoryInfo();
|
||||
}
|
||||
|
||||
/* Update block state. */
|
||||
it->UpdateAttribute(mask, attr);
|
||||
cur_address += cur_info.GetSize();
|
||||
remaining_pages -= cur_info.GetNumPages();
|
||||
} else {
|
||||
/* If we already have the right attributes, just advance. */
|
||||
if (cur_address + remaining_size < cur_info.GetEndAddress()) {
|
||||
remaining_pages = 0;
|
||||
cur_address += remaining_size;
|
||||
} else {
|
||||
remaining_pages = (cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize;
|
||||
cur_address = cur_info.GetEndAddress();
|
||||
}
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
this->CoalesceForUpdate(allocator, address, num_pages);
|
||||
}
|
||||
|
||||
/* Debug. */
|
||||
bool KMemoryBlockManager::CheckState() const {
|
||||
/* If we fail, we should dump blocks. */
|
||||
|
|
|
@ -987,7 +987,7 @@ namespace ams::kern {
|
|||
|
||||
/* Verify that the destination memory is aliasable code. */
|
||||
size_t num_dst_allocator_blocks;
|
||||
R_TRY(this->CheckMemoryStateContiguous(std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState_FlagCanCodeAlias, KMemoryState_FlagCanCodeAlias, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_All, KMemoryAttribute_None));
|
||||
R_TRY(this->CheckMemoryStateContiguous(std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState_FlagCanCodeAlias, KMemoryState_FlagCanCodeAlias, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_All & ~KMemoryAttribute_PermissionLocked, KMemoryAttribute_None));
|
||||
|
||||
/* Determine whether any pages being unmapped are code. */
|
||||
bool any_code_pages = false;
|
||||
|
@ -1649,9 +1649,10 @@ namespace ams::kern {
|
|||
KMemoryAttribute old_attr;
|
||||
size_t num_allocator_blocks;
|
||||
constexpr u32 AttributeTestMask = ~(KMemoryAttribute_SetMask | KMemoryAttribute_DeviceShared);
|
||||
const u32 state_test_mask = ((mask & KMemoryAttribute_Uncached) ? static_cast<u32>(KMemoryState_FlagCanChangeAttribute) : 0) | ((mask & KMemoryAttribute_PermissionLocked) ? static_cast<u32>(KMemoryState_FlagCanPermissionLock) : 0);
|
||||
R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr), std::addressof(num_allocator_blocks),
|
||||
addr, size,
|
||||
KMemoryState_FlagCanChangeAttribute, KMemoryState_FlagCanChangeAttribute,
|
||||
state_test_mask, state_test_mask,
|
||||
KMemoryPermission_None, KMemoryPermission_None,
|
||||
AttributeTestMask, KMemoryAttribute_None, ~AttributeTestMask));
|
||||
|
||||
|
@ -1663,15 +1664,18 @@ namespace ams::kern {
|
|||
/* We're going to perform an update, so create a helper. */
|
||||
KScopedPageTableUpdater updater(this);
|
||||
|
||||
/* Determine the new attribute. */
|
||||
const KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(((old_attr & ~mask) | (attr & mask)));
|
||||
/* If we need to, perform a change attribute operation. */
|
||||
if ((mask & KMemoryAttribute_Uncached) != 0) {
|
||||
/* Determine the new attribute. */
|
||||
const KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(((old_attr & ~mask) | (attr & mask)));
|
||||
|
||||
/* Perform operation. */
|
||||
const KPageProperties properties = { old_perm, false, (new_attr & KMemoryAttribute_Uncached) != 0, DisableMergeAttribute_None };
|
||||
R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissionsAndRefreshAndFlush, false));
|
||||
/* Perform operation. */
|
||||
const KPageProperties properties = { old_perm, false, (new_attr & KMemoryAttribute_Uncached) != 0, DisableMergeAttribute_None };
|
||||
R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null<KPhysicalAddress>, false, properties, OperationType_ChangePermissionsAndRefreshAndFlush, false));
|
||||
}
|
||||
|
||||
/* Update the blocks. */
|
||||
m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm, new_attr, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None);
|
||||
m_memory_block_manager.UpdateAttribute(std::addressof(allocator), addr, num_pages, mask, attr);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
@ -1957,10 +1961,16 @@ namespace ams::kern {
|
|||
|
||||
/* Select an address to map at. */
|
||||
KProcessAddress addr = Null<KProcessAddress>;
|
||||
const size_t phys_alignment = std::min(std::min(util::GetAlignment(GetInteger(phys_addr)), util::GetAlignment(size)), MaxPhysicalMapAlignment);
|
||||
for (s32 block_type = KPageTable::GetMaxBlockType(); block_type >= 0; block_type--) {
|
||||
const size_t alignment = KPageTable::GetBlockSize(static_cast<KPageTable::BlockType>(block_type));
|
||||
if (alignment > phys_alignment) {
|
||||
|
||||
const KPhysicalAddress aligned_phys = util::AlignUp(GetInteger(phys_addr), alignment) + alignment - 1;
|
||||
if (aligned_phys <= phys_addr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const KPhysicalAddress last_aligned_paddr = util::AlignDown(GetInteger(last) + 1, alignment) - 1;
|
||||
if (!(last_aligned_paddr <= last && aligned_phys <= last_aligned_paddr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2142,10 +2152,16 @@ namespace ams::kern {
|
|||
|
||||
/* Select an address to map at. */
|
||||
KProcessAddress addr = Null<KProcessAddress>;
|
||||
const size_t phys_alignment = std::min(std::min(util::GetAlignment(GetInteger(phys_addr)), util::GetAlignment(size)), MaxPhysicalMapAlignment);
|
||||
for (s32 block_type = KPageTable::GetMaxBlockType(); block_type >= 0; block_type--) {
|
||||
const size_t alignment = KPageTable::GetBlockSize(static_cast<KPageTable::BlockType>(block_type));
|
||||
if (alignment > phys_alignment) {
|
||||
|
||||
const KPhysicalAddress aligned_phys = util::AlignUp(GetInteger(phys_addr), alignment) + alignment - 1;
|
||||
if (aligned_phys <= phys_addr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const KPhysicalAddress last_aligned_paddr = util::AlignDown(GetInteger(last) + 1, alignment) - 1;
|
||||
if (!(last_aligned_paddr <= last && aligned_phys <= last_aligned_paddr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -4467,7 +4483,9 @@ namespace ams::kern {
|
|||
/* Update the relevant memory blocks. */
|
||||
m_memory_block_manager.UpdateIfMatch(std::addressof(allocator), address, size / PageSize,
|
||||
KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None,
|
||||
KMemoryState_Normal, KMemoryPermission_UserReadWrite, KMemoryAttribute_None);
|
||||
KMemoryState_Normal, KMemoryPermission_UserReadWrite, KMemoryAttribute_None,
|
||||
address == this->GetAliasRegionStart() ? KMemoryBlockDisableMergeAttribute_Normal : KMemoryBlockDisableMergeAttribute_None,
|
||||
KMemoryBlockDisableMergeAttribute_None);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
@ -4562,6 +4580,9 @@ namespace ams::kern {
|
|||
|
||||
/* Iterate over the memory, unmapping as we go. */
|
||||
auto it = m_memory_block_manager.FindIterator(cur_address);
|
||||
|
||||
const auto clear_merge_attr = (it->GetState() == KMemoryState_Normal && it->GetAddress() == this->GetAliasRegionStart() && it->GetAddress() == address) ? KMemoryBlockDisableMergeAttribute_Normal : KMemoryBlockDisableMergeAttribute_None;
|
||||
|
||||
while (true) {
|
||||
/* Check that the iterator is valid. */
|
||||
MESOSPHERE_ASSERT(it != m_memory_block_manager.end());
|
||||
|
@ -4594,7 +4615,7 @@ namespace ams::kern {
|
|||
m_resource_limit->Release(ams::svc::LimitableResource_PhysicalMemoryMax, mapped_size);
|
||||
|
||||
/* Update memory blocks. */
|
||||
m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None);
|
||||
m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, clear_merge_attr);
|
||||
|
||||
/* We succeeded. */
|
||||
R_SUCCEED();
|
||||
|
|
|
@ -58,10 +58,13 @@ namespace ams::kern::svc {
|
|||
R_UNLESS((address < address + size), svc::ResultInvalidCurrentMemory());
|
||||
|
||||
/* Validate the attribute and mask. */
|
||||
constexpr u32 SupportedMask = ams::svc::MemoryAttribute_Uncached;
|
||||
constexpr u32 SupportedMask = ams::svc::MemoryAttribute_Uncached | ams::svc::MemoryAttribute_PermissionLocked;
|
||||
R_UNLESS((mask | attr) == mask, svc::ResultInvalidCombination());
|
||||
R_UNLESS((mask | attr | SupportedMask) == SupportedMask, svc::ResultInvalidCombination());
|
||||
|
||||
/* Check that permission locked is either being set or not masked. */
|
||||
R_UNLESS((mask & ams::svc::MemoryAttribute_PermissionLocked) == (attr & ams::svc::MemoryAttribute_PermissionLocked), svc::ResultInvalidCombination());
|
||||
|
||||
/* Validate that the region is in range for the current process. */
|
||||
auto &page_table = GetCurrentProcess().GetPageTable();
|
||||
R_UNLESS(page_table.Contains(address, size), svc::ResultInvalidCurrentMemory());
|
||||
|
|
Loading…
Reference in a new issue