code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <random>

#include "common/literals.h"
#include "common/settings.h"

#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_trace.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"

namespace Kernel::Board::Nintendo::Nx {

namespace impl {

using namespace Common::Literals;

constexpr const std::size_t RequiredNonSecureSystemMemorySizeVi = 0x2280 * 4_KiB;
constexpr const std::size_t RequiredNonSecureSystemMemorySizeViFatal = 0x200 * 4_KiB;
constexpr const std::size_t RequiredNonSecureSystemMemorySizeNvservices = 0x704 * 4_KiB;
constexpr const std::size_t RequiredNonSecureSystemMemorySizeMisc = 0x80 * 4_KiB;

} // namespace impl

constexpr const std::size_t RequiredNonSecureSystemMemorySize =
    impl::RequiredNonSecureSystemMemorySizeVi + impl::RequiredNonSecureSystemMemorySizeNvservices +
    impl::RequiredNonSecureSystemMemorySizeMisc;

constexpr const std::size_t RequiredNonSecureSystemMemorySizeWithFatal =
    RequiredNonSecureSystemMemorySize + impl::RequiredNonSecureSystemMemorySizeViFatal;

constexpr const std::size_t SecureAlignment = 128_KiB;

namespace {

using namespace Common::Literals;

u32 GetMemorySizeForInit() {
    switch (Settings::values.memory_layout_mode.GetValue()) {
    case Settings::MemoryLayout::Memory_4Gb:
        return Smc::MemorySize_4GB;
    case Settings::MemoryLayout::Memory_6Gb:
        return Smc::MemorySize_6GB;
    case Settings::MemoryLayout::Memory_8Gb:
        return Smc::MemorySize_8GB;
    }
    return Smc::MemorySize_4GB;
}

Smc::MemoryArrangement GetMemoryArrangeForInit() {
    switch (Settings::values.memory_layout_mode.GetValue()) {
    case Settings::MemoryLayout::Memory_4Gb:
        return Smc::MemoryArrangement_4GB;
    case Settings::MemoryLayout::Memory_6Gb:
        return Smc::MemoryArrangement_6GB;
    case Settings::MemoryLayout::Memory_8Gb:
        return Smc::MemoryArrangement_8GB;
    }
    return Smc::MemoryArrangement_4GB;
}
} // namespace

size_t KSystemControl::Init::GetRealMemorySize() {
    return GetIntendedMemorySize();
}

// Initialization.
size_t KSystemControl::Init::GetIntendedMemorySize() {
    switch (GetMemorySizeForInit()) {
    case Smc::MemorySize_4GB:
    default: // All invalid modes should go to 4GB.
        return 4_GiB;
    case Smc::MemorySize_6GB:
        return 6_GiB;
    case Smc::MemorySize_8GB:
        return 8_GiB;
    }
}

KPhysicalAddress KSystemControl::Init::GetKernelPhysicalBaseAddress(KPhysicalAddress base_address) {
    const size_t real_dram_size = KSystemControl::Init::GetRealMemorySize();
    const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize();
    if (intended_dram_size * 2 < real_dram_size) {
        return base_address;
    } else {
        return base_address + ((real_dram_size - intended_dram_size) / 2);
    }
}

bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
    return true;
}

std::size_t KSystemControl::Init::GetApplicationPoolSize() {
    // Get the base pool size.
    const size_t base_pool_size = []() -> size_t {
        switch (GetMemoryArrangeForInit()) {
        case Smc::MemoryArrangement_4GB:
        default:
            return 3285_MiB;
        case Smc::MemoryArrangement_4GBForAppletDev:
            return 2048_MiB;
        case Smc::MemoryArrangement_4GBForSystemDev:
            return 3285_MiB;
        case Smc::MemoryArrangement_6GB:
            return 4916_MiB;
        case Smc::MemoryArrangement_6GBForAppletDev:
            return 3285_MiB;
        case Smc::MemoryArrangement_8GB:
            // Real kernel sets this to 4916_MiB. We are not debugging applets.
            return 6547_MiB;
        }
    }();

    // Return (possibly) adjusted size.
    return base_pool_size;
}

size_t KSystemControl::Init::GetAppletPoolSize() {
    // Get the base pool size.
    const size_t base_pool_size = []() -> size_t {
        switch (GetMemoryArrangeForInit()) {
        case Smc::MemoryArrangement_4GB:
        default:
            return 507_MiB;
        case Smc::MemoryArrangement_4GBForAppletDev:
            return 1554_MiB;
        case Smc::MemoryArrangement_4GBForSystemDev:
            return 448_MiB;
        case Smc::MemoryArrangement_6GB:
            return 562_MiB;
        case Smc::MemoryArrangement_6GBForAppletDev:
            return 2193_MiB;
        case Smc::MemoryArrangement_8GB:
            //! Real kernel sets this to 2193_MiB. We are not debugging applets.
            return 562_MiB;
        }
    }();

    // Return (possibly) adjusted size.
    constexpr size_t ExtraSystemMemoryForAtmosphere = 33_MiB;
    return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize;
}

size_t KSystemControl::Init::GetMinimumNonSecureSystemPoolSize() {
    // Verify that our minimum is at least as large as Nintendo's.
    constexpr size_t MinimumSizeWithFatal = RequiredNonSecureSystemMemorySizeWithFatal;
    static_assert(MinimumSizeWithFatal >= 0x2C04000);

    constexpr size_t MinimumSizeWithoutFatal = RequiredNonSecureSystemMemorySize;
    static_assert(MinimumSizeWithoutFatal >= 0x2A00000);

    return MinimumSizeWithFatal;
}

namespace {
template <typename F>
u64 GenerateUniformRange(u64 min, u64 max, F f) {
    // Handle the case where the difference is too large to represent.
    if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
        return f();
    }

    // Iterate until we get a value in range.
    const u64 range_size = ((max + 1) - min);
    const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
    while (true) {
        if (const u64 rnd = f(); rnd < effective_max) {
            return min + (rnd % range_size);
        }
    }
}

} // Anonymous namespace

u64 KSystemControl::GenerateRandomU64() {
    std::random_device device;
    std::mt19937 gen(device());
    std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
    return distribution(gen);
}

u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
    return GenerateUniformRange(min, max, GenerateRandomU64);
}

size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) {
    if (pool == static_cast<u32>(KMemoryManager::Pool::Applet)) {
        return 0;
    } else {
        // return KSystemControlBase::CalculateRequiredSecureMemorySize(size, pool);
        return size;
    }
}

Result KSystemControl::AllocateSecureMemory(KernelCore& kernel, KVirtualAddress* out, size_t size,
                                            u32 pool) {
    // Applet secure memory is handled separately.
    UNIMPLEMENTED_IF(pool == static_cast<u32>(KMemoryManager::Pool::Applet));

    // Ensure the size is aligned.
    const size_t alignment =
        (pool == static_cast<u32>(KMemoryManager::Pool::System) ? PageSize : SecureAlignment);
    R_UNLESS(Common::IsAligned(size, alignment), ResultInvalidSize);

    // Allocate the memory.
    const size_t num_pages = size / PageSize;
    const KPhysicalAddress paddr = kernel.MemoryManager().AllocateAndOpenContinuous(
        num_pages, alignment / PageSize,
        KMemoryManager::EncodeOption(static_cast<KMemoryManager::Pool>(pool),
                                     KMemoryManager::Direction::FromFront));
    R_UNLESS(paddr != 0, ResultOutOfMemory);

    // Ensure we don't leak references to the memory on error.
    ON_RESULT_FAILURE {
        kernel.MemoryManager().Close(paddr, num_pages);
    };

    // We succeeded.
    *out = KPageTable::GetHeapVirtualAddress(kernel, paddr);
    R_SUCCEED();
}

void KSystemControl::FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
                                      u32 pool) {
    // Applet secure memory is handled separately.
    UNIMPLEMENTED_IF(pool == static_cast<u32>(KMemoryManager::Pool::Applet));

    // Ensure the size is aligned.
    const size_t alignment =
        (pool == static_cast<u32>(KMemoryManager::Pool::System) ? PageSize : SecureAlignment);
    ASSERT(Common::IsAligned(GetInteger(address), alignment));
    ASSERT(Common::IsAligned(size, alignment));

    // Close the secure region's pages.
    kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel, address),
                                 size / PageSize);
}

// Insecure Memory.
KResourceLimit* KSystemControl::GetInsecureMemoryResourceLimit(KernelCore& kernel) {
    return kernel.GetSystemResourceLimit();
}

u32 KSystemControl::GetInsecureMemoryPool() {
    return static_cast<u32>(KMemoryManager::Pool::SystemNonSecure);
}

} // namespace Kernel::Board::Nintendo::Nx


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/kernel/k_typed_address.h"
#include "core/hle/result.h"

namespace Kernel {
class KernelCore;
class KResourceLimit;
} // namespace Kernel

namespace Kernel::Board::Nintendo::Nx {

class KSystemControl {
public:
    // This can be overridden as needed.
    static constexpr size_t SecureAppletMemorySize = 4 * 1024 * 1024; // 4_MB

public:
    class Init {
    public:
        // Initialization.
        static std::size_t GetRealMemorySize();
        static std::size_t GetIntendedMemorySize();
        static KPhysicalAddress GetKernelPhysicalBaseAddress(KPhysicalAddress base_address);
        static bool ShouldIncreaseThreadResourceLimit();
        static std::size_t GetApplicationPoolSize();
        static std::size_t GetAppletPoolSize();
        static std::size_t GetMinimumNonSecureSystemPoolSize();
    };

    // Randomness.
    static u64 GenerateRandomRange(u64 min, u64 max);
    static u64 GenerateRandomU64();

    // Secure Memory.
    static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
    static Result AllocateSecureMemory(KernelCore& kernel, KVirtualAddress* out, size_t size,
                                       u32 pool);
    static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
                                 u32 pool);

    // Insecure Memory.
    static KResourceLimit* GetInsecureMemoryResourceLimit(KernelCore& kernel);
    static u32 GetInsecureMemoryPool();
};

} // namespace Kernel::Board::Nintendo::Nx

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

namespace Kernel::Board::Nintendo::Nx::Smc {

enum MemorySize {
    MemorySize_4GB = 0,
    MemorySize_6GB = 1,
    MemorySize_8GB = 2,
};

enum MemoryArrangement {
    MemoryArrangement_4GB = 0,
    MemoryArrangement_4GBForAppletDev = 1,
    MemoryArrangement_4GBForSystemDev = 2,
    MemoryArrangement_6GB = 3,
    MemoryArrangement_6GBForAppletDev = 4,
    MemoryArrangement_8GB = 5,
};

} // namespace Kernel::Board::Nintendo::Nx::Smc


code:
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/kernel/code_set.h"

