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

#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/svc.h"

namespace Kernel::Svc {
namespace {

constexpr bool IsValidSharedMemoryPermission(MemoryPermission perm) {
    switch (perm) {
    case MemoryPermission::Read:
    case MemoryPermission::ReadWrite:
        return true;
    default:
        return false;
    }
}

[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(MemoryPermission perm) {
    return IsValidSharedMemoryPermission(perm) || perm == MemoryPermission::DontCare;
}

} // namespace

Result MapSharedMemory(Core::System& system, Handle shmem_handle, u64 address, u64 size,
                       Svc::MemoryPermission map_perm) {
    LOG_TRACE(Kernel_SVC,
              "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
              shmem_handle, address, size, map_perm);

    // Validate the address/size.
    R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
    R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
    R_UNLESS(size > 0, ResultInvalidSize);
    R_UNLESS((address < address + size), ResultInvalidCurrentMemory);

    // Validate the permission.
    R_UNLESS(IsValidSharedMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);

    // Get the current process.
    auto& process = GetCurrentProcess(system.Kernel());
    auto& page_table = process.GetPageTable();

    // Get the shared memory.
    KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
    R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);

    // Verify that the mapping is in range.
    R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);

    // Add the shared memory to the process.
    R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size));

    // Ensure that we clean up the shared memory if we fail to map it.
    ON_RESULT_FAILURE {
        process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size);
    };

    // Map the shared memory.
    R_RETURN(shmem->Map(process, address, size, map_perm));
}

Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, u64 address, u64 size) {
    // Validate the address/size.
    R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
    R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
    R_UNLESS(size > 0, ResultInvalidSize);
    R_UNLESS((address < address + size), ResultInvalidCurrentMemory);

    // Get the current process.
    auto& process = GetCurrentProcess(system.Kernel());
    auto& page_table = process.GetPageTable();

    // Get the shared memory.
    KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
    R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);

    // Verify that the mapping is in range.
    R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);

    // Unmap the shared memory.
    R_TRY(shmem->Unmap(process, address, size));

    // Remove the shared memory from the process.
    process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size);

    R_SUCCEED();
}

Result CreateSharedMemory(Core::System& system, Handle* out_handle, uint64_t size,
                          MemoryPermission owner_perm, MemoryPermission remote_perm) {
    UNIMPLEMENTED();
    R_THROW(ResultNotImplemented);
}

Result MapSharedMemory64(Core::System& system, Handle shmem_handle, uint64_t address, uint64_t size,
                         MemoryPermission map_perm) {
    R_RETURN(MapSharedMemory(system, shmem_handle, address, size, map_perm));
}

Result UnmapSharedMemory64(Core::System& system, Handle shmem_handle, uint64_t address,
                           uint64_t size) {
    R_RETURN(UnmapSharedMemory(system, shmem_handle, address, size));
}

Result CreateSharedMemory64(Core::System& system, Handle* out_handle, uint64_t size,
                            MemoryPermission owner_perm, MemoryPermission remote_perm) {
    R_RETURN(CreateSharedMemory(system, out_handle, size, owner_perm, remote_perm));
}

Result MapSharedMemory64From32(Core::System& system, Handle shmem_handle, uint32_t address,
                               uint32_t size, MemoryPermission map_perm) {
    R_RETURN(MapSharedMemory(system, shmem_handle, address, size, map_perm));
}

Result UnmapSharedMemory64From32(Core::System& system, Handle shmem_handle, uint32_t address,
                                 uint32_t size) {
    R_RETURN(UnmapSharedMemory(system, shmem_handle, address, size));
}

Result CreateSharedMemory64From32(Core::System& system, Handle* out_handle, uint32_t size,
                                  MemoryPermission owner_perm, MemoryPermission remote_perm) {
    R_RETURN(CreateSharedMemory(system, out_handle, size, owner_perm, remote_perm));
}

} // namespace Kernel::Svc


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

#include "common/scope_exit.h"
#include "common/scratch_buffer.h"
#include "core/core.h"
#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_results.h"

namespace Kernel::Svc {

/// Close a handle
Result CloseHandle(Core::System& system, Handle handle) {
    LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);

    // Remove the handle.
    R_UNLESS(GetCurrentProcess(system.Kernel()).GetHandleTable().Remove(handle),
             ResultInvalidHandle);

    R_SUCCEED();
}

/// Clears the signaled state of an event or process.
Result ResetSignal(Core::System& system, Handle handle) {
    LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);

    // Get the current handle table.
    const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();

    // Try to reset as readable event.
    {
        KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle);
        if (readable_event.IsNotNull()) {
            R_RETURN(readable_event->Reset());
        }
    }

    // Try to reset as process.
    {
        KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
        if (process.IsNotNull()) {
            R_RETURN(process->Reset());
        }
    }

    R_THROW(ResultInvalidHandle);
}

/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_handles,
                           int32_t num_handles, int64_t timeout_ns) {
    LOG_TRACE(Kernel_SVC, "called user_handles={:#x}, num_handles={}, timeout_ns={}", user_handles,
              num_handles, timeout_ns);

    // Ensure number of handles is valid.
    R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange);

    // Get the synchronization context.
    auto& kernel = system.Kernel();
    auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
    auto objs = GetCurrentThread(kernel).GetSynchronizationObjectBuffer();
    auto handles = GetCurrentThread(kernel).GetHandleBuffer();

    // Copy user handles.
    if (num_handles > 0) {
        // Get the handles.
        R_UNLESS(GetCurrentMemory(kernel).ReadBlock(user_handles, handles.data(),
                                                    sizeof(Handle) * num_handles),
                 ResultInvalidPointer);

        // Convert the handles to objects.
        R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(
                     objs.data(), handles.data(), num_handles),
                 ResultInvalidHandle);
    }

    // Ensure handles are closed when we're done.
    SCOPE_EXIT {
        for (auto i = 0; i < num_handles; ++i) {
            objs[i]->Close();
        }
    };

    // Convert the timeout from nanoseconds to ticks.
    s64 timeout;
    if (timeout_ns > 0) {
        u64 ticks = kernel.HardwareTimer().GetTick();
        ticks += timeout_ns;
        ticks += 2;

        timeout = ticks;
    } else {
        timeout = timeout_ns;
    }

    // Wait on the objects.
    Result res = KSynchronizationObject::Wait(kernel, out_index, objs.data(), num_handles, timeout);

    R_SUCCEED_IF(res == ResultSessionClosed);
    R_RETURN(res);
}

/// Resumes a thread waiting on WaitSynchronization
Result CancelSynchronization(Core::System& system, Handle handle) {
    LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle);

    // Get the thread from its handle.
    KScopedAutoObject thread =
        GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(handle);
    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);

    // Cancel the thread's wait.
    thread->WaitCancel();
    R_SUCCEED();
}

void SynchronizePreemptionState(Core::System& system) {
    auto& kernel = system.Kernel();

    // Lock the scheduler.
    KScopedSchedulerLock sl{kernel};

    // If the current thread is pinned, unpin it.
    KProcess* cur_process = GetCurrentProcessPointer(kernel);
    const auto core_id = GetCurrentCoreId(kernel);

    if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) {
        // Clear the current thread's interrupt flag.
        GetCurrentThread(kernel).ClearInterruptFlag();

        // Unpin the current thread.
        cur_process->UnpinCurrentThread();
    }
}

Result CloseHandle64(Core::System& system, Handle handle) {
    R_RETURN(CloseHandle(system, handle));
}

Result ResetSignal64(Core::System& system, Handle handle) {
    R_RETURN(ResetSignal(system, handle));
}

Result WaitSynchronization64(Core::System& system, int32_t* out_index, uint64_t handles,
                             int32_t num_handles, int64_t timeout_ns) {
    R_RETURN(WaitSynchronization(system, out_index, handles, num_handles, timeout_ns));
}

Result CancelSynchronization64(Core::System& system, Handle handle) {
    R_RETURN(CancelSynchronization(system, handle));
}

void SynchronizePreemptionState64(Core::System& system) {
    SynchronizePreemptionState(system);
}

Result CloseHandle64From32(Core::System& system, Handle handle) {
    R_RETURN(CloseHandle(system, handle));
}

Result ResetSignal64From32(Core::System& system, Handle handle) {
    R_RETURN(ResetSignal(system, handle));
}

Result WaitSynchronization64From32(Core::System& system, int32_t* out_index, uint32_t handles,
                                   int32_t num_handles, int64_t timeout_ns) {
    R_RETURN(WaitSynchronization(system, out_index, handles, num_handles, timeout_ns));
}

Result CancelSynchronization64From32(Core::System& system, Handle handle) {
    R_RETURN(CancelSynchronization(system, handle));
}

void SynchronizePreemptionState64From32(Core::System& system) {
    SynchronizePreemptionState(system);
}

} // namespace Kernel::Svc

repo_name: litucks/torzu

license: mit


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

#include "common/scope_exit.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/svc.h"

namespace Kernel::Svc {
namespace {

constexpr bool IsValidVirtualCoreId(int32_t core_id) {
    return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
}

} // Anonymous namespace

/// Creates a new thread
Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u64 arg,
                    u64 stack_bottom, s32 priority, s32 core_id) {
    LOG_DEBUG(Kernel_SVC,
              "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
              "priority=0x{:08X}, core_id=0x{:08X}",
              entry_point, arg, stack_bottom, priority, core_id);

    // Adjust core id, if it's the default magic.
    auto& kernel = system.Kernel();
    auto& process = GetCurrentProcess(kernel);
    if (core_id == IdealCoreUseProcessValue) {
        core_id = process.GetIdealCoreId();
    }

    // Validate arguments.
    R_UNLESS(IsValidVirtualCoreId(core_id), ResultInvalidCoreId);
    R_UNLESS(((1ull << core_id) & process.GetCoreMask()) != 0, ResultInvalidCoreId);

    R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority,
             ResultInvalidPriority);
    R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);

    // Reserve a new thread from the process resource limit (waiting up to 100ms).
    KScopedResourceReservation thread_reservation(std::addressof(process),
                                                  LimitableResource::ThreadCountMax, 1,
                                                  kernel.HardwareTimer().GetTick() + 100000000);
    R_UNLESS(thread_reservation.Succeeded(), ResultLimitReached);

    // Create the thread.
    KThread* thread = KThread::Create(kernel);
    R_UNLESS(thread != nullptr, ResultOutOfResource)
    SCOPE_EXIT {
        thread->Close();
    };

    // Initialize the thread.
    {
        KScopedLightLock lk{process.GetStateLock()};
        R_TRY(KThread::InitializeUserThread(system, thread, entry_point, arg, stack_bottom,
                                            priority, core_id, std::addressof(process)));
    }

    // Commit the thread reservation.
    thread_reservation.Commit();

    // Clone the current fpu status to the new thread.
    thread->CloneFpuStatus();

    // Register the new thread.
    KThread::Register(kernel, thread);

    // Add the thread to the handle table.
    R_RETURN(process.GetHandleTable().Add(out_handle, thread));
}

/// Starts the thread for the provided handle
Result StartThread(Core::System& system, Handle thread_handle) {
    LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);

    // Get the thread from its handle.
    KScopedAutoObject thread =
        GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(thread_handle);
    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);

    // Try to start the thread.
    R_TRY(thread->Run());

    R_SUCCEED();
}

/// Called when a thread exits
void ExitThread(Core::System& system) {
    auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
    system.GlobalSchedulerContext().RemoveThread(current_thread);
    current_thread->Exit();
}

/// Sleep the current thread
void SleepThread(Core::System& system, s64 ns) {
    auto& kernel = system.Kernel();
    const auto yield_type = static_cast<Svc::YieldType>(ns);

    LOG_TRACE(Kernel_SVC, "called nanoseconds={}", ns);

    // When the input tick is positive, sleep.
    if (ns > 0) {
        // Convert the timeout from nanoseconds to ticks.
        // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
        s64 timeout;

        const s64 offset_tick(ns);
        if (offset_tick > 0) {
            timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
            if (timeout <= 0) {
                timeout = std::numeric_limits<s64>::max();
            }
        } else {
            timeout = std::numeric_limits<s64>::max();
        }

        // Sleep.
        // NOTE: Nintendo does not check the result of this sleep.
        static_cast<void>(GetCurrentThread(kernel).Sleep(timeout));
    } else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
        KScheduler::YieldWithoutCoreMigration(kernel);
    } else if (yield_type == Svc::YieldType::WithCoreMigration) {
        KScheduler::YieldWithCoreMigration(kernel);
    } else if (yield_type == Svc::YieldType::ToAnyThread) {
        KScheduler::YieldToAnyThread(kernel);
    } else {
        // Nintendo does nothing at all if an otherwise invalid value is passed.
    }
}

