From 22afa2c7a39e11ccbe572e600a5a863cd323f2b0 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Wed, 11 Oct 2023 09:44:49 -0400
Subject: [PATCH 01/10] kernel: reshuffle ini1 size, add slab clear note

---
 src/core/hle/kernel/init/init_slab_setup.cpp | 9 +++++++--
 src/core/hle/kernel/initial_process.h        | 4 ++++
 src/core/hle/kernel/k_memory_layout.h        | 6 ++----
 src/core/hle/kernel/k_memory_manager.cpp     | 9 +++++----
 4 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index 1f2db673c2..a0e20bbbb4 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -106,7 +106,7 @@ static_assert(KernelPageBufferAdditionalSize ==
 /// memory.
 static KPhysicalAddress TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout,
                                                     KVirtualAddress slab_addr) {
-    slab_addr -= GetInteger(memory_layout.GetSlabRegionAddress());
+    slab_addr -= memory_layout.GetSlabRegion().GetAddress();
     return GetInteger(slab_addr) + Core::DramMemoryMap::SlabHeapBase;
 }
 
@@ -196,7 +196,12 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
     auto& kernel = system.Kernel();
 
     // Get the start of the slab region, since that's where we'll be working.
-    KVirtualAddress address = memory_layout.GetSlabRegionAddress();
+    const KMemoryRegion& slab_region = memory_layout.GetSlabRegion();
+    KVirtualAddress address = slab_region.GetAddress();
+
+    // Clear the slab region.
+    // TODO: implement access to kernel VAs.
+    // std::memset(device_ptr, 0, slab_region.GetSize());
 
     // Initialize slab type array to be in sorted order.
     std::array<KSlabType, KSlabType_Count> slab_types;
diff --git a/src/core/hle/kernel/initial_process.h b/src/core/hle/kernel/initial_process.h
index 82195f4f7e..2c95269fc5 100644
--- a/src/core/hle/kernel/initial_process.h
+++ b/src/core/hle/kernel/initial_process.h
@@ -19,4 +19,8 @@ static inline KPhysicalAddress GetInitialProcessBinaryPhysicalAddress() {
         MainMemoryAddress);
 }
 
+static inline size_t GetInitialProcessBinarySize() {
+    return InitialProcessBinarySizeMax;
+}
+
 } // namespace Kernel
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h
index 54a71df56c..c8122644fd 100644
--- a/src/core/hle/kernel/k_memory_layout.h
+++ b/src/core/hle/kernel/k_memory_layout.h
@@ -137,11 +137,9 @@ public:
         return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack);
     }
 
-    KVirtualAddress GetSlabRegionAddress() const {
-        return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab))
-            .GetAddress();
+    const KMemoryRegion& GetSlabRegion() const {
+        return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab));
     }
-
     const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const {
         return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type));
     }
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index 74d8169e00..637558e102 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -119,7 +119,8 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage
     // Free each region to its corresponding heap.
     size_t reserved_sizes[MaxManagerCount] = {};
     const KPhysicalAddress ini_start = GetInitialProcessBinaryPhysicalAddress();
-    const KPhysicalAddress ini_end = ini_start + InitialProcessBinarySizeMax;
+    const size_t ini_size = GetInitialProcessBinarySize();
+    const KPhysicalAddress ini_end = ini_start + ini_size;
     const KPhysicalAddress ini_last = ini_end - 1;
     for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
         if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
@@ -137,13 +138,13 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage
                 }
 
                 // Open/reserve the ini memory.
-                manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize);
-                reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax;
+                manager.OpenFirst(ini_start, ini_size / PageSize);
+                reserved_sizes[it.GetAttributes()] += ini_size;
 
                 // Free memory after the ini to the heap.
                 if (ini_last != cur_last) {
                     ASSERT(cur_end != 0);
-                    manager.Free(ini_end, cur_end - ini_end);
+                    manager.Free(ini_end, (cur_end - ini_end) / PageSize);
                 }
             } else {
                 // Ensure there's no partial overlap with the ini image.

From 794e6c7a96dbca24780a4301ea439db7a532acc3 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Wed, 11 Oct 2023 10:24:48 -0400
Subject: [PATCH 02/10] kernel: split Io memory state, add PermissionLocked
 attribute

---
 src/core/hle/kernel/k_memory_block.h          |  39 +++--
 src/core/hle/kernel/k_page_table.cpp          | 147 +++++++++---------
 src/core/hle/kernel/k_page_table.h            |  19 ++-
 src/core/hle/kernel/svc_types.h               |   1 +
 .../nvnflinger/fb_share_buffer_manager.cpp    |   2 +-
 5 files changed, 116 insertions(+), 92 deletions(-)

diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index 41a29da244..8437cb659e 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -36,6 +36,7 @@ enum class KMemoryState : u32 {
     FlagCanChangeAttribute = (1 << 24),
     FlagCanCodeMemory = (1 << 25),
     FlagLinearMapped = (1 << 26),
+    FlagCanPermissionLock = (1 << 27),
 
     FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
                 FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
@@ -50,12 +51,16 @@ enum class KMemoryState : u32 {
                 FlagLinearMapped,
 
     Free = static_cast<u32>(Svc::MemoryState::Free),
-    Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
-         FlagCanAlignedDeviceMap,
+
+    IoMemory = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
+               FlagCanAlignedDeviceMap,
+    IoRegister =
+        static_cast<u32>(Svc::MemoryState::Io) | FlagCanDeviceMap | FlagCanAlignedDeviceMap,
+
     Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
     Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
     CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
-               FlagCanCodeMemory,
+               FlagCanCodeMemory | FlagCanPermissionLock,
     Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
     Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted |
              FlagLinearMapped,
@@ -65,7 +70,8 @@ enum class KMemoryState : u32 {
     AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
                 FlagCanCodeAlias,
     AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData |
-                    FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory,
+                    FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory |
+                    FlagCanPermissionLock,
 
     Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap |
           FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
@@ -73,7 +79,7 @@ enum class KMemoryState : u32 {
     Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
             FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
 
-    ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagLinearMapped,
+    ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagLinearMapped,
 
     Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
                  FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
@@ -94,7 +100,7 @@ enum class KMemoryState : u32 {
     NonDeviceIpc =
         static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc,
 
-    Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped,
+    Kernel = static_cast<u32>(Svc::MemoryState::Kernel),
 
     GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
                     FlagReferenceCounted | FlagCanDebug | FlagLinearMapped,
@@ -105,34 +111,36 @@ enum class KMemoryState : u32 {
 
     Insecure = static_cast<u32>(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted |
                FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap |
-               FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+               FlagCanAlignedDeviceMap | FlagCanQueryPhysical | FlagCanUseNonSecureIpc |
+               FlagCanUseNonDeviceIpc,
 };
 DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
 
 static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
-static_assert(static_cast<u32>(KMemoryState::Io) == 0x00182001);
+static_assert(static_cast<u32>(KMemoryState::IoMemory) == 0x00182001);
+static_assert(static_cast<u32>(KMemoryState::IoRegister) == 0x00180001);
 static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
 static_assert(static_cast<u32>(KMemoryState::Code) == 0x04DC7E03);
-static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x07FEBD04);
+static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x0FFEBD04);
 static_assert(static_cast<u32>(KMemoryState::Normal) == 0x077EBD05);
 static_assert(static_cast<u32>(KMemoryState::Shared) == 0x04402006);
 
 static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x04DD7E08);
-static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x07FFBD09);
+static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x0FFFBD09);
 static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
 static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
-static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400200C);
+static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400000C);
 static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
 static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
 static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
 static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
 static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
 static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x044C2812);