namespace Kernel {

CodeSet::CodeSet() = default;
CodeSet::~CodeSet() = default;

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <cstddef>

#include "core/hle/kernel/k_typed_address.h"
#include "core/hle/kernel/physical_memory.h"

namespace Kernel {

/**
 * Represents executable data that may be loaded into a kernel process.
 *
 * A code set consists of three basic segments:
 *   - A code (AKA text) segment,
 *   - A read-only data segment (rodata)
 *   - A data segment
 *
 * The code segment is the portion of the object file that contains
 * executable instructions.
 *
 * The read-only data segment in the portion of the object file that
 * contains (as one would expect) read-only data, such as fixed constant
 * values and data structures.
 *
 * The data segment is similar to the read-only data segment -- it contains
 * variables and data structures that have predefined values, however,
 * entities within this segment can be modified.
 */
struct CodeSet final {
    /// A single segment within a code set.
    struct Segment final {
        /// The byte offset that this segment is located at.
        std::size_t offset = 0;

        /// The address to map this segment to.
        KProcessAddress addr = 0;

        /// The size of this segment in bytes.
        u32 size = 0;
    };

    explicit CodeSet();
    ~CodeSet();

    CodeSet(const CodeSet&) = delete;
    CodeSet& operator=(const CodeSet&) = delete;

    CodeSet(CodeSet&&) = default;
    CodeSet& operator=(CodeSet&&) = default;

    Segment& CodeSegment() {
        return segments[0];
    }

    const Segment& CodeSegment() const {
        return segments[0];
    }

    Segment& RODataSegment() {
        return segments[1];
    }

    const Segment& RODataSegment() const {
        return segments[1];
    }

    Segment& DataSegment() {
        return segments[2];
    }

    const Segment& DataSegment() const {
        return segments[2];
    }

#ifdef HAS_NCE
    Segment& PatchSegment() {
        return patch_segment;
    }

    const Segment& PatchSegment() const {
        return patch_segment;
    }
#endif

    /// The overall data that backs this code set.
    Kernel::PhysicalMemory memory;

    /// The segments that comprise this code set.
    std::array<Segment, 3> segments;

#ifdef HAS_NCE
    Segment patch_segment;
#endif

    /// The entry point address for this code set.
    KProcessAddress entrypoint = 0;
};

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <mutex>

#include "common/assert.h"
#include "core/core.h"
#include "core/hle/kernel/global_scheduler_context.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"

namespace Kernel {

GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel)
    : m_kernel{kernel}, m_scheduler_lock{kernel} {}

GlobalSchedulerContext::~GlobalSchedulerContext() = default;

void GlobalSchedulerContext::AddThread(KThread* thread) {
    std::scoped_lock lock{m_global_list_guard};
    m_thread_list.push_back(thread);
}

void GlobalSchedulerContext::RemoveThread(KThread* thread) {
    std::scoped_lock lock{m_global_list_guard};
    std::erase(m_thread_list, thread);
}

void GlobalSchedulerContext::PreemptThreads() {
    // The priority levels at which the global scheduler preempts threads every 10 ms. They are
    // ordered from Core 0 to Core 3.
    static constexpr std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities{
        59,
        59,
        59,
        63,
    };

    ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel));
    for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
        const u32 priority = preemption_priorities[core_id];
        KScheduler::RotateScheduledQueue(m_kernel, core_id, priority);
    }
}

bool GlobalSchedulerContext::IsLocked() const {
    return m_scheduler_lock.IsLockedByCurrentThread();
}

void GlobalSchedulerContext::RegisterDummyThreadForWakeup(KThread* thread) {
    ASSERT(this->IsLocked());

    m_woken_dummy_threads.insert(thread);
}

void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) {
    ASSERT(this->IsLocked());

    m_woken_dummy_threads.erase(thread);
}

void GlobalSchedulerContext::WakeupWaitingDummyThreads() {
    ASSERT(this->IsLocked());

    for (auto* thread : m_woken_dummy_threads) {
        thread->DummyThreadEndWait();
    }

    m_woken_dummy_threads.clear();
}

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <atomic>
#include <set>
#include <vector>

#include "common/common_types.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_priority_queue.h"
#include "core/hle/kernel/k_scheduler_lock.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/svc_types.h"

namespace Kernel {

class KernelCore;
class SchedulerLock;

using KSchedulerPriorityQueue =
    KPriorityQueue<KThread, Core::Hardware::NUM_CPU_CORES, Svc::LowestThreadPriority,
                   Svc::HighestThreadPriority>;

static constexpr s32 HighestCoreMigrationAllowedPriority = 2;
static_assert(Svc::LowestThreadPriority >= HighestCoreMigrationAllowedPriority);
static_assert(Svc::HighestThreadPriority <= HighestCoreMigrationAllowedPriority);

class GlobalSchedulerContext final {
    friend class KScheduler;

public:
    using LockType = KAbstractSchedulerLock<KScheduler>;

    explicit GlobalSchedulerContext(KernelCore& kernel);
    ~GlobalSchedulerContext();

    /// Adds a new thread to the scheduler
    void AddThread(KThread* thread);

    /// Removes a thread from the scheduler
    void RemoveThread(KThread* thread);

    /// Returns a list of all threads managed by the scheduler
    /// This is only safe to iterate while holding the scheduler lock
    const std::vector<KThread*>& GetThreadList() const {
        return m_thread_list;
    }

    /**
     * Rotates the scheduling queues of threads at a preemption priority and then does
     * some core rebalancing. Preemption priorities can be found in the array
     * 'preemption_priorities'.
     *
     * @note This operation happens every 10ms.
     */
    void PreemptThreads();

    /// Returns true if the global scheduler lock is acquired
    bool IsLocked() const;

    void UnregisterDummyThreadForWakeup(KThread* thread);
    void RegisterDummyThreadForWakeup(KThread* thread);
    void WakeupWaitingDummyThreads();

    LockType& SchedulerLock() {
        return m_scheduler_lock;
    }

private:
    friend class KScopedSchedulerLock;
    friend class KScopedSchedulerLockAndSleep;

    KernelCore& m_kernel;

    std::atomic_bool m_scheduler_update_needed{};
    KSchedulerPriorityQueue m_priority_queue;
    LockType m_scheduler_lock;

    /// Lists dummy threads pending wakeup on lock release
    std::set<KThread*> m_woken_dummy_threads;

    /// Lists all thread ids that aren't deleted/etc.
    std::vector<KThread*> m_thread_list;
    std::mutex m_global_list_guard;
};

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_funcs.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/init/init_slab_setup.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_debug.h"
#include "core/hle/kernel/k_device_address_space.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_event_info.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_object_name.h"
#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_session_request.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_shared_memory_info.h"
#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/k_system_resource.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_local_page.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/k_typed_address.h"

namespace Kernel::Init {

// For macro convenience.
using KThreadLockInfo = KThread::LockWithPriorityInheritanceInfo;

#define SLAB_COUNT(CLASS) kernel.SlabResourceCounts().num_##CLASS

#define FOREACH_SLAB_TYPE(HANDLER, ...)                                                            \
    HANDLER(KProcess, (SLAB_COUNT(KProcess)), ##__VA_ARGS__)                                       \
    HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__)                                         \
    HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__)                                           \
    HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__)                                             \
    HANDLER(KSessionRequest, (SLAB_COUNT(KSession) * 2), ##__VA_ARGS__)                            \
    HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__)                             \
    HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__)                     \
    HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__)                         \
    HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__)                                 \
    HANDLER(KDeviceAddressSpace, (SLAB_COUNT(KDeviceAddressSpace)), ##__VA_ARGS__)                 \
    HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__)                                       \
    HANDLER(KThreadLocalPage,                                                                      \
            (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8),             \
            ##__VA_ARGS__)                                                                         \
    HANDLER(KObjectName, (SLAB_COUNT(KObjectName)), ##__VA_ARGS__)                                 \
    HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)                           \
    HANDLER(KEventInfo, (SLAB_COUNT(KThread) + SLAB_COUNT(KDebug)), ##__VA_ARGS__)                 \
    HANDLER(KDebug, (SLAB_COUNT(KDebug)), ##__VA_ARGS__)                                           \
    HANDLER(KSecureSystemResource, (SLAB_COUNT(KProcess)), ##__VA_ARGS__)                          \
    HANDLER(KThreadLockInfo, (SLAB_COUNT(KThread)), ##__VA_ARGS__)

namespace {

#define DEFINE_SLAB_TYPE_ENUM_MEMBER(NAME, COUNT, ...) KSlabType_##NAME,

enum KSlabType : u32 {
    FOREACH_SLAB_TYPE(DEFINE_SLAB_TYPE_ENUM_MEMBER) KSlabType_Count,
};

#undef DEFINE_SLAB_TYPE_ENUM_MEMBER

// Constexpr counts.
constexpr size_t SlabCountKProcess = 80;
constexpr size_t SlabCountKThread = 800;
constexpr size_t SlabCountKEvent = 900;
constexpr size_t SlabCountKInterruptEvent = 100;
constexpr size_t SlabCountKPort = 384;
constexpr size_t SlabCountKSharedMemory = 80;
constexpr size_t SlabCountKTransferMemory = 200;
constexpr size_t SlabCountKCodeMemory = 10;
constexpr size_t SlabCountKDeviceAddressSpace = 300;
constexpr size_t SlabCountKSession = 1133;
constexpr size_t SlabCountKLightSession = 100;
constexpr size_t SlabCountKObjectName = 7;
constexpr size_t SlabCountKResourceLimit = 5;
constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
constexpr size_t SlabCountKIoPool = 1;
constexpr size_t SlabCountKIoRegion = 6;
constexpr size_t SlabcountKSessionRequestMappings = 40;

constexpr size_t SlabCountExtraKThread = (1024 + 256 + 256) - SlabCountKThread;

namespace test {

static_assert(KernelPageBufferHeapSize ==
              2 * PageSize + (SlabCountKProcess + SlabCountKThread +
                              (SlabCountKProcess + SlabCountKThread) / 8) *
                                 PageSize);
static_assert(KernelPageBufferAdditionalSize ==
              (SlabCountExtraKThread + (SlabCountExtraKThread / 8)) * PageSize);

} // namespace test

/// Helper function to translate from the slab virtual address to the reserved location in physical
/// memory.
static KPhysicalAddress TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout,
                                                    KVirtualAddress slab_addr) {
    slab_addr -= memory_layout.GetSlabRegion().GetAddress();
    return GetInteger(slab_addr) + Core::DramMemoryMap::SlabHeapBase;
}

template <typename T>
KVirtualAddress InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout,
                                   KVirtualAddress address, size_t num_objects) {

    const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*));
    KVirtualAddress start = Common::AlignUp(GetInteger(address), alignof(T));

    // This should use the virtual memory address passed in, but currently, we do not setup the
    // kernel virtual memory layout. Instead, we simply map these at a region of physical memory
    // that we reserve for the slab heaps.
    // TODO(bunnei): Fix this once we support the kernel virtual memory layout.

    if (size > 0) {
        void* backing_kernel_memory{system.DeviceMemory().GetPointer<void>(
            TranslateSlabAddrToPhysical(memory_layout, start))};

        const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1);
        ASSERT(region != nullptr);
        ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab));
        T::InitializeSlabHeap(system.Kernel(), backing_kernel_memory, size);
    }

    return start + size;
}