/// Gets the thread context
Result GetThreadContext3(Core::System& system, u64 out_context, Handle thread_handle) {
    LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
              thread_handle);

    auto& kernel = system.Kernel();

    // Get the thread from its handle.
    KScopedAutoObject thread =
        GetCurrentProcess(kernel).GetHandleTable().GetObject<KThread>(thread_handle);
    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);

    // Require the handle be to a non-current thread in the current process.
    R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(kernel), ResultInvalidHandle);
    R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultBusy);

    // Get the thread context.
    Svc::ThreadContext context{};
    R_TRY(thread->GetThreadContext3(std::addressof(context)));

    // Copy the thread context to user space.
    R_UNLESS(
        GetCurrentMemory(kernel).WriteBlock(out_context, std::addressof(context), sizeof(context)),
        ResultInvalidPointer);

    R_SUCCEED();
}

/// Gets the priority for the specified thread
Result GetThreadPriority(Core::System& system, s32* out_priority, Handle handle) {
    LOG_TRACE(Kernel_SVC, "called");

    // Get the thread from its handle.
    KScopedAutoObject thread =
        GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(handle);
    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);

    // Get the thread's priority.
    *out_priority = thread->GetPriority();
    R_SUCCEED();
}

/// Sets the priority for the specified thread
Result SetThreadPriority(Core::System& system, Handle thread_handle, s32 priority) {
    // Get the current process.
    KProcess& process = GetCurrentProcess(system.Kernel());

    // Validate the priority.
    R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority,
             ResultInvalidPriority);
    R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);

    // Get the thread from its handle.
    KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle);
    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);

    // Set the thread priority.
    thread->SetBasePriority(priority);
    R_SUCCEED();
}

Result GetThreadList(Core::System& system, s32* out_num_threads, u64 out_thread_ids,
                     s32 out_thread_ids_size, Handle debug_handle) {
    // TODO: Handle this case when debug events are supported.
    UNIMPLEMENTED_IF(debug_handle != InvalidHandle);

    LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
              out_thread_ids, out_thread_ids_size);

    // If the size is negative or larger than INT32_MAX / sizeof(u64)
    if ((out_thread_ids_size & 0xF0000000) != 0) {
        LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
                  out_thread_ids_size);
        R_THROW(ResultOutOfRange);
    }

    auto* const current_process = GetCurrentProcessPointer(system.Kernel());
    const auto total_copy_size = out_thread_ids_size * sizeof(u64);

    if (out_thread_ids_size > 0 &&
        !current_process->GetPageTable().Contains(out_thread_ids, total_copy_size)) {
        LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
                  out_thread_ids, out_thread_ids + total_copy_size);
        R_THROW(ResultInvalidCurrentMemory);
    }

    auto& memory = GetCurrentMemory(system.Kernel());
    const auto& thread_list = current_process->GetThreadList();
    const auto num_threads = thread_list.size();
    const auto copy_amount = std::min(static_cast<std::size_t>(out_thread_ids_size), num_threads);

    auto list_iter = thread_list.cbegin();
    for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
        memory.Write64(out_thread_ids, list_iter->GetThreadId());
        out_thread_ids += sizeof(u64);
    }

    *out_num_threads = static_cast<u32>(num_threads);
    R_SUCCEED();
}

Result GetThreadCoreMask(Core::System& system, s32* out_core_id, u64* out_affinity_mask,
                         Handle thread_handle) {
    LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);

    // Get the thread from its handle.
    KScopedAutoObject thread =
        GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(thread_handle);
    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);

    // Get the core mask.
    R_RETURN(thread->GetCoreMask(out_core_id, out_affinity_mask));
}

Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
                         u64 affinity_mask) {
    // Determine the core id/affinity mask.
    if (core_id == IdealCoreUseProcessValue) {
        core_id = GetCurrentProcess(system.Kernel()).GetIdealCoreId();
        affinity_mask = (1ULL << core_id);
    } else {
        // Validate the affinity mask.
        const u64 process_core_mask = GetCurrentProcess(system.Kernel()).GetCoreMask();
        R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId);
        R_UNLESS(affinity_mask != 0, ResultInvalidCombination);

        // Validate the core id.
        if (IsValidVirtualCoreId(core_id)) {
            R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination);
        } else {
            R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare,
                     ResultInvalidCoreId);
        }
    }

    // Get the thread from its handle.
    KScopedAutoObject thread =
        GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(thread_handle);
    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);

    // Set the core mask.
    R_RETURN(thread->SetCoreMask(core_id, affinity_mask));
}

/// Get the ID for the specified thread.
Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
    // Get the thread from its handle.
    KScopedAutoObject thread =
        GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(thread_handle);
    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);

    // Get the thread's id.
    *out_thread_id = thread->GetId();
    R_SUCCEED();
}

Result CreateThread64(Core::System& system, Handle* out_handle, uint64_t func, uint64_t arg,
                      uint64_t stack_bottom, int32_t priority, int32_t core_id) {
    R_RETURN(CreateThread(system, out_handle, func, arg, stack_bottom, priority, core_id));
}

Result StartThread64(Core::System& system, Handle thread_handle) {
    R_RETURN(StartThread(system, thread_handle));
}

void ExitThread64(Core::System& system) {
    return ExitThread(system);
}

void SleepThread64(Core::System& system, int64_t ns) {
    return SleepThread(system, ns);
}

Result GetThreadPriority64(Core::System& system, int32_t* out_priority, Handle thread_handle) {
    R_RETURN(GetThreadPriority(system, out_priority, thread_handle));
}

Result SetThreadPriority64(Core::System& system, Handle thread_handle, int32_t priority) {
    R_RETURN(SetThreadPriority(system, thread_handle, priority));
}

Result GetThreadCoreMask64(Core::System& system, int32_t* out_core_id, uint64_t* out_affinity_mask,
                           Handle thread_handle) {
    R_RETURN(GetThreadCoreMask(system, out_core_id, out_affinity_mask, thread_handle));
}

Result SetThreadCoreMask64(Core::System& system, Handle thread_handle, int32_t core_id,
                           uint64_t affinity_mask) {
    R_RETURN(SetThreadCoreMask(system, thread_handle, core_id, affinity_mask));
}

Result GetThreadId64(Core::System& system, uint64_t* out_thread_id, Handle thread_handle) {
    R_RETURN(GetThreadId(system, out_thread_id, thread_handle));
}

Result GetThreadContext364(Core::System& system, uint64_t out_context, Handle thread_handle) {
    R_RETURN(GetThreadContext3(system, out_context, thread_handle));
}

Result GetThreadList64(Core::System& system, int32_t* out_num_threads, uint64_t out_thread_ids,
                       int32_t max_out_count, Handle debug_handle) {
    R_RETURN(GetThreadList(system, out_num_threads, out_thread_ids, max_out_count, debug_handle));
}

Result CreateThread64From32(Core::System& system, Handle* out_handle, uint32_t func, uint32_t arg,
                            uint32_t stack_bottom, int32_t priority, int32_t core_id) {
    R_RETURN(CreateThread(system, out_handle, func, arg, stack_bottom, priority, core_id));
}

Result StartThread64From32(Core::System& system, Handle thread_handle) {
    R_RETURN(StartThread(system, thread_handle));
}

void ExitThread64From32(Core::System& system) {
    return ExitThread(system);
}

void SleepThread64From32(Core::System& system, int64_t ns) {
    return SleepThread(system, ns);
}

Result GetThreadPriority64From32(Core::System& system, int32_t* out_priority,
                                 Handle thread_handle) {
    R_RETURN(GetThreadPriority(system, out_priority, thread_handle));
}

Result SetThreadPriority64From32(Core::System& system, Handle thread_handle, int32_t priority) {
    R_RETURN(SetThreadPriority(system, thread_handle, priority));
}

Result GetThreadCoreMask64From32(Core::System& system, int32_t* out_core_id,
                                 uint64_t* out_affinity_mask, Handle thread_handle) {
    R_RETURN(GetThreadCoreMask(system, out_core_id, out_affinity_mask, thread_handle));
}

Result SetThreadCoreMask64From32(Core::System& system, Handle thread_handle, int32_t core_id,
                                 uint64_t affinity_mask) {
    R_RETURN(SetThreadCoreMask(system, thread_handle, core_id, affinity_mask));
}

Result GetThreadId64From32(Core::System& system, uint64_t* out_thread_id, Handle thread_handle) {
    R_RETURN(GetThreadId(system, out_thread_id, thread_handle));
}

Result GetThreadContext364From32(Core::System& system, uint32_t out_context, Handle thread_handle) {
    R_RETURN(GetThreadContext3(system, out_context, thread_handle));
}

Result GetThreadList64From32(Core::System& system, int32_t* out_num_threads,
                             uint32_t out_thread_ids, int32_t max_out_count, Handle debug_handle) {
    R_RETURN(GetThreadList(system, out_num_threads, out_thread_ids, max_out_count, debug_handle));
}

} // namespace Kernel::Svc


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

#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_results.h"

namespace Kernel::Svc {

Result GetDebugFutureThreadInfo(Core::System& system, lp64::LastThreadContext* out_context,
                                uint64_t* out_thread_id, Handle debug_handle, int64_t ns) {
    UNIMPLEMENTED();
    R_THROW(ResultNotImplemented);
}

Result GetLastThreadInfo(Core::System& system, lp64::LastThreadContext* out_context,
                         uint64_t* out_tls_address, uint32_t* out_flags) {
    UNIMPLEMENTED();
    R_THROW(ResultNotImplemented);
}

Result GetDebugFutureThreadInfo64(Core::System& system, lp64::LastThreadContext* out_context,
                                  uint64_t* out_thread_id, Handle debug_handle, int64_t ns) {
    R_RETURN(GetDebugFutureThreadInfo(system, out_context, out_thread_id, debug_handle, ns));
}

Result GetLastThreadInfo64(Core::System& system, lp64::LastThreadContext* out_context,
                           uint64_t* out_tls_address, uint32_t* out_flags) {
    R_RETURN(GetLastThreadInfo(system, out_context, out_tls_address, out_flags));
}

Result GetDebugFutureThreadInfo64From32(Core::System& system, ilp32::LastThreadContext* out_context,
                                        uint64_t* out_thread_id, Handle debug_handle, int64_t ns) {
    lp64::LastThreadContext context{};
    R_TRY(
        GetDebugFutureThreadInfo(system, std::addressof(context), out_thread_id, debug_handle, ns));

    *out_context = {
        .fp = static_cast<u32>(context.fp),
        .sp = static_cast<u32>(context.sp),
        .lr = static_cast<u32>(context.lr),
        .pc = static_cast<u32>(context.pc),
    };
    R_SUCCEED();
}

Result GetLastThreadInfo64From32(Core::System& system, ilp32::LastThreadContext* out_context,
                                 uint64_t* out_tls_address, uint32_t* out_flags) {
    lp64::LastThreadContext context{};
    R_TRY(GetLastThreadInfo(system, std::addressof(context), out_tls_address, out_flags));

    *out_context = {
        .fp = static_cast<u32>(context.fp),
        .sp = static_cast<u32>(context.sp),
        .lr = static_cast<u32>(context.lr),
        .pc = static_cast<u32>(context.pc),
    };
    R_SUCCEED();
}

} // namespace Kernel::Svc


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

#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc.h"

