From c2f4dcb1e3ddca062a51cad11e6314196d9d16bc Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sun, 5 Apr 2020 14:45:58 -0400
Subject: [PATCH] kernel: memory: Add MemoryBlock class, for managing memory
 blocks and their state.

---
 src/core/CMakeLists.txt                   |   1 +
 src/core/hle/kernel/memory/memory_block.h | 315 ++++++++++++++++++++++
 2 files changed, 316 insertions(+)
 create mode 100644 src/core/hle/kernel/memory/memory_block.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 6875cff27..504ee2777 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -156,6 +156,7 @@ add_library(core STATIC
     hle/kernel/kernel.h
     hle/kernel/memory/address_space_info.cpp
     hle/kernel/memory/address_space_info.h
+    hle/kernel/memory/memory_block.h
     hle/kernel/memory/memory_types.h
     hle/kernel/memory/slab_heap.h
     hle/kernel/mutex.cpp
diff --git a/src/core/hle/kernel/memory/memory_block.h b/src/core/hle/kernel/memory/memory_block.h
new file mode 100644
index 000000000..1bb31405a
--- /dev/null
+++ b/src/core/hle/kernel/memory/memory_block.h
@@ -0,0 +1,315 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "core/hle/kernel/memory/memory_types.h"
+#include "core/hle/kernel/svc_types.h"
+
+namespace Kernel::Memory {
+
+enum class MemoryState : u32 {
+    None = 0,
+    Mask = 0xFFFFFFFF, // TODO(bunnei): This should probable be 0xFF
+    All = ~None,
+
+    FlagCanReprotect = (1 << 8),
+    FlagCanDebug = (1 << 9),
+    FlagCanUseIpc = (1 << 10),
+    FlagCanUseNonDeviceIpc = (1 << 11),
+    FlagCanUseNonSecureIpc = (1 << 12),
+    FlagMapped = (1 << 13),
+    FlagCode = (1 << 14),
+    FlagCanAlias = (1 << 15),
+    FlagCanCodeAlias = (1 << 16),
+    FlagCanTransfer = (1 << 17),
+    FlagCanQueryPhysical = (1 << 18),
+    FlagCanDeviceMap = (1 << 19),
+    FlagCanAlignedDeviceMap = (1 << 20),
+    FlagCanIpcUserBuffer = (1 << 21),
+    FlagReferenceCounted = (1 << 22),
+    FlagCanMapProcess = (1 << 23),
+    FlagCanChangeAttribute = (1 << 24),
+    FlagCanCodeMemory = (1 << 25),
+
+    FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
+                FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
+                FlagCanDeviceMap | FlagCanAlignedDeviceMap | FlagCanIpcUserBuffer |
+                FlagReferenceCounted | FlagCanChangeAttribute,
+
+    FlagsCode = FlagCanDebug | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
+                FlagMapped | FlagCode | FlagCanQueryPhysical | FlagCanDeviceMap |
+                FlagCanAlignedDeviceMap | FlagReferenceCounted,
+
+    FlagsMisc = FlagMapped | FlagReferenceCounted | FlagCanQueryPhysical | FlagCanDeviceMap,
+
+    Free = static_cast<u32>(Svc::MemoryState::Free),
+    Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped,
+    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,
+    Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted,
+    Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
+
+    AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
+                FlagCanCodeAlias,
+    AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData |
+                    FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory,
+
+    Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap |
+          FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+
+    Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
+            FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+
+    ThreadLocal =
+        static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted,
+
+    Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
+                 FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
+                 FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+
+    SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc |
+                       FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+
+    SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
+                 FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+
+    Inaccessible = static_cast<u32>(Svc::MemoryState::Inaccessible),
+
+    NonSecureIpc = static_cast<u32>(Svc::MemoryState::NonSecureIpc) | FlagsMisc |
+                   FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
+
+    NonDeviceIpc =
+        static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc,
+
+    Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped,
+
+    GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
+                    FlagReferenceCounted | FlagCanDebug,
+    CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted,
+};
+DECLARE_ENUM_FLAG_OPERATORS(MemoryState);
+
+static_assert(static_cast<u32>(MemoryState::Free) == 0x00000000);
+static_assert(static_cast<u32>(MemoryState::Io) == 0x00002001);
+static_assert(static_cast<u32>(MemoryState::Static) == 0x00042002);
+static_assert(static_cast<u32>(MemoryState::Code) == 0x00DC7E03);
+static_assert(static_cast<u32>(MemoryState::CodeData) == 0x03FEBD04);
+static_assert(static_cast<u32>(MemoryState::Normal) == 0x037EBD05);
+static_assert(static_cast<u32>(MemoryState::Shared) == 0x00402006);
+static_assert(static_cast<u32>(MemoryState::AliasCode) == 0x00DD7E08);
+static_assert(static_cast<u32>(MemoryState::AliasCodeData) == 0x03FFBD09);
+static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A);
+static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B);
+static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C);
+static_assert(static_cast<u32>(MemoryState::Transfered) == 0x015C3C0D);
+static_assert(static_cast<u32>(MemoryState::SharedTransfered) == 0x005C380E);
+static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F);
+static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010);
+static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811);
+static_assert(static_cast<u32>(MemoryState::NonDeviceIpc) == 0x004C2812);
+static_assert(static_cast<u32>(MemoryState::Kernel) == 0x00002013);
+static_assert(static_cast<u32>(MemoryState::GeneratedCode) == 0x00402214);
+static_assert(static_cast<u32>(MemoryState::CodeOut) == 0x00402015);
+
+enum class MemoryPermission : u8 {
+    None = 0,
+    Mask = static_cast<u8>(~None),
+
+    Read = 1 << 0,
+    Write = 1 << 1,
+    Execute = 1 << 2,
+
+    ReadAndWrite = Read | Write,
+    ReadAndExecute = Read | Execute,
+
+    UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
+                               Svc::MemoryPermission::Execute),
+};
+DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission);
+
+enum class MemoryAttribute : u8 {
+    None = 0x00,
+    Mask = 0x7F,
+    All = Mask,
+    DontCareMask = 0x80,
+
+    Locked = static_cast<u8>(Svc::MemoryAttribute::Locked),
+    IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked),
+    DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
+    Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
+
+    IpcAndDeviceMapped = IpcLocked | DeviceShared,
+    LockedAndIpcLocked = Locked | IpcLocked,
+    DeviceSharedAndUncached = DeviceShared | Uncached
+};
+DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute);
+
+static_assert((static_cast<u8>(MemoryAttribute::Mask) &
+               static_cast<u8>(MemoryAttribute::DontCareMask)) == 0);
+
+struct MemoryInfo {
+    VAddr addr{};
+    std::size_t size{};
+    MemoryState state{};
+    MemoryPermission perm{};
+    MemoryAttribute attribute{};
+    MemoryPermission original_perm{};
+    u16 ipc_lock_count{};
+    u16 device_use_count{};
+
+    constexpr Svc::MemoryInfo GetSvcMemoryInfo() const {
+        return {
+            addr,
+            size,
+            static_cast<Svc::MemoryState>(state & MemoryState::Mask),
+            static_cast<Svc::MemoryAttribute>(attribute & MemoryAttribute::Mask),
+            static_cast<Svc::MemoryPermission>(perm & MemoryPermission::UserMask),
+            ipc_lock_count,
+            device_use_count,
+        };
+    }
+
+    constexpr VAddr GetAddress() const {
+        return addr;
+    }
+    constexpr std::size_t GetSize() const {
+        return size;
+    }
+    constexpr std::size_t GetNumPages() const {
+        return GetSize() / PageSize;
+    }
+    constexpr VAddr GetEndAddress() const {
+        return GetAddress() + GetSize();
+    }
+    constexpr VAddr GetLastAddress() const {
+        return GetEndAddress() - 1;
+    }
+};
+
+class MemoryBlock final {
+    friend class MemoryBlockManager;
+
+private:
+    VAddr addr{};
+    std::size_t num_pages{};
+    MemoryState state{MemoryState::None};
+    u16 ipc_lock_count{};
+    u16 device_use_count{};
+    MemoryPermission perm{MemoryPermission::None};
+    MemoryPermission original_perm{MemoryPermission::None};
+    MemoryAttribute attribute{MemoryAttribute::None};
+
+public:
+    static constexpr int Compare(const MemoryBlock& lhs, const MemoryBlock& rhs) {
+        if (lhs.GetAddress() < rhs.GetAddress()) {
+            return -1;
+        } else if (lhs.GetAddress() <= rhs.GetLastAddress()) {
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+public:
+    constexpr MemoryBlock() = default;
+    constexpr MemoryBlock(VAddr addr, std::size_t num_pages, MemoryState state,
+                          MemoryPermission perm, MemoryAttribute attribute)
+        : addr{addr}, num_pages(num_pages), state{state}, perm{perm}, attribute{attribute} {}
+
+    constexpr VAddr GetAddress() const {
+        return addr;
+    }
+
+    constexpr std::size_t GetNumPages() const {
+        return num_pages;
+    }
+
+    constexpr std::size_t GetSize() const {
+        return GetNumPages() * PageSize;
+    }
+
+    constexpr VAddr GetEndAddress() const {
+        return GetAddress() + GetSize();
+    }
+
+    constexpr VAddr GetLastAddress() const {
+        return GetEndAddress() - 1;
+    }
+
+    constexpr MemoryInfo GetMemoryInfo() const {
+        return {
+            GetAddress(), GetSize(),     state,          perm,
+            attribute,    original_perm, ipc_lock_count, device_use_count,
+        };
+    }
+
+private:
+    constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const {
+        constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask |
+                                                      MemoryAttribute::IpcLocked |
+                                                      MemoryAttribute::DeviceShared};
+        return state == s && perm == p &&
+               (attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask);
+    }
+
+    constexpr bool HasSameProperties(const MemoryBlock& rhs) const {
+        return state == rhs.state && perm == rhs.perm && original_perm == rhs.original_perm &&
+               attribute == rhs.attribute && ipc_lock_count == rhs.ipc_lock_count &&
+               device_use_count == rhs.device_use_count;
+    }
+
+    constexpr bool Contains(VAddr start) const {
+        return GetAddress() <= start && start <= GetEndAddress();
+    }
+
+    constexpr void Add(std::size_t count) {
+        ASSERT(count > 0);
+        ASSERT(GetAddress() + count * PageSize - 1 < GetEndAddress() + count * PageSize - 1);
+
+        num_pages += count;
+    }
+
+    constexpr void Update(MemoryState new_state, MemoryPermission new_perm,
+                          MemoryAttribute new_attribute) {
+        ASSERT(original_perm == MemoryPermission::None);
+        ASSERT((attribute & MemoryAttribute::IpcLocked) == MemoryAttribute::None);
+
+        state = new_state;
+        perm = new_perm;
+
+        // TODO(bunnei): Is this right?
+        attribute = static_cast<MemoryAttribute>(
+            new_attribute /*| (attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared))*/);
+    }
+
+    constexpr MemoryBlock Split(VAddr split_addr) {
+        ASSERT(GetAddress() < split_addr);
+        ASSERT(Contains(split_addr));
+        ASSERT(Common::IsAligned(split_addr, PageSize));
+
+        MemoryBlock block;
+        block.addr = addr;
+        block.num_pages = (split_addr - GetAddress()) / PageSize;
+        block.state = state;
+        block.ipc_lock_count = ipc_lock_count;
+        block.device_use_count = device_use_count;
+        block.perm = perm;
+        block.original_perm = original_perm;
+        block.attribute = attribute;
+
+        addr = split_addr;
+        num_pages -= block.num_pages;
+
+        return block;
+    }
+};
+static_assert(std::is_trivially_destructible<MemoryBlock>::value);
+
+} // namespace Kernel::Memory