size_t CalculateSlabHeapGapSize() {
    constexpr size_t KernelSlabHeapGapSize = 2_MiB - 356_KiB;
    static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
    return KernelSlabHeapGapSize;
}

} // namespace

KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
    return {
        .num_KProcess = SlabCountKProcess,
        .num_KThread = SlabCountKThread,
        .num_KEvent = SlabCountKEvent,
        .num_KInterruptEvent = SlabCountKInterruptEvent,
        .num_KPort = SlabCountKPort,
        .num_KSharedMemory = SlabCountKSharedMemory,
        .num_KTransferMemory = SlabCountKTransferMemory,
        .num_KCodeMemory = SlabCountKCodeMemory,
        .num_KDeviceAddressSpace = SlabCountKDeviceAddressSpace,
        .num_KSession = SlabCountKSession,
        .num_KLightSession = SlabCountKLightSession,
        .num_KObjectName = SlabCountKObjectName,
        .num_KResourceLimit = SlabCountKResourceLimit,
        .num_KDebug = SlabCountKDebug,
        .num_KIoPool = SlabCountKIoPool,
        .num_KIoRegion = SlabCountKIoRegion,
        .num_KSessionRequestMappings = SlabcountKSessionRequestMappings,
    };
}

void InitializeSlabResourceCounts(KernelCore& kernel) {
    kernel.SlabResourceCounts() = KSlabResourceCounts::CreateDefault();
    if (KSystemControl::Init::ShouldIncreaseThreadResourceLimit()) {
        kernel.SlabResourceCounts().num_KThread += SlabCountExtraKThread;
    }
}

size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) {
    size_t size = 0;

#define ADD_SLAB_SIZE(NAME, COUNT, ...)                                                            \
    {                                                                                \
        size += alignof(NAME);                                                                     \
        size += Common::AlignUp(sizeof(NAME) * (COUNT), alignof(void*));                           \
    };

    // Add the size required for each slab.
    FOREACH_SLAB_TYPE(ADD_SLAB_SIZE)

#undef ADD_SLAB_SIZE

    // Add the reserved size.
    size += CalculateSlabHeapGapSize();

    return size;
}

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.
    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;
    for (size_t i = 0; i < slab_types.size(); i++) {
        slab_types[i] = static_cast<KSlabType>(i);
    }

    // N shuffles the slab type array with the following simple algorithm.
    for (size_t i = 0; i < slab_types.size(); i++) {
        const size_t rnd = KSystemControl::GenerateRandomRange(0, slab_types.size() - 1);
        std::swap(slab_types[i], slab_types[rnd]);
    }

    // Create an array to represent the gaps between the slabs.
    const size_t total_gap_size = CalculateSlabHeapGapSize();
    std::array<size_t, slab_types.size()> slab_gaps;
    for (auto& slab_gap : slab_gaps) {
        // Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange
        // is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we
        // will include it ourselves.
        slab_gap = KSystemControl::GenerateRandomRange(0, total_gap_size);
    }

    // Sort the array, so that we can treat differences between values as offsets to the starts of
    // slabs.
    for (size_t i = 1; i < slab_gaps.size(); i++) {
        for (size_t j = i; j > 0 && slab_gaps[j - 1] > slab_gaps[j]; j--) {
            std::swap(slab_gaps[j], slab_gaps[j - 1]);
        }
    }

    // Track the gaps, so that we can free them to the unused slab tree.
    KVirtualAddress gap_start = address;
    size_t gap_size = 0;

    for (size_t i = 0; i < slab_gaps.size(); i++) {
        // Add the random gap to the address.
        const auto cur_gap = (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1];
        address += cur_gap;
        gap_size += cur_gap;

#define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...)                                                     \
    case KSlabType_##NAME:                                                                         \
        if (COUNT > 0) {                                                                           \
            address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT);             \
        }                                                                                \
        break;

        // Initialize the slabheap.
        switch (slab_types[i]) {
            // For each of the slab types, we want to initialize that heap.
            FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP)
            // If we somehow get an invalid type, abort.
        default:
            ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
            break;
        }

        // If we've hit the end of a gap, free it.
        if (gap_start + gap_size != address) {
            gap_start = address;
            gap_size = 0;
        }
    }
}

} // namespace Kernel::Init

namespace Kernel {

void KPageBufferSlabHeap::Initialize(Core::System& system) {
    auto& kernel = system.Kernel();
    const auto& counts = kernel.SlabResourceCounts();
    const size_t num_pages =
        counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
    const size_t slab_size = num_pages * PageSize;

    // Reserve memory from the system resource limit.
    ASSERT(
        kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemoryMax, slab_size));

    // Allocate memory for the slab.
    constexpr auto AllocateOption = KMemoryManager::EncodeOption(
        KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
    const KPhysicalAddress slab_address =
        kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
    ASSERT(slab_address != 0);

    // Initialize the slabheap.
    KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer<void>(slab_address),
                                    slab_size);
}

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

namespace Core {
class System;
} // namespace Core

namespace Kernel {
class KernelCore;
class KMemoryLayout;
} // namespace Kernel

namespace Kernel::Init {

struct KSlabResourceCounts {
    static KSlabResourceCounts CreateDefault();

    size_t num_KProcess;
    size_t num_KThread;
    size_t num_KEvent;
    size_t num_KInterruptEvent;
    size_t num_KPort;
    size_t num_KSharedMemory;
    size_t num_KTransferMemory;
    size_t num_KCodeMemory;
    size_t num_KDeviceAddressSpace;
    size_t num_KSession;
    size_t num_KLightSession;
    size_t num_KObjectName;
    size_t num_KResourceLimit;
    size_t num_KDebug;
    size_t num_KIoPool;
    size_t num_KIoRegion;
    size_t num_KSessionRequestMappings;
};

void InitializeSlabResourceCounts(KernelCore& kernel);
size_t CalculateTotalSlabHeapSize(const KernelCore& kernel);
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout);

} // namespace Kernel::Init


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/common_types.h"
#include "common/literals.h"
#include "core/hle/kernel/board/nintendo/nx/k_memory_layout.h"
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"

namespace Kernel {

using namespace Common::Literals;

constexpr std::size_t InitialProcessBinarySizeMax = 12_MiB;

static inline KPhysicalAddress GetInitialProcessBinaryPhysicalAddress() {
    return Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetKernelPhysicalBaseAddress(
        MainMemoryAddress);
}

static inline size_t GetInitialProcessBinarySize() {
    return InitialProcessBinarySizeMax;
}

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/k_typed_address.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/memory.h"

namespace Kernel {

KAddressArbiter::KAddressArbiter(Core::System& system)
    : m_system{system}, m_kernel{system.Kernel()} {}
KAddressArbiter::~KAddressArbiter() = default;

namespace {

bool ReadFromUser(KernelCore& kernel, s32* out, KProcessAddress address) {
    *out = GetCurrentMemory(kernel).Read32(GetInteger(address));
    return true;
}

bool DecrementIfLessThan(KernelCore& kernel, s32* out, KProcessAddress address, s32 value) {
    auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
    const auto current_core = kernel.CurrentPhysicalCoreIndex();

    // NOTE: If scheduler lock is not held here, interrupt disable is required.
    // KScopedInterruptDisable di;

    // TODO(bunnei): We should call CanAccessAtomic(..) here.

    s32 current_value{};

    while (true) {
        // Load the value from the address.
        current_value =
            static_cast<s32>(monitor.ExclusiveRead32(current_core, GetInteger(address)));

        // Compare it to the desired one.
        if (current_value < value) {
            // If less than, we want to try to decrement.
            const s32 decrement_value = current_value - 1;

            // Decrement and try to store.
            if (monitor.ExclusiveWrite32(current_core, GetInteger(address),
                                         static_cast<u32>(decrement_value))) {
                break;
            }

            // If we failed to store, try again.
        } else {
            // Otherwise, clear our exclusive hold and finish
            monitor.ClearExclusive(current_core);
            break;
        }
    }

    // We're done.
    *out = current_value;
    return true;
}

bool UpdateIfEqual(KernelCore& kernel, s32* out, KProcessAddress address, s32 value,
                   s32 new_value) {
    auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
    const auto current_core = kernel.CurrentPhysicalCoreIndex();

    // NOTE: If scheduler lock is not held here, interrupt disable is required.
    // KScopedInterruptDisable di;

    // TODO(bunnei): We should call CanAccessAtomic(..) here.

    s32 current_value{};

    // Load the value from the address.
    while (true) {
        current_value =
            static_cast<s32>(monitor.ExclusiveRead32(current_core, GetInteger(address)));

        // Compare it to the desired one.
        if (current_value == value) {
            // If equal, we want to try to write the new value.

            // Try to store.
            if (monitor.ExclusiveWrite32(current_core, GetInteger(address),
                                         static_cast<u32>(new_value))) {
                break;
            }

            // If we failed to store, try again.
        } else {
            // Otherwise, clear our exclusive hold and finish.
            monitor.ClearExclusive(current_core);
            break;
        }
    }

    // We're done.
    *out = current_value;
    return true;
}

class ThreadQueueImplForKAddressArbiter final : public KThreadQueue {
public:
    explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel, KAddressArbiter::ThreadTree* t)
        : KThreadQueue(kernel), m_tree(t) {}

    void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
        // If the thread is waiting on an address arbiter, remove it from the tree.
        if (waiting_thread->IsWaitingForAddressArbiter()) {
            m_tree->erase(m_tree->iterator_to(*waiting_thread));
            waiting_thread->ClearAddressArbiter();
        }

        // Invoke the base cancel wait handler.
        KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
    }

private:
    KAddressArbiter::ThreadTree* m_tree{};
};

} // namespace

Result KAddressArbiter::Signal(uint64_t addr, s32 count) {
    // Perform signaling.
    s32 num_waiters{};
    {
        KScopedSchedulerLock sl(m_kernel);

        auto it = m_tree.nfind_key({addr, -1});
        while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) &&
               (it->GetAddressArbiterKey() == addr)) {
            // End the thread's wait.
            KThread* target_thread = std::addressof(*it);
            target_thread->EndWait(ResultSuccess);

            ASSERT(target_thread->IsWaitingForAddressArbiter());
            target_thread->ClearAddressArbiter();

            it = m_tree.erase(it);
            ++num_waiters;
        }
    }
    R_SUCCEED();
}

