diff --git a/src/core/hle/kernel/k_memory_region_type.h b/src/core/hle/kernel/k_memory_region_type.h index 6fb5abde95..243e2fd3d5 100644 --- a/src/core/hle/kernel/k_memory_region_type.h +++ b/src/core/hle/kernel/k_memory_region_type.h @@ -4,6 +4,8 @@ #pragma once +#include "common/bit_util.h" +#include "common/common_funcs.h" #include "common/common_types.h" namespace Kernel { @@ -18,5 +20,330 @@ enum class KMemoryRegionAttr : typename std::underlying_type: NoUserMap = 0x40000000, LinearMapped = 0x80000000, }; +DECLARE_ENUM_FLAG_OPERATORS(KMemoryRegionAttr); + +namespace impl { + +constexpr size_t BitsForDeriveSparse(size_t n) { + return n + 1; +} + +constexpr size_t BitsForDeriveDense(size_t n) { + size_t low = 0, high = 1; + for (size_t i = 0; i < n - 1; ++i) { + if ((++low) == high) { + ++high; + low = 0; + } + } + return high + 1; +} + +class KMemoryRegionTypeValue { +private: + using ValueType = typename std::underlying_type::type; + +private: + ValueType m_value{}; + size_t m_next_bit{}; + bool m_finalized{}; + bool m_sparse_only{}; + bool m_dense_only{}; + +private: + constexpr KMemoryRegionTypeValue(ValueType v) : m_value(v) {} + +public: + constexpr KMemoryRegionTypeValue() = default; + + constexpr operator KMemoryRegionType() const { + return static_cast(m_value); + } + + constexpr ValueType GetValue() const { + return m_value; + } + + constexpr const KMemoryRegionTypeValue& Finalize() { + m_finalized = true; + return *this; + } + + constexpr const KMemoryRegionTypeValue& SetSparseOnly() { + m_sparse_only = true; + return *this; + } + + constexpr const KMemoryRegionTypeValue& SetDenseOnly() { + m_dense_only = true; + return *this; + } + + constexpr KMemoryRegionTypeValue& SetAttribute(KMemoryRegionAttr attr) { + m_value |= static_cast(attr); + return *this; + } + + constexpr KMemoryRegionTypeValue DeriveInitial( + size_t i, size_t next = Common::BitSize()) const { + KMemoryRegionTypeValue new_type = *this; + new_type.m_value = (ValueType{1} << i); + new_type.m_next_bit = next; + return new_type; + } + + constexpr KMemoryRegionTypeValue DeriveAttribute(KMemoryRegionAttr attr) const { + KMemoryRegionTypeValue new_type = *this; + new_type.m_value |= static_cast(attr); + return new_type; + } + + constexpr KMemoryRegionTypeValue DeriveTransition(size_t ofs = 0, size_t adv = 1) const { + KMemoryRegionTypeValue new_type = *this; + new_type.m_value |= (ValueType{1} << (m_next_bit + ofs)); + new_type.m_next_bit += adv; + return new_type; + } + + constexpr KMemoryRegionTypeValue DeriveSparse(size_t ofs, size_t n, size_t i) const { + KMemoryRegionTypeValue new_type = *this; + new_type.m_value |= (ValueType{1} << (m_next_bit + ofs)); + new_type.m_value |= (ValueType{1} << (m_next_bit + ofs + 1 + i)); + new_type.m_next_bit += ofs + n + 1; + return new_type; + } + + constexpr KMemoryRegionTypeValue Derive(size_t n, size_t i) const { + size_t low = 0, high = 1; + for (size_t j = 0; j < i; ++j) { + if ((++low) == high) { + ++high; + low = 0; + } + } + + KMemoryRegionTypeValue new_type = *this; + new_type.m_value |= (ValueType{1} << (m_next_bit + low)); + new_type.m_value |= (ValueType{1} << (m_next_bit + high)); + new_type.m_next_bit += BitsForDeriveDense(n); + return new_type; + } + + constexpr KMemoryRegionTypeValue Advance(size_t n) const { + KMemoryRegionTypeValue new_type = *this; + new_type.m_next_bit += n; + return new_type; + } + + constexpr bool IsAncestorOf(ValueType v) const { + return (m_value | v) == v; + } +}; + +} // namespace impl + +constexpr auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue(); +constexpr auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2); +constexpr auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2); +static_assert(KMemoryRegionType_Kernel.GetValue() == 0x1); +static_assert(KMemoryRegionType_Dram.GetValue() == 0x2); + +constexpr auto KMemoryRegionType_DramKernelBase = + KMemoryRegionType_Dram.DeriveSparse(0, 3, 0) + .SetAttribute(KMemoryRegionAttr::NoUserMap) + .SetAttribute(KMemoryRegionAttr::CarveoutProtected); +constexpr auto KMemoryRegionType_DramReservedBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 1); +constexpr auto KMemoryRegionType_DramHeapBase = + KMemoryRegionType_Dram.DeriveSparse(0, 3, 2).SetAttribute(KMemoryRegionAttr::LinearMapped); +static_assert(static_cast(KMemoryRegionType_DramKernelBase.GetValue()) == + (static_cast(0xE) | KMemoryRegionAttr::CarveoutProtected | + KMemoryRegionAttr::NoUserMap)); +static_assert(KMemoryRegionType_DramReservedBase.GetValue() == (0x16)); +static_assert(static_cast(KMemoryRegionType_DramHeapBase.GetValue()) == + (static_cast(0x26) | KMemoryRegionAttr::LinearMapped)); + +constexpr auto KMemoryRegionType_DramKernelCode = + KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 0); +constexpr auto KMemoryRegionType_DramKernelSlab = + KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 1); +constexpr auto KMemoryRegionType_DramKernelPtHeap = + KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 2).SetAttribute( + KMemoryRegionAttr::LinearMapped); +constexpr auto KMemoryRegionType_DramKernelInitPt = + KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 3).SetAttribute( + KMemoryRegionAttr::LinearMapped); +static_assert(static_cast(KMemoryRegionType_DramKernelCode.GetValue()) == + (static_cast(0xCE) | KMemoryRegionAttr::CarveoutProtected | + KMemoryRegionAttr::NoUserMap)); +static_assert(static_cast(KMemoryRegionType_DramKernelSlab.GetValue()) == + (static_cast(0x14E) | KMemoryRegionAttr::CarveoutProtected | + KMemoryRegionAttr::NoUserMap)); +static_assert(static_cast(KMemoryRegionType_DramKernelPtHeap.GetValue()) == + (static_cast(0x24E) | KMemoryRegionAttr::CarveoutProtected | + KMemoryRegionAttr::NoUserMap | KMemoryRegionAttr::LinearMapped)); +static_assert(static_cast(KMemoryRegionType_DramKernelInitPt.GetValue()) == + (static_cast(0x44E) | KMemoryRegionAttr::CarveoutProtected | + KMemoryRegionAttr::NoUserMap | KMemoryRegionAttr::LinearMapped)); + +constexpr auto KMemoryRegionType_DramReservedEarly = + KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr::NoUserMap); +static_assert(static_cast(KMemoryRegionType_DramReservedEarly.GetValue()) == + (static_cast(0x16) | KMemoryRegionAttr::NoUserMap)); + +constexpr auto KMemoryRegionType_KernelTraceBuffer = + KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 0) + .SetAttribute(KMemoryRegionAttr::LinearMapped) + .SetAttribute(KMemoryRegionAttr::UserReadOnly); +constexpr auto KMemoryRegionType_OnMemoryBootImage = + KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 1); +constexpr auto KMemoryRegionType_DTB = KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2); +static_assert(static_cast(KMemoryRegionType_KernelTraceBuffer.GetValue()) == + (static_cast(0xD6) | KMemoryRegionAttr::LinearMapped | + KMemoryRegionAttr::UserReadOnly)); +static_assert(KMemoryRegionType_OnMemoryBootImage.GetValue() == 0x156); +static_assert(KMemoryRegionType_DTB.GetValue() == 0x256); + +constexpr auto KMemoryRegionType_DramPoolPartition = + KMemoryRegionType_DramHeapBase.DeriveAttribute(KMemoryRegionAttr::NoUserMap); +static_assert(static_cast(KMemoryRegionType_DramPoolPartition.GetValue()) == + (static_cast(0x26) | KMemoryRegionAttr::LinearMapped | + KMemoryRegionAttr::NoUserMap)); + +constexpr auto KMemoryRegionType_DramPoolManagement = + KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute( + KMemoryRegionAttr::CarveoutProtected); +constexpr auto KMemoryRegionType_DramUserPool = + KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition(); +static_assert(static_cast(KMemoryRegionType_DramPoolManagement.GetValue()) == + (static_cast(0x166) | KMemoryRegionAttr::LinearMapped | + KMemoryRegionAttr::NoUserMap | KMemoryRegionAttr::CarveoutProtected)); +static_assert(static_cast(KMemoryRegionType_DramUserPool.GetValue()) == + (static_cast(0x1A6) | KMemoryRegionAttr::LinearMapped | + KMemoryRegionAttr::NoUserMap)); + +constexpr auto KMemoryRegionType_DramApplicationPool = KMemoryRegionType_DramUserPool.Derive(4, 0); +constexpr auto KMemoryRegionType_DramAppletPool = KMemoryRegionType_DramUserPool.Derive(4, 1); +constexpr auto KMemoryRegionType_DramSystemNonSecurePool = + KMemoryRegionType_DramUserPool.Derive(4, 2); +constexpr auto KMemoryRegionType_DramSystemPool = + KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr::CarveoutProtected); +static_assert(static_cast(KMemoryRegionType_DramApplicationPool.GetValue()) == + (static_cast(0x7A6) | KMemoryRegionAttr::LinearMapped | + KMemoryRegionAttr::NoUserMap)); +static_assert(static_cast(KMemoryRegionType_DramAppletPool.GetValue()) == + (static_cast(0xBA6) | KMemoryRegionAttr::LinearMapped | + KMemoryRegionAttr::NoUserMap)); +static_assert( + static_cast(KMemoryRegionType_DramSystemNonSecurePool.GetValue()) == + (static_cast(0xDA6) | KMemoryRegionAttr::LinearMapped | + KMemoryRegionAttr::NoUserMap)); +static_assert(static_cast(KMemoryRegionType_DramSystemPool.GetValue()) == + (static_cast(0x13A6) | KMemoryRegionAttr::LinearMapped | + KMemoryRegionAttr::NoUserMap | KMemoryRegionAttr::CarveoutProtected)); + +constexpr auto KMemoryRegionType_VirtualDramHeapBase = KMemoryRegionType_Dram.DeriveSparse(1, 3, 0); +constexpr auto KMemoryRegionType_VirtualDramKernelPtHeap = + KMemoryRegionType_Dram.DeriveSparse(1, 3, 1); +constexpr auto KMemoryRegionType_VirtualDramKernelTraceBuffer = + KMemoryRegionType_Dram.DeriveSparse(1, 3, 2); +static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A); +static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A); +static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A); + +constexpr auto KMemoryRegionType_VirtualDramKernelInitPt = + KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0); +constexpr auto KMemoryRegionType_VirtualDramPoolManagement = + KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1); +constexpr 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); + +// 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 auto KMemoryRegionType_VirtualDramAppletPool = + KMemoryRegionType_VirtualDramUserPool.Derive(6, 2); +constexpr auto KMemoryRegionType_VirtualDramApplicationPool = + KMemoryRegionType_VirtualDramUserPool.Derive(6, 3); +constexpr auto KMemoryRegionType_VirtualDramSystemNonSecurePool = + KMemoryRegionType_VirtualDramUserPool.Derive(6, 4); +constexpr 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 auto KMemoryRegionType_ArchDeviceBase = + KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly(); +constexpr auto KMemoryRegionType_BoardDeviceBase = + KMemoryRegionType_Kernel.DeriveTransition(0, 2).SetDenseOnly(); +static_assert(KMemoryRegionType_ArchDeviceBase.GetValue() == 0x5); +static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5); + +#if defined(ATMOSPHERE_ARCH_ARM64) +#include +#elif defined(ATMOSPHERE_ARCH_ARM) +#include +#else +// Default to no architecture devices. +constexpr auto NumArchitectureDeviceRegions = 0; +#endif +static_assert(NumArchitectureDeviceRegions >= 0); + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) +#include +#else +// Default to no board devices. +constexpr auto NumBoardDeviceRegions = 0; +#endif +static_assert(NumBoardDeviceRegions >= 0); + +constexpr auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0); +constexpr auto KMemoryRegionType_KernelStack = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1); +constexpr auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2); +constexpr auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3); +static_assert(KMemoryRegionType_KernelCode.GetValue() == 0x19); +static_assert(KMemoryRegionType_KernelStack.GetValue() == 0x29); +static_assert(KMemoryRegionType_KernelMisc.GetValue() == 0x49); +static_assert(KMemoryRegionType_KernelSlab.GetValue() == 0x89); + +constexpr auto KMemoryRegionType_KernelMiscDerivedBase = + KMemoryRegionType_KernelMisc.DeriveTransition(); +static_assert(KMemoryRegionType_KernelMiscDerivedBase.GetValue() == 0x149); + +// UNUSED: .Derive(7, 0); +constexpr auto KMemoryRegionType_KernelMiscMainStack = + KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 1); +constexpr auto KMemoryRegionType_KernelMiscMappedDevice = + KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 2); +constexpr auto KMemoryRegionType_KernelMiscExceptionStack = + KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 3); +constexpr auto KMemoryRegionType_KernelMiscUnknownDebug = + KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 4); +// UNUSED: .Derive(7, 5); +constexpr auto KMemoryRegionType_KernelMiscIdleStack = + KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 6); +static_assert(KMemoryRegionType_KernelMiscMainStack.GetValue() == 0xB49); +static_assert(KMemoryRegionType_KernelMiscMappedDevice.GetValue() == 0xD49); +static_assert(KMemoryRegionType_KernelMiscExceptionStack.GetValue() == 0x1349); +static_assert(KMemoryRegionType_KernelMiscUnknownDebug.GetValue() == 0x1549); +static_assert(KMemoryRegionType_KernelMiscIdleStack.GetValue() == 0x2349); + +constexpr auto KMemoryRegionType_KernelTemp = KMemoryRegionType_Kernel.Advance(2).Derive(2, 0); +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)) { + return KMemoryRegionType_VirtualDramKernelPtHeap; + } else { + return KMemoryRegionType_Dram; + } +} } // namespace Kernel