-static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
+static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00000013);
 static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x04402214);
 static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x04402015);
 static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
-static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x05583817);
+static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x055C3817);
 
 enum class KMemoryPermission : u8 {
     None = 0,
@@ -182,6 +190,7 @@ enum class KMemoryAttribute : u8 {
     IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked),
     DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
     Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
+    PermissionLocked = static_cast<u8>(Svc::MemoryAttribute::PermissionLocked),
 
     SetMask = Uncached,
 };
@@ -261,6 +270,10 @@ struct KMemoryInfo {
         return m_state;
     }
 
+    constexpr Svc::MemoryState GetSvcState() const {
+        return static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask);
+    }
+
     constexpr KMemoryPermission GetPermission() const {
         return m_permission;
     }
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 0b0cef9849..4d564a04f6 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -2379,8 +2379,7 @@ Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
     KScopedPageTableUpdater updater(this);
 
     // Perform mapping operation.
-    const KPageProperties properties = {perm, state == KMemoryState::Io, false,
-                                        DisableMergeAttribute::DisableHead};
+    const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
     R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
 
     // Update the blocks.
@@ -2422,8 +2421,7 @@ Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMem
     KScopedPageTableUpdater updater(this);
 
     // Perform mapping operation.
-    const KPageProperties properties = {perm, state == KMemoryState::Io, false,
-                                        DisableMergeAttribute::DisableHead};
+    const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
     R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
 
     // Update the blocks.
@@ -2863,7 +2861,8 @@ Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress
                                       &KMemoryBlock::ShareToDevice, KMemoryPermission::None);
 
     // Set whether the locked memory was io.
-    *out_is_io = old_state == KMemoryState::Io;
+    *out_is_io =
+        static_cast<Svc::MemoryState>(old_state & KMemoryState::Mask) == Svc::MemoryState::Io;
 
     R_SUCCEED();
 }
@@ -3106,79 +3105,79 @@ void KPageTable::FinalizeUpdate(PageLinkedList* page_list) {
     }
 }
 