Result KAddressArbiter::SignalAndIncrementIfEqual(uint64_t addr, s32 value, s32 count) {
    // Perform signaling.
    s32 num_waiters{};
    {
        KScopedSchedulerLock sl(m_kernel);

        // Check the userspace value.
        s32 user_value{};
        R_UNLESS(UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, value + 1),
                 ResultInvalidCurrentMemory);
        R_UNLESS(user_value == value, ResultInvalidState);

        auto it = m_tree.nfind_key({addr, -1});
        while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) &&
               (it->GetAddressArbiterKey() == addr)) {
            // End the thread's wait.
            KThread* target_thread = std::addressof(*it);
            target_thread->EndWait(ResultSuccess);

            ASSERT(target_thread->IsWaitingForAddressArbiter());
            target_thread->ClearAddressArbiter();

            it = m_tree.erase(it);
            ++num_waiters;
        }
    }
    R_SUCCEED();
}

Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32 value, s32 count) {
    // Perform signaling.
    s32 num_waiters{};
    {
        KScopedSchedulerLock sl(m_kernel);

        auto it = m_tree.nfind_key({addr, -1});
        // Determine the updated value.
        s32 new_value{};
        if (count <= 0) {
            if (it != m_tree.end() && it->GetAddressArbiterKey() == addr) {
                new_value = value - 2;
            } else {
                new_value = value + 1;
            }
        } else {
            if (it != m_tree.end() && it->GetAddressArbiterKey() == addr) {
                auto tmp_it = it;
                s32 tmp_num_waiters{};
                while (++tmp_it != m_tree.end() && tmp_it->GetAddressArbiterKey() == addr) {
                    if (tmp_num_waiters++ >= count) {
                        break;
                    }
                }

                if (tmp_num_waiters < count) {
                    new_value = value - 1;
                } else {
                    new_value = value;
                }
            } else {
                new_value = value + 1;
            }
        }

        // Check the userspace value.
        s32 user_value{};
        bool succeeded{};
        if (value != new_value) {
            succeeded = UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, new_value);
        } else {
            succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr);
        }

        R_UNLESS(succeeded, ResultInvalidCurrentMemory);
        R_UNLESS(user_value == value, ResultInvalidState);

        while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) &&
               (it->GetAddressArbiterKey() == addr)) {
            // End the thread's wait.
            KThread* target_thread = std::addressof(*it);
            target_thread->EndWait(ResultSuccess);

            ASSERT(target_thread->IsWaitingForAddressArbiter());
            target_thread->ClearAddressArbiter();

            it = m_tree.erase(it);
            ++num_waiters;
        }
    }
    R_SUCCEED();
}

Result KAddressArbiter::WaitIfLessThan(uint64_t addr, s32 value, bool decrement, s64 timeout) {
    // Prepare to wait.
    KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
    KHardwareTimer* timer{};
    ThreadQueueImplForKAddressArbiter wait_queue(m_kernel, std::addressof(m_tree));

    {
        KScopedSchedulerLockAndSleep slp{m_kernel, std::addressof(timer), cur_thread, timeout};

        // Check that the thread isn't terminating.
        if (cur_thread->IsTerminationRequested()) {
            slp.CancelSleep();
            R_THROW(ResultTerminationRequested);
        }

        // Read the value from userspace.
        s32 user_value{};
        bool succeeded{};
        if (decrement) {
            succeeded = DecrementIfLessThan(m_kernel, std::addressof(user_value), addr, value);
        } else {
            succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr);
        }

        if (!succeeded) {
            slp.CancelSleep();
            R_THROW(ResultInvalidCurrentMemory);
        }

        // Check that the value is less than the specified one.
        if (user_value >= value) {
            slp.CancelSleep();
            R_THROW(ResultInvalidState);
        }

        // Check that the timeout is non-zero.
        if (timeout == 0) {
            slp.CancelSleep();
            R_THROW(ResultTimedOut);
        }

        // Set the arbiter.
        cur_thread->SetAddressArbiter(std::addressof(m_tree), addr);
        m_tree.insert(*cur_thread);

        // Wait for the thread to finish.
        wait_queue.SetHardwareTimer(timer);
        cur_thread->BeginWait(std::addressof(wait_queue));
        cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
    }

    // Get the result.
    return cur_thread->GetWaitResult();
}

Result KAddressArbiter::WaitIfEqual(uint64_t addr, s32 value, s64 timeout) {
    // Prepare to wait.
    KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
    KHardwareTimer* timer{};
    ThreadQueueImplForKAddressArbiter wait_queue(m_kernel, std::addressof(m_tree));

    {
        KScopedSchedulerLockAndSleep slp{m_kernel, std::addressof(timer), cur_thread, timeout};

        // Check that the thread isn't terminating.
        if (cur_thread->IsTerminationRequested()) {
            slp.CancelSleep();
            R_THROW(ResultTerminationRequested);
        }

        // Read the value from userspace.
        s32 user_value{};
        if (!ReadFromUser(m_kernel, std::addressof(user_value), addr)) {
            slp.CancelSleep();
            R_THROW(ResultInvalidCurrentMemory);
        }

        // Check that the value is equal.
        if (value != user_value) {
            slp.CancelSleep();
            R_THROW(ResultInvalidState);
        }

        // Check that the timeout is non-zero.
        if (timeout == 0) {
            slp.CancelSleep();
            R_THROW(ResultTimedOut);
        }

        // Set the arbiter.
        cur_thread->SetAddressArbiter(std::addressof(m_tree), addr);
        m_tree.insert(*cur_thread);

        // Wait for the thread to finish.
        wait_queue.SetHardwareTimer(timer);
        cur_thread->BeginWait(std::addressof(wait_queue));
        cur_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Arbitration);
    }

    // Get the result.
    return cur_thread->GetWaitResult();
}

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/svc_types.h"

union Result;

namespace Core {
class System;
}

namespace Kernel {

class KernelCore;

class KAddressArbiter {
public:
    using ThreadTree = KConditionVariable::ThreadTree;

    explicit KAddressArbiter(Core::System& system);
    ~KAddressArbiter();

    Result SignalToAddress(uint64_t addr, Svc::SignalType type, s32 value, s32 count) {
        switch (type) {
        case Svc::SignalType::Signal:
            R_RETURN(this->Signal(addr, count));
        case Svc::SignalType::SignalAndIncrementIfEqual:
            R_RETURN(this->SignalAndIncrementIfEqual(addr, value, count));
        case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
            R_RETURN(this->SignalAndModifyByWaitingCountIfEqual(addr, value, count));
        default:
            UNREACHABLE();
        }
    }

    Result WaitForAddress(uint64_t addr, Svc::ArbitrationType type, s32 value, s64 timeout) {
        switch (type) {
        case Svc::ArbitrationType::WaitIfLessThan:
            R_RETURN(WaitIfLessThan(addr, value, false, timeout));
        case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
            R_RETURN(WaitIfLessThan(addr, value, true, timeout));
        case Svc::ArbitrationType::WaitIfEqual:
            R_RETURN(WaitIfEqual(addr, value, timeout));
        default:
            UNREACHABLE();
        }
    }

private:
    Result Signal(uint64_t addr, s32 count);
    Result SignalAndIncrementIfEqual(uint64_t addr, s32 value, s32 count);
    Result SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32 value, s32 count);
    Result WaitIfLessThan(uint64_t addr, s32 value, bool decrement, s64 timeout);
    Result WaitIfEqual(uint64_t addr, s32 value, s64 timeout);

private:
    ThreadTree m_tree;
    Core::System& m_system;
    KernelCore& m_kernel;
};

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <array>

#include "common/assert.h"
#include "common/literals.h"
#include "core/hle/kernel/k_address_space_info.h"

namespace Kernel {

namespace {

using namespace Common::Literals;

constexpr u64 Size_Invalid = UINT64_MAX;

// clang-format off
constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{
   { .bit_width = 32, .address = 2_MiB       , .size = 1_GiB   - 2_MiB  , .type = KAddressSpaceInfo::Type::MapSmall, },
   { .bit_width = 32, .address = 1_GiB       , .size = 4_GiB   - 1_GiB  , .type = KAddressSpaceInfo::Type::MapLarge, },
   { .bit_width = 32, .address = Size_Invalid, .size = 1_GiB            , .type = KAddressSpaceInfo::Type::Alias,    },
   { .bit_width = 32, .address = Size_Invalid, .size = 1_GiB            , .type = KAddressSpaceInfo::Type::Heap,     },
   { .bit_width = 36, .address = 128_MiB     , .size = 2_GiB   - 128_MiB, .type = KAddressSpaceInfo::Type::MapSmall, },
   { .bit_width = 36, .address = 2_GiB       , .size = 64_GiB  - 2_GiB  , .type = KAddressSpaceInfo::Type::MapLarge, },
   { .bit_width = 36, .address = Size_Invalid, .size = 8_GiB            , .type = KAddressSpaceInfo::Type::Heap,     },
   { .bit_width = 36, .address = Size_Invalid, .size = 6_GiB            , .type = KAddressSpaceInfo::Type::Alias,    },
#ifdef HAS_NCE
   // With NCE, we use a 38-bit address space due to memory limitations. This should (safely) truncate ASLR region.
   { .bit_width = 39, .address = 128_MiB     , .size = 256_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, },
#else
   { .bit_width = 39, .address = 128_MiB     , .size = 512_GiB - 128_MiB, .type = KAddressSpaceInfo::Type::Map39Bit, },
#endif
   { .bit_width = 39, .address = Size_Invalid, .size = 64_GiB           , .type = KAddressSpaceInfo::Type::MapSmall  },
   { .bit_width = 39, .address = Size_Invalid, .size = 8_GiB            , .type = KAddressSpaceInfo::Type::Heap,     },
   { .bit_width = 39, .address = Size_Invalid, .size = 64_GiB           , .type = KAddressSpaceInfo::Type::Alias,    },
   { .bit_width = 39, .address = Size_Invalid, .size = 2_GiB            , .type = KAddressSpaceInfo::Type::Stack,    },
}};
// clang-format on

const KAddressSpaceInfo& GetAddressSpaceInfo(size_t width, KAddressSpaceInfo::Type type) {
    for (auto& info : AddressSpaceInfos) {
        if (info.bit_width == width && info.type == type) {
            return info;
        }
    }
    UNREACHABLE_MSG("Could not find AddressSpaceInfo");
}

} // namespace

std::size_t KAddressSpaceInfo::GetAddressSpaceStart(size_t width, KAddressSpaceInfo::Type type) {
    return GetAddressSpaceInfo(width, type).address;
}

std::size_t KAddressSpaceInfo::GetAddressSpaceSize(size_t width, KAddressSpaceInfo::Type type) {
    return GetAddressSpaceInfo(width, type).size;
}

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/common_types.h"

namespace Kernel {

struct KAddressSpaceInfo final {
    enum class Type : u32 {
        MapSmall = 0,
        MapLarge = 1,
        Map39Bit = 2,
        Heap = 3,
        Stack = 4,
        Alias = 5,
        Count,
    };

    static std::size_t GetAddressSpaceStart(std::size_t width, Type type);
    static std::size_t GetAddressSpaceSize(std::size_t width, Type type);

