diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index 9e51c33ce0..dcca47f1b7 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -70,12 +70,12 @@ enum class KMemoryState : u32 {
     ThreadLocal =
         static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted,
 
-    Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
-                  FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
-                  FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+    Transfered = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
+                 FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
+                 FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
 
-    SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
-                        FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+    SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
+                       FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
 
     SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
                  FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
@@ -93,6 +93,8 @@ enum class KMemoryState : u32 {
     GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
                     FlagReferenceCounted | FlagCanDebug,
     CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted,
+
+    Coverage = static_cast<u32>(Svc::MemoryState::Coverage) | FlagMapped,
 };
 DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
 
@@ -108,8 +110,8 @@ static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x03FFBD09);
 static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x005C3C0A);
 static_assert(static_cast<u32>(KMemoryState::Stack) == 0x005C3C0B);
 static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0040200C);
-static_assert(static_cast<u32>(KMemoryState::Transferred) == 0x015C3C0D);
-static_assert(static_cast<u32>(KMemoryState::SharedTransferred) == 0x005C380E);
+static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x015C3C0D);
+static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x005C380E);
 static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0040380F);
 static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
 static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x005C3811);
@@ -117,6 +119,7 @@ static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x004C2812);
 static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
 static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x00402214);
 static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
+static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
 
 enum class KMemoryPermission : u8 {
     None = 0,
@@ -155,7 +158,13 @@ enum class KMemoryPermission : u8 {
 DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
 
 constexpr KMemoryPermission ConvertToKMemoryPermission(Svc::MemoryPermission perm) {
-    return static_cast<KMemoryPermission>(perm);
+    return static_cast<KMemoryPermission>(
+        (static_cast<KMemoryPermission>(perm) & KMemoryPermission::UserMask) |
+        KMemoryPermission::KernelRead |
+        ((static_cast<KMemoryPermission>(perm) & KMemoryPermission::UserWrite)
+         << KMemoryPermission::KernelShift) |
+        (perm == Svc::MemoryPermission::None ? KMemoryPermission::NotMapped
+                                             : KMemoryPermission::None));
 }
 
 enum class KMemoryAttribute : u8 {
@@ -169,6 +178,8 @@ enum class KMemoryAttribute : u8 {
     DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
     Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
 
+    SetMask = Uncached,
+
     IpcAndDeviceMapped = IpcLocked | DeviceShared,
     LockedAndIpcLocked = Locked | IpcLocked,
     DeviceSharedAndUncached = DeviceShared | Uncached
@@ -215,6 +226,15 @@ struct KMemoryInfo {
     constexpr VAddr GetLastAddress() const {
         return GetEndAddress() - 1;
     }
+    constexpr KMemoryState GetState() const {
+        return state;
+    }
+    constexpr KMemoryAttribute GetAttribute() const {
+        return attribute;
+    }
+    constexpr KMemoryPermission GetPermission() const {
+        return perm;
+    }
 };
 
 class KMemoryBlock final {
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 4da509224d..6077985b55 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -305,8 +305,8 @@ ResultCode KPageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std:
 
     KMemoryState state{};
     KMemoryPermission perm{};
-    CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, src_addr, size, KMemoryState::All,
-                                  KMemoryState::Normal, KMemoryPermission::All,
+    CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size,
+                                  KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All,
                                   KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask,
                                   KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
 
@@ -344,14 +344,14 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st
 
     const std::size_t num_pages{size / PageSize};
 
-    CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, src_addr, size, KMemoryState::All,
-                                  KMemoryState::Normal, KMemoryPermission::None,
+    CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size,
+                                  KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
                                   KMemoryPermission::None, KMemoryAttribute::Mask,
                                   KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
 
     KMemoryState state{};
     CASCADE_CODE(CheckMemoryState(
-        &state, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias,
+        &state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias,
         KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
         KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
     CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None,
@@ -553,7 +553,7 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
 
     KMemoryState src_state{};
     CASCADE_CODE(CheckMemoryState(
-        &src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
+        &src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
         KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::ReadAndWrite,
         KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
 
@@ -592,13 +592,13 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
 
     KMemoryState src_state{};
     CASCADE_CODE(CheckMemoryState(
-        &src_state, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
+        &src_state, nullptr, nullptr, nullptr, src_addr, size, KMemoryState::FlagCanAlias,
         KMemoryState::FlagCanAlias, KMemoryPermission::All, KMemoryPermission::None,
         KMemoryAttribute::Mask, KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
 
     KMemoryPermission dst_perm{};
-    CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, dst_addr, size, KMemoryState::All,
-                                  KMemoryState::Stack, KMemoryPermission::None,
+    CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, nullptr, dst_addr, size,
+                                  KMemoryState::All, KMemoryState::Stack, KMemoryPermission::None,
                                   KMemoryPermission::None, KMemoryAttribute::Mask,
                                   KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
 
@@ -721,7 +721,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
     KMemoryPermission prev_perm{};
 
     CASCADE_CODE(CheckMemoryState(
-        &prev_state, &prev_perm, nullptr, addr, size, KMemoryState::FlagCode,
+        &prev_state, &prev_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCode,
         KMemoryState::FlagCode, KMemoryPermission::None, KMemoryPermission::None,
         KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
 
@@ -782,7 +782,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
     KMemoryAttribute attribute{};
 
     CASCADE_CODE(CheckMemoryState(
-        &state, nullptr, &attribute, addr, size,
+        &state, nullptr, &attribute, nullptr, addr, size,
         KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
         KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::All,
         KMemoryPermission::ReadAndWrite, KMemoryAttribute::Mask, KMemoryAttribute::None,
@@ -799,7 +799,7 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
     KMemoryState state{};
 
     CASCADE_CODE(
-        CheckMemoryState(&state, nullptr, nullptr, addr, size,
+        CheckMemoryState(&state, nullptr, nullptr, nullptr, addr, size,
                          KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
                          KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
                          KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask,
@@ -820,7 +820,7 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
     KMemoryState old_state;
     KMemoryPermission old_perm;
     R_TRY(this->CheckMemoryState(
-        std::addressof(old_state), std::addressof(old_perm), nullptr, addr, size,
+        std::addressof(old_state), std::addressof(old_perm), nullptr, nullptr, addr, size,
         KMemoryState::FlagCanReprotect, KMemoryState::FlagCanReprotect, KMemoryPermission::None,
         KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
 
@@ -837,24 +837,36 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
     return ResultSuccess;
 }
 
-ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
-                                          KMemoryAttribute value) {
+ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) {
+    const size_t num_pages = size / PageSize;
+    ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) ==
+           KMemoryAttribute::SetMask);
+
+    // Lock the table.
     std::lock_guard lock{page_table_lock};
 
-    KMemoryState state{};
-    KMemoryPermission perm{};
-    KMemoryAttribute attribute{};
-
-    CASCADE_CODE(CheckMemoryState(
-        &state, &perm, &attribute, addr, size, KMemoryState::FlagCanChangeAttribute,
+    // Verify we can change the memory attribute.
+    KMemoryState old_state;
+    KMemoryPermission old_perm;
+    KMemoryAttribute old_attr;
+    size_t num_allocator_blocks;
+    constexpr auto AttributeTestMask =
+        ~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
+    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, KMemoryPermission::None, KMemoryPermission::None,
-        KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
-        KMemoryAttribute::DeviceSharedAndUncached));
+        AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
 
-    attribute = attribute & ~mask;
-    attribute = attribute | (mask & value);
+    // Determine the new attribute.
+    const auto new_attr = ((old_attr & static_cast<KMemoryAttribute>(~mask)) |
+                           static_cast<KMemoryAttribute>(attr & mask));
 
-    block_manager->Update(addr, size / PageSize, state, perm, attribute);
+    // Perform operation.
+    this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh);
+
+    // Update the blocks.
+    block_manager->Update(addr, num_pages, old_state, old_perm, new_attr);
 
     return ResultSuccess;
 }
@@ -1019,7 +1031,7 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
 
     KMemoryPermission perm{};
     if (const ResultCode result{CheckMemoryState(
-            nullptr, &perm, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
+            nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
             KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
             KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
             KMemoryAttribute::DeviceSharedAndUncached)};
@@ -1042,7 +1054,7 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
 
     KMemoryPermission perm{};
     if (const ResultCode result{CheckMemoryState(
-            nullptr, &perm, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
+            nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
             KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
             KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
             KMemoryAttribute::DeviceSharedAndUncached)};
@@ -1068,7 +1080,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
     KMemoryPermission old_perm{};
 
     if (const ResultCode result{CheckMemoryState(
-            nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
+            nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
             KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
             KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
         result.IsError()) {
@@ -1095,7 +1107,7 @@ ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
     KMemoryPermission old_perm{};
 
     if (const ResultCode result{CheckMemoryState(
-            nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
+            nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
             KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
             KMemoryAttribute::All, KMemoryAttribute::Locked)};
         result.IsError()) {
@@ -1225,18 +1237,19 @@ constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
         return alias_region_start;
     case KMemoryState::Stack:
         return stack_region_start;
-    case KMemoryState::Io:
     case KMemoryState::Static:
     case KMemoryState::ThreadLocal:
         return kernel_map_region_start;
+    case KMemoryState::Io:
     case KMemoryState::Shared:
     case KMemoryState::AliasCode:
     case KMemoryState::AliasCodeData:
-    case KMemoryState::Transferred:
-    case KMemoryState::SharedTransferred:
+    case KMemoryState::Transfered:
+    case KMemoryState::SharedTransfered:
     case KMemoryState::SharedCode:
     case KMemoryState::GeneratedCode:
     case KMemoryState::CodeOut:
+    case KMemoryState::Coverage:
         return alias_code_region_start;
     case KMemoryState::Code:
     case KMemoryState::CodeData:
@@ -1260,18 +1273,19 @@ constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
         return alias_region_end - alias_region_start;
     case KMemoryState::Stack:
         return stack_region_end - stack_region_start;
-    case KMemoryState::Io:
     case KMemoryState::Static:
     case KMemoryState::ThreadLocal:
         return kernel_map_region_end - kernel_map_region_start;
+    case KMemoryState::Io:
     case KMemoryState::Shared:
     case KMemoryState::AliasCode:
     case KMemoryState::AliasCodeData:
-    case KMemoryState::Transferred:
-    case KMemoryState::SharedTransferred:
+    case KMemoryState::Transfered:
+    case KMemoryState::SharedTransfered:
     case KMemoryState::SharedCode:
     case KMemoryState::GeneratedCode:
     case KMemoryState::CodeOut:
+    case KMemoryState::Coverage:
         return alias_code_region_end - alias_code_region_start;
     case KMemoryState::Code:
     case KMemoryState::CodeData:
@@ -1283,15 +1297,18 @@ constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
 }
 
 bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) const {
-    const VAddr end{addr + size};
-    const VAddr last{end - 1};
-    const VAddr region_start{GetRegionAddress(state)};
-    const std::size_t region_size{GetRegionSize(state)};
-    const bool is_in_region{region_start <= addr && addr < end &&
-                            last <= region_start + region_size - 1};
-    const bool is_in_heap{!(end <= heap_region_start || heap_region_end <= addr)};
-    const bool is_in_alias{!(end <= alias_region_start || alias_region_end <= addr)};
+    const VAddr end = addr + size;
+    const VAddr last = end - 1;
 
+    const VAddr region_start = this->GetRegionAddress(state);
+    const size_t region_size = this->GetRegionSize(state);
+
+    const bool is_in_region =
+        region_start <= addr && addr < end && last <= region_start + region_size - 1;
+    const bool is_in_heap = !(end <= heap_region_start || heap_region_end <= addr ||
+                              heap_region_start == heap_region_end);
+    const bool is_in_alias = !(end <= alias_region_start || alias_region_end <= addr ||
+                               alias_region_start == alias_region_end);
     switch (state) {
     case KMemoryState::Free:
     case KMemoryState::Kernel:
@@ -1305,11 +1322,12 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co
     case KMemoryState::AliasCodeData:
     case KMemoryState::Stack:
     case KMemoryState::ThreadLocal:
-    case KMemoryState::Transferred:
-    case KMemoryState::SharedTransferred:
+    case KMemoryState::Transfered:
+    case KMemoryState::SharedTransfered:
     case KMemoryState::SharedCode:
     case KMemoryState::GeneratedCode:
     case KMemoryState::CodeOut:
+    case KMemoryState::Coverage:
         return is_in_region && !is_in_heap && !is_in_alias;
     case KMemoryState::Normal:
         ASSERT(is_in_heap);
@@ -1324,91 +1342,29 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co
     }
 }
 
-constexpr ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
+ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
+                                        KMemoryState state, KMemoryPermission perm_mask,
+                                        KMemoryPermission perm, KMemoryAttribute attr_mask,
+                                        KMemoryAttribute attr) const {
+    // Validate the states match expectation.
+    R_UNLESS((info.state & state_mask) == state, ResultInvalidCurrentMemory);
+    R_UNLESS((info.perm & perm_mask) == perm, ResultInvalidCurrentMemory);
+    R_UNLESS((info.attribute & attr_mask) == attr, ResultInvalidCurrentMemory);
+
+    return ResultSuccess;
+}
+
+ResultCode KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
+                                                  std::size_t size, KMemoryState state_mask,
                                                   KMemoryState state, KMemoryPermission perm_mask,
                                                   KMemoryPermission perm,
                                                   KMemoryAttribute attr_mask,
                                                   KMemoryAttribute attr) const {
-    // Validate the states match expectation
-    if ((info.state & state_mask) != state) {
-        return ResultInvalidCurrentMemory;
-    }
-    if ((info.perm & perm_mask) != perm) {
-        return ResultInvalidCurrentMemory;
-    }
-    if ((info.attribute & attr_mask) != attr) {
-        return ResultInvalidCurrentMemory;
-    }
+    ASSERT(this->IsLockedByCurrentThread());
 
-    return ResultSuccess;
-}
-
-ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
-                                        KMemoryAttribute* out_attr, VAddr addr, std::size_t size,
-                                        KMemoryState state_mask, KMemoryState state,
-                                        KMemoryPermission perm_mask, KMemoryPermission perm,
-                                        KMemoryAttribute attr_mask, KMemoryAttribute attr,
-                                        KMemoryAttribute ignore_attr) {
-    std::lock_guard lock{page_table_lock};
-
-    // Get information about the first block
-    const VAddr last_addr{addr + size - 1};
-    KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)};
-    KMemoryInfo info{it->GetMemoryInfo()};
-
-    // Validate all blocks in the range have correct state
-    const KMemoryState first_state{info.state};
-    const KMemoryPermission first_perm{info.perm};
-    const KMemoryAttribute first_attr{info.attribute};
-
-    while (true) {
-        // Validate the current block
-        if (!(info.state == first_state)) {
-            return ResultInvalidCurrentMemory;
-        }
-        if (!(info.perm == first_perm)) {
-            return ResultInvalidCurrentMemory;
-        }
-        if (!((info.attribute | static_cast<KMemoryAttribute>(ignore_attr)) ==
-              (first_attr | static_cast<KMemoryAttribute>(ignore_attr)))) {
-            return ResultInvalidCurrentMemory;
-        }
-
-        // Validate against the provided masks
-        CASCADE_CODE(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
-
-        // Break once we're done
-        if (last_addr <= info.GetLastAddress()) {
-            break;
-        }
-
-        // Advance our iterator
-        it++;
-        ASSERT(it != block_manager->cend());
-        info = it->GetMemoryInfo();
-    }
-
-    // Write output state
-    if (out_state) {
-        *out_state = first_state;
-    }
-    if (out_perm) {
-        *out_perm = first_perm;
-    }
-    if (out_attr) {
-        *out_attr = first_attr & static_cast<KMemoryAttribute>(~ignore_attr);
-    }
-
-    return ResultSuccess;
-}
-
-ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
-                                        KMemoryState state_mask, KMemoryState state,
-                                        KMemoryPermission perm_mask, KMemoryPermission perm,
-                                        KMemoryAttribute attr_mask, KMemoryAttribute attr) const {
     // Get information about the first block.
     const VAddr last_addr = addr + size - 1;
-    KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)};
+    KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr);
     KMemoryInfo info = it->GetMemoryInfo();
 
     // If the start address isn't aligned, we need a block.
@@ -1417,7 +1373,7 @@ ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, s
 
     while (true) {
         // Validate against the provided masks.
-        R_TRY(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
+        R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
 
         // Break once we're done.
         if (last_addr <= info.GetLastAddress()) {
@@ -1426,6 +1382,7 @@ ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, s
 
         // Advance our iterator.
         it++;
+        ASSERT(it != block_manager->cend());
         info = it->GetMemoryInfo();
     }
 
@@ -1440,4 +1397,66 @@ ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, s
     return ResultSuccess;
 }
 
+ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
+                                        KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
+                                        VAddr addr, std::size_t size, KMemoryState state_mask,
+                                        KMemoryState state, KMemoryPermission perm_mask,
+                                        KMemoryPermission perm, KMemoryAttribute attr_mask,
+                                        KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
+    ASSERT(this->IsLockedByCurrentThread());
+
+    // Get information about the first block.
+    const VAddr last_addr = addr + size - 1;
+    KMemoryBlockManager::const_iterator it = block_manager->FindIterator(addr);
+    KMemoryInfo info = it->GetMemoryInfo();
+
+    // If the start address isn't aligned, we need a block.
+    const size_t blocks_for_start_align =
+        (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0;
+
+    // Validate all blocks in the range have correct state.
+    const KMemoryState first_state = info.state;
+    const KMemoryPermission first_perm = info.perm;
+    const KMemoryAttribute first_attr = info.attribute;
+    while (true) {
+        // Validate the current block.
+        R_UNLESS(info.state == first_state, ResultInvalidCurrentMemory);
+        R_UNLESS(info.perm == first_perm, ResultInvalidCurrentMemory);
+        R_UNLESS((info.attribute | ignore_attr) == (first_attr | ignore_attr),
+                 ResultInvalidCurrentMemory);
+
+        // Validate against the provided masks.
+        R_TRY(this->CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
+
+        // Break once we're done.
+        if (last_addr <= info.GetLastAddress()) {
+            break;
+        }
+
+        // Advance our iterator.
+        it++;
+        ASSERT(it != block_manager->cend());
+        info = it->GetMemoryInfo();
+    }
+
+    // If the end address isn't aligned, we need a block.
+    const size_t blocks_for_end_align =
+        (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
+
+    // Write output state.
+    if (out_state != nullptr) {
+        *out_state = first_state;
+    }
+    if (out_perm != nullptr) {
+        *out_perm = first_perm;
+    }
+    if (out_attr != nullptr) {
+        *out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr);
+    }
+    if (out_blocks_needed != nullptr) {
+        *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
+    }
+    return ResultSuccess;
+}
+
 } // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 564410dca2..9235b822a5 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -48,8 +48,7 @@ public:
     ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
     ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
     ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm);
-    ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
-                                  KMemoryAttribute value);
+    ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr);
     ResultCode SetMaxHeapSize(std::size_t size);
     ResultCode SetHeapSize(VAddr* out, std::size_t size);
     ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
@@ -102,28 +101,50 @@ private:
     constexpr VAddr GetRegionAddress(KMemoryState state) const;
     constexpr std::size_t GetRegionSize(KMemoryState state) const;
 
-    constexpr ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
+    ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
+                                          std::size_t size, KMemoryState state_mask,
                                           KMemoryState state, KMemoryPermission perm_mask,
                                           KMemoryPermission perm, KMemoryAttribute attr_mask,
                                           KMemoryAttribute attr) const;
+    ResultCode CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask,
+                                          KMemoryState state, KMemoryPermission perm_mask,
+                                          KMemoryPermission perm, KMemoryAttribute attr_mask,
+                                          KMemoryAttribute attr) const {
+        return this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
+                                                perm, attr_mask, attr);
+    }
+
+    ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
+                                KMemoryState state, KMemoryPermission perm_mask,
+                                KMemoryPermission perm, KMemoryAttribute attr_mask,
+                                KMemoryAttribute attr) const;
     ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
-                                KMemoryAttribute* out_attr, VAddr addr, std::size_t size,
-                                KMemoryState state_mask, KMemoryState state,
-                                KMemoryPermission perm_mask, KMemoryPermission perm,
-                                KMemoryAttribute attr_mask, KMemoryAttribute attr,
-                                KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr);
-    ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
+                                KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
+                                VAddr addr, std::size_t size, KMemoryState state_mask,
                                 KMemoryState state, KMemoryPermission perm_mask,
                                 KMemoryPermission perm, KMemoryAttribute attr_mask,
                                 KMemoryAttribute attr,
-                                KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) {
-        return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask,
-                                perm, attr_mask, attr, ignore_attr);
-    }
-    ResultCode CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
+                                KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
+    ResultCode CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size,
                                 KMemoryState state_mask, KMemoryState state,
                                 KMemoryPermission perm_mask, KMemoryPermission perm,
-                                KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
+                                KMemoryAttribute attr_mask, KMemoryAttribute attr,
+                                KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
+        return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
+                                state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
+    }
+    ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask,
+                                KMemoryState state, KMemoryPermission perm_mask,
+                                KMemoryPermission perm, KMemoryAttribute attr_mask,
+                                KMemoryAttribute attr,
+                                KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
+        return this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
+                                      attr_mask, attr, ignore_attr);
+    }
+
+    bool IsLockedByCurrentThread() const {
+        return true;
+    }
 
     std::recursive_mutex page_table_lock;
     std::unique_ptr<KMemoryBlockManager> block_manager;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 250ef9042d..4362508a3a 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -168,6 +168,9 @@ constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
 
 static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size,
                                       MemoryPermission perm) {
+    LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
+              perm);
+
     // Validate address / size.
     R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
     R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