namespace Kernel::Svc {

/// This returns the total CPU ticks elapsed since the CPU was powered-on
int64_t GetSystemTick(Core::System& system) {
    LOG_TRACE(Kernel_SVC, "called");

    // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
    return static_cast<int64_t>(system.CoreTiming().GetClockTicks());
}

int64_t GetSystemTick64(Core::System& system) {
    return GetSystemTick(system);
}

int64_t GetSystemTick64From32(Core::System& system) {
    return GetSystemTick(system);
}

} // namespace Kernel::Svc


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

#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/svc.h"

namespace Kernel::Svc {
namespace {

constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
    switch (perm) {
    case MemoryPermission::None:
    case MemoryPermission::Read:
    case MemoryPermission::ReadWrite:
        return true;
    default:
        return false;
    }
}

} // Anonymous namespace

/// Creates a TransferMemory object
Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64 size,
                            MemoryPermission map_perm) {
    auto& kernel = system.Kernel();

    // Validate the size.
    R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
    R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
    R_UNLESS(size > 0, ResultInvalidSize);
    R_UNLESS((address < address + size), ResultInvalidCurrentMemory);

    // Validate the permissions.
    R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);

    // Get the current process and handle table.
    auto& process = GetCurrentProcess(kernel);
    auto& handle_table = process.GetHandleTable();

    // Reserve a new transfer memory from the process resource limit.
    KScopedResourceReservation trmem_reservation(std::addressof(process),
                                                 LimitableResource::TransferMemoryCountMax);
    R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);

    // Create the transfer memory.
    KTransferMemory* trmem = KTransferMemory::Create(kernel);
    R_UNLESS(trmem != nullptr, ResultOutOfResource);

    // Ensure the only reference is in the handle table when we're done.
    SCOPE_EXIT {
        trmem->Close();
    };

    // Ensure that the region is in range.
    R_UNLESS(process.GetPageTable().Contains(address, size), ResultInvalidCurrentMemory);

    // Initialize the transfer memory.
    R_TRY(trmem->Initialize(address, size, map_perm));

    // Commit the reservation.
    trmem_reservation.Commit();

    // Register the transfer memory.
    KTransferMemory::Register(kernel, trmem);

    // Add the transfer memory to the handle table.
    R_RETURN(handle_table.Add(out, trmem));
}

Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size,
                         MemoryPermission map_perm) {
    // Validate the address/size.
    R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
    R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
    R_UNLESS(size > 0, ResultInvalidSize);
    R_UNLESS((address < address + size), ResultInvalidCurrentMemory);

    // Validate the permission.
    R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidState);

    // Get the transfer memory.
    KScopedAutoObject trmem = GetCurrentProcess(system.Kernel())
                                  .GetHandleTable()
                                  .GetObject<KTransferMemory>(trmem_handle);
    R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle);

    // Verify that the mapping is in range.
    R_UNLESS(GetCurrentProcess(system.Kernel())
                 .GetPageTable()
                 .CanContain(address, size, KMemoryState::Transferred),
             ResultInvalidMemoryRegion);

    // Map the transfer memory.
    R_TRY(trmem->Map(address, size, map_perm));

    // We succeeded.
    R_SUCCEED();
}

Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address,
                           uint64_t size) {
    // Validate the address/size.
    R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
    R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
    R_UNLESS(size > 0, ResultInvalidSize);
    R_UNLESS((address < address + size), ResultInvalidCurrentMemory);

    // Get the transfer memory.
    KScopedAutoObject trmem = GetCurrentProcess(system.Kernel())
                                  .GetHandleTable()
                                  .GetObject<KTransferMemory>(trmem_handle);
    R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle);

    // Verify that the mapping is in range.
    R_UNLESS(GetCurrentProcess(system.Kernel())
                 .GetPageTable()
                 .CanContain(address, size, KMemoryState::Transferred),
             ResultInvalidMemoryRegion);

    // Unmap the transfer memory.
    R_TRY(trmem->Unmap(address, size));

    R_SUCCEED();
}

Result MapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address,
                           uint64_t size, MemoryPermission owner_perm) {
    R_RETURN(MapTransferMemory(system, trmem_handle, address, size, owner_perm));
}

Result UnmapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address,
                             uint64_t size) {
    R_RETURN(UnmapTransferMemory(system, trmem_handle, address, size));
}

Result CreateTransferMemory64(Core::System& system, Handle* out_handle, uint64_t address,
                              uint64_t size, MemoryPermission map_perm) {
    R_RETURN(CreateTransferMemory(system, out_handle, address, size, map_perm));
}

Result MapTransferMemory64From32(Core::System& system, Handle trmem_handle, uint32_t address,
                                 uint32_t size, MemoryPermission owner_perm) {
    R_RETURN(MapTransferMemory(system, trmem_handle, address, size, owner_perm));
}

Result UnmapTransferMemory64From32(Core::System& system, Handle trmem_handle, uint32_t address,
                                   uint32_t size) {
    R_RETURN(UnmapTransferMemory(system, trmem_handle, address, size));
}

Result CreateTransferMemory64From32(Core::System& system, Handle* out_handle, uint32_t address,
                                    uint32_t size, MemoryPermission map_perm) {
    R_RETURN(CreateTransferMemory(system, out_handle, address, size, map_perm));
}

} // namespace Kernel::Svc


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

#pragma once

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

namespace Kernel {
using Handle = u32;
}

namespace Kernel::Svc {

using namespace Common::Literals;

constexpr inline s32 ArgumentHandleCountMax = 0x40;

constexpr inline u32 HandleWaitMask = 1u << 30;

constexpr inline s64 WaitInfinite = -1;

constexpr inline std::size_t HeapSizeAlignment = 2_MiB;

constexpr inline Handle InvalidHandle = Handle(0);

enum PseudoHandle : Handle {
    CurrentThread = 0xFFFF8000,
    CurrentProcess = 0xFFFF8001,
};

constexpr bool IsPseudoHandle(Handle handle) {
    return handle == PseudoHandle::CurrentProcess || handle == PseudoHandle::CurrentThread;
}

} // namespace Kernel::Svc


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