    const std::size_t bit_width{};
    const std::size_t address{};
    const std::size_t size{};
    const Type type{};
};

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/assert.h"
#include "common/common_types.h"
#include "core/hardware_properties.h"

namespace Kernel {

class KAffinityMask {
public:
    constexpr KAffinityMask() = default;

    constexpr u64 GetAffinityMask() const {
        return m_mask;
    }

    constexpr void SetAffinityMask(u64 new_mask) {
        ASSERT((new_mask & ~AllowedAffinityMask) == 0);
        m_mask = new_mask;
    }

    constexpr bool GetAffinity(s32 core) const {
        return (m_mask & GetCoreBit(core)) != 0;
    }

    constexpr void SetAffinity(s32 core, bool set) {
        if (set) {
            m_mask |= GetCoreBit(core);
        } else {
            m_mask &= ~GetCoreBit(core);
        }
    }

    constexpr void SetAll() {
        m_mask = AllowedAffinityMask;
    }

private:
    static constexpr u64 GetCoreBit(s32 core) {
        ASSERT(0 <= core && core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
        return (1ULL << core);
    }

    static constexpr u64 AllowedAffinityMask = (1ULL << Core::Hardware::NUM_CPU_CORES) - 1;

    u64 m_mask{};
};

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/kernel.h"

namespace Kernel {

KAutoObject* KAutoObject::Create(KAutoObject* obj) {
    obj->m_ref_count = 1;
    return obj;
}

void KAutoObject::RegisterWithKernel() {
    m_kernel.RegisterKernelObject(this);
}

void KAutoObject::UnregisterWithKernel(KernelCore& kernel, KAutoObject* self) {
    kernel.UnregisterKernelObject(self);
}

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <atomic>
#include <string>

#include <boost/intrusive/rbtree.hpp>

#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/kernel/k_class_token.h"

namespace Kernel {

class KernelCore;
class KProcess;

#define KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, ATTRIBUTE)                                \
                                                                                \
private:                                                                                \
    friend class ::Kernel::KClassTokenGenerator;                                                   \
    static constexpr inline auto ObjectType = ::Kernel::KClassTokenGenerator::ObjectType::CLASS;   \
    static constexpr inline const char* const TypeName = #CLASS;                                   \
    static constexpr inline ClassTokenType ClassToken() { return ::Kernel::ClassToken<CLASS>; }    \
                                                                                \
public:                                                                                \
    YUZU_NON_COPYABLE(CLASS);                                                                      \
    YUZU_NON_MOVEABLE(CLASS);                                                                      \
                                                                                \
    using BaseClass = BASE_CLASS;                                                                  \
    static constexpr TypeObj GetStaticTypeObj() {                                                  \
        constexpr ClassTokenType Token = ClassToken();                                             \
        return TypeObj(TypeName, Token);                                                           \
    }                                                                                \
    static constexpr const char* GetStaticTypeName() { return TypeName; }                          \
    virtual TypeObj GetTypeObj() ATTRIBUTE { return GetStaticTypeObj(); }                          \
    virtual const char* GetTypeName() ATTRIBUTE { return GetStaticTypeName(); }                    \
                                                                                \
private:                                                                                \
    constexpr bool operator!=(const TypeObj& rhs)

#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS)                                                \
    KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, const override)

class KAutoObject {
protected:
    class TypeObj {
    public:
        constexpr explicit TypeObj(const char* n, ClassTokenType tok)
            : m_name(n), m_class_token(tok) {}

        constexpr const char* GetName() const {
            return m_name;
        }
        constexpr ClassTokenType GetClassToken() const {
            return m_class_token;
        }

        constexpr bool operator==(const TypeObj& rhs) const {
            return this->GetClassToken() == rhs.GetClassToken();
        }

        constexpr bool operator!=(const TypeObj& rhs) const {
            return this->GetClassToken() != rhs.GetClassToken();
        }

        constexpr bool IsDerivedFrom(const TypeObj& rhs) const {
            return (this->GetClassToken() | rhs.GetClassToken()) == this->GetClassToken();
        }

    private:
        const char* m_name;
        ClassTokenType m_class_token;
    };

private:
    KERNEL_AUTOOBJECT_TRAITS_IMPL(KAutoObject, KAutoObject, const);

public:
    explicit KAutoObject(KernelCore& kernel) : m_kernel(kernel) {
        RegisterWithKernel();
    }
    virtual ~KAutoObject() = default;

    static KAutoObject* Create(KAutoObject* ptr);

    // Destroy is responsible for destroying the auto object's resources when ref_count hits zero.
    virtual void Destroy() {
        UNIMPLEMENTED();
    }

    // Finalize is responsible for cleaning up resource, but does not destroy the object.
    virtual void Finalize() {}

    virtual KProcess* GetOwner() const {
        return nullptr;
    }

    u32 GetReferenceCount() const {
        return m_ref_count.load();
    }

    bool IsDerivedFrom(const TypeObj& rhs) const {
        return this->GetTypeObj().IsDerivedFrom(rhs);
    }

    bool IsDerivedFrom(const KAutoObject& rhs) const {
        return this->IsDerivedFrom(rhs.GetTypeObj());
    }

    template <typename Derived>
    Derived DynamicCast() {
        static_assert(std::is_pointer_v<Derived>);
        using DerivedType = std::remove_pointer_t<Derived>;

        if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) {
            return static_cast<Derived>(this);
        } else {
            return nullptr;
        }
    }

    template <typename Derived>
    const Derived DynamicCast() const {
        static_assert(std::is_pointer_v<Derived>);
        using DerivedType = std::remove_pointer_t<Derived>;

        if (this->IsDerivedFrom(DerivedType::GetStaticTypeObj())) {
            return static_cast<Derived>(this);
        } else {
            return nullptr;
        }
    }

    bool Open() {
        // Atomically increment the reference count, only if it's positive.
        u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire);
        do {
            if (cur_ref_count == 0) {
                return false;
            }
            ASSERT(cur_ref_count < cur_ref_count + 1);
        } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count + 1,
                                                    std::memory_order_relaxed));

        return true;
    }

    void Close() {
        // Atomically decrement the reference count, not allowing it to become negative.
        u32 cur_ref_count = m_ref_count.load(std::memory_order_acquire);
        do {
            ASSERT(cur_ref_count > 0);
        } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1,
                                                    std::memory_order_acq_rel));

        // If ref count hits zero, destroy the object.
        if (cur_ref_count - 1 == 0) {
            KernelCore& kernel = m_kernel;
            this->Destroy();
            KAutoObject::UnregisterWithKernel(kernel, this);
        }
    }

private:
    void RegisterWithKernel();
    static void UnregisterWithKernel(KernelCore& kernel, KAutoObject* self);

protected:
    KernelCore& m_kernel;

private:
    std::atomic<u32> m_ref_count{};
};

class KAutoObjectWithListContainer;

class KAutoObjectWithList : public KAutoObject, public boost::intrusive::set_base_hook<> {
public:
    explicit KAutoObjectWithList(KernelCore& kernel) : KAutoObject(kernel) {}

    static int Compare(const KAutoObjectWithList& lhs, const KAutoObjectWithList& rhs) {
        const uintptr_t lid = reinterpret_cast<uintptr_t>(std::addressof(lhs));
        const uintptr_t rid = reinterpret_cast<uintptr_t>(std::addressof(rhs));

        if (lid < rid) {
            return -1;
        } else if (lid > rid) {
            return 1;
        } else {
            return 0;
        }
    }

    friend bool operator<(const KAutoObjectWithList& left, const KAutoObjectWithList& right) {
        return KAutoObjectWithList::Compare(left, right) < 0;
    }

public:
    virtual u64 GetId() const {
        return reinterpret_cast<u64>(this);
    }

private:
    friend class KAutoObjectWithListContainer;
};

template <typename T>
class KScopedAutoObject {
public:
    YUZU_NON_COPYABLE(KScopedAutoObject);

    constexpr KScopedAutoObject() = default;

    constexpr KScopedAutoObject(T* o) : m_obj(o) {
        if (m_obj != nullptr) {
            m_obj->Open();
        }
    }

    ~KScopedAutoObject() {
        if (m_obj != nullptr) {
            m_obj->Close();
        }
        m_obj = nullptr;
    }

    template <typename U>
        requires(std::derived_from<T, U> || std::derived_from<U, T>)
    constexpr KScopedAutoObject(KScopedAutoObject<U>&& rhs) {
        if constexpr (std::derived_from<U, T>) {
            // Upcast.
            m_obj = rhs.m_obj;
            rhs.m_obj = nullptr;
        } else {
            // Downcast.
            T* derived = nullptr;
            if (rhs.m_obj != nullptr) {
                derived = rhs.m_obj->template DynamicCast<T*>();
                if (derived == nullptr) {
                    rhs.m_obj->Close();
                }
            }

            m_obj = derived;
            rhs.m_obj = nullptr;
        }
    }

    constexpr KScopedAutoObject<T>& operator=(KScopedAutoObject<T>&& rhs) {
        rhs.Swap(*this);
        return *this;
    }

    constexpr T* operator->() {
        return m_obj;
    }
    constexpr T& operator*() {
        return *m_obj;
    }

    constexpr void Reset(T* o) {
        KScopedAutoObject(o).Swap(*this);
    }

    constexpr T* GetPointerUnsafe() {
        return m_obj;
    }

    constexpr T* GetPointerUnsafe() const {
        return m_obj;
    }

    constexpr T* ReleasePointerUnsafe() {
        T* ret = m_obj;
        m_obj = nullptr;
        return ret;
    }

    constexpr bool IsNull() const {
        return m_obj == nullptr;
    }
    constexpr bool IsNotNull() const {
        return m_obj != nullptr;
    }

private:
    template <typename U>
    friend class KScopedAutoObject;

private:
    T* m_obj{};

private:
    constexpr void Swap(KScopedAutoObject& rhs) noexcept {
        std::swap(m_obj, rhs.m_obj);
    }
};

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>

#include "core/hle/kernel/k_auto_object_container.h"

namespace Kernel {

void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) {
    // KScopedInterruptDisable di;
    KScopedSpinLock lk(m_lock);

    m_object_list.insert_unique(*obj);
}

void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) {
    // KScopedInterruptDisable di;
    KScopedSpinLock lk(m_lock);

    m_object_list.erase(*obj);
}

size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) {
    // KScopedInterruptDisable di;
    KScopedSpinLock lk(m_lock);

    return std::count_if(m_object_list.begin(), m_object_list.end(),
                         [&](const auto& obj) { return obj.GetOwner() == owner; });
}

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <boost/intrusive/rbtree.hpp>

#include "common/common_funcs.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_spin_lock.h"

namespace Kernel {

class KernelCore;
class KProcess;

class KAutoObjectWithListContainer {
public:
    YUZU_NON_COPYABLE(KAutoObjectWithListContainer);
    YUZU_NON_MOVEABLE(KAutoObjectWithListContainer);

    using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;

    KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(), m_object_list() {}