-KProcessAddress KPageTable::GetRegionAddress(KMemoryState state) const {
+KProcessAddress KPageTable::GetRegionAddress(Svc::MemoryState state) const {
     switch (state) {
-    case KMemoryState::Free:
-    case KMemoryState::Kernel:
+    case Svc::MemoryState::Free:
+    case Svc::MemoryState::Kernel:
         return m_address_space_start;
-    case KMemoryState::Normal:
+    case Svc::MemoryState::Normal:
         return m_heap_region_start;
-    case KMemoryState::Ipc:
-    case KMemoryState::NonSecureIpc:
-    case KMemoryState::NonDeviceIpc:
+    case Svc::MemoryState::Ipc:
+    case Svc::MemoryState::NonSecureIpc:
+    case Svc::MemoryState::NonDeviceIpc:
         return m_alias_region_start;
-    case KMemoryState::Stack:
+    case Svc::MemoryState::Stack:
         return m_stack_region_start;
-    case KMemoryState::Static:
-    case KMemoryState::ThreadLocal:
+    case Svc::MemoryState::Static:
+    case Svc::MemoryState::ThreadLocal:
         return m_kernel_map_region_start;
-    case KMemoryState::Io:
-    case KMemoryState::Shared:
-    case KMemoryState::AliasCode:
-    case KMemoryState::AliasCodeData:
-    case KMemoryState::Transfered:
-    case KMemoryState::SharedTransfered:
-    case KMemoryState::SharedCode:
-    case KMemoryState::GeneratedCode:
-    case KMemoryState::CodeOut:
-    case KMemoryState::Coverage:
-    case KMemoryState::Insecure:
+    case Svc::MemoryState::Io:
+    case Svc::MemoryState::Shared:
+    case Svc::MemoryState::AliasCode:
+    case Svc::MemoryState::AliasCodeData:
+    case Svc::MemoryState::Transfered:
+    case Svc::MemoryState::SharedTransfered:
+    case Svc::MemoryState::SharedCode:
+    case Svc::MemoryState::GeneratedCode:
+    case Svc::MemoryState::CodeOut:
+    case Svc::MemoryState::Coverage:
+    case Svc::MemoryState::Insecure:
         return m_alias_code_region_start;
-    case KMemoryState::Code:
-    case KMemoryState::CodeData:
+    case Svc::MemoryState::Code:
+    case Svc::MemoryState::CodeData:
         return m_code_region_start;
     default:
         UNREACHABLE();
     }
 }
 
-size_t KPageTable::GetRegionSize(KMemoryState state) const {
+size_t KPageTable::GetRegionSize(Svc::MemoryState state) const {
     switch (state) {
-    case KMemoryState::Free:
-    case KMemoryState::Kernel:
+    case Svc::MemoryState::Free:
+    case Svc::MemoryState::Kernel:
         return m_address_space_end - m_address_space_start;
-    case KMemoryState::Normal:
+    case Svc::MemoryState::Normal:
         return m_heap_region_end - m_heap_region_start;
-    case KMemoryState::Ipc:
-    case KMemoryState::NonSecureIpc:
-    case KMemoryState::NonDeviceIpc:
+    case Svc::MemoryState::Ipc:
+    case Svc::MemoryState::NonSecureIpc:
+    case Svc::MemoryState::NonDeviceIpc:
         return m_alias_region_end - m_alias_region_start;
-    case KMemoryState::Stack:
+    case Svc::MemoryState::Stack:
         return m_stack_region_end - m_stack_region_start;
-    case KMemoryState::Static:
-    case KMemoryState::ThreadLocal:
+    case Svc::MemoryState::Static:
+    case Svc::MemoryState::ThreadLocal:
         return m_kernel_map_region_end - m_kernel_map_region_start;
-    case KMemoryState::Io:
-    case KMemoryState::Shared:
-    case KMemoryState::AliasCode:
-    case KMemoryState::AliasCodeData:
-    case KMemoryState::Transfered:
-    case KMemoryState::SharedTransfered:
-    case KMemoryState::SharedCode:
-    case KMemoryState::GeneratedCode:
-    case KMemoryState::CodeOut:
-    case KMemoryState::Coverage:
-    case KMemoryState::Insecure:
+    case Svc::MemoryState::Io:
+    case Svc::MemoryState::Shared:
+    case Svc::MemoryState::AliasCode:
+    case Svc::MemoryState::AliasCodeData:
+    case Svc::MemoryState::Transfered:
+    case Svc::MemoryState::SharedTransfered:
+    case Svc::MemoryState::SharedCode:
+    case Svc::MemoryState::GeneratedCode:
+    case Svc::MemoryState::CodeOut:
+    case Svc::MemoryState::Coverage:
+    case Svc::MemoryState::Insecure:
         return m_alias_code_region_end - m_alias_code_region_start;
-    case KMemoryState::Code:
-    case KMemoryState::CodeData:
+    case Svc::MemoryState::Code:
+    case Svc::MemoryState::CodeData:
         return m_code_region_end - m_code_region_start;
     default:
         UNREACHABLE();
     }
 }
 
-bool KPageTable::CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
+bool KPageTable::CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const {
     const KProcessAddress end = addr + size;
     const KProcessAddress last = end - 1;
 
@@ -3192,32 +3191,32 @@ bool KPageTable::CanContain(KProcessAddress addr, size_t size, KMemoryState stat
     const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr ||
                                m_alias_region_start == m_alias_region_end);
     switch (state) {
-    case KMemoryState::Free:
-    case KMemoryState::Kernel:
+    case Svc::MemoryState::Free:
+    case Svc::MemoryState::Kernel:
         return is_in_region;
-    case KMemoryState::Io:
-    case KMemoryState::Static:
-    case KMemoryState::Code:
-    case KMemoryState::CodeData:
-    case KMemoryState::Shared:
-    case KMemoryState::AliasCode:
-    case KMemoryState::AliasCodeData:
-    case KMemoryState::Stack:
-    case KMemoryState::ThreadLocal:
-    case KMemoryState::Transfered:
-    case KMemoryState::SharedTransfered:
-    case KMemoryState::SharedCode:
-    case KMemoryState::GeneratedCode:
-    case KMemoryState::CodeOut:
-    case KMemoryState::Coverage:
-    case KMemoryState::Insecure:
+    case Svc::MemoryState::Io:
+    case Svc::MemoryState::Static:
+    case Svc::MemoryState::Code:
+    case Svc::MemoryState::CodeData:
+    case Svc::MemoryState::Shared:
+    case Svc::MemoryState::AliasCode:
+    case Svc::MemoryState::AliasCodeData:
+    case Svc::MemoryState::Stack:
+    case Svc::MemoryState::ThreadLocal:
+    case Svc::MemoryState::Transfered:
+    case Svc::MemoryState::SharedTransfered:
+    case Svc::MemoryState::SharedCode:
+    case Svc::MemoryState::GeneratedCode:
+    case Svc::MemoryState::CodeOut:
+    case Svc::MemoryState::Coverage:
+    case Svc::MemoryState::Insecure:
         return is_in_region && !is_in_heap && !is_in_alias;
-    case KMemoryState::Normal:
+    case Svc::MemoryState::Normal:
         ASSERT(is_in_heap);
         return is_in_region && !is_in_alias;
-    case KMemoryState::Ipc:
-    case KMemoryState::NonSecureIpc:
-    case KMemoryState::NonDeviceIpc:
+    case Svc::MemoryState::Ipc:
+    case Svc::MemoryState::NonSecureIpc:
+    case Svc::MemoryState::NonDeviceIpc:
         ASSERT(is_in_alias);
         return is_in_region && !is_in_heap;
     default:
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 7da675f278..af12582d95 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -126,8 +126,6 @@ public:
         return m_block_info_manager;
     }
 
-    bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const;
-
     Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
                     KPhysicalAddress phys_addr, KProcessAddress region_start,
                     size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
@@ -162,6 +160,21 @@ public:
     void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
                         const KPageGroup& pg);
 
+    KProcessAddress GetRegionAddress(Svc::MemoryState state) const;
+    size_t GetRegionSize(Svc::MemoryState state) const;
+    bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const;
+
+    KProcessAddress GetRegionAddress(KMemoryState state) const {
+        return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
+    }
+    size_t GetRegionSize(KMemoryState state) const {
+        return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
+    }
+    bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
+        return this->CanContain(addr, size,
+                                static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
+    }
+
 protected:
     struct PageLinkedList {
     private:
@@ -228,8 +241,6 @@ private:
     Result Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm,
                    OperationType operation, KPhysicalAddress map_addr = 0);
     void FinalizeUpdate(PageLinkedList* page_list);
-    KProcessAddress GetRegionAddress(KMemoryState state) const;
-    size_t GetRegionSize(KMemoryState state) const;
 
     KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
                                  size_t num_pages, size_t alignment, size_t offset,
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 7f380ca4fb..251e6013c9 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -46,6 +46,7 @@ enum class MemoryAttribute : u32 {
     IpcLocked = (1 << 1),
     DeviceShared = (1 << 2),
     Uncached = (1 << 3),
+    PermissionLocked = (1 << 4),
 };
 DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute);
 
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
index 469a532448..2e29bc8487 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
@@ -46,7 +46,7 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
     // Get bounds of where mapping is possible.
     const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
     const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE;
-    const auto state = Kernel::KMemoryState::Io;
+    const auto state = Kernel::KMemoryState::IoMemory;
     const auto perm = Kernel::KMemoryPermission::UserReadWrite;
     std::mt19937_64 rng{process->GetRandomEntropy(0)};
 

From 60a1c6b95bca1e7053ee26818adbff0f217c2e2e Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Wed, 11 Oct 2023 10:50:44 -0400
Subject: [PATCH 03/10] k_page_table: add new CheckMemoryState helper

---
 src/core/hle/kernel/k_page_table.cpp | 45 ++++++++++++++++++++--------
 src/core/hle/kernel/k_page_table.h   |  7 +++++
 2 files changed, 40 insertions(+), 12 deletions(-)

diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 4d564a04f6..ebc5403169 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -3280,21 +3280,16 @@ Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProces
 
 Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
                                     KMemoryAttribute* out_attr, size_t* out_blocks_needed,
-                                    KProcessAddress addr, size_t size, KMemoryState state_mask,
+                                    KMemoryBlockManager::const_iterator it,
+                                    KProcessAddress last_addr, 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 KProcessAddress last_addr = addr + size - 1;
-    KMemoryBlockManager::const_iterator it = m_memory_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(GetInteger(addr), PageSize) != info.GetAddress()) ? 1 : 0;
-
     // Validate all blocks in the range have correct state.
     const KMemoryState first_state = info.m_state;
     const KMemoryPermission first_perm = info.m_permission;