# Raw SVC definitions from the kernel.
#
# Avoid modifying the prototypes; see below for how to customize generation
# for a given typename.
SVCS = [
     [0x01, "Result SetHeapSize(Address* out_address, Size size);"],
     [0x02, "Result SetMemoryPermission(Address address, Size size, MemoryPermission perm);"],
     [0x03, "Result SetMemoryAttribute(Address address, Size size, uint32_t mask, uint32_t attr);"],
     [0x04, "Result MapMemory(Address dst_address, Address src_address, Size size);"],
     [0x05, "Result UnmapMemory(Address dst_address, Address src_address, Size size);"],
     [0x06, "Result QueryMemory(Address out_memory_info, PageInfo* out_page_info, Address address);"],
     [0x07, "void ExitProcess();"],
     [0x08, "Result CreateThread(Handle* out_handle, ThreadFunc func, Address arg, Address stack_bottom, int32_t priority, int32_t core_id);"],
     [0x09, "Result StartThread(Handle thread_handle);"],
     [0x0A, "void ExitThread();"],
     [0x0B, "void SleepThread(int64_t ns);"],
     [0x0C, "Result GetThreadPriority(int32_t* out_priority, Handle thread_handle);"],
     [0x0D, "Result SetThreadPriority(Handle thread_handle, int32_t priority);"],
     [0x0E, "Result GetThreadCoreMask(int32_t* out_core_id, uint64_t* out_affinity_mask, Handle thread_handle);"],
     [0x0F, "Result SetThreadCoreMask(Handle thread_handle, int32_t core_id, uint64_t affinity_mask);"],
     [0x10, "int32_t GetCurrentProcessorNumber();"],
     [0x11, "Result SignalEvent(Handle event_handle);"],
     [0x12, "Result ClearEvent(Handle event_handle);"],
     [0x13, "Result MapSharedMemory(Handle shmem_handle, Address address, Size size, MemoryPermission map_perm);"],
     [0x14, "Result UnmapSharedMemory(Handle shmem_handle, Address address, Size size);"],
     [0x15, "Result CreateTransferMemory(Handle* out_handle, Address address, Size size, MemoryPermission map_perm);"],
     [0x16, "Result CloseHandle(Handle handle);"],
     [0x17, "Result ResetSignal(Handle handle);"],
     [0x18, "Result WaitSynchronization(int32_t* out_index, Address handles, int32_t num_handles, int64_t timeout_ns);"],
     [0x19, "Result CancelSynchronization(Handle handle);"],
     [0x1A, "Result ArbitrateLock(Handle thread_handle, Address address, uint32_t tag);"],
     [0x1B, "Result ArbitrateUnlock(Address address);"],
     [0x1C, "Result WaitProcessWideKeyAtomic(Address address, Address cv_key, uint32_t tag, int64_t timeout_ns);"],
     [0x1D, "void SignalProcessWideKey(Address cv_key, int32_t count);"],
     [0x1E, "int64_t GetSystemTick();"],
     [0x1F, "Result ConnectToNamedPort(Handle* out_handle, Address name);"],
     [0x20, "Result SendSyncRequestLight(Handle session_handle);"],
     [0x21, "Result SendSyncRequest(Handle session_handle);"],
     [0x22, "Result SendSyncRequestWithUserBuffer(Address message_buffer, Size message_buffer_size, Handle session_handle);"],
     [0x23, "Result SendAsyncRequestWithUserBuffer(Handle* out_event_handle, Address message_buffer, Size message_buffer_size, Handle session_handle);"],
     [0x24, "Result GetProcessId(uint64_t* out_process_id, Handle process_handle);"],
     [0x25, "Result GetThreadId(uint64_t* out_thread_id, Handle thread_handle);"],
     [0x26, "void Break(BreakReason break_reason, Address arg, Size size);"],
     [0x27, "Result OutputDebugString(Address debug_str, Size len);"],
     [0x28, "void ReturnFromException(Result result);"],
     [0x29, "Result GetInfo(uint64_t* out, InfoType info_type, Handle handle, uint64_t info_subtype);"],
     [0x2A, "void FlushEntireDataCache();"],
     [0x2B, "Result FlushDataCache(Address address, Size size);"],
     [0x2C, "Result MapPhysicalMemory(Address address, Size size);"],
     [0x2D, "Result UnmapPhysicalMemory(Address address, Size size);"],
     [0x2E, "Result GetDebugFutureThreadInfo(LastThreadContext* out_context, uint64_t* out_thread_id, Handle debug_handle, int64_t ns);"],
     [0x2F, "Result GetLastThreadInfo(LastThreadContext* out_context, Address* out_tls_address, uint32_t* out_flags);"],
     [0x30, "Result GetResourceLimitLimitValue(int64_t* out_limit_value, Handle resource_limit_handle, LimitableResource which);"],
     [0x31, "Result GetResourceLimitCurrentValue(int64_t* out_current_value, Handle resource_limit_handle, LimitableResource which);"],
     [0x32, "Result SetThreadActivity(Handle thread_handle, ThreadActivity thread_activity);"],
     [0x33, "Result GetThreadContext3(Address out_context, Handle thread_handle);"],
     [0x34, "Result WaitForAddress(Address address, ArbitrationType arb_type, int32_t value, int64_t timeout_ns);"],
     [0x35, "Result SignalToAddress(Address address, SignalType signal_type, int32_t value, int32_t count);"],
     [0x36, "void SynchronizePreemptionState();"],
     [0x37, "Result GetResourceLimitPeakValue(int64_t* out_peak_value, Handle resource_limit_handle, LimitableResource which);"],

     [0x39, "Result CreateIoPool(Handle* out_handle, IoPoolType which);"],
     [0x3A, "Result CreateIoRegion(Handle* out_handle, Handle io_pool, PhysicalAddress physical_address, Size size, MemoryMapping mapping, MemoryPermission perm);"],

     [0x3C, "void KernelDebug(KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2);"],
     [0x3D, "void ChangeKernelTraceState(KernelTraceState kern_trace_state);"],

     [0x40, "Result CreateSession(Handle* out_server_session_handle, Handle* out_client_session_handle, bool is_light, Address name);"],
     [0x41, "Result AcceptSession(Handle* out_handle, Handle port);"],
     [0x42, "Result ReplyAndReceiveLight(Handle handle);"],
     [0x43, "Result ReplyAndReceive(int32_t* out_index, Address handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);"],
     [0x44, "Result ReplyAndReceiveWithUserBuffer(int32_t* out_index, Address message_buffer, Size message_buffer_size, Address handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);"],
     [0x45, "Result CreateEvent(Handle* out_write_handle, Handle* out_read_handle);"],
     [0x46, "Result MapIoRegion(Handle io_region, Address address, Size size, MemoryPermission perm);"],
     [0x47, "Result UnmapIoRegion(Handle io_region, Address address, Size size);"],
     [0x48, "Result MapPhysicalMemoryUnsafe(Address address, Size size);"],
     [0x49, "Result UnmapPhysicalMemoryUnsafe(Address address, Size size);"],
     [0x4A, "Result SetUnsafeLimit(Size limit);"],
     [0x4B, "Result CreateCodeMemory(Handle* out_handle, Address address, Size size);"],
     [0x4C, "Result ControlCodeMemory(Handle code_memory_handle, CodeMemoryOperation operation, uint64_t address, uint64_t size, MemoryPermission perm);"],
     [0x4D, "void SleepSystem();"],
     [0x4E, "Result ReadWriteRegister(uint32_t* out_value, PhysicalAddress address, uint32_t mask, uint32_t value);"],
     [0x4F, "Result SetProcessActivity(Handle process_handle, ProcessActivity process_activity);"],
     [0x50, "Result CreateSharedMemory(Handle* out_handle, Size size, MemoryPermission owner_perm, MemoryPermission remote_perm);"],
     [0x51, "Result MapTransferMemory(Handle trmem_handle, Address address, Size size, MemoryPermission owner_perm);"],
     [0x52, "Result UnmapTransferMemory(Handle trmem_handle, Address address, Size size);"],
     [0x53, "Result CreateInterruptEvent(Handle* out_read_handle, int32_t interrupt_id, InterruptType interrupt_type);"],
     [0x54, "Result QueryPhysicalAddress(PhysicalMemoryInfo* out_info, Address address);"],
     [0x55, "Result QueryIoMapping(Address* out_address, Size* out_size, PhysicalAddress physical_address, Size size);"],
     [0x56, "Result CreateDeviceAddressSpace(Handle* out_handle, uint64_t das_address, uint64_t das_size);"],
     [0x57, "Result AttachDeviceAddressSpace(DeviceName device_name, Handle das_handle);"],
     [0x58, "Result DetachDeviceAddressSpace(DeviceName device_name, Handle das_handle);"],
     [0x59, "Result MapDeviceAddressSpaceByForce(Handle das_handle, Handle process_handle, uint64_t process_address, Size size, uint64_t device_address, uint32_t option);"],
     [0x5A, "Result MapDeviceAddressSpaceAligned(Handle das_handle, Handle process_handle, uint64_t process_address, Size size, uint64_t device_address, uint32_t option);"],
     [0x5C, "Result UnmapDeviceAddressSpace(Handle das_handle, Handle process_handle, uint64_t process_address, Size size, uint64_t device_address);"],
     [0x5D, "Result InvalidateProcessDataCache(Handle process_handle, uint64_t address, uint64_t size);"],
     [0x5E, "Result StoreProcessDataCache(Handle process_handle, uint64_t address, uint64_t size);"],
     [0x5F, "Result FlushProcessDataCache(Handle process_handle, uint64_t address, uint64_t size);"],
     [0x60, "Result DebugActiveProcess(Handle* out_handle, uint64_t process_id);"],
     [0x61, "Result BreakDebugProcess(Handle debug_handle);"],
     [0x62, "Result TerminateDebugProcess(Handle debug_handle);"],
     [0x63, "Result GetDebugEvent(Address out_info, Handle debug_handle);"],
     [0x64, "Result ContinueDebugEvent(Handle debug_handle, uint32_t flags, Address thread_ids, int32_t num_thread_ids);"],
     [0x65, "Result GetProcessList(int32_t* out_num_processes, Address out_process_ids, int32_t max_out_count);"],
     [0x66, "Result GetThreadList(int32_t* out_num_threads, Address out_thread_ids, int32_t max_out_count, Handle debug_handle);"],
     [0x67, "Result GetDebugThreadContext(Address out_context, Handle debug_handle, uint64_t thread_id, uint32_t context_flags);"],
     [0x68, "Result SetDebugThreadContext(Handle debug_handle, uint64_t thread_id, Address context, uint32_t context_flags);"],
     [0x69, "Result QueryDebugProcessMemory(Address out_memory_info, PageInfo* out_page_info, Handle process_handle, Address address);"],
     [0x6A, "Result ReadDebugProcessMemory(Address buffer, Handle debug_handle, Address address, Size size);"],
     [0x6B, "Result WriteDebugProcessMemory(Handle debug_handle, Address buffer, Address address, Size size);"],
     [0x6C, "Result SetHardwareBreakPoint(HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value);"],
     [0x6D, "Result GetDebugThreadParam(uint64_t* out_64, uint32_t* out_32, Handle debug_handle, uint64_t thread_id, DebugThreadParam param);"],

     [0x6F, "Result GetSystemInfo(uint64_t* out, SystemInfoType info_type, Handle handle, uint64_t info_subtype);"],
     [0x70, "Result CreatePort(Handle* out_server_handle, Handle* out_client_handle, int32_t max_sessions, bool is_light, Address name);"],
     [0x71, "Result ManageNamedPort(Handle* out_server_handle, Address name, int32_t max_sessions);"],
     [0x72, "Result ConnectToPort(Handle* out_handle, Handle port);"],
     [0x73, "Result SetProcessMemoryPermission(Handle process_handle, uint64_t address, uint64_t size, MemoryPermission perm);"],
     [0x74, "Result MapProcessMemory(Address dst_address, Handle process_handle, uint64_t src_address, Size size);"],
     [0x75, "Result UnmapProcessMemory(Address dst_address, Handle process_handle, uint64_t src_address, Size size);"],
     [0x76, "Result QueryProcessMemory(Address out_memory_info, PageInfo* out_page_info, Handle process_handle, uint64_t address);"],
     [0x77, "Result MapProcessCodeMemory(Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);"],
     [0x78, "Result UnmapProcessCodeMemory(Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);"],
     [0x79, "Result CreateProcess(Handle* out_handle, Address parameters, Address caps, int32_t num_caps);"],
     [0x7A, "Result StartProcess(Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size);"],
     [0x7B, "Result TerminateProcess(Handle process_handle);"],
     [0x7C, "Result GetProcessInfo(int64_t* out_info, Handle process_handle, ProcessInfoType info_type);"],
     [0x7D, "Result CreateResourceLimit(Handle* out_handle);"],
     [0x7E, "Result SetResourceLimitLimitValue(Handle resource_limit_handle, LimitableResource which, int64_t limit_value);"],
     [0x7F, "void CallSecureMonitor(SecureMonitorArguments args);"],

     [0x90, "Result MapInsecureMemory(Address address, Size size);"],
     [0x91, "Result UnmapInsecureMemory(Address address, Size size);"],
]

# These use a custom ABI, and therefore require custom wrappers
SKIP_WRAPPERS = {
    0x20: "SendSyncRequestLight",
    0x42: "ReplyAndReceiveLight",
    0x7F: "CallSecureMonitor",
}

BIT_32 = 0
BIT_64 = 1

REG_SIZES = [4, 8]
SUFFIX_NAMES = ["64From32", "64"]
TYPE_SIZES = {
    # SVC types
    "ArbitrationType": 4,
    "BreakReason": 4,
    "CodeMemoryOperation": 4,
    "DebugThreadParam": 4,
    "DeviceName": 4,
    "HardwareBreakPointRegisterName": 4,
    "Handle": 4,
    "InfoType": 4,
    "InterruptType": 4,
    "IoPoolType": 4,
    "KernelDebugType": 4,
    "KernelTraceState": 4,
    "LimitableResource": 4,
    "MemoryMapping": 4,
    "MemoryPermission": 4,
    "PageInfo": 4,
    "ProcessActivity": 4,
    "ProcessInfoType": 4,
    "Result": 4,
    "SignalType": 4,
    "SystemInfoType": 4,
    "ThreadActivity": 4,

    # Arch-specific types
    "ilp32::LastThreadContext": 16,
    "ilp32::PhysicalMemoryInfo": 16,
    "ilp32::SecureMonitorArguments": 32,
    "lp64::LastThreadContext": 32,
    "lp64::PhysicalMemoryInfo": 24,
    "lp64::SecureMonitorArguments": 64,

    # Generic types
    "bool": 1,
    "int32_t": 4,
    "int64_t": 8,
    "uint32_t": 4,
    "uint64_t": 8,
    "void": 0,
}

TYPE_REPLACEMENTS = {
    "Address": ["uint32_t", "uint64_t"],
    "LastThreadContext": ["ilp32::LastThreadContext", "lp64::LastThreadContext"],
    "PhysicalAddress": ["uint64_t", "uint64_t"],
    "PhysicalMemoryInfo": ["ilp32::PhysicalMemoryInfo", "lp64::PhysicalMemoryInfo"],
    "SecureMonitorArguments": ["ilp32::SecureMonitorArguments", "lp64::SecureMonitorArguments"],
    "Size": ["uint32_t", "uint64_t"],
    "ThreadFunc": ["uint32_t", "uint64_t"],
}

# Statically verify that the hardcoded sizes match the intended
# sizes in C++.
def emit_size_check():
    lines = []

    for type, size in TYPE_SIZES.items():
        if type != "void":
            lines.append(f"static_assert(sizeof({type}) == {size});")

    return "\n".join(lines)

# Replaces a type with an arch-specific one, if it exists.
def substitute_type(name, bitness):
    if name in TYPE_REPLACEMENTS:
        return TYPE_REPLACEMENTS[name][bitness]
    else:
        return name

class Argument:
    def __init__(self, type_name, var_name, is_output, is_outptr, is_address):
        self.type_name = type_name
        self.var_name = var_name
        self.is_output = is_output
        self.is_outptr = is_outptr
        self.is_address = is_address

# Parses C-style string declarations for SVCs.
def parse_declaration(declaration, bitness):
    return_type, rest = declaration.split(" ", 1)
    func_name, rest = rest.split("(", 1)
    arg_names, rest = rest.split(")", 1)
    argument_types = []

    return_type = substitute_type(return_type, bitness)
    assert return_type in TYPE_SIZES, f"Unknown type '{return_type}'"

    if arg_names:
        for arg_name in arg_names.split(", "):
            type_name, var_name = arg_name.replace("*", "").split(" ", 1)

            # All outputs must contain out_ in the name.
            is_output = var_name == "out" or var_name.find("out_") != -1

            # User-pointer outputs are not written to registers.
            is_outptr = is_output and arg_name.find("*") == -1

            # Special handling is performed for output addresses to avoid awkwardness
            # in conversion for the 32-bit equivalents.
            is_address = is_output and not is_outptr and \
                type_name in ["Address", "Size"]
            type_name = substitute_type(type_name, bitness)

            assert type_name in TYPE_SIZES, f"Unknown type '{type_name}'"

            argument_types.append(
                Argument(type_name, var_name, is_output, is_outptr, is_address))

    return (return_type, func_name, argument_types)

class RegisterAllocator:
    def __init__(self, num_regs, byte_size, parameter_count):
        self.registers = {}
        self.num_regs = num_regs
        self.byte_size = byte_size
        self.parameter_count = parameter_count

    # Mark the given register as allocated, for use in layout
    # calculation if the NGRN exceeds the ABI parameter count.
    def allocate(self, i):
        assert i not in self.registers, f"Register R{i} already allocated"
        self.registers[i] = True
        return i

    # Calculate the next available location for a register;
    # the NGRN has exceeded the ABI parameter count.
    def allocate_first_free(self):
        for i in range(0, self.num_regs):
            if i in self.registers:
                continue

            self.allocate(i)
            return i

        assert False, "No registers available"

    # Add a single register at the given NGRN.
    # If the index exceeds the ABI parameter count, try to find a
    # location to add it. Returns the output location and increment.
    def add_single(self, ngrn):
        if ngrn >= self.parameter_count:
            return (self.allocate_first_free(), 0)
        else:
            return (self.allocate(ngrn), 1)

    # Add registers at the given NGRN for a data type of
    # the given size. Returns the output locations and increment.
    def add(self, ngrn, data_size, align=True):
        if data_size <= self.byte_size:
            r, i = self.add_single(ngrn)
            return ([r], i)

        regs = []
        inc = ngrn % 2 if align else 0
        remaining_size = data_size
        while remaining_size > 0:
            r, i = self.add_single(ngrn + inc)
            regs.append(r)
            inc += i
            remaining_size -= self.byte_size

        return (regs, inc)