    void Initialize() {}
    void Finalize() {}

    void Register(KAutoObjectWithList* obj);
    void Unregister(KAutoObjectWithList* obj);
    size_t GetOwnedCount(KProcess* owner);

private:
    KSpinLock m_lock;
    ListType m_object_list;
};

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hardware_properties.h"
#include "core/hle/kernel/k_capabilities.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/kernel/k_trace.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_version.h"

namespace Kernel {

Result KCapabilities::InitializeForKip(std::span<const u32> kern_caps,
                                       KProcessPageTable* page_table) {
    // We're initializing an initial process.
    m_svc_access_flags.reset();
    m_irq_access_flags.reset();
    m_debug_capabilities = 0;
    m_handle_table_size = 0;
    m_intended_kernel_version = 0;
    m_program_type = 0;

    // Initial processes may run on all cores.
    constexpr u64 VirtMask = Core::Hardware::VirtualCoreMask;
    constexpr u64 PhysMask = Core::Hardware::ConvertVirtualCoreMaskToPhysical(VirtMask);

    m_core_mask = VirtMask;
    m_phys_core_mask = PhysMask;

    // Initial processes may use any user priority they like.
    m_priority_mask = ~0xFULL;

    // Here, Nintendo sets the kernel version to the current kernel version.
    // We will follow suit and set the version to the highest supported kernel version.
    KernelVersion intended_kernel_version{};
    intended_kernel_version.major_version.Assign(Svc::SupportedKernelMajorVersion);
    intended_kernel_version.minor_version.Assign(Svc::SupportedKernelMinorVersion);
    m_intended_kernel_version = intended_kernel_version.raw;

    // Parse the capabilities array.
    R_RETURN(this->SetCapabilities(kern_caps, page_table));
}

Result KCapabilities::InitializeForUser(std::span<const u32> user_caps,
                                        KProcessPageTable* page_table) {
    // We're initializing a user process.
    m_svc_access_flags.reset();
    m_irq_access_flags.reset();
    m_debug_capabilities = 0;
    m_handle_table_size = 0;
    m_intended_kernel_version = 0;
    m_program_type = 0;

    // User processes must specify what cores/priorities they can use.
    m_core_mask = 0;
    m_priority_mask = 0;

    // Parse the user capabilities array.
    R_RETURN(this->SetCapabilities(user_caps, page_table));
}

Result KCapabilities::SetCorePriorityCapability(const u32 cap) {
    // We can't set core/priority if we've already set them.
    R_UNLESS(m_core_mask == 0, ResultInvalidArgument);
    R_UNLESS(m_priority_mask == 0, ResultInvalidArgument);

    // Validate the core/priority.
    CorePriority pack{cap};
    const u32 min_core = pack.minimum_core_id;
    const u32 max_core = pack.maximum_core_id;
    const u32 max_prio = pack.lowest_thread_priority;
    const u32 min_prio = pack.highest_thread_priority;

    R_UNLESS(min_core <= max_core, ResultInvalidCombination);
    R_UNLESS(min_prio <= max_prio, ResultInvalidCombination);
    R_UNLESS(max_core < Core::Hardware::NumVirtualCores, ResultInvalidCoreId);

    ASSERT(max_prio < Common::BitSize<u64>());

    // Set core mask.
    for (auto core_id = min_core; core_id <= max_core; core_id++) {
        m_core_mask |= (1ULL << core_id);
    }
    ASSERT((m_core_mask & Core::Hardware::VirtualCoreMask) == m_core_mask);

    // Set physical core mask.
    m_phys_core_mask = Core::Hardware::ConvertVirtualCoreMaskToPhysical(m_core_mask);

    // Set priority mask.
    for (auto prio = min_prio; prio <= max_prio; prio++) {
        m_priority_mask |= (1ULL << prio);
    }

    // We must have some core/priority we can use.
    R_UNLESS(m_core_mask != 0, ResultInvalidArgument);
    R_UNLESS(m_priority_mask != 0, ResultInvalidArgument);

    // Processes must not have access to kernel thread priorities.
    R_UNLESS((m_priority_mask & 0xF) == 0, ResultInvalidArgument);

    R_SUCCEED();
}

Result KCapabilities::SetSyscallMaskCapability(const u32 cap, u32& set_svc) {
    // Validate the index.
    SyscallMask pack{cap};
    const u32 mask = pack.mask;
    const u32 index = pack.index;

    const u32 index_flag = (1U << index);
    R_UNLESS((set_svc & index_flag) == 0, ResultInvalidCombination);
    set_svc |= index_flag;

    // Set SVCs.
    for (size_t i = 0; i < decltype(SyscallMask::mask)::bits; i++) {
        const u32 svc_id = static_cast<u32>(decltype(SyscallMask::mask)::bits * index + i);
        if (mask & (1U << i)) {
            R_UNLESS(this->SetSvcAllowed(svc_id), ResultOutOfRange);
        }
    }

    R_SUCCEED();
}

Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table) {
    const auto range_pack = MapRange{cap};
    const auto size_pack = MapRangeSize{size_cap};

    // Get/validate address/size
    const u64 phys_addr = range_pack.address.Value() * PageSize;

    // Validate reserved bits are unused.
    R_UNLESS(size_pack.reserved.Value() == 0, ResultOutOfRange);

    const size_t num_pages = size_pack.pages;
    const size_t size = num_pages * PageSize;
    R_UNLESS(num_pages != 0, ResultInvalidSize);
    R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
    R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);

    // Do the mapping.
    [[maybe_unused]] const KMemoryPermission perm = range_pack.read_only.Value()
                                                        ? KMemoryPermission::UserRead
                                                        : KMemoryPermission::UserReadWrite;
    if (MapRangeSize{size_cap}.normal) {
        R_RETURN(page_table->MapStatic(phys_addr, size, perm));
    } else {
        R_RETURN(page_table->MapIo(phys_addr, size, perm));
    }
}

Result KCapabilities::MapIoPage_(const u32 cap, KProcessPageTable* page_table) {
    // Get/validate address/size
    const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
    const size_t num_pages = 1;
    const size_t size = num_pages * PageSize;
    R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
    R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);

    // Do the mapping.
    R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission::UserReadWrite));
}

template <typename F>
Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
    // Define the allowed memory regions.
    constexpr std::array<KMemoryRegionType, 4> MemoryRegions{
        KMemoryRegionType_None,
        KMemoryRegionType_KernelTraceBuffer,
        KMemoryRegionType_OnMemoryBootImage,
        KMemoryRegionType_DTB,
    };

    // Extract regions/read only.
    const MapRegion pack{cap};
    const std::array<RegionType, 3> types{pack.region0, pack.region1, pack.region2};
    const std::array<u32, 3> ro{pack.read_only0, pack.read_only1, pack.read_only2};

    for (size_t i = 0; i < types.size(); i++) {
        const auto type = types[i];
        const auto perm = ro[i] ? KMemoryPermission::UserRead : KMemoryPermission::UserReadWrite;
        switch (type) {
        case RegionType::NoMapping:
            break;
        case RegionType::KernelTraceBuffer:
            if constexpr (!IsKTraceEnabled) {
                break;
            }
            [[fallthrough]];
        case RegionType::OnMemoryBootImage:
        case RegionType::DTB:
            R_TRY(f(MemoryRegions[static_cast<u32>(type)], perm));
            break;
        default:
            R_THROW(ResultNotFound);
        }
    }

    R_SUCCEED();
}

Result KCapabilities::MapRegion_(const u32 cap, KProcessPageTable* page_table) {
    // Map each region into the process's page table.
    return ProcessMapRegionCapability(
        cap, [page_table](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
            R_RETURN(page_table->MapRegion(region_type, perm));
        });
}

Result KCapabilities::CheckMapRegion(KernelCore& kernel, const u32 cap) {
    // Check that each region has a physical backing store.
    return ProcessMapRegionCapability(
        cap, [&](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
            R_UNLESS(kernel.MemoryLayout().GetPhysicalMemoryRegionTree().FindFirstDerived(
                         region_type) != nullptr,
                     ResultOutOfRange);
            R_SUCCEED();
        });
}

Result KCapabilities::SetInterruptPairCapability(const u32 cap) {
    // Extract interrupts.
    const InterruptPair pack{cap};
    const std::array<u32, 2> ids{pack.interrupt_id0, pack.interrupt_id1};

    for (size_t i = 0; i < ids.size(); i++) {
        if (ids[i] != PaddingInterruptId) {
            UNIMPLEMENTED();
            // R_UNLESS(Kernel::GetInterruptManager().IsInterruptDefined(ids[i]), ResultOutOfRange);
            // R_UNLESS(this->SetInterruptPermitted(ids[i]), ResultOutOfRange);
        }
    }

    R_SUCCEED();
}

Result KCapabilities::SetProgramTypeCapability(const u32 cap) {
    // Validate.
    const ProgramType pack{cap};
    R_UNLESS(pack.reserved == 0, ResultReservedUsed);

    m_program_type = pack.type;
    R_SUCCEED();
}

Result KCapabilities::SetKernelVersionCapability(const u32 cap) {
    // Ensure we haven't set our version before.
    R_UNLESS(KernelVersion{m_intended_kernel_version}.major_version == 0, ResultInvalidArgument);

    // Set, ensure that we set a valid version.
    m_intended_kernel_version = cap;
    R_UNLESS(KernelVersion{m_intended_kernel_version}.major_version != 0, ResultInvalidArgument);

    R_SUCCEED();
}

Result KCapabilities::SetHandleTableCapability(const u32 cap) {
    // Validate.
    const HandleTable pack{cap};
    R_UNLESS(pack.reserved == 0, ResultReservedUsed);

    m_handle_table_size = pack.size;
    R_SUCCEED();
}

Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
    // Validate.
    const DebugFlags pack{cap};
    R_UNLESS(pack.reserved == 0, ResultReservedUsed);

    DebugFlags debug_capabilities{m_debug_capabilities};
    debug_capabilities.allow_debug.Assign(pack.allow_debug);
    debug_capabilities.force_debug.Assign(pack.force_debug);
    m_debug_capabilities = debug_capabilities.raw;

    R_SUCCEED();
}

Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
                                    KProcessPageTable* page_table) {
    // Validate this is a capability we can act on.
    const auto type = GetCapabilityType(cap);
    R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument);

    // If the type is padding, we have no work to do.
    R_SUCCEED_IF(type == CapabilityType::Padding);

    // Check that we haven't already processed this capability.
    const auto flag = GetCapabilityFlag(type);
    R_UNLESS(((set_flags & InitializeOnceFlags) & flag) == 0, ResultInvalidCombination);
    set_flags |= flag;

    // Process the capability.
    switch (type) {
    case CapabilityType::CorePriority:
        R_RETURN(this->SetCorePriorityCapability(cap));
    case CapabilityType::SyscallMask:
        R_RETURN(this->SetSyscallMaskCapability(cap, set_svc));
    case CapabilityType::MapIoPage:
        R_RETURN(this->MapIoPage_(cap, page_table));
    case CapabilityType::MapRegion:
        R_RETURN(this->MapRegion_(cap, page_table));
    case CapabilityType::InterruptPair:
        R_RETURN(this->SetInterruptPairCapability(cap));
    case CapabilityType::ProgramType:
        R_RETURN(this->SetProgramTypeCapability(cap));
    case CapabilityType::KernelVersion:
        R_RETURN(this->SetKernelVersionCapability(cap));
    case CapabilityType::HandleTable:
        R_RETURN(this->SetHandleTableCapability(cap));
    case CapabilityType::DebugFlags:
        R_RETURN(this->SetDebugFlagsCapability(cap));
    default:
        R_THROW(ResultInvalidArgument);
    }
}