@@ -3320,10 +3315,6 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission*
         info = it->GetMemoryInfo();
     }
 
-    // If the end address isn't aligned, we need a block.
-    const size_t blocks_for_end_align =
-        (Common::AlignUp(GetInteger(addr) + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
-
     // Write output state.
     if (out_state != nullptr) {
         *out_state = first_state;
@@ -3334,9 +3325,39 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission*
     if (out_attr != nullptr) {
         *out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr);
     }
+
+    // If the end address isn't aligned, we need a block.
     if (out_blocks_needed != nullptr) {
-        *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
+        const size_t blocks_for_end_align =
+            (Common::AlignDown(GetInteger(last_addr), PageSize) + PageSize != info.GetEndAddress())
+                ? 1
+                : 0;
+        *out_blocks_needed = blocks_for_end_align;
     }
+
+    R_SUCCEED();
+}
+
+Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
+                                    KMemoryAttribute* out_attr, size_t* out_blocks_needed,
+                                    KProcessAddress addr, 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());
+
+    // Check memory state.
+    const KProcessAddress last_addr = addr + size - 1;
+    KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
+    R_TRY(this->CheckMemoryState(out_state, out_perm, out_attr, out_blocks_needed, it, last_addr,
+                                 state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr));
+
+    // If the start address isn't aligned, we need a block.
+    if (out_blocks_needed != nullptr &&
+        Common::AlignDown(GetInteger(addr), PageSize) != it->GetAddress()) {
+        ++(*out_blocks_needed);
+    }
+
     R_SUCCEED();
 }
 
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index af12582d95..e69498f02d 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -261,6 +261,13 @@ private:
     Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
                             KMemoryPermission perm_mask, KMemoryPermission perm,
                             KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
+    Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
+                            KMemoryAttribute* out_attr, size_t* out_blocks_needed,
+                            KMemoryBlockManager::const_iterator it, KProcessAddress last_addr,
+                            KMemoryState state_mask, KMemoryState state,
+                            KMemoryPermission perm_mask, KMemoryPermission perm,
+                            KMemoryAttribute attr_mask, KMemoryAttribute attr,
+                            KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
     Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
                             KMemoryAttribute* out_attr, size_t* out_blocks_needed,
                             KProcessAddress addr, size_t size, KMemoryState state_mask,

From 0441853d0f82ce244f2fa1dec61f64e86304e636 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Wed, 11 Oct 2023 11:39:09 -0400
Subject: [PATCH 04/10] k_page_table: implement PermissionLocked

---
 src/core/hle/kernel/k_memory_block.h          | 13 +++-
 .../hle/kernel/k_memory_block_manager.cpp     | 70 +++++++++++++++++--
 src/core/hle/kernel/k_memory_block_manager.h  |  6 +-
 src/core/hle/kernel/k_page_table.cpp          | 54 +++++++++-----
 src/core/hle/kernel/k_page_table.h            |  3 +-
 src/core/hle/kernel/svc/svc_memory.cpp        |  8 ++-
 6 files changed, 128 insertions(+), 26 deletions(-)

diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index 8437cb659e..ef3f61321a 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -192,7 +192,7 @@ enum class KMemoryAttribute : u8 {
     Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
     PermissionLocked = static_cast<u8>(Svc::MemoryAttribute::PermissionLocked),
 
-    SetMask = Uncached,
+    SetMask = Uncached | PermissionLocked,
 };
 DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
 
@@ -339,6 +339,10 @@ public:
         return this->GetEndAddress() - 1;
     }
 
+    constexpr KMemoryState GetState() const {
+        return m_memory_state;
+    }
+
     constexpr u16 GetIpcLockCount() const {
         return m_ipc_lock_count;
     }
@@ -456,6 +460,13 @@ public:
         }
     }
 
+    constexpr void UpdateAttribute(KMemoryAttribute mask, KMemoryAttribute attr) {
+        ASSERT(False(mask & KMemoryAttribute::IpcLocked));
+        ASSERT(False(mask & KMemoryAttribute::DeviceShared));
+
+        m_attribute = (m_attribute & ~mask) | attr;
+    }
+
     constexpr void Split(KMemoryBlock* block, KProcessAddress addr) {
         ASSERT(this->GetAddress() < addr);
         ASSERT(this->Contains(addr));
diff --git a/src/core/hle/kernel/k_memory_block_manager.cpp b/src/core/hle/kernel/k_memory_block_manager.cpp
index ab75f550e8..58a1e7216b 100644
--- a/src/core/hle/kernel/k_memory_block_manager.cpp
+++ b/src/core/hle/kernel/k_memory_block_manager.cpp
@@ -160,8 +160,8 @@ void KMemoryBlockManager::Update(KMemoryBlockManagerUpdateAllocator* allocator,
             }
 
             // Update block state.
-            it->Update(state, perm, attr, cur_address == address, static_cast<u8>(set_disable_attr),
-                       static_cast<u8>(clear_disable_attr));
+            it->Update(state, perm, attr, it->GetAddress() == address,
+                       static_cast<u8>(set_disable_attr), static_cast<u8>(clear_disable_attr));
             cur_address += cur_info.GetSize();
             remaining_pages -= cur_info.GetNumPages();
         }