def reg_alloc(bitness):
    if bitness == 0:
        # aapcs32: 4 4-byte registers
        return RegisterAllocator(8, 4, 4)
    elif bitness == 1:
        # aapcs64: 8 8-byte registers
        return RegisterAllocator(8, 8, 8)

# Converts a parsed SVC declaration into register lists for
# the return value, outputs, and inputs.
def get_registers(parse_result, bitness):
    output_alloc = reg_alloc(bitness)
    input_alloc = reg_alloc(bitness)
    return_type, _, arguments = parse_result

    return_write = []
    output_writes = []
    input_reads = []

    input_ngrn = 0
    output_ngrn = 0

    # Run the input calculation.
    for arg in arguments:
        if arg.is_output and not arg.is_outptr:
            input_ngrn += 1
            continue

        regs, increment = input_alloc.add(
            input_ngrn, TYPE_SIZES[arg.type_name], align=True)
        input_reads.append([arg.type_name, arg.var_name, regs])
        input_ngrn += increment

    # Include the return value if this SVC returns a value.
    if return_type != "void":
        regs, increment = output_alloc.add(
            output_ngrn, TYPE_SIZES[return_type], align=False)
        return_write.append([return_type, regs])
        output_ngrn += increment

    # Run the output calculation.
    for arg in arguments:
        if not arg.is_output or arg.is_outptr:
            continue

        regs, increment = output_alloc.add(
            output_ngrn, TYPE_SIZES[arg.type_name], align=False)
        output_writes.append(
            [arg.type_name, arg.var_name, regs, arg.is_address])
        output_ngrn += increment

    return (return_write, output_writes, input_reads)

# Collects possibly multiple source registers into the named C++ value.
def emit_gather(sources, name, type_name, reg_size):
    get_fn = f"GetArg{reg_size*8}"

    if len(sources) == 1:
        s, = sources
        line = f"{name} = Convert<{type_name}>({get_fn}(args, {s}));"
        return [line]

    var_type = f"std::array<uint{reg_size*8}_t, {len(sources)}>"
    lines = [
        f"{var_type} {name}_gather{{}};"
    ]
    for i in range(0, len(sources)):
        lines.append(
            f"{name}_gather[{i}] = {get_fn}(args, {sources[i]});")

    lines.append(f"{name} = Convert<{type_name}>({name}_gather);")
    return lines

# Produces one or more statements which assign the named C++ value
# into possibly multiple registers.
def emit_scatter(destinations, name, reg_size):
    set_fn = f"SetArg{reg_size*8}"
    reg_type = f"uint{reg_size*8}_t"

    if len(destinations) == 1:
        d, = destinations
        line = f"{set_fn}(args, {d}, Convert<{reg_type}>({name}));"
        return [line]

    var_type = f"std::array<{reg_type}, {len(destinations)}>"
    lines = [
        f"auto {name}_scatter = Convert<{var_type}>({name});"
    ]

    for i in range(0, len(destinations)):
        lines.append(
            f"{set_fn}(args, {destinations[i]}, {name}_scatter[{i}]);")

    return lines

def emit_lines(lines, indent='    '):
    output_lines = []
    first = True
    for line in lines:
        if line and not first:
            output_lines.append(indent + line)
        else:
            output_lines.append(line)
        first = False

    return "\n".join(output_lines)

# Emit a C++ function to wrap a guest SVC.
def emit_wrapper(wrapped_fn, suffix, register_info, arguments, byte_size):
    return_write, output_writes, input_reads = register_info
    lines = [
        f"static void SvcWrap_{wrapped_fn}{suffix}(Core::System& system, std::span<uint64_t, 8> args) {{"
    ]

    # Get everything ready.
    for return_type, _ in return_write:
        lines.append(f"{return_type} ret{{}};")
    if return_write:
        lines.append("")

    for output_type, var_name, _, is_address in output_writes:
        output_type = "uint64_t" if is_address else output_type
        lines.append(f"{output_type} {var_name}{{}};")
    for input_type, var_name, _ in input_reads:
        lines.append(f"{input_type} {var_name}{{}};")

    if output_writes or input_reads:
        lines.append("")

    for input_type, var_name, sources in input_reads:
        lines += emit_gather(sources, var_name, input_type, byte_size)
    if input_reads:
        lines.append("")

    # Build the call.
    call_arguments = ["system"]
    for arg in arguments:
        if arg.is_output and not arg.is_outptr:
            call_arguments.append(f"std::addressof({arg.var_name})")
        else:
            call_arguments.append(arg.var_name)

    line = ""
    if return_write:
        line += "ret = "

    line += f"{wrapped_fn}{suffix}({', '.join(call_arguments)});"
    lines.append(line)

    if return_write or output_writes:
        lines.append("")

    # Write back the return value and outputs.
    for _, destinations in return_write:
        lines += emit_scatter(destinations, "ret", byte_size)
    for _, var_name, destinations, _ in output_writes:
        lines += emit_scatter(destinations, var_name, byte_size)

    # Finish.
    return emit_lines(lines) + "\n}"

COPYRIGHT = """\
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

// This file is automatically generated using svc_generator.py.
"""

PROLOGUE_H = """
#pragma once

namespace Core {
class System;
}

#include <span>

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

namespace Kernel::Svc {

// clang-format off
"""

EPILOGUE_H = """
// clang-format on

// Custom ABI.
Result ReplyAndReceiveLight(Core::System& system, Handle handle, uint32_t* args);
Result ReplyAndReceiveLight64From32(Core::System& system, Handle handle, uint32_t* args);
Result ReplyAndReceiveLight64(Core::System& system, Handle handle, uint32_t* args);

Result SendSyncRequestLight(Core::System& system, Handle session_handle, uint32_t* args);
Result SendSyncRequestLight64From32(Core::System& system, Handle session_handle, uint32_t* args);
Result SendSyncRequestLight64(Core::System& system, Handle session_handle, uint32_t* args);

void CallSecureMonitor(Core::System& system, lp64::SecureMonitorArguments* args);
void CallSecureMonitor64From32(Core::System& system, ilp32::SecureMonitorArguments* args);
void CallSecureMonitor64(Core::System& system, lp64::SecureMonitorArguments* args);

// Defined in svc_light_ipc.cpp.
void SvcWrap_ReplyAndReceiveLight64From32(Core::System& system, std::span<uint64_t, 8> args);
void SvcWrap_ReplyAndReceiveLight64(Core::System& system, std::span<uint64_t, 8> args);

void SvcWrap_SendSyncRequestLight64From32(Core::System& system, std::span<uint64_t, 8> args);
void SvcWrap_SendSyncRequestLight64(Core::System& system, std::span<uint64_t, 8> args);

// Defined in svc_secure_monitor_call.cpp.
void SvcWrap_CallSecureMonitor64From32(Core::System& system, std::span<uint64_t, 8> args);
void SvcWrap_CallSecureMonitor64(Core::System& system, std::span<uint64_t, 8> args);

// Perform a supervisor call by index.
void Call(Core::System& system, u32 imm);

} // namespace Kernel::Svc
"""

PROLOGUE_CPP = """
#include <type_traits>

#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc.h"

namespace Kernel::Svc {

static uint32_t GetArg32(std::span<uint64_t, 8> args, int n) {
    return static_cast<uint32_t>(args[n]);
}

static void SetArg32(std::span<uint64_t, 8> args, int n, uint32_t result) {
    args[n] = result;
}

static uint64_t GetArg64(std::span<uint64_t, 8> args, int n) {
    return args[n];
}

static void SetArg64(std::span<uint64_t, 8> args, int n, uint64_t result) {
    args[n] = result;
}

// Like bit_cast, but handles the case when the source and dest
// are differently-sized.
template <typename To, typename From>
    requires(std::is_trivial_v<To> && std::is_trivially_copyable_v<From>)
static To Convert(const From& from) {
    To to{};

    if constexpr (sizeof(To) >= sizeof(From)) {
        std::memcpy(std::addressof(to), std::addressof(from), sizeof(From));
    } else {
        std::memcpy(std::addressof(to), std::addressof(from), sizeof(To));
    }

    return to;
}

// clang-format off
"""

EPILOGUE_CPP = """
// clang-format on

void Call(Core::System& system, u32 imm) {
    auto& kernel = system.Kernel();
    auto& process = GetCurrentProcess(kernel);

    std::array<uint64_t, 8> args;
    kernel.CurrentPhysicalCore().SaveSvcArguments(process, args);
    kernel.EnterSVCProfile();

    if (process.Is64Bit()) {
        Call64(system, imm, args);
    } else {
        Call32(system, imm, args);
    }

    kernel.ExitSVCProfile();
    kernel.CurrentPhysicalCore().LoadSvcArguments(process, args);
}

} // namespace Kernel::Svc
"""

def emit_call(bitness, names, suffix):
    bit_size = REG_SIZES[bitness]*8
    indent = "    "
    lines = [
        f"static void Call{bit_size}(Core::System& system, u32 imm, std::span<uint64_t, 8> args) {{",
        f"{indent}switch (static_cast<SvcId>(imm)) {{"
    ]

    for _, name in names:
        lines.append(f"{indent}case SvcId::{name}:")
        lines.append(f"{indent*2}return SvcWrap_{name}{suffix}(system, args);")

    lines.append(f"{indent}default:")
    lines.append(
        f"{indent*2}LOG_CRITICAL(Kernel_SVC, \"Unknown SVC {{:x}}!\", imm);")
    lines.append(f"{indent*2}break;")
    lines.append(f"{indent}}}")
    lines.append("}")

    return "\n".join(lines)

def build_fn_declaration(return_type, name, arguments):
    arg_list = ["Core::System& system"]
    for arg in arguments:
        type_name = "uint64_t" if arg.is_address else arg.type_name
        pointer = "*" if arg.is_output and not arg.is_outptr else ""
        arg_list.append(f"{type_name}{pointer} {arg.var_name}")

    return f"{return_type} {name}({', '.join(arg_list)});"

def build_enum_declarations():
    lines = ["enum class SvcId : u32 {"]
    indent = "    "

    for imm, decl in SVCS:
        _, name, _ = parse_declaration(decl, BIT_64)
        lines.append(f"{indent}{name} = {hex(imm)},")

    lines.append("};")
    return "\n".join(lines)

def main():
    arch_fw_declarations = [[], []]
    svc_fw_declarations = []
    wrapper_fns = []
    names = []

    for imm, decl in SVCS:
        return_type, name, arguments = parse_declaration(decl, BIT_64)

        if imm not in SKIP_WRAPPERS:
            svc_fw_declarations.append(
                build_fn_declaration(return_type, name, arguments))

        names.append([imm, name])

    for bitness in range(2):
        byte_size = REG_SIZES[bitness]
        suffix = SUFFIX_NAMES[bitness]

        for imm, decl in SVCS:
            if imm in SKIP_WRAPPERS:
                continue

            parse_result = parse_declaration(decl, bitness)
            return_type, name, arguments = parse_result

            register_info = get_registers(parse_result, bitness)
            wrapper_fns.append(
                emit_wrapper(name, suffix, register_info, arguments, byte_size))
            arch_fw_declarations[bitness].append(
                build_fn_declaration(return_type, name + suffix, arguments))

    call_32 = emit_call(BIT_32, names, SUFFIX_NAMES[BIT_32])
    call_64 = emit_call(BIT_64, names, SUFFIX_NAMES[BIT_64])
    enum_decls = build_enum_declarations()

    with open("svc.h", "w") as f:
        f.write(COPYRIGHT)
        f.write(PROLOGUE_H)
        f.write("\n".join(svc_fw_declarations))
        f.write("\n\n")
        f.write("\n".join(arch_fw_declarations[BIT_32]))
        f.write("\n\n")
        f.write("\n".join(arch_fw_declarations[BIT_64]))
        f.write("\n\n")
        f.write(enum_decls)
        f.write(EPILOGUE_H)

    with open("svc.cpp", "w") as f:
        f.write(COPYRIGHT)
        f.write(PROLOGUE_CPP)
        f.write(emit_size_check())
        f.write("\n\n")
        f.write("\n\n".join(wrapper_fns))
        f.write("\n\n")
        f.write(call_32)
        f.write("\n\n")
        f.write(call_64)
        f.write(EPILOGUE_CPP)

    print(f"Done (emitted {len(names)} definitions)")