Result KCapabilities::SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table) {
    u32 set_flags = 0, set_svc = 0;

    for (size_t i = 0; i < caps.size(); i++) {
        const u32 cap{caps[i]};

        if (GetCapabilityType(cap) == CapabilityType::MapRange) {
            // Check that the pair cap exists.
            R_UNLESS((++i) < caps.size(), ResultInvalidCombination);

            // Check the pair cap is a map range cap.
            const u32 size_cap{caps[i]};
            R_UNLESS(GetCapabilityType(size_cap) == CapabilityType::MapRange,
                     ResultInvalidCombination);

            // Map the range.
            R_TRY(this->MapRange_(cap, size_cap, page_table));
        } else {
            R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
        }
    }

    R_SUCCEED();
}

Result KCapabilities::CheckCapabilities(KernelCore& kernel, std::span<const u32> caps) {
    for (auto cap : caps) {
        // Check the capability refers to a valid region.
        if (GetCapabilityType(cap) == CapabilityType::MapRegion) {
            R_TRY(CheckMapRegion(kernel, cap));
        }
    }

    R_SUCCEED();
}

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <bitset>
#include <span>

#include "common/bit_field.h"
#include "common/common_types.h"

#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"

namespace Kernel {

class KProcessPageTable;
class KernelCore;

class KCapabilities {
public:
    constexpr explicit KCapabilities() = default;

    Result InitializeForKip(std::span<const u32> kern_caps, KProcessPageTable* page_table);
    Result InitializeForUser(std::span<const u32> user_caps, KProcessPageTable* page_table);

    static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps);

    constexpr u64 GetCoreMask() const {
        return m_core_mask;
    }

    constexpr u64 GetPhysicalCoreMask() const {
        return m_phys_core_mask;
    }

    constexpr u64 GetPriorityMask() const {
        return m_priority_mask;
    }

    constexpr s32 GetHandleTableSize() const {
        return m_handle_table_size;
    }

    constexpr const Svc::SvcAccessFlagSet& GetSvcPermissions() const {
        return m_svc_access_flags;
    }

    constexpr bool IsPermittedSvc(u32 id) const {
        return (id < m_svc_access_flags.size()) && m_svc_access_flags[id];
    }

    constexpr bool IsPermittedInterrupt(u32 id) const {
        return (id < m_irq_access_flags.size()) && m_irq_access_flags[id];
    }

    constexpr bool IsPermittedDebug() const {
        return DebugFlags{m_debug_capabilities}.allow_debug.Value() != 0;
    }

    constexpr bool CanForceDebug() const {
        return DebugFlags{m_debug_capabilities}.force_debug.Value() != 0;
    }

    constexpr u32 GetIntendedKernelMajorVersion() const {
        return KernelVersion{m_intended_kernel_version}.major_version;
    }

    constexpr u32 GetIntendedKernelMinorVersion() const {
        return KernelVersion{m_intended_kernel_version}.minor_version;
    }

private:
    static constexpr size_t InterruptIdCount = 0x400;
    using InterruptFlagSet = std::bitset<InterruptIdCount>;

    enum class CapabilityType : u32 {
        CorePriority = (1U << 3) - 1,
        SyscallMask = (1U << 4) - 1,
        MapRange = (1U << 6) - 1,
        MapIoPage = (1U << 7) - 1,
        MapRegion = (1U << 10) - 1,
        InterruptPair = (1U << 11) - 1,
        ProgramType = (1U << 13) - 1,
        KernelVersion = (1U << 14) - 1,
        HandleTable = (1U << 15) - 1,
        DebugFlags = (1U << 16) - 1,

        Invalid = 0U,
        Padding = ~0U,
    };

    using RawCapabilityValue = u32;

    static constexpr CapabilityType GetCapabilityType(const RawCapabilityValue value) {
        return static_cast<CapabilityType>((~value & (value + 1)) - 1);
    }

    static constexpr u32 GetCapabilityFlag(CapabilityType type) {
        return static_cast<u32>(type) + 1;
    }

    template <CapabilityType Type>
    static constexpr inline u32 CapabilityFlag = static_cast<u32>(Type) + 1;

    template <CapabilityType Type>
    static constexpr inline u32 CapabilityId = std::countr_zero(CapabilityFlag<Type>);

    union CorePriority {
        static_assert(CapabilityId<CapabilityType::CorePriority> + 1 == 4);

        RawCapabilityValue raw;
        BitField<0, 4, CapabilityType> id;
        BitField<4, 6, u32> lowest_thread_priority;
        BitField<10, 6, u32> highest_thread_priority;
        BitField<16, 8, u32> minimum_core_id;
        BitField<24, 8, u32> maximum_core_id;
    };

    union SyscallMask {
        static_assert(CapabilityId<CapabilityType::SyscallMask> + 1 == 5);

        RawCapabilityValue raw;
        BitField<0, 5, CapabilityType> id;
        BitField<5, 24, u32> mask;
        BitField<29, 3, u32> index;
    };

    // #undef MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES
    static constexpr u64 PhysicalMapAllowedMask = (1ULL << 36) - 1;

    union MapRange {
        static_assert(CapabilityId<CapabilityType::MapRange> + 1 == 7);

        RawCapabilityValue raw;
        BitField<0, 7, CapabilityType> id;
        BitField<7, 24, u32> address;
        BitField<31, 1, u32> read_only;
    };

    union MapRangeSize {
        static_assert(CapabilityId<CapabilityType::MapRange> + 1 == 7);

        RawCapabilityValue raw;
        BitField<0, 7, CapabilityType> id;
        BitField<7, 20, u32> pages;
        BitField<27, 4, u32> reserved;
        BitField<31, 1, u32> normal;
    };

    union MapIoPage {
        static_assert(CapabilityId<CapabilityType::MapIoPage> + 1 == 8);

        RawCapabilityValue raw;
        BitField<0, 8, CapabilityType> id;
        BitField<8, 24, u32> address;
    };

    enum class RegionType : u32 {
        NoMapping = 0,
        KernelTraceBuffer = 1,
        OnMemoryBootImage = 2,
        DTB = 3,
    };

    union MapRegion {
        static_assert(CapabilityId<CapabilityType::MapRegion> + 1 == 11);

        RawCapabilityValue raw;
        BitField<0, 11, CapabilityType> id;
        BitField<11, 6, RegionType> region0;
        BitField<17, 1, u32> read_only0;
        BitField<18, 6, RegionType> region1;
        BitField<24, 1, u32> read_only1;
        BitField<25, 6, RegionType> region2;
        BitField<31, 1, u32> read_only2;
    };

    union InterruptPair {
        static_assert(CapabilityId<CapabilityType::InterruptPair> + 1 == 12);

        RawCapabilityValue raw;
        BitField<0, 12, CapabilityType> id;
        BitField<12, 10, u32> interrupt_id0;
        BitField<22, 10, u32> interrupt_id1;
    };

    union ProgramType {
        static_assert(CapabilityId<CapabilityType::ProgramType> + 1 == 14);

        RawCapabilityValue raw;
        BitField<0, 14, CapabilityType> id;
        BitField<14, 3, u32> type;
        BitField<17, 15, u32> reserved;
    };

    union KernelVersion {
        static_assert(CapabilityId<CapabilityType::KernelVersion> + 1 == 15);

        RawCapabilityValue raw;
        BitField<0, 15, CapabilityType> id;
        BitField<15, 4, u32> minor_version;
        BitField<19, 13, u32> major_version;
    };

    union HandleTable {
        static_assert(CapabilityId<CapabilityType::HandleTable> + 1 == 16);

        RawCapabilityValue raw;
        BitField<0, 16, CapabilityType> id;
        BitField<16, 10, u32> size;
        BitField<26, 6, u32> reserved;
    };

    union DebugFlags {
        static_assert(CapabilityId<CapabilityType::DebugFlags> + 1 == 17);

        RawCapabilityValue raw;
        BitField<0, 17, CapabilityType> id;
        BitField<17, 1, u32> allow_debug;
        BitField<18, 1, u32> force_debug;
        BitField<19, 13, u32> reserved;
    };

    static_assert(sizeof(CorePriority) == 4);
    static_assert(sizeof(SyscallMask) == 4);
    static_assert(sizeof(MapRange) == 4);
    static_assert(sizeof(MapRangeSize) == 4);
    static_assert(sizeof(MapIoPage) == 4);
    static_assert(sizeof(MapRegion) == 4);
    static_assert(sizeof(InterruptPair) == 4);
    static_assert(sizeof(ProgramType) == 4);
    static_assert(sizeof(KernelVersion) == 4);
    static_assert(sizeof(HandleTable) == 4);
    static_assert(sizeof(DebugFlags) == 4);

    static constexpr u32 InitializeOnceFlags =
        CapabilityFlag<CapabilityType::CorePriority> | CapabilityFlag<CapabilityType::ProgramType> |
        CapabilityFlag<CapabilityType::KernelVersion> |
        CapabilityFlag<CapabilityType::HandleTable> | CapabilityFlag<CapabilityType::DebugFlags>;

    static const u32 PaddingInterruptId = 0x3FF;
    static_assert(PaddingInterruptId < InterruptIdCount);

private:
    constexpr bool SetSvcAllowed(u32 id) {
        if (id < m_svc_access_flags.size()) [[likely]] {
            m_svc_access_flags[id] = true;
            return true;
        } else {
            return false;
        }
    }

    constexpr bool SetInterruptPermitted(u32 id) {
        if (id < m_irq_access_flags.size()) [[likely]] {
            m_irq_access_flags[id] = true;
            return true;
        } else {
            return false;
        }
    }

    Result SetCorePriorityCapability(const u32 cap);
    Result SetSyscallMaskCapability(const u32 cap, u32& set_svc);
    Result MapRange_(const u32 cap, const u32 size_cap, KProcessPageTable* page_table);
    Result MapIoPage_(const u32 cap, KProcessPageTable* page_table);
    Result MapRegion_(const u32 cap, KProcessPageTable* page_table);
    Result SetInterruptPairCapability(const u32 cap);
    Result SetProgramTypeCapability(const u32 cap);
    Result SetKernelVersionCapability(const u32 cap);
    Result SetHandleTableCapability(const u32 cap);
    Result SetDebugFlagsCapability(const u32 cap);