@@ -175,7 +175,9 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
                                         KProcessAddress address, size_t num_pages,
                                         KMemoryState test_state, KMemoryPermission test_perm,
                                         KMemoryAttribute test_attr, KMemoryState state,
-                                        KMemoryPermission perm, KMemoryAttribute attr) {
+                                        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);
     ASSERT(Common::IsAligned(GetInteger(address), PageSize));
@@ -214,7 +216,8 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
             }
 
             // Update block state.
-            it->Update(state, perm, attr, false, 0, 0);
+            it->Update(state, perm, attr, false, static_cast<u8>(set_disable_attr),
+                       static_cast<u8>(clear_disable_attr));
             cur_address += cur_info.GetSize();
             remaining_pages -= cur_info.GetNumPages();
         } else {
@@ -284,6 +287,65 @@ void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator* allocat
     this->CoalesceForUpdate(allocator, address, num_pages);
 }
 
+void KMemoryBlockManager::UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator,
+                                          KProcessAddress address, size_t num_pages,
+                                          KMemoryAttribute mask, KMemoryAttribute attr) {
+    // Ensure for auditing that we never end up with an invalid tree.
+    KScopedMemoryBlockManagerAuditor auditor(this);
+    ASSERT(Common::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 {
     // Loop over every block, ensuring that we are sorted and coalesced.
diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h
index 96496e9904..cb7b6f4305 100644
--- a/src/core/hle/kernel/k_memory_block_manager.h
+++ b/src/core/hle/kernel/k_memory_block_manager.h
@@ -115,7 +115,11 @@ public:
     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);
+                       KMemoryAttribute attr, KMemoryBlockDisableMergeAttribute set_disable_attr,
+                       KMemoryBlockDisableMergeAttribute clear_disable_attr);
+
+    void UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
+                         size_t num_pages, KMemoryAttribute mask, KMemoryAttribute attr);
 
     iterator FindIterator(KProcessAddress address) const {
         return m_memory_block_tree.find(KMemoryBlock(
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index ebc5403169..387f2c9622 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -505,7 +505,7 @@ Result KPageTable::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress
     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));
+        KMemoryAttribute::All & ~KMemoryAttribute::PermissionLocked, KMemoryAttribute::None));
 
     // Determine whether any pages being unmapped are code.
     bool any_code_pages = false;
@@ -1770,7 +1770,11 @@ Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
                 m_memory_block_manager.UpdateIfMatch(
                     std::addressof(allocator), address, size / PageSize, KMemoryState::Free,
                     KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
-                    KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
+                    KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
+                    address == this->GetAliasRegionStart()
+                        ? KMemoryBlockDisableMergeAttribute::Normal
+                        : KMemoryBlockDisableMergeAttribute::None,
+                    KMemoryBlockDisableMergeAttribute::None);
 
                 R_SUCCEED();
             }
@@ -1868,6 +1872,13 @@ Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
 
     // 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.
         ASSERT(it != m_memory_block_manager.end());
@@ -1905,7 +1916,7 @@ Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
     m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
                                   KMemoryState::Free, KMemoryPermission::None,
                                   KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
-                                  KMemoryBlockDisableMergeAttribute::None);
+                                  clear_merge_attr);
 
     // We succeeded.
     R_SUCCEED();
@@ -2650,11 +2661,18 @@ Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mas
     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,
-        AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
+    const KMemoryState state_test_mask =
+        static_cast<KMemoryState>(((mask & static_cast<u32>(KMemoryAttribute::Uncached))
+                                       ? static_cast<u32>(KMemoryState::FlagCanChangeAttribute)
+                                       : 0) |
+                                  ((mask & static_cast<u32>(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, state_test_mask, state_test_mask,
+                                 KMemoryPermission::None, KMemoryPermission::None,
+                                 AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
 
     // Create an update allocator.
     Result allocator_result{ResultSuccess};
@@ -2662,18 +2680,17 @@ Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mas
                                                  m_memory_block_slab_manager, num_allocator_blocks);
     R_TRY(allocator_result);
 
-    // Determine the new attribute.
-    const KMemoryAttribute new_attr =
-        static_cast<KMemoryAttribute>(((old_attr & static_cast<KMemoryAttribute>(~mask)) |
-                                       static_cast<KMemoryAttribute>(attr & mask)));
-
-    // Perform operation.
-    this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh);
+    // If we need to, perform a change attribute operation.
+    if (True(KMemoryAttribute::Uncached & static_cast<KMemoryAttribute>(mask))) {
+        // Perform operation.
+        R_TRY(this->Operate(addr, num_pages, old_perm,
+                            OperationType::ChangePermissionsAndRefreshAndFlush, 0));
+    }
 
     // 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,
+                                           static_cast<KMemoryAttribute>(mask),
+                                           static_cast<KMemoryAttribute>(attr));
 
     R_SUCCEED();
 }
@@ -3086,6 +3103,7 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
     }
     case OperationType::ChangePermissions:
     case OperationType::ChangePermissionsAndRefresh:
+    case OperationType::ChangePermissionsAndRefreshAndFlush:
         break;
     default:
         ASSERT(false);
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index e69498f02d..cbcbb6f62c 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -222,7 +222,8 @@ private:
         Unmap = 3,
         ChangePermissions = 4,
         ChangePermissionsAndRefresh = 5,
-        Separate = 6,
+        ChangePermissionsAndRefreshAndFlush = 6,
+        Separate = 7,
     };
 
     static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
diff --git a/src/core/hle/kernel/svc/svc_memory.cpp b/src/core/hle/kernel/svc/svc_memory.cpp
index 2cab741274..3726840948 100644
--- a/src/core/hle/kernel/svc/svc_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_memory.cpp
@@ -108,10 +108,16 @@ Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask,
     R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
 
     // Validate the attribute and mask.
-    constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached);
+    constexpr u32 SupportedMask =
+        static_cast<u32>(MemoryAttribute::Uncached | MemoryAttribute::PermissionLocked);
     R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
     R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
 
+    // Check that permission locked is either being set or not masked.
+    R_UNLESS((static_cast<Svc::MemoryAttribute>(mask) & Svc::MemoryAttribute::PermissionLocked) ==
+                 (static_cast<Svc::MemoryAttribute>(attr) & Svc::MemoryAttribute::PermissionLocked),
+             ResultInvalidCombination);
+
     // Validate that the region is in range for the current process.
     auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
     R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);