if __name__ == "__main__":
    main()


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

#pragma once

#include "core/hle/result.h"

namespace Kernel {

// Confirmed Switch kernel error codes

constexpr Result ResultOutOfSessions{ErrorModule::Kernel, 7};
constexpr Result ResultInvalidArgument{ErrorModule::Kernel, 14};
constexpr Result ResultNotImplemented{ErrorModule::Kernel, 33};
constexpr Result ResultNoSynchronizationObject{ErrorModule::Kernel, 57};
constexpr Result ResultTerminationRequested{ErrorModule::Kernel, 59};
constexpr Result ResultInvalidSize{ErrorModule::Kernel, 101};
constexpr Result ResultInvalidAddress{ErrorModule::Kernel, 102};
constexpr Result ResultOutOfResource{ErrorModule::Kernel, 103};
constexpr Result ResultOutOfMemory{ErrorModule::Kernel, 104};
constexpr Result ResultOutOfHandles{ErrorModule::Kernel, 105};
constexpr Result ResultInvalidCurrentMemory{ErrorModule::Kernel, 106};
constexpr Result ResultInvalidNewMemoryPermission{ErrorModule::Kernel, 108};
constexpr Result ResultInvalidMemoryRegion{ErrorModule::Kernel, 110};
constexpr Result ResultInvalidPriority{ErrorModule::Kernel, 112};
constexpr Result ResultInvalidCoreId{ErrorModule::Kernel, 113};
constexpr Result ResultInvalidHandle{ErrorModule::Kernel, 114};
constexpr Result ResultInvalidPointer{ErrorModule::Kernel, 115};
constexpr Result ResultInvalidCombination{ErrorModule::Kernel, 116};
constexpr Result ResultTimedOut{ErrorModule::Kernel, 117};
constexpr Result ResultCancelled{ErrorModule::Kernel, 118};
constexpr Result ResultOutOfRange{ErrorModule::Kernel, 119};
constexpr Result ResultInvalidEnumValue{ErrorModule::Kernel, 120};
constexpr Result ResultNotFound{ErrorModule::Kernel, 121};
constexpr Result ResultBusy{ErrorModule::Kernel, 122};
constexpr Result ResultSessionClosed{ErrorModule::Kernel, 123};
constexpr Result ResultInvalidState{ErrorModule::Kernel, 125};
constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126};
constexpr Result ResultPortClosed{ErrorModule::Kernel, 131};
constexpr Result ResultLimitReached{ErrorModule::Kernel, 132};
constexpr Result ResultReceiveListBroken{ErrorModule::Kernel, 258};
constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259};
constexpr Result ResultMessageTooLarge{ErrorModule::Kernel, 260};
constexpr Result ResultInvalidId{ErrorModule::Kernel, 519};

} // namespace Kernel


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

#pragma once

#include <bitset>

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

namespace Kernel::Svc {

using Handle = u32;

enum class MemoryState : u32 {
    Free = 0x00,
    Io = 0x01,
    Static = 0x02,
    Code = 0x03,
    CodeData = 0x04,
    Normal = 0x05,
    Shared = 0x06,
    Alias = 0x07,
    AliasCode = 0x08,
    AliasCodeData = 0x09,
    Ipc = 0x0A,
    Stack = 0x0B,
    ThreadLocal = 0x0C,
    Transferred = 0x0D,
    SharedTransferred = 0x0E,
    SharedCode = 0x0F,
    Inaccessible = 0x10,
    NonSecureIpc = 0x11,
    NonDeviceIpc = 0x12,
    Kernel = 0x13,
    GeneratedCode = 0x14,
    CodeOut = 0x15,
    Coverage = 0x16,
    Insecure = 0x17,
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryState);

enum class MemoryAttribute : u32 {
    Locked = (1 << 0),
    IpcLocked = (1 << 1),
    DeviceShared = (1 << 2),
    Uncached = (1 << 3),
    PermissionLocked = (1 << 4),
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute);

enum class MemoryPermission : u32 {
    None = (0 << 0),
    Read = (1 << 0),
    Write = (1 << 1),
    Execute = (1 << 2),
    ReadWrite = Read | Write,
    ReadExecute = Read | Execute,
    DontCare = (1 << 28),
};
DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission);

enum class SignalType : u32 {
    Signal = 0,
    SignalAndIncrementIfEqual = 1,
    SignalAndModifyByWaitingCountIfEqual = 2,
};

enum class ArbitrationType : u32 {
    WaitIfLessThan = 0,
    DecrementAndWaitIfLessThan = 1,
    WaitIfEqual = 2,
};

enum class YieldType : s64 {
    WithoutCoreMigration = 0,
    WithCoreMigration = -1,
    ToAnyThread = -2,
};

enum class ThreadExitReason : u32 {
    ExitThread = 0,
    TerminateThread = 1,
    ExitProcess = 2,
    TerminateProcess = 3,
};

enum class ThreadActivity : u32 {
    Runnable = 0,
    Paused = 1,
};

constexpr inline s32 IdealCoreDontCare = -1;
constexpr inline s32 IdealCoreUseProcessValue = -2;
constexpr inline s32 IdealCoreNoUpdate = -3;

constexpr inline s32 LowestThreadPriority = 63;
constexpr inline s32 HighestThreadPriority = 0;

constexpr inline s32 SystemThreadPriorityHighest = 16;

enum class ProcessState : u32 {
    Created = 0,
    CreatedAttached = 1,
    Running = 2,
    Crashed = 3,
    RunningAttached = 4,
    Terminating = 5,
    Terminated = 6,
    DebugBreak = 7,
};

enum class ProcessExitReason : u32 {
    ExitProcess = 0,
    TerminateProcess = 1,
    Exception = 2,
};

constexpr inline size_t ThreadLocalRegionSize = 0x200;

struct PageInfo {
    u32 flags;
};

// Info Types.
enum class InfoType : u32 {
    CoreMask = 0,
    PriorityMask = 1,
    AliasRegionAddress = 2,
    AliasRegionSize = 3,
    HeapRegionAddress = 4,
    HeapRegionSize = 5,
    TotalMemorySize = 6,
    UsedMemorySize = 7,
    DebuggerAttached = 8,
    ResourceLimit = 9,
    IdleTickCount = 10,
    RandomEntropy = 11,
    AslrRegionAddress = 12,
    AslrRegionSize = 13,
    StackRegionAddress = 14,
    StackRegionSize = 15,
    SystemResourceSizeTotal = 16,
    SystemResourceSizeUsed = 17,
    ProgramId = 18,
    InitialProcessIdRange = 19,
    UserExceptionContextAddress = 20,
    TotalNonSystemMemorySize = 21,
    UsedNonSystemMemorySize = 22,
    IsApplication = 23,
    FreeThreadCount = 24,
    ThreadTickCount = 25,
    IsSvcPermitted = 26,
    IoRegionHint = 27,

    MesosphereMeta = 65000,
    MesosphereCurrentProcess = 65001,
};

enum class BreakReason : u32 {
    Panic = 0,
    Assert = 1,
    User = 2,
    PreLoadDll = 3,
    PostLoadDll = 4,
    PreUnloadDll = 5,
    PostUnloadDll = 6,
    CppException = 7,

    NotificationOnlyFlag = 0x80000000,
};
DECLARE_ENUM_FLAG_OPERATORS(BreakReason);

enum class DebugEvent : u32 {
    CreateProcess = 0,
    CreateThread = 1,
    ExitProcess = 2,
    ExitThread = 3,
    Exception = 4,
};

enum class DebugThreadParam : u32 {
    Priority = 0,
    State = 1,
    IdealCore = 2,
    CurrentCore = 3,
    AffinityMask = 4,
};

enum class DebugException : u32 {
    UndefinedInstruction = 0,
    InstructionAbort = 1,
    DataAbort = 2,
    AlignmentFault = 3,
    DebuggerAttached = 4,
    BreakPoint = 5,
    UserBreak = 6,
    DebuggerBreak = 7,
    UndefinedSystemCall = 8,
    MemorySystemError = 9,
};

enum class DebugEventFlag : u32 {
    Stopped = (1u << 0),
};

enum class BreakPointType : u32 {
    HardwareInstruction = 0,
    HardwareData = 1,
};

enum class HardwareBreakPointRegisterName : u32 {
    I0 = 0,
    I1 = 1,
    I2 = 2,
    I3 = 3,
    I4 = 4,
    I5 = 5,
    I6 = 6,
    I7 = 7,
    I8 = 8,
    I9 = 9,
    I10 = 10,
    I11 = 11,
    I12 = 12,
    I13 = 13,
    I14 = 14,
    I15 = 15,
    D0 = 16,
    D1 = 17,
    D2 = 18,
    D3 = 19,
    D4 = 20,
    D5 = 21,
    D6 = 22,
    D7 = 23,
    D8 = 24,
    D9 = 25,
    D10 = 26,
    D11 = 27,
    D12 = 28,
    D13 = 29,
    D14 = 30,
    D15 = 31,
};

namespace lp64 {
struct LastThreadContext {
    u64 fp;
    u64 sp;
    u64 lr;
    u64 pc;
};

struct PhysicalMemoryInfo {
    u64 physical_address;
    u64 virtual_address;
    u64 size;
};

struct DebugInfoCreateProcess {
    u64 program_id;
    u64 process_id;
    std::array<char, 0xC> name;
    u32 flags;
    u64 user_exception_context_address; // 5.0.0+
};

struct DebugInfoCreateThread {
    u64 thread_id;
    u64 tls_address;
    // Removed in 11.0.0 u64 entrypoint;
};

struct DebugInfoExitProcess {
    ProcessExitReason reason;
};

struct DebugInfoExitThread {
    ThreadExitReason reason;
};

struct DebugInfoUndefinedInstructionException {
    u32 insn;
};

struct DebugInfoDataAbortException {
    u64 address;
};

struct DebugInfoAlignmentFaultException {
    u64 address;
};

struct DebugInfoBreakPointException {
    BreakPointType type;
    u64 address;
};

struct DebugInfoUserBreakException {
    BreakReason break_reason;
    u64 address;
    u64 size;
};

struct DebugInfoDebuggerBreakException {
    std::array<u64, 4> active_thread_ids;
};

struct DebugInfoUndefinedSystemCallException {
    u32 id;
};

union DebugInfoSpecificException {
    DebugInfoUndefinedInstructionException undefined_instruction;
    DebugInfoDataAbortException data_abort;
    DebugInfoAlignmentFaultException alignment_fault;
    DebugInfoBreakPointException break_point;
    DebugInfoUserBreakException user_break;
    DebugInfoDebuggerBreakException debugger_break;
    DebugInfoUndefinedSystemCallException undefined_system_call;
    u64 raw;
};

struct DebugInfoException {
    DebugException type;
    u64 address;
    DebugInfoSpecificException specific;
};

union DebugInfo {
    DebugInfoCreateProcess create_process;
    DebugInfoCreateThread create_thread;
    DebugInfoExitProcess exit_process;
    DebugInfoExitThread exit_thread;
    DebugInfoException exception;
};

struct DebugEventInfo {
    DebugEvent type;
    u32 flags;
    u64 thread_id;
    DebugInfo info;
};
static_assert(sizeof(DebugEventInfo) >= 0x40);

struct SecureMonitorArguments {
    std::array<u64, 8> r;
};
static_assert(sizeof(SecureMonitorArguments) == 0x40);
} // namespace lp64