@@ -186,46 +189,33 @@ static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 s
 }
 
 static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
-                                     u32 attribute) {
+                                     u32 attr) {
     LOG_DEBUG(Kernel_SVC,
               "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
-              size, mask, attribute);
+              size, mask, attr);
 
-    if (!Common::Is4KBAligned(address)) {
-        LOG_ERROR(Kernel_SVC, "Address not page aligned (0x{:016X})", address);
-        return ResultInvalidAddress;
-    }
+    // Validate address / size.
+    R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+    R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+    R_UNLESS(size > 0, ResultInvalidSize);
+    R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
 
-    if (size == 0 || !Common::Is4KBAligned(size)) {
-        LOG_ERROR(Kernel_SVC, "Invalid size (0x{:X}). Size must be non-zero and page aligned.",
-                  size);
-        return ResultInvalidAddress;
-    }
-
-    if (!IsValidAddressRange(address, size)) {
-        LOG_ERROR(Kernel_SVC, "Address range overflowed (Address: 0x{:016X}, Size: 0x{:016X})",
-                  address, size);
-        return ResultInvalidCurrentMemory;
-    }
-
-    const auto attributes{static_cast<MemoryAttribute>(mask | attribute)};
-    if (attributes != static_cast<MemoryAttribute>(mask) ||
-        (attributes | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) {
-        LOG_ERROR(Kernel_SVC,
-                  "Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}",
-                  attribute, mask);
-        return ResultInvalidCombination;
-    }
+    // Validate the attribute and mask.
+    constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached);
+    R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
+    R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
 
+    // Validate that the region is in range for the current process.
     auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
+    R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
 
-    return page_table.SetMemoryAttribute(address, size, static_cast<KMemoryAttribute>(mask),
-                                         static_cast<KMemoryAttribute>(attribute));
+    // Set the memory attribute.
+    return page_table.SetMemoryAttribute(address, size, mask, attr);
 }
 
 static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
-                                       u32 attribute) {
-    return SetMemoryAttribute(system, address, size, mask, attribute);
+                                       u32 attr) {
+    return SetMemoryAttribute(system, address, size, mask, attr);
 }
 
 /// Maps a memory range into a different range.
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index ec463b97ce..365e22e4e0 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -32,6 +32,7 @@ enum class MemoryState : u32 {
     Kernel = 0x13,
     GeneratedCode = 0x14,
     CodeOut = 0x15,
+    Coverage = 0x16,
 };
 DECLARE_ENUM_FLAG_OPERATORS(MemoryState);