From b456af31e6f1419a01374e9d06f56788d1406fe3 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Wed, 11 Oct 2023 12:55:12 -0400
Subject: [PATCH 05/10] kernel: update KMemoryRegionType values

---
 src/core/hle/kernel/k_memory_region_type.h | 103 ++++++++++++---------
 src/core/hle/kernel/kernel.cpp             |  23 ++++-
 2 files changed, 79 insertions(+), 47 deletions(-)

diff --git a/src/core/hle/kernel/k_memory_region_type.h b/src/core/hle/kernel/k_memory_region_type.h
index e5630c1ace..bcbf450f0b 100644
--- a/src/core/hle/kernel/k_memory_region_type.h
+++ b/src/core/hle/kernel/k_memory_region_type.h
@@ -190,9 +190,15 @@ static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() ==
 constexpr inline auto KMemoryRegionType_DramKernelSecureAppletMemory =
     KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 0).SetAttribute(
         KMemoryRegionAttr_LinearMapped);
+constexpr inline const auto KMemoryRegionType_DramKernelSecureUnknown =
+    KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 1).SetAttribute(
+        KMemoryRegionAttr_LinearMapped);
 static_assert(KMemoryRegionType_DramKernelSecureAppletMemory.GetValue() ==
               (0x18E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
                KMemoryRegionAttr_LinearMapped));
+static_assert(KMemoryRegionType_DramKernelSecureUnknown.GetValue() ==
+              (0x28E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
+               KMemoryRegionAttr_LinearMapped));
 
 constexpr inline auto KMemoryRegionType_DramReservedEarly =
     KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
@@ -217,16 +223,18 @@ constexpr inline auto KMemoryRegionType_DramPoolPartition =
 static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
               (0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
 
-constexpr inline auto KMemoryRegionType_DramPoolManagement =
-    KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute(
+// UNUSED: .Derive(4, 1);
+// UNUSED: .Derive(4, 2);
+constexpr inline const auto KMemoryRegionType_DramPoolManagement =
+    KMemoryRegionType_DramPoolPartition.Derive(4, 0).SetAttribute(
         KMemoryRegionAttr_CarveoutProtected);
-constexpr inline auto KMemoryRegionType_DramUserPool =
-    KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition();
+constexpr inline const auto KMemoryRegionType_DramUserPool =
+    KMemoryRegionType_DramPoolPartition.Derive(4, 3);
 static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
-              (0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
+              (0xE6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
                KMemoryRegionAttr_CarveoutProtected));
 static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
-              (0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
+              (0x266 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
 
 constexpr inline auto KMemoryRegionType_DramApplicationPool =
     KMemoryRegionType_DramUserPool.Derive(4, 0);
@@ -237,60 +245,63 @@ constexpr inline auto KMemoryRegionType_DramSystemNonSecurePool =
 constexpr inline auto KMemoryRegionType_DramSystemPool =
     KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
 static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
-              (0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
+              (0xE66 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
 static_assert(KMemoryRegionType_DramAppletPool.GetValue() ==
-              (0xBA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
+              (0x1666 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
 static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() ==
-              (0xDA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
+              (0x1A66 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
 static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
-              (0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
+              (0x2666 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
                KMemoryRegionAttr_CarveoutProtected));
 
 constexpr inline auto KMemoryRegionType_VirtualDramHeapBase =
-    KMemoryRegionType_Dram.DeriveSparse(1, 3, 0);
+    KMemoryRegionType_Dram.DeriveSparse(1, 4, 0);
 constexpr inline auto KMemoryRegionType_VirtualDramKernelPtHeap =
-    KMemoryRegionType_Dram.DeriveSparse(1, 3, 1);
+    KMemoryRegionType_Dram.DeriveSparse(1, 4, 1);
 constexpr inline auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
-    KMemoryRegionType_Dram.DeriveSparse(1, 3, 2);
+    KMemoryRegionType_Dram.DeriveSparse(1, 4, 2);
 static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
 static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
 static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
 
-// UNUSED: .DeriveSparse(2, 2, 0);
-constexpr inline auto KMemoryRegionType_VirtualDramUnknownDebug =
-    KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
-static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
+// UNUSED: .Derive(4, 2);
+constexpr inline const auto KMemoryRegionType_VirtualDramUnknownDebug =
+    KMemoryRegionType_Dram.Advance(2).Derive(4, 0);
+constexpr inline const auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory =
+    KMemoryRegionType_Dram.Advance(2).Derive(4, 1);
+constexpr inline const auto KMemoryRegionType_VirtualDramKernelSecureUnknown =
+    KMemoryRegionType_Dram.Advance(2).Derive(4, 3);
+static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x32));
+static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x52));
+static_assert(KMemoryRegionType_VirtualDramKernelSecureUnknown.GetValue() == (0x92));
 
-constexpr inline auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory =
-    KMemoryRegionType_Dram.DeriveSparse(3, 1, 0);
-static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x62));
-
-constexpr inline auto KMemoryRegionType_VirtualDramKernelInitPt =
-    KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
-constexpr inline auto KMemoryRegionType_VirtualDramPoolManagement =
-    KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1);
-constexpr inline auto KMemoryRegionType_VirtualDramUserPool =
-    KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2);
-static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x19A);
-static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A);
-static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x31A);
+// UNUSED: .Derive(4, 3);
+constexpr inline const auto KMemoryRegionType_VirtualDramKernelInitPt =
+    KMemoryRegionType_VirtualDramHeapBase.Derive(4, 0);
+constexpr inline const auto KMemoryRegionType_VirtualDramPoolManagement =
+    KMemoryRegionType_VirtualDramHeapBase.Derive(4, 1);
+constexpr inline const auto KMemoryRegionType_VirtualDramUserPool =
+    KMemoryRegionType_VirtualDramHeapBase.Derive(4, 2);
+static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x31A);
+static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x51A);
+static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x61A);
 
 // NOTE: For unknown reason, the pools are derived out-of-order here.
 // It's worth eventually trying to understand why Nintendo made this choice.
 // UNUSED: .Derive(6, 0);
 // UNUSED: .Derive(6, 1);