namespace ilp32 {
struct LastThreadContext {
    u32 fp;
    u32 sp;
    u32 lr;
    u32 pc;
};

struct PhysicalMemoryInfo {
    u64 physical_address;
    u32 virtual_address;
    u32 size;
};

struct DebugInfoCreateProcess {
    u64 program_id;
    u64 process_id;
    std::array<char, 0xC> name;
    u32 flags;
    u32 user_exception_context_address; // 5.0.0+
};

struct DebugInfoCreateThread {
    u64 thread_id;
    u32 tls_address;
    // Removed in 11.0.0 u32 entrypoint;
};

struct DebugInfoExitProcess {
    ProcessExitReason reason;
};

struct DebugInfoExitThread {
    ThreadExitReason reason;
};

struct DebugInfoUndefinedInstructionException {
    u32 insn;
};

struct DebugInfoDataAbortException {
    u32 address;
};

struct DebugInfoAlignmentFaultException {
    u32 address;
};

struct DebugInfoBreakPointException {
    BreakPointType type;
    u32 address;
};

struct DebugInfoUserBreakException {
    BreakReason break_reason;
    u32 address;
    u32 size;
};

struct DebugInfoDebuggerBreakException {
    std::array<u64, 4> active_thread_ids;
};

struct DebugInfoUndefinedSystemCallException {
    u32 id;
};

union DebugInfoSpecificException {
    DebugInfoUndefinedInstructionException undefined_instruction;
    DebugInfoDataAbortException data_abort;
    DebugInfoAlignmentFaultException alignment_fault;
    DebugInfoBreakPointException break_point;
    DebugInfoUserBreakException user_break;
    DebugInfoDebuggerBreakException debugger_break;
    DebugInfoUndefinedSystemCallException undefined_system_call;
    u64 raw;
};

struct DebugInfoException {
    DebugException type;
    u32 address;
    DebugInfoSpecificException specific;
};

union DebugInfo {
    DebugInfoCreateProcess create_process;
    DebugInfoCreateThread create_thread;
    DebugInfoExitProcess exit_process;
    DebugInfoExitThread exit_thread;
    DebugInfoException exception;
};

struct DebugEventInfo {
    DebugEvent type;
    u32 flags;
    u64 thread_id;
    DebugInfo info;
};

struct SecureMonitorArguments {
    std::array<u32, 8> r;
};
static_assert(sizeof(SecureMonitorArguments) == 0x20);
} // namespace ilp32

struct ThreadContext {
    std::array<u64, 29> r;
    u64 fp;
    u64 lr;
    u64 sp;
    u64 pc;
    u32 pstate;
    u32 padding;
    std::array<u128, 32> v;
    u32 fpcr;
    u32 fpsr;
    u64 tpidr;
};
static_assert(sizeof(ThreadContext) == 0x320);

struct MemoryInfo {
    u64 base_address;
    u64 size;
    MemoryState state;
    MemoryAttribute attribute;
    MemoryPermission permission;
    u32 ipc_count;
    u32 device_count;
    u32 padding;
};

enum class LimitableResource : u32 {
    PhysicalMemoryMax = 0,
    ThreadCountMax = 1,
    EventCountMax = 2,
    TransferMemoryCountMax = 3,
    SessionCountMax = 4,
    Count,
};

enum class IoPoolType : u32 {
    // Not supported.
    Count = 0,
};

enum class MemoryMapping : u32 {
    IoRegister = 0,
    Uncached = 1,
    Memory = 2,
};

enum class MapDeviceAddressSpaceFlag : u32 {
    None = (0U << 0),
    NotIoRegister = (1U << 0),
};
DECLARE_ENUM_FLAG_OPERATORS(MapDeviceAddressSpaceFlag);

union MapDeviceAddressSpaceOption {
    u32 raw;
    BitField<0, 16, MemoryPermission> permission;
    BitField<16, 1, MapDeviceAddressSpaceFlag> flags;
    BitField<17, 15, u32> reserved;
};

enum class KernelDebugType : u32 {
    Thread = 0,
    ThreadCallStack = 1,
    KernelObject = 2,
    Handle_ = 3,
    Memory = 4,
    PageTable = 5,
    CpuUtilization = 6,
    Process = 7,
    SuspendProcess = 8,
    ResumeProcess = 9,
    Port = 10,
};

enum class KernelTraceState : u32 {
    Disabled = 0,
    Enabled = 1,
};

enum class CodeMemoryOperation : u32 {
    Map = 0,
    MapToOwner = 1,
    Unmap = 2,
    UnmapFromOwner = 3,
};

enum class InterruptType : u32 {
    Edge = 0,
    Level = 1,
};

enum class DeviceName {
    Afi = 0,
    Avpc = 1,
    Dc = 2,
    Dcb = 3,
    Hc = 4,
    Hda = 5,
    Isp2 = 6,
    MsencNvenc = 7,
    Nv = 8,
    Nv2 = 9,
    Ppcs = 10,
    Sata = 11,
    Vi = 12,
    Vic = 13,
    XusbHost = 14,
    XusbDev = 15,
    Tsec = 16,
    Ppcs1 = 17,
    Dc1 = 18,
    Sdmmc1a = 19,
    Sdmmc2a = 20,
    Sdmmc3a = 21,
    Sdmmc4a = 22,
    Isp2b = 23,
    Gpu = 24,
    Gpub = 25,
    Ppcs2 = 26,
    Nvdec = 27,
    Ape = 28,
    Se = 29,
    Nvjpg = 30,
    Hc1 = 31,
    Se1 = 32,
    Axiap = 33,
    Etr = 34,
    Tsecb = 35,
    Tsec1 = 36,
    Tsecb1 = 37,
    Nvdec1 = 38,
    Count,
};

enum class SystemInfoType : u32 {
    TotalPhysicalMemorySize = 0,
    UsedPhysicalMemorySize = 1,
    InitialProcessIdRange = 2,
};

enum class ProcessInfoType : u32 {
    ProcessState = 0,
};

enum class ProcessActivity : u32 {
    Runnable,
    Paused,
};

enum class CreateProcessFlag : u32 {
    // Is 64 bit?
    Is64Bit = (1 << 0),

    // What kind of address space?
    AddressSpaceShift = 1,
    AddressSpaceMask = (7 << AddressSpaceShift),
    AddressSpace32Bit = (0 << AddressSpaceShift),
    AddressSpace64BitDeprecated = (1 << AddressSpaceShift),
    AddressSpace32BitWithoutAlias = (2 << AddressSpaceShift),
    AddressSpace64Bit = (3 << AddressSpaceShift),

    // Should JIT debug be done on crash?
    EnableDebug = (1 << 4),

    // Should ASLR be enabled for the process?
    EnableAslr = (1 << 5),

    // Is the process an application?
    IsApplication = (1 << 6),

    // 4.x deprecated: Should use secure memory?
    DeprecatedUseSecureMemory = (1 << 7),

    // 5.x+ Pool partition type.
    PoolPartitionShift = 7,
    PoolPartitionMask = (0xF << PoolPartitionShift),
    PoolPartitionApplication = (0 << PoolPartitionShift),
    PoolPartitionApplet = (1 << PoolPartitionShift),
    PoolPartitionSystem = (2 << PoolPartitionShift),
    PoolPartitionSystemNonSecure = (3 << PoolPartitionShift),

    // 7.x+ Should memory allocation be optimized? This requires IsApplication.
    OptimizeMemoryAllocation = (1 << 11),

    // 11.x+ DisableDeviceAddressSpaceMerge.
    DisableDeviceAddressSpaceMerge = (1 << 12),

    // Mask of all flags.
    All = Is64Bit | AddressSpaceMask | EnableDebug | EnableAslr | IsApplication |
          PoolPartitionMask | OptimizeMemoryAllocation | DisableDeviceAddressSpaceMerge,
};
DECLARE_ENUM_FLAG_OPERATORS(CreateProcessFlag);

struct CreateProcessParameter {
    std::array<char, 12> name;
    u32 version;
    u64 program_id;
    u64 code_address;
    s32 code_num_pages;
    CreateProcessFlag flags;
    Handle reslimit;
    s32 system_resource_num_pages;
};
static_assert(sizeof(CreateProcessParameter) == 0x30);

constexpr size_t NumSupervisorCalls = 0xC0;
using SvcAccessFlagSet = std::bitset<NumSupervisorCalls>;

enum class InitialProcessIdRangeInfo : u64 {
    Minimum = 0,
    Maximum = 1,
};

} // namespace Kernel::Svc


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

#pragma once

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

namespace Kernel::Svc {

constexpr inline u32 ConvertToSvcMajorVersion(u32 sdk) {
    return sdk + 4;
}
constexpr inline u32 ConvertToSdkMajorVersion(u32 svc) {
    return svc - 4;
}

constexpr inline u32 ConvertToSvcMinorVersion(u32 sdk) {
    return sdk;
}
constexpr inline u32 ConvertToSdkMinorVersion(u32 svc) {
    return svc;
}

union KernelVersion {
    u32 value;
    BitField<0, 4, u32> minor_version;
    BitField<4, 13, u32> major_version;
};

constexpr inline u32 EncodeKernelVersion(u32 major, u32 minor) {
    return decltype(KernelVersion::minor_version)::FormatValue(minor) |
           decltype(KernelVersion::major_version)::FormatValue(major);
}

constexpr inline u32 GetKernelMajorVersion(u32 encoded) {
    return decltype(KernelVersion::major_version)::ExtractValue(encoded);
}

constexpr inline u32 GetKernelMinorVersion(u32 encoded) {
    return decltype(KernelVersion::minor_version)::ExtractValue(encoded);
}

// Nintendo doesn't support programs targeting SVC versions < 3.0.
constexpr inline u32 RequiredKernelMajorVersion = 3;
constexpr inline u32 RequiredKernelMinorVersion = 0;
constexpr inline u32 RequiredKernelVersion =
    EncodeKernelVersion(RequiredKernelMajorVersion, RequiredKernelMinorVersion);

// This is the highest SVC version supported, to be updated on new kernel releases.
// NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor.
constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(15);
constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion(3);
constexpr inline u32 SupportedKernelVersion =
    EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion);

} // namespace Kernel::Svc


code:
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/expected.h"

// All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes

/**
 * Identifies the module which caused the error. Error codes can be propagated through a call
 * chain, meaning that this doesn't always correspond to the module where the API call made is
 * contained.
 */