    template <typename F>
    static Result ProcessMapRegionCapability(const u32 cap, F f);
    static Result CheckMapRegion(KernelCore& kernel, const u32 cap);

    Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
                         KProcessPageTable* page_table);
    Result SetCapabilities(std::span<const u32> caps, KProcessPageTable* page_table);

private:
    Svc::SvcAccessFlagSet m_svc_access_flags{};
    InterruptFlagSet m_irq_access_flags{};
    u64 m_core_mask{};
    u64 m_phys_core_mask{};
    u64 m_priority_mask{};
    u32 m_debug_capabilities{};
    s32 m_handle_table_size{};
    u32 m_intended_kernel_version{};
    u32 m_program_type{};
};

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_class_token.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_server_port.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_system_resource.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_transfer_memory.h"

namespace Kernel {

// Ensure that we generate correct class tokens for all types.

// Ensure that the absolute token values are correct.
static_assert(ClassToken<KAutoObject> == 0b00000000'00000000);
static_assert(ClassToken<KSynchronizationObject> == 0b00000000'00000001);
static_assert(ClassToken<KReadableEvent> == 0b00000000'00000011);
// static_assert(ClassToken<KInterruptEvent> == 0b00000111'00000011);
// static_assert(ClassToken<KDebug> == 0b00001011'00000001);
static_assert(ClassToken<KThread> == 0b00010011'00000001);
static_assert(ClassToken<KServerPort> == 0b00100011'00000001);
static_assert(ClassToken<KServerSession> == 0b01000011'00000001);
static_assert(ClassToken<KClientPort> == 0b10000011'00000001);
static_assert(ClassToken<KClientSession> == 0b00001101'00000000);
static_assert(ClassToken<KProcess> == 0b00010101'00000001);
static_assert(ClassToken<KResourceLimit> == 0b00100101'00000000);
// static_assert(ClassToken<KLightSession> == 0b01000101'00000000);
static_assert(ClassToken<KPort> == 0b10000101'00000000);
static_assert(ClassToken<KSession> == 0b00011001'00000000);
static_assert(ClassToken<KSharedMemory> == 0b00101001'00000000);
static_assert(ClassToken<KEvent> == 0b01001001'00000000);
// static_assert(ClassToken<KLightClientSession> == 0b00110001'00000000);
// static_assert(ClassToken<KLightServerSession> == 0b01010001'00000000);
static_assert(ClassToken<KTransferMemory> == 0b01010001'00000000);
// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000);
// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000);
static_assert(ClassToken<KCodeMemory> == 0b10100001'00000000);

// Ensure that the token hierarchy is correct.

// Base classes
static_assert(ClassToken<KAutoObject> == (0b00000000));
static_assert(ClassToken<KSynchronizationObject> == (0b00000001 | ClassToken<KAutoObject>));
static_assert(ClassToken<KReadableEvent> == (0b00000010 | ClassToken<KSynchronizationObject>));

// Final classes
// static_assert(ClassToken<KInterruptEvent> == ((0b00000111 << 8) | ClassToken<KReadableEvent>));
// static_assert(ClassToken<KDebug> == ((0b00001011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KThread> == ((0b00010011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KServerPort> == ((0b00100011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KServerSession> ==
              ((0b01000011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KClientPort> == ((0b10000011 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KClientSession> == ((0b00001101 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KProcess> == ((0b00010101 << 8) | ClassToken<KSynchronizationObject>));
static_assert(ClassToken<KResourceLimit> == ((0b00100101 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KLightSession> == ((0b01000101 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KPort> == ((0b10000101 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KSession> == ((0b00011001 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KSharedMemory> == ((0b00101001 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KEvent> == ((0b01001001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KLightClientSession> == ((0b00110001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KLightServerSession> == ((0b01010001 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KTransferMemory> == ((0b01010001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
static_assert(ClassToken<KCodeMemory> == ((0b10100001 << 8) | ClassToken<KAutoObject>));

// Ensure that the token hierarchy reflects the class hierarchy.

// Base classes.
static_assert(!std::is_final_v<KSynchronizationObject> &&
              std::is_base_of_v<KAutoObject, KSynchronizationObject>);
static_assert(!std::is_final_v<KReadableEvent> &&
              std::is_base_of_v<KSynchronizationObject, KReadableEvent>);

// Final classes
// static_assert(std::is_final_v<KInterruptEvent> &&
//              std::is_base_of_v<KReadableEvent, KInterruptEvent>);
// static_assert(std::is_final_v<KDebug> &&
//              std::is_base_of_v<KSynchronizationObject, KDebug>);
static_assert(std::is_final_v<KThread> && std::is_base_of_v<KSynchronizationObject, KThread>);
static_assert(std::is_final_v<KServerPort> &&
              std::is_base_of_v<KSynchronizationObject, KServerPort>);
static_assert(std::is_final_v<KServerSession> &&
              std::is_base_of_v<KSynchronizationObject, KServerSession>);
static_assert(std::is_final_v<KClientPort> &&
              std::is_base_of_v<KSynchronizationObject, KClientPort>);
static_assert(std::is_final_v<KClientSession> && std::is_base_of_v<KAutoObject, KClientSession>);
static_assert(std::is_final_v<KProcess> && std::is_base_of_v<KSynchronizationObject, KProcess>);
static_assert(std::is_final_v<KResourceLimit> && std::is_base_of_v<KAutoObject, KResourceLimit>);
// static_assert(std::is_final_v<KLightSession> &&
//              std::is_base_of_v<KAutoObject, KLightSession>);
static_assert(std::is_final_v<KPort> && std::is_base_of_v<KAutoObject, KPort>);
static_assert(std::is_final_v<KSession> && std::is_base_of_v<KAutoObject, KSession>);
static_assert(std::is_final_v<KSharedMemory> && std::is_base_of_v<KAutoObject, KSharedMemory>);
static_assert(std::is_final_v<KEvent> && std::is_base_of_v<KAutoObject, KEvent>);
// static_assert(std::is_final_v<KLightClientSession> &&
//              std::is_base_of_v<KAutoObject, KLightClientSession>);
// static_assert(std::is_final_v<KLightServerSession> &&
//              std::is_base_of_v<KAutoObject, KLightServerSession>);
static_assert(std::is_final_v<KTransferMemory> && std::is_base_of_v<KAutoObject, KTransferMemory>);
// static_assert(std::is_final_v<KDeviceAddressSpace> &&
//              std::is_base_of_v<KAutoObject, KDeviceAddressSpace>);
// static_assert(std::is_final_v<KSessionRequest> &&
//              std::is_base_of_v<KAutoObject, KSessionRequest>);
// static_assert(std::is_final_v<KCodeMemory> &&
//              std::is_base_of_v<KAutoObject, KCodeMemory>);

static_assert(std::is_base_of_v<KAutoObject, KSystemResource>);

} // namespace Kernel


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/bit_util.h"
#include "common/common_types.h"

namespace Kernel {

class KAutoObject;

class KSystemResource;

class KClassTokenGenerator {
public:
    using TokenBaseType = u16;

public:
    static constexpr size_t BaseClassBits = 8;
    static constexpr size_t FinalClassBits = (sizeof(TokenBaseType) * CHAR_BIT) - BaseClassBits;
    // One bit per base class.
    static constexpr size_t NumBaseClasses = BaseClassBits;
    // Final classes are permutations of three bits.
    static constexpr size_t NumFinalClasses = [] {
        TokenBaseType index = 0;
        for (size_t i = 0; i < FinalClassBits; i++) {
            for (size_t j = i + 1; j < FinalClassBits; j++) {
                for (size_t k = j + 1; k < FinalClassBits; k++) {
                    index++;
                }
            }
        }
        return index;
    }();

private:
    template <TokenBaseType Index>
    static constexpr inline TokenBaseType BaseClassToken = 1U << Index;

    template <TokenBaseType Index>
    static constexpr inline TokenBaseType FinalClassToken = [] {
        TokenBaseType index = 0;
        for (size_t i = 0; i < FinalClassBits; i++) {
            for (size_t j = i + 1; j < FinalClassBits; j++) {
                for (size_t k = j + 1; k < FinalClassBits; k++) {
                    if ((index++) == Index) {
                        return static_cast<TokenBaseType>(((1ULL << i) | (1ULL << j) | (1ULL << k))
                                                          << BaseClassBits);
                    }
                }
            }
        }
        UNREACHABLE();
    }();

    template <typename T>
    static constexpr inline TokenBaseType GetClassToken() {
        static_assert(std::is_base_of<KAutoObject, T>::value);
        if constexpr (std::is_same<T, KAutoObject>::value) {
            static_assert(T::ObjectType == ObjectType::KAutoObject);
            return 0;
        } else if constexpr (!std::is_final<T>::value && !std::same_as<T, KSystemResource>) {
            static_assert(ObjectType::BaseClassesStart <= T::ObjectType &&
                          T::ObjectType < ObjectType::BaseClassesEnd);
            constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) -
                                        static_cast<TokenBaseType>(ObjectType::BaseClassesStart);
            return BaseClassToken<ClassIndex> | GetClassToken<typename T::BaseClass>();
        } else if constexpr (ObjectType::FinalClassesStart <= T::ObjectType &&
                             T::ObjectType < ObjectType::FinalClassesEnd) {
            constexpr auto ClassIndex = static_cast<TokenBaseType>(T::ObjectType) -
                                        static_cast<TokenBaseType>(ObjectType::FinalClassesStart);
            return FinalClassToken<ClassIndex> | GetClassToken<typename T::BaseClass>();
        } else {
            static_assert(!std::is_same<T, T>::value, "GetClassToken: Invalid Type");
        }
    };

public:
    enum class ObjectType {
        KAutoObject,

        BaseClassesStart,

        KSynchronizationObject = BaseClassesStart,
        KReadableEvent,

        BaseClassesEnd,

        FinalClassesStart = BaseClassesEnd,

        KInterruptEvent = FinalClassesStart,
        KDebug,
        KThread,
        KServerPort,
        KServerSession,
        KClientPort,
        KClientSession,
        KProcess,
        KResourceLimit,
        KLightSession,
        KPort,
        KSession,
        KSharedMemory,
        KEvent,
        KLightClientSession,
        KLightServerSession,
        KTransferMemory,
        KDeviceAddressSpace,
        KSessionRequest,
        KCodeMemory,

        KSystemResource,

        // NOTE: True order for these has not been determined yet.
        KAlpha,
        KBeta,

        FinalClassesEnd = FinalClassesStart + NumFinalClasses,
    };

    template <typename T>
    static constexpr inline TokenBaseType ClassToken = GetClassToken<T>();
};

using ClassTokenType = KClassTokenGenerator::TokenBaseType;

template <typename T>
static constexpr inline ClassTokenType ClassToken = KClassTokenGenerator::ClassToken<T>;

} // namespace Kernel