-constexpr inline auto KMemoryRegionType_VirtualDramAppletPool =
-    KMemoryRegionType_VirtualDramUserPool.Derive(6, 2);
-constexpr inline auto KMemoryRegionType_VirtualDramApplicationPool =
-    KMemoryRegionType_VirtualDramUserPool.Derive(6, 3);
-constexpr inline auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
-    KMemoryRegionType_VirtualDramUserPool.Derive(6, 4);
-constexpr inline auto KMemoryRegionType_VirtualDramSystemPool =
-    KMemoryRegionType_VirtualDramUserPool.Derive(6, 5);
-static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A);
-static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A);
-static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A);
-static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A);
+constexpr inline const auto KMemoryRegionType_VirtualDramApplicationPool =
+    KMemoryRegionType_VirtualDramUserPool.Derive(4, 0);
+constexpr inline const auto KMemoryRegionType_VirtualDramAppletPool =
+    KMemoryRegionType_VirtualDramUserPool.Derive(4, 1);
+constexpr inline const auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
+    KMemoryRegionType_VirtualDramUserPool.Derive(4, 2);
+constexpr inline const auto KMemoryRegionType_VirtualDramSystemPool =
+    KMemoryRegionType_VirtualDramUserPool.Derive(4, 3);
+static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x361A);
+static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x561A);
+static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x661A);
+static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x961A);
 
 constexpr inline auto KMemoryRegionType_ArchDeviceBase =
     KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
@@ -354,12 +365,14 @@ constexpr inline auto KMemoryRegionType_KernelTemp =
 static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
 
 constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
-    if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) {
-        return KMemoryRegionType_VirtualDramKernelTraceBuffer;
-    } else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
+    if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
         return KMemoryRegionType_VirtualDramKernelPtHeap;
     } else if (KMemoryRegionType_DramKernelSecureAppletMemory.IsAncestorOf(type_id)) {
         return KMemoryRegionType_VirtualDramKernelSecureAppletMemory;
+    } else if (KMemoryRegionType_DramKernelSecureUnknown.IsAncestorOf(type_id)) {
+        return KMemoryRegionType_VirtualDramKernelSecureUnknown;
+    } else if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) {
+        return KMemoryRegionType_VirtualDramKernelTraceBuffer;
     } else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
         return KMemoryRegionType_VirtualDramUnknownDebug;
     } else {
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index cb025c3d6d..6f19860a15 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -623,14 +623,33 @@ struct KernelCore::Impl {
         ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
             GetInteger(slab_start_phys_addr), slab_region_size, KMemoryRegionType_DramKernelSlab));
 
+        // Insert a physical region for the secure applet memory.
+        const auto secure_applet_end_phys_addr =
+            slab_end_phys_addr + KSystemControl::SecureAppletMemorySize;
+        if constexpr (KSystemControl::SecureAppletMemorySize > 0) {
+            ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
+                GetInteger(slab_end_phys_addr), KSystemControl::SecureAppletMemorySize,
+                KMemoryRegionType_DramKernelSecureAppletMemory));
+        }
+
+        // Insert a physical region for the unknown debug2 region.
+        constexpr size_t SecureUnknownRegionSize = 0;
+        const size_t secure_unknown_size = SecureUnknownRegionSize;
+        const auto secure_unknown_end_phys_addr = secure_applet_end_phys_addr + secure_unknown_size;
+        if (secure_unknown_size > 0) {
+            ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
+                GetInteger(secure_applet_end_phys_addr), secure_unknown_size,
+                KMemoryRegionType_DramKernelSecureUnknown));
+        }
+
         // Determine size available for kernel page table heaps, requiring > 8 MB.
         const KPhysicalAddress resource_end_phys_addr = slab_start_phys_addr + resource_region_size;
-        const size_t page_table_heap_size = resource_end_phys_addr - slab_end_phys_addr;
+        const size_t page_table_heap_size = resource_end_phys_addr - secure_unknown_end_phys_addr;
         ASSERT(page_table_heap_size / 4_MiB > 2);
 
         // Insert a physical region for the kernel page table heap region
         ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
-            GetInteger(slab_end_phys_addr), page_table_heap_size,
+            GetInteger(secure_unknown_end_phys_addr), page_table_heap_size,
             KMemoryRegionType_DramKernelPtHeap));
 
         // All DRAM regions that we haven't tagged by this point will be mapped under the linear

From f21058a6c0aa825d42014d7073d7074ba28df775 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Wed, 11 Oct 2023 13:12:02 -0400
Subject: [PATCH 06/10] k_page_table: add MapFirstGroup

---
 src/core/hle/kernel/k_page_group.h   | 11 +++--
 src/core/hle/kernel/k_page_table.cpp | 66 ++++++++++++++++------------
 src/core/hle/kernel/k_page_table.h   |  4 +-
 3 files changed, 48 insertions(+), 33 deletions(-)