enum class ErrorModule : u32 {
    Common = 0,
    Kernel = 1,
    FS = 2,
    OS = 3, // used for Memory, Thread, Mutex, Nvidia
    HTCS = 4,
    NCM = 5,
    DD = 6,
    LR = 8,
    Loader = 9,
    CMIF = 10,
    HIPC = 11,
    TMA = 12,
    DMNT = 13,
    GDS = 14,
    PM = 15,
    NS = 16,
    BSDSockets = 17,
    HTC = 18,
    TSC = 19,
    NCMContent = 20,
    SM = 21,
    RO = 22,
    GC = 23,
    SDMMC = 24,
    OVLN = 25,
    SPL = 26,
    Socket = 27,
    HTCLOW = 29,
    DDSF = 30,
    HTCFS = 31,
    Async = 32,
    Util = 33,
    TIPC = 35,
    ANIF = 37,
    ETHC = 100,
    I2C = 101,
    GPIO = 102,
    UART = 103,
    CPAD = 104,
    Settings = 105,
    FTM = 106,
    WLAN = 107,
    XCD = 108,
    TMP451 = 109,
    NIFM = 110,
    HwOpus = 111,
    LSM6DS3 = 112,
    Bluetooth = 113,
    VI = 114,
    NFP = 115,
    Time = 116,
    FGM = 117,
    OE = 118,
    BH1730FVC = 119,
    PCIe = 120,
    Friends = 121,
    BCAT = 122,
    SSLSrv = 123,
    Account = 124,
    News = 125,
    Mii = 126,
    NFC = 127,
    AM = 128,
    PlayReport = 129,
    AHID = 130,
    Qlaunch = 132,
    PCV = 133,
    USBPD = 134,
    BPC = 135,
    PSM = 136,
    NIM = 137,
    PSC = 138,
    TC = 139,
    USB = 140,
    NSD = 141,
    PCTL = 142,
    BTM = 143,
    LA = 144,
    ETicket = 145,
    NGC = 146,
    ERPT = 147,
    APM = 148,
    CEC = 149,
    Profiler = 150,
    ErrorUpload = 151,
    LIDBE = 152,
    Audio = 153,
    NPNS = 154,
    NPNSHTTPSTREAM = 155,
    ARP = 157,
    SWKBD = 158,
    BOOT = 159,
    NetDiag = 160,
    NFCMifare = 161,
    UserlandAssert = 162,
    Fatal = 163,
    NIMShop = 164,
    SPSM = 165,
    BGTC = 167,
    UserlandCrash = 168,
    SASBUS = 169,
    PI = 170,
    AudioCtrl = 172,
    LBL = 173,
    JIT = 175,
    HDCP = 176,
    OMM = 177,
    PDM = 178,
    OLSC = 179,
    SREPO = 180,
    Dauth = 181,
    STDFU = 182,
    DBG = 183,
    DHCPS = 186,
    SPI = 187,
    AVM = 188,
    PWM = 189,
    RTC = 191,
    Regulator = 192,
    LED = 193,
    SIO = 195,
    PCM = 196,
    CLKRST = 197,
    POWCTL = 198,
    AudioOld = 201,
    HID = 202,
    LDN = 203,
    CS = 204,
    Irsensor = 205,
    Capture = 206,
    Manu = 208,
    ATK = 209,
    WEB = 210,
    LCS = 211,
    GRC = 212,
    Repair = 213,
    Album = 214,
    RID = 215,
    Migration = 216,
    MigrationLdcServ = 217,
    HIDBUS = 218,
    ENS = 219,
    WebSocket = 223,
    DCDMTP = 227,
    PGL = 228,
    Notification = 229,
    INS = 230,
    LP2P = 231,
    RCD = 232,
    LCM40607 = 233,
    PRC = 235,
    TMAHTC = 237,
    ECTX = 238,
    MNPP = 239,
    HSHL = 240,
    CAPMTP = 242,
    DP2HDMI = 244,
    Cradle = 245,
    SProfile = 246,
    NDRM = 250,
    TSPM = 499,
    DevMenu = 500,
    GeneralWebApplet = 800,
    WifiWebAuthApplet = 809,
    WhitelistedApplet = 810,
    ShopN = 811,
};

/// Encapsulates a Horizon OS error code, allowing it to be separated into its constituent fields.
union Result {
    u32 raw;

    using Module = BitField<0, 9, ErrorModule>;
    using Description = BitField<9, 13, u32>;

    Result() = default;
    constexpr explicit Result(u32 raw_) : raw(raw_) {}

    constexpr Result(ErrorModule module_, u32 description_)
        : raw(Module::FormatValue(module_) | Description::FormatValue(description_)) {}

    [[nodiscard]] constexpr bool IsSuccess() const {
        return raw == 0;
    }

    [[nodiscard]] constexpr bool IsError() const {
        return !IsSuccess();
    }

    [[nodiscard]] constexpr bool IsFailure() const {
        return !IsSuccess();
    }

    [[nodiscard]] constexpr u32 GetInnerValue() const {
        return raw;
    }

    [[nodiscard]] constexpr ErrorModule GetModule() const {
        return Module::ExtractValue(raw);
    }

    [[nodiscard]] constexpr u32 GetDescription() const {
        return Description::ExtractValue(raw);
    }

    [[nodiscard]] constexpr bool Includes(Result result) const {
        return GetInnerValue() == result.GetInnerValue();
    }
};
static_assert(std::is_trivial_v<Result>);

[[nodiscard]] constexpr bool operator==(const Result& a, const Result& b) {
    return a.raw == b.raw;
}

[[nodiscard]] constexpr bool operator!=(const Result& a, const Result& b) {
    return !operator==(a, b);
}

// Convenience functions for creating some common kinds of errors:

/// The default success `Result`.
constexpr Result ResultSuccess(0);

/**
 * Placeholder result code used for unknown error codes.
 *
 * @note This should only be used when a particular error code
 *       is not known yet.
 */
constexpr Result ResultUnknown(UINT32_MAX);

/**
 * A ResultRange defines an inclusive range of error descriptions within an error module.
 * This can be used to check whether the description of a given Result falls within the range.
 * The conversion function returns a Result with its description set to description_start.
 *
 * An example of how it could be used:
 * \code
 * constexpr ResultRange ResultCommonError{ErrorModule::Common, 0, 9999};
 *
 * Result Example(int value) {
 *     const Result result = OtherExample(value);
 *
 *     // This will only evaluate to true if result.module is ErrorModule::Common and
 *     // result.description is in between 0 and 9999 inclusive.
 *     if (ResultCommonError.Includes(result)) {
 *         // This returns Result{ErrorModule::Common, 0};
 *         return ResultCommonError;
 *     }
 *
 *     return ResultSuccess;
 * }
 * \endcode
 */
class ResultRange {
public:
    consteval ResultRange(ErrorModule module, u32 description_start, u32 description_end_)
        : code{module, description_start}, description_end{description_end_} {}

    [[nodiscard]] constexpr operator Result() const {
        return code;
    }

    [[nodiscard]] constexpr bool Includes(Result other) const {
        return code.GetModule() == other.GetModule() &&
               code.GetDescription() <= other.GetDescription() &&
               other.GetDescription() <= description_end;
    }

private:
    Result code;
    u32 description_end;
};

#define R_SUCCEEDED(res) (static_cast<Result>(res).IsSuccess())
#define R_FAILED(res) (static_cast<Result>(res).IsFailure())

namespace ResultImpl {
template <auto EvaluateResult, class F>
class ScopedResultGuard {
    YUZU_NON_COPYABLE(ScopedResultGuard);
    YUZU_NON_MOVEABLE(ScopedResultGuard);

private:
    Result& m_ref;
    F m_f;

public:
    constexpr ScopedResultGuard(Result& ref, F f) : m_ref(ref), m_f(std::move(f)) {}
    constexpr ~ScopedResultGuard() {
        if (EvaluateResult(m_ref)) {
            m_f();
        }
    }
};

template <auto EvaluateResult>
class ResultReferenceForScopedResultGuard {
private:
    Result& m_ref;

public:
    constexpr ResultReferenceForScopedResultGuard(Result& r) : m_ref(r) {}
    constexpr operator Result&() const {
        return m_ref;
    }
};

template <auto EvaluateResult, typename F>
constexpr ScopedResultGuard<EvaluateResult, F> operator+(
    ResultReferenceForScopedResultGuard<EvaluateResult> ref, F&& f) {
    return ScopedResultGuard<EvaluateResult, F>(static_cast<Result&>(ref), std::forward<F>(f));
}

constexpr bool EvaluateResultSuccess(const Result& r) {
    return R_SUCCEEDED(r);
}
constexpr bool EvaluateResultFailure(const Result& r) {
    return R_FAILED(r);
}

template <auto... R>
constexpr bool EvaluateAnyResultIncludes(const Result& r) {
    return ((r == R) || ...);
}

template <auto... R>
constexpr bool EvaluateResultNotIncluded(const Result& r) {
    return !EvaluateAnyResultIncludes<R...>(r);
}

template <typename T>
constexpr void UpdateCurrentResultReference(T result_reference, Result result) = delete;
// Intentionally not defined

template <>
constexpr void UpdateCurrentResultReference<Result&>(Result& result_reference, Result result) {
    result_reference = result;
}

template <>
constexpr void UpdateCurrentResultReference<const Result>(Result result_reference, Result result) {}
} // namespace ResultImpl

#define DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(COUNTER_VALUE)                                \
    [[maybe_unused]] constexpr bool CONCAT2(HasPrevRef_, COUNTER_VALUE) =                          \
        std::same_as<decltype(__TmpCurrentResultReference), Result&>;                              \
    [[maybe_unused]] Result CONCAT2(PrevRef_, COUNTER_VALUE) = __TmpCurrentResultReference;        \
    [[maybe_unused]] Result CONCAT2(__tmp_result_, COUNTER_VALUE) = ResultSuccess;                 \
    Result& __TmpCurrentResultReference = CONCAT2(HasPrevRef_, COUNTER_VALUE)                      \
                                              ? CONCAT2(PrevRef_, COUNTER_VALUE)                   \
                                              : CONCAT2(__tmp_result_, COUNTER_VALUE)

#define ON_RESULT_RETURN_IMPL(...)                                                                 \
    static_assert(std::same_as<decltype(__TmpCurrentResultReference), Result&>);                   \
    auto CONCAT2(RESULT_GUARD_STATE_, __COUNTER__) =                                               \
        ResultImpl::ResultReferenceForScopedResultGuard<__VA_ARGS__>(                              \
            __TmpCurrentResultReference) +                                                         \
        [&]()

#define ON_RESULT_FAILURE_2 ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateResultFailure)

#define ON_RESULT_FAILURE                                                                          \
    DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__);                                     \
    ON_RESULT_FAILURE_2

#define ON_RESULT_SUCCESS_2 ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateResultSuccess)

#define ON_RESULT_SUCCESS                                                                          \
    DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__);                                     \
    ON_RESULT_SUCCESS_2

#define ON_RESULT_INCLUDED_2(...)                                                                  \
    ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateAnyResultIncludes<__VA_ARGS__>)

#define ON_RESULT_INCLUDED(...)                                                                    \
    DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__);                                     \
    ON_RESULT_INCLUDED_2(__VA_ARGS__)

constexpr inline Result __TmpCurrentResultReference = ResultSuccess;

/// Returns a result.
#define R_RETURN(res_expr)                                                                         \
    {                                                                                \
        const Result _tmp_r_throw_rc = (res_expr);                                                 \
        ResultImpl::UpdateCurrentResultReference<decltype(__TmpCurrentResultReference)>(           \
            __TmpCurrentResultReference, _tmp_r_throw_rc);                                         \
        return _tmp_r_throw_rc;                                                                    \
    }

/// Returns ResultSuccess()
#define R_SUCCEED() R_RETURN(ResultSuccess)

/// Throws a result.
#define R_THROW(res_expr) R_RETURN(res_expr)

/// Evaluates a boolean expression, and returns a result unless that expression is true.
#define R_UNLESS(expr, res)                                                                        \
    {                                                                                \
        if (!(expr)) {                                                                             \
            R_THROW(res);                                                                          \
        }                                                                                \
    }

/// Evaluates an expression that returns a result, and returns the result if it would fail.
#define R_TRY(res_expr)                                                                            \
    {                                                                                \
        const auto _tmp_r_try_rc = (res_expr);                                                     \
        if (R_FAILED(_tmp_r_try_rc)) {                                                             \
            R_THROW(_tmp_r_try_rc);                                                                \
        }                                                                                \
    }

/// Evaluates a boolean expression, and succeeds if that expression is true.
#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), ResultSuccess)

#define R_TRY_CATCH(res_expr)                                                                      \
    {                                                                                \
        const auto R_CURRENT_RESULT = (res_expr);                                                  \
        if (R_FAILED(R_CURRENT_RESULT)) {                                                          \
            if (false)

#define R_END_TRY_CATCH                                                                            \
    else if (R_FAILED(R_CURRENT_RESULT)) {                                                         \
        R_THROW(R_CURRENT_RESULT);                                                                 \
    }                                                                                \
    }                                                                                \
    }

#define R_CATCH_ALL()                                                                              \
    }                                                                                \
    else if (R_FAILED(R_CURRENT_RESULT)) {                                                         \
        if (true)

#define R_CATCH(res_expr)                                                                          \
    }                                                                                \
    else if ((res_expr) == (R_CURRENT_RESULT)) {                                                   \
        if (true)

#define R_CONVERT(catch_type, convert_type)                                                        \
    R_CATCH(catch_type) { R_THROW(static_cast<Result>(convert_type)); }

#define R_CONVERT_ALL(convert_type)                                                                \
    R_CATCH_ALL() { R_THROW(static_cast<Result>(convert_type)); }

#define R_ASSERT(res_expr) ASSERT(R_SUCCEEDED(res_expr))