diff --git a/src/core/hle/kernel/k_page_group.h b/src/core/hle/kernel/k_page_group.h
index b32909f05a..de9d63a8d2 100644
--- a/src/core/hle/kernel/k_page_group.h
+++ b/src/core/hle/kernel/k_page_group.h
@@ -183,12 +183,17 @@ private:
 
 class KScopedPageGroup {
 public:
-    explicit KScopedPageGroup(const KPageGroup* gp) : m_pg(gp) {
+    explicit KScopedPageGroup(const KPageGroup* gp, bool not_first = true) : m_pg(gp) {
         if (m_pg) {
-            m_pg->Open();
+            if (not_first) {
+                m_pg->Open();
+            } else {
+                m_pg->OpenFirst();
+            }
         }
     }
-    explicit KScopedPageGroup(const KPageGroup& gp) : KScopedPageGroup(std::addressof(gp)) {}
+    explicit KScopedPageGroup(const KPageGroup& gp, bool not_first = true)
+        : KScopedPageGroup(std::addressof(gp), not_first) {}
     ~KScopedPageGroup() {
         if (m_pg) {
             m_pg->Close();
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 387f2c9622..217ccbae36 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -1724,29 +1724,43 @@ Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
                             PageSize;
 
                         // While we have pages to map, map them.
-                        while (map_pages > 0) {
-                            // Check if we're at the end of the physical block.
-                            if (pg_pages == 0) {
-                                // Ensure there are more pages to map.
-                                ASSERT(pg_it != pg.end());
+                        {
+                            // Create a page group for the current mapping range.
+                            KPageGroup cur_pg(m_kernel, m_block_info_manager);
+                            {
+                                ON_RESULT_FAILURE_2 {
+                                    cur_pg.OpenFirst();
+                                    cur_pg.Close();
+                                };
 
-                                // Advance our physical block.
-                                ++pg_it;
-                                pg_phys_addr = pg_it->GetAddress();
-                                pg_pages = pg_it->GetNumPages();
+                                size_t remain_pages = map_pages;
+                                while (remain_pages > 0) {
+                                    // Check if we're at the end of the physical block.
+                                    if (pg_pages == 0) {
+                                        // Ensure there are more pages to map.
+                                        ASSERT(pg_it != pg.end());
+
+                                        // Advance our physical block.
+                                        ++pg_it;
+                                        pg_phys_addr = pg_it->GetAddress();
+                                        pg_pages = pg_it->GetNumPages();
+                                    }
+
+                                    // Add whatever we can to the current block.
+                                    const size_t cur_pages = std::min(pg_pages, remain_pages);
+                                    R_TRY(cur_pg.AddBlock(pg_phys_addr +
+                                                              ((pg_pages - cur_pages) * PageSize),
+                                                          cur_pages));
+
+                                    // Advance.
+                                    remain_pages -= cur_pages;
+                                    pg_pages -= cur_pages;
+                                }
                             }
 
-                            // Map whatever we can.
-                            const size_t cur_pages = std::min(pg_pages, map_pages);
-                            R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
-                                          OperationType::MapFirst, pg_phys_addr));
-
-                            // Advance.
-                            cur_address += cur_pages * PageSize;
-                            map_pages -= cur_pages;
-
-                            pg_phys_addr += cur_pages * PageSize;
-                            pg_pages -= cur_pages;
+                            // Map the pages.
+                            R_TRY(this->Operate(cur_address, map_pages, cur_pg,
+                                                OperationType::MapFirstGroup));
                         }
                     }
 
@@ -3037,9 +3051,10 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, const KPageGr
     ASSERT(num_pages == page_group.GetNumPages());
 
     switch (operation) {
-    case OperationType::MapGroup: {
+    case OperationType::MapGroup:
+    case OperationType::MapFirstGroup: {
         // We want to maintain a new reference to every page in the group.
-        KScopedPageGroup spg(page_group);
+        KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup);
 
         for (const auto& node : page_group) {
             const size_t size{node.GetNumPages() * PageSize};
@@ -3081,7 +3096,6 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
         m_memory->UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
         break;
     }
-    case OperationType::MapFirst:
     case OperationType::Map: {
         ASSERT(map_addr);
         ASSERT(Common::IsAligned(GetInteger(map_addr), PageSize));
@@ -3089,11 +3103,7 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
 
         // Open references to pages, if we should.
         if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) {
-            if (operation == OperationType::MapFirst) {
-                m_kernel.MemoryManager().OpenFirst(map_addr, num_pages);
-            } else {
-                m_kernel.MemoryManager().Open(map_addr, num_pages);
-            }
+            m_kernel.MemoryManager().Open(map_addr, num_pages);
         }
         break;
     }
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index cbcbb6f62c..3d64b6fb0f 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -217,8 +217,8 @@ protected:
 private:
     enum class OperationType : u32 {
         Map = 0,
-        MapFirst = 1,
-        MapGroup = 2,
+        MapGroup = 1,
+        MapFirstGroup = 2,
         Unmap = 3,
         ChangePermissions = 4,
         ChangePermissionsAndRefresh = 5,

From 67e983a3544880b8f6b2ec8eee521f6965195b37 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Wed, 11 Oct 2023 14:23:01 -0400
Subject: [PATCH 07/10] codespell: allow 'VAs'

---
 .codespellrc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.codespellrc b/.codespellrc
index 944194a257..3336d31fec 100644
--- a/.codespellrc
+++ b/.codespellrc
@@ -3,4 +3,4 @@
 
 [codespell]
 skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
-ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink
+ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,vas,zink

From d8507332c116f3b5d39a8ac6ad5fd66a6a2914f8 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Wed, 11 Oct 2023 15:08:00 -0400
Subject: [PATCH 08/10] kernel: make check fully constexpr for broken msvc
 constant folding

---
 src/core/hle/kernel/kernel.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 6f19860a15..24433d32ba 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -636,7 +636,7 @@ struct KernelCore::Impl {
         constexpr size_t SecureUnknownRegionSize = 0;
         const size_t secure_unknown_size = SecureUnknownRegionSize;
         const auto secure_unknown_end_phys_addr = secure_applet_end_phys_addr + secure_unknown_size;
-        if (secure_unknown_size > 0) {
+        if constexpr (SecureUnknownRegionSize > 0) {
             ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
                 GetInteger(secure_applet_end_phys_addr), secure_unknown_size,
                 KMemoryRegionType_DramKernelSecureUnknown));

From 687158fe00f7a9ba35d1a62cd914d9dbb5ddeba4 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Fri, 20 Oct 2023 02:41:32 -0400
Subject: [PATCH 09/10] kernel: fix format string error

---
 src/core/hle/kernel/svc/svc_memory.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/core/hle/kernel/svc/svc_memory.cpp b/src/core/hle/kernel/svc/svc_memory.cpp
index 3726840948..97f1210def 100644
--- a/src/core/hle/kernel/svc/svc_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_memory.cpp
@@ -76,7 +76,7 @@ Result MapUnmapMemorySanityChecks(const KPageTable& manager, u64 dst_addr, u64 s
 } // namespace
 
 Result SetMemoryPermission(Core::System& system, u64 address, u64 size, MemoryPermission perm) {
-    LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
+    LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X}", address, size,
               perm);
 
     // Validate address / size.

From 9526ce95dd51aad0c54246f7f970f0acaf28e561 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Fri, 20 Oct 2023 02:53:31 -0400
Subject: [PATCH 10/10] gdbstub: add PermissionLocked to mappings table

---
 src/core/debugger/gdbstub.cpp | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 82964f0a16..2076aa8a23 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -822,11 +822,13 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
                 const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
                 const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
                 const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
+                const char p =
+                    True(mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
 
-                reply +=
-                    fmt::format("  {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n",
-                                mem_info.base_address, mem_info.base_address + mem_info.size - 1,
-                                perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count);
+                reply += fmt::format("  {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n",
+                                     mem_info.base_address,
+                                     mem_info.base_address + mem_info.size - 1, perm, state, l, i,
+                                     d, u, p, mem_info.ipc_count, mem_info.device_count);
             }
 
             const uintptr_t next_address = mem_info.base_address + mem_info.size;