diff options
| author | Liam <byteslice@airmail.cc> | 2022-10-04 20:15:40 -0400 | 
|---|---|---|
| committer | Liam <byteslice@airmail.cc> | 2023-02-04 22:37:43 -0500 | 
| commit | 92eb091ddb9dfd96e59a75937e185079a63626e3 (patch) | |
| tree | be79f36453a4735088d9230e02f426792077eeb4 | |
| parent | a64fc3ee77f26dc488f51e2348ef5b63ae4e8299 (diff) | |
kernel/svc: Split implementations into separate files
40 files changed, 3196 insertions, 2688 deletions
| diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 112c61b80..f16072e6c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -298,7 +298,42 @@ add_library(core STATIC      hle/kernel/svc.h      hle/kernel/svc_common.h      hle/kernel/svc_types.h -    hle/kernel/svc_wrap.h +    hle/kernel/svc/svc_activity.cpp +    hle/kernel/svc/svc_address_arbiter.cpp +    hle/kernel/svc/svc_address_translation.cpp +    hle/kernel/svc/svc_cache.cpp +    hle/kernel/svc/svc_code_memory.cpp +    hle/kernel/svc/svc_condition_variable.cpp +    hle/kernel/svc/svc_debug.cpp +    hle/kernel/svc/svc_debug_string.cpp +    hle/kernel/svc/svc_device_address_space.cpp +    hle/kernel/svc/svc_event.cpp +    hle/kernel/svc/svc_exception.cpp +    hle/kernel/svc/svc_info.cpp +    hle/kernel/svc/svc_interrupt_event.cpp +    hle/kernel/svc/svc_io_pool.cpp +    hle/kernel/svc/svc_ipc.cpp +    hle/kernel/svc/svc_kernel_debug.cpp +    hle/kernel/svc/svc_light_ipc.cpp +    hle/kernel/svc/svc_lock.cpp +    hle/kernel/svc/svc_memory.cpp +    hle/kernel/svc/svc_physical_memory.cpp +    hle/kernel/svc/svc_port.cpp +    hle/kernel/svc/svc_power_management.cpp +    hle/kernel/svc/svc_process.cpp +    hle/kernel/svc/svc_process_memory.cpp +    hle/kernel/svc/svc_processor.cpp +    hle/kernel/svc/svc_query_memory.cpp +    hle/kernel/svc/svc_register.cpp +    hle/kernel/svc/svc_resource_limit.cpp +    hle/kernel/svc/svc_secure_monitor_call.cpp +    hle/kernel/svc/svc_session.cpp +    hle/kernel/svc/svc_shared_memory.cpp +    hle/kernel/svc/svc_synchronization.cpp +    hle/kernel/svc/svc_thread.cpp +    hle/kernel/svc/svc_thread_profiler.cpp +    hle/kernel/svc/svc_tick.cpp +    hle/kernel/svc/svc_transfer_memory.cpp      hle/result.h      hle/service/acc/acc.cpp      hle/service/acc/acc.h diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 67fa5d71c..4cb6f40a0 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1,2697 +1,16 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later -#include <algorithm> -#include <cinttypes> -#include <iterator> -#include <mutex> -#include <vector> - -#include "common/alignment.h" -#include "common/assert.h" -#include "common/common_funcs.h" -#include "common/fiber.h" -#include "common/logging/log.h" -#include "common/scope_exit.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/debugger/debugger.h" -#include "core/hle/kernel/k_client_port.h" -#include "core/hle/kernel/k_client_session.h" -#include "core/hle/kernel/k_code_memory.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_handle_table.h" -#include "core/hle/kernel/k_memory_block.h" -#include "core/hle/kernel/k_memory_layout.h" -#include "core/hle/kernel/k_page_table.h" -#include "core/hle/kernel/k_port.h" +#include "common/common_types.h"  #include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_resource_limit.h" -#include "core/hle/kernel/k_scheduler.h" -#include "core/hle/kernel/k_scoped_resource_reservation.h" -#include "core/hle/kernel/k_session.h" -#include "core/hle/kernel/k_shared_memory.h" -#include "core/hle/kernel/k_synchronization_object.h"  #include "core/hle/kernel/k_thread.h" -#include "core/hle/kernel/k_thread_queue.h" -#include "core/hle/kernel/k_transfer_memory.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/physical_core.h"  #include "core/hle/kernel/svc.h" -#include "core/hle/kernel/svc_results.h" -#include "core/hle/kernel/svc_types.h"  #include "core/hle/kernel/svc_wrap.h"  #include "core/hle/result.h" -#include "core/memory.h" -#include "core/reporter.h"  namespace Kernel::Svc {  namespace { -// Checks if address + size is greater than the given address -// This can return false if the size causes an overflow of a 64-bit type -// or if the given size is zero. -constexpr bool IsValidAddressRange(VAddr address, u64 size) { -    return address + size > address; -} - -// Helper function that performs the common sanity checks for svcMapMemory -// and svcUnmapMemory. This is doable, as both functions perform their sanitizing -// in the same order. -Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr, -                                  u64 size) { -    if (!Common::Is4KBAligned(dst_addr)) { -        LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr); -        return ResultInvalidAddress; -    } - -    if (!Common::Is4KBAligned(src_addr)) { -        LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr); -        return ResultInvalidSize; -    } - -    if (size == 0) { -        LOG_ERROR(Kernel_SVC, "Size is 0"); -        return ResultInvalidSize; -    } - -    if (!Common::Is4KBAligned(size)) { -        LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size); -        return ResultInvalidSize; -    } - -    if (!IsValidAddressRange(dst_addr, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}", -                  dst_addr, size); -        return ResultInvalidCurrentMemory; -    } - -    if (!IsValidAddressRange(src_addr, size)) { -        LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}", -                  src_addr, size); -        return ResultInvalidCurrentMemory; -    } - -    if (!manager.IsInsideAddressSpace(src_addr, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", -                  src_addr, size); -        return ResultInvalidCurrentMemory; -    } - -    if (manager.IsOutsideStackRegion(dst_addr, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}", -                  dst_addr, size); -        return ResultInvalidMemoryRegion; -    } - -    if (manager.IsInsideHeapRegion(dst_addr, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Destination does not fit within the heap region, addr=0x{:016X}, " -                  "size=0x{:016X}", -                  dst_addr, size); -        return ResultInvalidMemoryRegion; -    } - -    if (manager.IsInsideAliasRegion(dst_addr, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Destination does not fit within the map region, addr=0x{:016X}, " -                  "size=0x{:016X}", -                  dst_addr, size); -        return ResultInvalidMemoryRegion; -    } - -    return ResultSuccess; -} - -enum class ResourceLimitValueType { -    CurrentValue, -    LimitValue, -    PeakValue, -}; - -} // Anonymous namespace - -/// Set the process heap to a given Size. It can both extend and shrink the heap. -static Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) { -    LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size); - -    // Validate size. -    R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize); -    R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize); - -    // Set the heap size. -    R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size)); - -    return ResultSuccess; -} - -static Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) { -    VAddr temp_heap_addr{}; -    const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)}; -    *heap_addr = static_cast<u32>(temp_heap_addr); -    return result; -} - -constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) { -    switch (perm) { -    case MemoryPermission::None: -    case MemoryPermission::Read: -    case MemoryPermission::ReadWrite: -        return true; -    default: -        return false; -    } -} - -static Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, -                                  MemoryPermission perm) { -    LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size, -              perm); - -    // Validate 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(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission); - -    // Validate that the region is in range for the current process. -    auto& page_table = system.Kernel().CurrentProcess()->PageTable(); -    R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); - -    // Set the memory attribute. -    return page_table.SetMemoryPermission(address, size, perm); -} - -static Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, -                                 u32 attr) { -    LOG_DEBUG(Kernel_SVC, -              "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, -              size, mask, attr); - -    // Validate 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 attribute and mask. -    constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached); -    R_UNLESS((mask | attr) == mask, ResultInvalidCombination); -    R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination); - -    // Validate that the region is in range for the current process. -    auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; -    R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); - -    // Set the memory attribute. -    return page_table.SetMemoryAttribute(address, size, mask, attr); -} - -static Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, -                                   u32 attr) { -    return SetMemoryAttribute(system, address, size, mask, attr); -} - -/// Maps a memory range into a different range. -static Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { -    LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, -              src_addr, size); - -    auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; - -    if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; -        result.IsError()) { -        return result; -    } - -    return page_table.MapMemory(dst_addr, src_addr, size); -} - -static Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { -    return MapMemory(system, dst_addr, src_addr, size); -} - -/// Unmaps a region that was previously mapped with svcMapMemory -static Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { -    LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, -              src_addr, size); - -    auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; - -    if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; -        result.IsError()) { -        return result; -    } - -    return page_table.UnmapMemory(dst_addr, src_addr, size); -} - -static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { -    return UnmapMemory(system, dst_addr, src_addr, size); -} - -template <typename T> -Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) { -    auto& process = *system.CurrentProcess(); -    auto& handle_table = process.GetHandleTable(); - -    // Declare the session we're going to allocate. -    T* session; - -    // Reserve a new session from the process resource limit. -    // FIXME: LimitableResource_SessionCountMax -    KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax); -    if (session_reservation.Succeeded()) { -        session = T::Create(system.Kernel()); -    } else { -        return ResultLimitReached; - -        // // We couldn't reserve a session. Check that we support dynamically expanding the -        // // resource limit. -        // R_UNLESS(process.GetResourceLimit() == -        //          &system.Kernel().GetSystemResourceLimit(), ResultLimitReached); -        // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached()); - -        // // Try to allocate a session from unused slab memory. -        // session = T::CreateFromUnusedSlabMemory(); -        // R_UNLESS(session != nullptr, ResultLimitReached); -        // ON_RESULT_FAILURE { session->Close(); }; - -        // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to -        // // prevent request exhaustion. -        // // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's -        // // no reason to not do this statically. -        // if constexpr (std::same_as<T, KSession>) { -        //     for (size_t i = 0; i < 2; i++) { -        //         KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory(); -        //         R_UNLESS(request != nullptr, ResultLimitReached); -        //         request->Close(); -        //     } -        // } - -        // We successfully allocated a session, so add the object we allocated to the resource -        // limit. -        // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1); -    } - -    // Check that we successfully created a session. -    R_UNLESS(session != nullptr, ResultOutOfResource); - -    // Initialize the session. -    session->Initialize(nullptr, fmt::format("{}", name)); - -    // Commit the session reservation. -    session_reservation.Commit(); - -    // Ensure that we clean up the session (and its only references are handle table) on function -    // end. -    SCOPE_EXIT({ -        session->GetClientSession().Close(); -        session->GetServerSession().Close(); -    }); - -    // Register the session. -    T::Register(system.Kernel(), session); - -    // Add the server session to the handle table. -    R_TRY(handle_table.Add(out_server, &session->GetServerSession())); - -    // Add the client session to the handle table. -    const auto result = handle_table.Add(out_client, &session->GetClientSession()); - -    if (!R_SUCCEEDED(result)) { -        // Ensure that we maintaing a clean handle state on exit. -        handle_table.Remove(*out_server); -    } - -    return result; -} - -static Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, -                            u32 is_light, u64 name) { -    if (is_light) { -        // return CreateSession<KLightSession>(system, out_server, out_client, name); -        return ResultUnknown; -    } else { -        return CreateSession<KSession>(system, out_server, out_client, name); -    } -} - -/// Connect to an OS service given the port name, returns the handle to the port to out -static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) { -    auto& memory = system.Memory(); -    if (!memory.IsValidVirtualAddress(port_name_address)) { -        LOG_ERROR(Kernel_SVC, -                  "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", -                  port_name_address); -        return ResultNotFound; -    } - -    static constexpr std::size_t PortNameMaxLength = 11; -    // Read 1 char beyond the max allowed port name to detect names that are too long. -    const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1); -    if (port_name.size() > PortNameMaxLength) { -        LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength, -                  port_name.size()); -        return ResultOutOfRange; -    } - -    LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); - -    // Get the current handle table. -    auto& kernel = system.Kernel(); -    auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); - -    // Find the client port. -    auto port = kernel.CreateNamedServicePort(port_name); -    if (!port) { -        LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name); -        return ResultNotFound; -    } - -    // Reserve a handle for the port. -    // NOTE: Nintendo really does write directly to the output handle here. -    R_TRY(handle_table.Reserve(out)); -    auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); }); - -    // Create a session. -    KClientSession* session{}; -    R_TRY(port->CreateSession(std::addressof(session))); - -    kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort()); - -    // Register the session in the table, close the extra reference. -    handle_table.Register(*out, session); -    session->Close(); - -    // We succeeded. -    handle_guard.Cancel(); -    return ResultSuccess; -} - -static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, -                                   u32 port_name_address) { - -    return ConnectToNamedPort(system, out_handle, port_name_address); -} - -/// Makes a blocking IPC call to a service. -static Result SendSyncRequest(Core::System& system, Handle handle) { -    auto& kernel = system.Kernel(); - -    // Create the wait queue. -    KThreadQueue wait_queue(kernel); - -    // Get the client session from its handle. -    KScopedAutoObject session = -        kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle); -    R_UNLESS(session.IsNotNull(), ResultInvalidHandle); - -    LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); - -    return session->SendSyncRequest(); -} - -static Result SendSyncRequest32(Core::System& system, Handle handle) { -    return SendSyncRequest(system, handle); -} - -static Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles, -                              s32 num_handles, Handle reply_target, s64 timeout_ns) { -    auto& kernel = system.Kernel(); -    auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable(); - -    // Convert handle list to object table. -    std::vector<KSynchronizationObject*> objs(num_handles); -    R_UNLESS( -        handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, num_handles), -        ResultInvalidHandle); - -    // Ensure handles are closed when we're done. -    SCOPE_EXIT({ -        for (auto i = 0; i < num_handles; ++i) { -            objs[i]->Close(); -        } -    }); - -    // Reply to the target, if one is specified. -    if (reply_target != InvalidHandle) { -        KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target); -        R_UNLESS(session.IsNotNull(), ResultInvalidHandle); - -        // If we fail to reply, we want to set the output index to -1. -        // ON_RESULT_FAILURE { *out_index = -1; }; - -        // Send the reply. -        // R_TRY(session->SendReply()); - -        Result rc = session->SendReply(); -        if (!R_SUCCEEDED(rc)) { -            *out_index = -1; -            return rc; -        } -    } - -    // Wait for a message. -    while (true) { -        // Wait for an object. -        s32 index; -        Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(), -                                                     static_cast<s32>(objs.size()), timeout_ns); -        if (result == ResultTimedOut) { -            return result; -        } - -        // Receive the request. -        if (R_SUCCEEDED(result)) { -            KServerSession* session = objs[index]->DynamicCast<KServerSession*>(); -            if (session != nullptr) { -                result = session->ReceiveRequest(); -                if (result == ResultNotFound) { -                    continue; -                } -            } -        } - -        *out_index = index; -        return result; -    } -} - -/// Get the ID for the specified thread. -static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { -    // Get the thread from its handle. -    KScopedAutoObject thread = -        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); -    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); - -    // Get the thread's id. -    *out_thread_id = thread->GetId(); -    return ResultSuccess; -} - -static Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high, -                            Handle thread_handle) { -    u64 out_thread_id{}; -    const Result result{GetThreadId(system, &out_thread_id, thread_handle)}; - -    *out_thread_id_low = static_cast<u32>(out_thread_id >> 32); -    *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max()); - -    return result; -} - -/// Gets the ID of the specified process or a specified thread's owning process. -static Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) { -    LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle); - -    // Get the object from the handle table. -    KScopedAutoObject obj = -        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KAutoObject>( -            static_cast<Handle>(handle)); -    R_UNLESS(obj.IsNotNull(), ResultInvalidHandle); - -    // Get the process from the object. -    KProcess* process = nullptr; -    if (KProcess* p = obj->DynamicCast<KProcess*>(); p != nullptr) { -        // The object is a process, so we can use it directly. -        process = p; -    } else if (KThread* t = obj->DynamicCast<KThread*>(); t != nullptr) { -        // The object is a thread, so we want to use its parent. -        process = reinterpret_cast<KThread*>(obj.GetPointerUnsafe())->GetOwnerProcess(); -    } else { -        // TODO(bunnei): This should also handle debug objects before returning. -        UNIMPLEMENTED_MSG("Debug objects not implemented"); -    } - -    // Make sure the target process exists. -    R_UNLESS(process != nullptr, ResultInvalidHandle); - -    // Get the process id. -    *out_process_id = process->GetId(); - -    return ResultSuccess; -} - -static Result GetProcessId32(Core::System& system, u32* out_process_id_low, -                             u32* out_process_id_high, Handle handle) { -    u64 out_process_id{}; -    const auto result = GetProcessId(system, &out_process_id, handle); -    *out_process_id_low = static_cast<u32>(out_process_id); -    *out_process_id_high = static_cast<u32>(out_process_id >> 32); -    return result; -} - -/// Wait for the given handles to synchronize, timeout after the specified nanoseconds -static Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, -                                  s32 num_handles, s64 nano_seconds) { -    LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}", -              handles_address, num_handles, nano_seconds); - -    // Ensure number of handles is valid. -    R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange); - -    auto& kernel = system.Kernel(); -    std::vector<KSynchronizationObject*> objs(num_handles); -    const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); -    Handle* handles = system.Memory().GetPointer<Handle>(handles_address); - -    // Copy user handles. -    if (num_handles > 0) { -        // Convert the handles to objects. -        R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, -                                                                         num_handles), -                 ResultInvalidHandle); -        for (const auto& obj : objs) { -            kernel.RegisterInUseObject(obj); -        } -    } - -    // Ensure handles are closed when we're done. -    SCOPE_EXIT({ -        for (s32 i = 0; i < num_handles; ++i) { -            kernel.UnregisterInUseObject(objs[i]); -            objs[i]->Close(); -        } -    }); - -    return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast<s32>(objs.size()), -                                        nano_seconds); -} - -static Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, -                                    s32 num_handles, u32 timeout_high, s32* index) { -    const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)}; -    return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds); -} - -/// Resumes a thread waiting on WaitSynchronization -static Result CancelSynchronization(Core::System& system, Handle handle) { -    LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle); - -    // Get the thread from its handle. -    KScopedAutoObject thread = -        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle); -    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); - -    // Cancel the thread's wait. -    thread->WaitCancel(); -    return ResultSuccess; -} - -static Result CancelSynchronization32(Core::System& system, Handle handle) { -    return CancelSynchronization(system, handle); -} - -/// Attempts to locks a mutex -static Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) { -    LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}", -              thread_handle, address, tag); - -    // Validate the input address. -    if (IsKernelAddress(address)) { -        LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})", -                  address); -        return ResultInvalidCurrentMemory; -    } -    if (!Common::IsAligned(address, sizeof(u32))) { -        LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address); -        return ResultInvalidAddress; -    } - -    return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag); -} - -static Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) { -    return ArbitrateLock(system, thread_handle, address, tag); -} - -/// Unlock a mutex -static Result ArbitrateUnlock(Core::System& system, VAddr address) { -    LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address); - -    // Validate the input address. -    if (IsKernelAddress(address)) { -        LOG_ERROR(Kernel_SVC, -                  "Attempting to arbitrate an unlock on a kernel address (address={:08X})", -                  address); -        return ResultInvalidCurrentMemory; -    } -    if (!Common::IsAligned(address, sizeof(u32))) { -        LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address); -        return ResultInvalidAddress; -    } - -    return system.Kernel().CurrentProcess()->SignalToAddress(address); -} - -static Result ArbitrateUnlock32(Core::System& system, u32 address) { -    return ArbitrateUnlock(system, address); -} - -/// Break program execution -static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { -    BreakReason break_reason = -        static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag)); -    bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0; - -    bool has_dumped_buffer{}; -    std::vector<u8> debug_buffer; - -    const auto handle_debug_buffer = [&](VAddr addr, u64 sz) { -        if (sz == 0 || addr == 0 || has_dumped_buffer) { -            return; -        } - -        auto& memory = system.Memory(); - -        // This typically is an error code so we're going to assume this is the case -        if (sz == sizeof(u32)) { -            LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr)); -        } else { -            // We don't know what's in here so we'll hexdump it -            debug_buffer.resize(sz); -            memory.ReadBlock(addr, debug_buffer.data(), sz); -            std::string hexdump; -            for (std::size_t i = 0; i < debug_buffer.size(); i++) { -                hexdump += fmt::format("{:02X} ", debug_buffer[i]); -                if (i != 0 && i % 16 == 0) { -                    hexdump += '\n'; -                } -            } -            LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump); -        } -        has_dumped_buffer = true; -    }; -    switch (break_reason) { -    case BreakReason::Panic: -        LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1, -                     info2); -        handle_debug_buffer(info1, info2); -        break; -    case BreakReason::Assert: -        LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}", -                     info1, info2); -        handle_debug_buffer(info1, info2); -        break; -    case BreakReason::User: -        LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2); -        handle_debug_buffer(info1, info2); -        break; -    case BreakReason::PreLoadDll: -        LOG_INFO(Debug_Emulated, -                 "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1, -                 info2); -        break; -    case BreakReason::PostLoadDll: -        LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, -                 info2); -        break; -    case BreakReason::PreUnloadDll: -        LOG_INFO(Debug_Emulated, -                 "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1, -                 info2); -        break; -    case BreakReason::PostUnloadDll: -        LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}", -                 info1, info2); -        break; -    case BreakReason::CppException: -        LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered."); -        break; -    default: -        LOG_WARNING( -            Debug_Emulated, -            "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}", -            reason, info1, info2); -        handle_debug_buffer(info1, info2); -        break; -    } - -    system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2, -                                            has_dumped_buffer ? std::make_optional(debug_buffer) -                                                              : std::nullopt); - -    if (!notification_only) { -        LOG_CRITICAL( -            Debug_Emulated, -            "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", -            reason, info1, info2); - -        handle_debug_buffer(info1, info2); - -        auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); -        const auto thread_processor_id = current_thread->GetActiveCore(); -        system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); -    } - -    if (system.DebuggerEnabled()) { -        auto* thread = system.Kernel().GetCurrentEmuThread(); -        system.GetDebugger().NotifyThreadStopped(thread); -        thread->RequestSuspend(Kernel::SuspendType::Debug); -    } -} - -static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) { -    Break(system, reason, info1, info2); -} - -/// Used to output a message on a debug hardware unit - does nothing on a retail unit -static void OutputDebugString(Core::System& system, VAddr address, u64 len) { -    if (len == 0) { -        return; -    } - -    std::string str(len, '\0'); -    system.Memory().ReadBlock(address, str.data(), str.size()); -    LOG_DEBUG(Debug_Emulated, "{}", str); -} - -static void OutputDebugString32(Core::System& system, u32 address, u32 len) { -    OutputDebugString(system, address, len); -} - -/// Gets system/memory information for the current process -static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, -                      u64 info_sub_id) { -    LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, -              info_sub_id, handle); - -    const auto info_id_type = static_cast<InfoType>(info_id); - -    switch (info_id_type) { -    case InfoType::CoreMask: -    case InfoType::PriorityMask: -    case InfoType::AliasRegionAddress: -    case InfoType::AliasRegionSize: -    case InfoType::HeapRegionAddress: -    case InfoType::HeapRegionSize: -    case InfoType::AslrRegionAddress: -    case InfoType::AslrRegionSize: -    case InfoType::StackRegionAddress: -    case InfoType::StackRegionSize: -    case InfoType::TotalMemorySize: -    case InfoType::UsedMemorySize: -    case InfoType::SystemResourceSizeTotal: -    case InfoType::SystemResourceSizeUsed: -    case InfoType::ProgramId: -    case InfoType::UserExceptionContextAddress: -    case InfoType::TotalNonSystemMemorySize: -    case InfoType::UsedNonSystemMemorySize: -    case InfoType::IsApplication: -    case InfoType::FreeThreadCount: { -        if (info_sub_id != 0) { -            LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, -                      info_sub_id); -            return ResultInvalidEnumValue; -        } - -        const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); -        KScopedAutoObject process = handle_table.GetObject<KProcess>(handle); -        if (process.IsNull()) { -            LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}", -                      info_id, info_sub_id, handle); -            return ResultInvalidHandle; -        } - -        switch (info_id_type) { -        case InfoType::CoreMask: -            *result = process->GetCoreMask(); -            return ResultSuccess; - -        case InfoType::PriorityMask: -            *result = process->GetPriorityMask(); -            return ResultSuccess; - -        case InfoType::AliasRegionAddress: -            *result = process->PageTable().GetAliasRegionStart(); -            return ResultSuccess; - -        case InfoType::AliasRegionSize: -            *result = process->PageTable().GetAliasRegionSize(); -            return ResultSuccess; - -        case InfoType::HeapRegionAddress: -            *result = process->PageTable().GetHeapRegionStart(); -            return ResultSuccess; - -        case InfoType::HeapRegionSize: -            *result = process->PageTable().GetHeapRegionSize(); -            return ResultSuccess; - -        case InfoType::AslrRegionAddress: -            *result = process->PageTable().GetAliasCodeRegionStart(); -            return ResultSuccess; - -        case InfoType::AslrRegionSize: -            *result = process->PageTable().GetAliasCodeRegionSize(); -            return ResultSuccess; - -        case InfoType::StackRegionAddress: -            *result = process->PageTable().GetStackRegionStart(); -            return ResultSuccess; - -        case InfoType::StackRegionSize: -            *result = process->PageTable().GetStackRegionSize(); -            return ResultSuccess; - -        case InfoType::TotalMemorySize: -            *result = process->GetTotalPhysicalMemoryAvailable(); -            return ResultSuccess; - -        case InfoType::UsedMemorySize: -            *result = process->GetTotalPhysicalMemoryUsed(); -            return ResultSuccess; - -        case InfoType::SystemResourceSizeTotal: -            *result = process->GetSystemResourceSize(); -            return ResultSuccess; - -        case InfoType::SystemResourceSizeUsed: -            LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); -            *result = process->GetSystemResourceUsage(); -            return ResultSuccess; - -        case InfoType::ProgramId: -            *result = process->GetProgramID(); -            return ResultSuccess; - -        case InfoType::UserExceptionContextAddress: -            *result = process->GetProcessLocalRegionAddress(); -            return ResultSuccess; - -        case InfoType::TotalNonSystemMemorySize: -            *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource(); -            return ResultSuccess; - -        case InfoType::UsedNonSystemMemorySize: -            *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource(); -            return ResultSuccess; - -        case InfoType::FreeThreadCount: -            *result = process->GetFreeThreadCount(); -            return ResultSuccess; - -        default: -            break; -        } - -        LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id); -        return ResultInvalidEnumValue; -    } - -    case InfoType::DebuggerAttached: -        *result = 0; -        return ResultSuccess; - -    case InfoType::ResourceLimit: { -        if (handle != 0) { -            LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle); -            return ResultInvalidHandle; -        } - -        if (info_sub_id != 0) { -            LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, -                      info_sub_id); -            return ResultInvalidCombination; -        } - -        KProcess* const current_process = system.Kernel().CurrentProcess(); -        KHandleTable& handle_table = current_process->GetHandleTable(); -        const auto resource_limit = current_process->GetResourceLimit(); -        if (!resource_limit) { -            *result = Svc::InvalidHandle; -            // Yes, the kernel considers this a successful operation. -            return ResultSuccess; -        } - -        Handle resource_handle{}; -        R_TRY(handle_table.Add(&resource_handle, resource_limit)); - -        *result = resource_handle; -        return ResultSuccess; -    } - -    case InfoType::RandomEntropy: -        if (handle != 0) { -            LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}", -                      handle); -            return ResultInvalidHandle; -        } - -        if (info_sub_id >= KProcess::RANDOM_ENTROPY_SIZE) { -            LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}", -                      KProcess::RANDOM_ENTROPY_SIZE, info_sub_id); -            return ResultInvalidCombination; -        } - -        *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id); -        return ResultSuccess; - -    case InfoType::InitialProcessIdRange: -        LOG_WARNING(Kernel_SVC, -                    "(STUBBED) Attempted to query privileged process id bounds, returned 0"); -        *result = 0; -        return ResultSuccess; - -    case InfoType::ThreadTickCount: { -        constexpr u64 num_cpus = 4; -        if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { -            LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, -                      info_sub_id); -            return ResultInvalidCombination; -        } - -        KScopedAutoObject thread = -            system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>( -                static_cast<Handle>(handle)); -        if (thread.IsNull()) { -            LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", -                      static_cast<Handle>(handle)); -            return ResultInvalidHandle; -        } - -        const auto& core_timing = system.CoreTiming(); -        const auto& scheduler = *system.Kernel().CurrentScheduler(); -        const auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); -        const bool same_thread = current_thread == thread.GetPointerUnsafe(); - -        const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime(); -        u64 out_ticks = 0; -        if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { -            const u64 thread_ticks = current_thread->GetCpuTime(); - -            out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); -        } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) { -            out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; -        } - -        *result = out_ticks; -        return ResultSuccess; -    } -    case InfoType::IdleTickCount: { -        // Verify the input handle is invalid. -        R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); - -        // Verify the requested core is valid. -        const bool core_valid = -            (info_sub_id == 0xFFFFFFFFFFFFFFFF) || -            (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex())); -        R_UNLESS(core_valid, ResultInvalidCombination); - -        // Get the idle tick count. -        *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); -        return ResultSuccess; -    } -    case InfoType::MesosphereCurrentProcess: { -        // Verify the input handle is invalid. -        R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); - -        // Verify the sub-type is valid. -        R_UNLESS(info_sub_id == 0, ResultInvalidCombination); - -        // Get the handle table. -        KProcess* current_process = system.Kernel().CurrentProcess(); -        KHandleTable& handle_table = current_process->GetHandleTable(); - -        // Get a new handle for the current process. -        Handle tmp; -        R_TRY(handle_table.Add(&tmp, current_process)); - -        // Set the output. -        *result = tmp; - -        // We succeeded. -        return ResultSuccess; -    } -    default: -        LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id); -        return ResultInvalidEnumValue; -    } -} - -static Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low, -                        u32 info_id, u32 handle, u32 sub_id_high) { -    const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)}; -    u64 res_value{}; - -    const Result result{GetInfo(system, &res_value, info_id, handle, sub_id)}; -    *result_high = static_cast<u32>(res_value >> 32); -    *result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max()); - -    return result; -} - -/// Maps memory at a desired address -static Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { -    LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); - -    if (!Common::Is4KBAligned(addr)) { -        LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); -        return ResultInvalidAddress; -    } - -    if (!Common::Is4KBAligned(size)) { -        LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); -        return ResultInvalidSize; -    } - -    if (size == 0) { -        LOG_ERROR(Kernel_SVC, "Size is zero"); -        return ResultInvalidSize; -    } - -    if (!(addr < addr + size)) { -        LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); -        return ResultInvalidMemoryRegion; -    } - -    KProcess* const current_process{system.Kernel().CurrentProcess()}; -    auto& page_table{current_process->PageTable()}; - -    if (current_process->GetSystemResourceSize() == 0) { -        LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); -        return ResultInvalidState; -    } - -    if (!page_table.IsInsideAddressSpace(addr, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, -                  size); -        return ResultInvalidMemoryRegion; -    } - -    if (page_table.IsOutsideAliasRegion(addr, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, -                  size); -        return ResultInvalidMemoryRegion; -    } - -    return page_table.MapPhysicalMemory(addr, size); -} - -static Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { -    return MapPhysicalMemory(system, addr, size); -} - -/// Unmaps memory previously mapped via MapPhysicalMemory -static Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { -    LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); - -    if (!Common::Is4KBAligned(addr)) { -        LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); -        return ResultInvalidAddress; -    } - -    if (!Common::Is4KBAligned(size)) { -        LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); -        return ResultInvalidSize; -    } - -    if (size == 0) { -        LOG_ERROR(Kernel_SVC, "Size is zero"); -        return ResultInvalidSize; -    } - -    if (!(addr < addr + size)) { -        LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); -        return ResultInvalidMemoryRegion; -    } - -    KProcess* const current_process{system.Kernel().CurrentProcess()}; -    auto& page_table{current_process->PageTable()}; - -    if (current_process->GetSystemResourceSize() == 0) { -        LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); -        return ResultInvalidState; -    } - -    if (!page_table.IsInsideAddressSpace(addr, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, -                  size); -        return ResultInvalidMemoryRegion; -    } - -    if (page_table.IsOutsideAliasRegion(addr, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, -                  size); -        return ResultInvalidMemoryRegion; -    } - -    return page_table.UnmapPhysicalMemory(addr, size); -} - -static Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { -    return UnmapPhysicalMemory(system, addr, size); -} - -/// Sets the thread activity -static Result SetThreadActivity(Core::System& system, Handle thread_handle, -                                ThreadActivity thread_activity) { -    LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle, -              thread_activity); - -    // Validate the activity. -    constexpr auto IsValidThreadActivity = [](ThreadActivity activity) { -        return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused; -    }; -    R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue); - -    // Get the thread from its handle. -    KScopedAutoObject thread = -        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); -    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); - -    // Check that the activity is being set on a non-current thread for the current process. -    R_UNLESS(thread->GetOwnerProcess() == system.Kernel().CurrentProcess(), ResultInvalidHandle); -    R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy); - -    // Set the activity. -    R_TRY(thread->SetActivity(thread_activity)); - -    return ResultSuccess; -} - -static Result SetThreadActivity32(Core::System& system, Handle thread_handle, -                                  Svc::ThreadActivity thread_activity) { -    return SetThreadActivity(system, thread_handle, thread_activity); -} - -/// Gets the thread context -static Result GetThreadContext(Core::System& system, VAddr 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 = -        kernel.CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); -    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); - -    // Require the handle be to a non-current thread in the current process. -    const auto* current_process = kernel.CurrentProcess(); -    R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId); - -    // Verify that the thread isn't terminated. -    R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested); - -    /// Check that the thread is not the current one. -    /// NOTE: Nintendo does not check this, and thus the following loop will deadlock. -    R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId); - -    // Try to get the thread context until the thread isn't current on any core. -    while (true) { -        KScopedSchedulerLock sl{kernel}; - -        // TODO(bunnei): Enforce that thread is suspended for debug here. - -        // If the thread's raw state isn't runnable, check if it's current on some core. -        if (thread->GetRawState() != ThreadState::Runnable) { -            bool current = false; -            for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { -                if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) { -                    current = true; -                    break; -                } -            } - -            // If the thread is current, retry until it isn't. -            if (current) { -                continue; -            } -        } - -        // Get the thread context. -        std::vector<u8> context; -        R_TRY(thread->GetThreadContext3(context)); - -        // Copy the thread context to user space. -        system.Memory().WriteBlock(out_context, context.data(), context.size()); - -        return ResultSuccess; -    } - -    return ResultSuccess; -} - -static Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) { -    return GetThreadContext(system, out_context, thread_handle); -} - -/// Gets the priority for the specified thread -static Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) { -    LOG_TRACE(Kernel_SVC, "called"); - -    // Get the thread from its handle. -    KScopedAutoObject thread = -        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle); -    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); - -    // Get the thread's priority. -    *out_priority = thread->GetPriority(); -    return ResultSuccess; -} - -static Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) { -    return GetThreadPriority(system, out_priority, handle); -} - -/// Sets the priority for the specified thread -static Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) { -    // Get the current process. -    KProcess& process = *system.Kernel().CurrentProcess(); - -    // 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); -    return ResultSuccess; -} - -static Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) { -    return SetThreadPriority(system, thread_handle, priority); -} - -/// Get which CPU core is executing the current thread -static u32 GetCurrentProcessorNumber(Core::System& system) { -    LOG_TRACE(Kernel_SVC, "called"); -    return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex()); -} - -static u32 GetCurrentProcessorNumber32(Core::System& system) { -    return GetCurrentProcessorNumber(system); -} - -namespace { - -constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) { -    switch (perm) { -    case Svc::MemoryPermission::Read: -    case Svc::MemoryPermission::ReadWrite: -        return true; -    default: -        return false; -    } -} - -[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) { -    return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare; -} - -constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) { -    switch (perm) { -    case Svc::MemoryPermission::None: -    case Svc::MemoryPermission::Read: -    case Svc::MemoryPermission::ReadWrite: -    case Svc::MemoryPermission::ReadExecute: -        return true; -    default: -        return false; -    } -} - -constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) { -    return perm == Svc::MemoryPermission::ReadWrite; -} - -constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) { -    return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute; -} - -constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) { -    return perm == Svc::MemoryPermission::None; -} - -constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) { -    return perm == Svc::MemoryPermission::None; -} - -} // Anonymous namespace - -static Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr 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 = *system.Kernel().CurrentProcess(); -    auto& page_table = process.PageTable(); - -    // 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. -    auto guard = -        SCOPE_GUARD({ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); }); - -    // Map the shared memory. -    R_TRY(shmem->Map(process, address, size, map_perm)); - -    // We succeeded. -    guard.Cancel(); -    return ResultSuccess; -} - -static Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size, -                                Svc::MemoryPermission map_perm) { -    return MapSharedMemory(system, shmem_handle, address, size, map_perm); -} - -static Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr 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 = *system.Kernel().CurrentProcess(); -    auto& page_table = process.PageTable(); - -    // 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); - -    return ResultSuccess; -} - -static Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, -                                  u32 size) { -    return UnmapSharedMemory(system, shmem_handle, address, size); -} - -static Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address, -                                         u64 size, Svc::MemoryPermission perm) { -    LOG_TRACE(Kernel_SVC, -              "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", -              process_handle, address, size, 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); -    R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory); -    R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory); - -    // Validate the memory permission. -    R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission); - -    // Get the process from its handle. -    KScopedAutoObject process = -        system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle); -    R_UNLESS(process.IsNotNull(), ResultInvalidHandle); - -    // Validate that the address is in range. -    auto& page_table = process->PageTable(); -    R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); - -    // Set the memory permission. -    return page_table.SetProcessMemoryPermission(address, size, perm); -} - -static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, -                               VAddr src_address, u64 size) { -    LOG_TRACE(Kernel_SVC, -              "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", -              dst_address, process_handle, src_address, size); - -    // Validate the address/size. -    R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); -    R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); -    R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); -    R_UNLESS(size > 0, ResultInvalidSize); -    R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); -    R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); - -    // Get the processes. -    KProcess* dst_process = system.CurrentProcess(); -    KScopedAutoObject src_process = -        dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); -    R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); - -    // Get the page tables. -    auto& dst_pt = dst_process->PageTable(); -    auto& src_pt = src_process->PageTable(); - -    // Validate that the mapping is in range. -    R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); -    R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), -             ResultInvalidMemoryRegion); - -    // Create a new page group. -    KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()}; -    R_TRY(src_pt.MakeAndOpenPageGroup( -        std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess, -        KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None, -        KMemoryAttribute::All, KMemoryAttribute::None)); - -    // Map the group. -    R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode, -                              KMemoryPermission::UserReadWrite)); - -    return ResultSuccess; -} - -static Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, -                                 VAddr src_address, u64 size) { -    LOG_TRACE(Kernel_SVC, -              "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", -              dst_address, process_handle, src_address, size); - -    // Validate the address/size. -    R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); -    R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); -    R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); -    R_UNLESS(size > 0, ResultInvalidSize); -    R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); -    R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); - -    // Get the processes. -    KProcess* dst_process = system.CurrentProcess(); -    KScopedAutoObject src_process = -        dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); -    R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); - -    // Get the page tables. -    auto& dst_pt = dst_process->PageTable(); -    auto& src_pt = src_process->PageTable(); - -    // Validate that the mapping is in range. -    R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); -    R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), -             ResultInvalidMemoryRegion); - -    // Unmap the memory. -    R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address)); - -    return ResultSuccess; -} - -static Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { -    LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size); - -    // Get kernel instance. -    auto& kernel = system.Kernel(); - -    // Validate 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); - -    // Create the code memory. - -    KCodeMemory* code_mem = KCodeMemory::Create(kernel); -    R_UNLESS(code_mem != nullptr, ResultOutOfResource); - -    // Verify that the region is in range. -    R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size), -             ResultInvalidCurrentMemory); - -    // Initialize the code memory. -    R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size)); - -    // Register the code memory. -    KCodeMemory::Register(kernel, code_mem); - -    // Add the code memory to the handle table. -    R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem)); - -    code_mem->Close(); - -    return ResultSuccess; -} - -static Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) { -    return CreateCodeMemory(system, out, address, size); -} - -static Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, -                                VAddr address, size_t size, Svc::MemoryPermission perm) { - -    LOG_TRACE(Kernel_SVC, -              "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, " -              "permission=0x{:X}", -              code_memory_handle, operation, address, size, 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); - -    // Get the code memory from its handle. -    KScopedAutoObject code_mem = -        system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle); -    R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle); - -    // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process. -    // This enables homebrew usage of these SVCs for JIT. - -    // Perform the operation. -    switch (static_cast<CodeMemoryOperation>(operation)) { -    case CodeMemoryOperation::Map: { -        // Check that the region is in range. -        R_UNLESS( -            system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), -            ResultInvalidMemoryRegion); - -        // Check the memory permission. -        R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); - -        // Map the memory. -        R_TRY(code_mem->Map(address, size)); -    } break; -    case CodeMemoryOperation::Unmap: { -        // Check that the region is in range. -        R_UNLESS( -            system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), -            ResultInvalidMemoryRegion); - -        // Check the memory permission. -        R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); - -        // Unmap the memory. -        R_TRY(code_mem->Unmap(address, size)); -    } break; -    case CodeMemoryOperation::MapToOwner: { -        // Check that the region is in range. -        R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, -                                                              KMemoryState::GeneratedCode), -                 ResultInvalidMemoryRegion); - -        // Check the memory permission. -        R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); - -        // Map the memory to its owner. -        R_TRY(code_mem->MapToOwner(address, size, perm)); -    } break; -    case CodeMemoryOperation::UnmapFromOwner: { -        // Check that the region is in range. -        R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, -                                                              KMemoryState::GeneratedCode), -                 ResultInvalidMemoryRegion); - -        // Check the memory permission. -        R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); - -        // Unmap the memory from its owner. -        R_TRY(code_mem->UnmapFromOwner(address, size)); -    } break; -    default: -        return ResultInvalidEnumValue; -    } - -    return ResultSuccess; -} - -static Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation, -                                  u64 address, u64 size, Svc::MemoryPermission perm) { -    return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm); -} - -static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address, -                                 VAddr page_info_address, Handle process_handle, VAddr address) { -    LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); -    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); -    KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); -    if (process.IsNull()) { -        LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", -                  process_handle); -        return ResultInvalidHandle; -    } - -    auto& memory{system.Memory()}; -    const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; - -    memory.Write64(memory_info_address + 0x00, memory_info.base_address); -    memory.Write64(memory_info_address + 0x08, memory_info.size); -    memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff); -    memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute)); -    memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission)); -    memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count); -    memory.Write32(memory_info_address + 0x20, memory_info.device_count); -    memory.Write32(memory_info_address + 0x24, 0); - -    // Page info appears to be currently unused by the kernel and is always set to zero. -    memory.Write32(page_info_address, 0); - -    return ResultSuccess; -} - -static Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, -                          VAddr query_address) { -    LOG_TRACE(Kernel_SVC, -              "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " -              "query_address=0x{:016X}", -              memory_info_address, page_info_address, query_address); - -    return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess, -                              query_address); -} - -static Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address, -                            u32 query_address) { -    return QueryMemory(system, memory_info_address, page_info_address, query_address); -} - -static Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, -                                   u64 src_address, u64 size) { -    LOG_DEBUG(Kernel_SVC, -              "called. process_handle=0x{:08X}, dst_address=0x{:016X}, " -              "src_address=0x{:016X}, size=0x{:016X}", -              process_handle, dst_address, src_address, size); - -    if (!Common::Is4KBAligned(src_address)) { -        LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", -                  src_address); -        return ResultInvalidAddress; -    } - -    if (!Common::Is4KBAligned(dst_address)) { -        LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", -                  dst_address); -        return ResultInvalidAddress; -    } - -    if (size == 0 || !Common::Is4KBAligned(size)) { -        LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size); -        return ResultInvalidSize; -    } - -    if (!IsValidAddressRange(dst_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Destination address range overflows the address space (dst_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  dst_address, size); -        return ResultInvalidCurrentMemory; -    } - -    if (!IsValidAddressRange(src_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Source address range overflows the address space (src_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  src_address, size); -        return ResultInvalidCurrentMemory; -    } - -    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); -    KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); -    if (process.IsNull()) { -        LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", -                  process_handle); -        return ResultInvalidHandle; -    } - -    auto& page_table = process->PageTable(); -    if (!page_table.IsInsideAddressSpace(src_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Source address range is not within the address space (src_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  src_address, size); -        return ResultInvalidCurrentMemory; -    } - -    if (!page_table.IsInsideASLRRegion(dst_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  dst_address, size); -        return ResultInvalidMemoryRegion; -    } - -    return page_table.MapCodeMemory(dst_address, src_address, size); -} - -static Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, -                                     u64 src_address, u64 size) { -    LOG_DEBUG(Kernel_SVC, -              "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " -              "size=0x{:016X}", -              process_handle, dst_address, src_address, size); - -    if (!Common::Is4KBAligned(dst_address)) { -        LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", -                  dst_address); -        return ResultInvalidAddress; -    } - -    if (!Common::Is4KBAligned(src_address)) { -        LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", -                  src_address); -        return ResultInvalidAddress; -    } - -    if (size == 0 || !Common::Is4KBAligned(size)) { -        LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); -        return ResultInvalidSize; -    } - -    if (!IsValidAddressRange(dst_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Destination address range overflows the address space (dst_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  dst_address, size); -        return ResultInvalidCurrentMemory; -    } - -    if (!IsValidAddressRange(src_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Source address range overflows the address space (src_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  src_address, size); -        return ResultInvalidCurrentMemory; -    } - -    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); -    KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); -    if (process.IsNull()) { -        LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", -                  process_handle); -        return ResultInvalidHandle; -    } - -    auto& page_table = process->PageTable(); -    if (!page_table.IsInsideAddressSpace(src_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Source address range is not within the address space (src_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  src_address, size); -        return ResultInvalidCurrentMemory; -    } - -    if (!page_table.IsInsideASLRRegion(dst_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  dst_address, size); -        return ResultInvalidMemoryRegion; -    } - -    return page_table.UnmapCodeMemory(dst_address, src_address, size, -                                      KPageTable::ICacheInvalidationStrategy::InvalidateAll); -} - -/// Exits the current process -static void ExitProcess(Core::System& system) { -    auto* current_process = system.Kernel().CurrentProcess(); - -    LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); -    ASSERT_MSG(current_process->GetState() == KProcess::State::Running, -               "Process has already exited"); - -    system.Exit(); -} - -static void ExitProcess32(Core::System& system) { -    ExitProcess(system); -} - -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 -static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, -                           VAddr stack_bottom, u32 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 = *kernel.CurrentProcess(); -    if (core_id == IdealCoreUseProcessValue) { -        core_id = process.GetIdealCoreId(); -    } - -    // Validate arguments. -    if (!IsValidVirtualCoreId(core_id)) { -        LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id); -        return ResultInvalidCoreId; -    } -    if (((1ULL << core_id) & process.GetCoreMask()) == 0) { -        LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id); -        return ResultInvalidCoreId; -    } - -    if (HighestThreadPriority > priority || priority > LowestThreadPriority) { -        LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority); -        return ResultInvalidPriority; -    } -    if (!process.CheckThreadPriority(priority)) { -        LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority); -        return ResultInvalidPriority; -    } - -    // Reserve a new thread from the process resource limit (waiting up to 100ms). -    KScopedResourceReservation thread_reservation( -        kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1, -        system.CoreTiming().GetGlobalTimeNs().count() + 100000000); -    if (!thread_reservation.Succeeded()) { -        LOG_ERROR(Kernel_SVC, "Could not reserve a new thread"); -        return ResultLimitReached; -    } - -    // Create the thread. -    KThread* thread = KThread::Create(kernel); -    if (!thread) { -        LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached."); -        return 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, &process)); -    } - -    // Set the thread name for debugging purposes. -    thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle)); - -    // Commit the thread reservation. -    thread_reservation.Commit(); - -    // Register the new thread. -    KThread::Register(kernel, thread); - -    // Add the thread to the handle table. -    R_TRY(process.GetHandleTable().Add(out_handle, thread)); - -    return ResultSuccess; -} - -static Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority, -                             u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) { -    return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id); -} - -/// Starts the thread for the provided handle -static 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 = -        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); -    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); - -    // Try to start the thread. -    R_TRY(thread->Run()); - -    // If we succeeded, persist a reference to the thread. -    thread->Open(); -    system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe()); - -    return ResultSuccess; -} - -static Result StartThread32(Core::System& system, Handle thread_handle) { -    return StartThread(system, thread_handle); -} - -/// Called when a thread exits -static void ExitThread(Core::System& system) { -    LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); - -    auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); -    system.GlobalSchedulerContext().RemoveThread(current_thread); -    current_thread->Exit(); -    system.Kernel().UnregisterInUseObject(current_thread); -} - -static void ExitThread32(Core::System& system) { -    ExitThread(system); -} - -/// Sleep the current thread -static void SleepThread(Core::System& system, s64 nanoseconds) { -    auto& kernel = system.Kernel(); -    const auto yield_type = static_cast<Svc::YieldType>(nanoseconds); - -    LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); - -    // When the input tick is positive, sleep. -    if (nanoseconds > 0) { -        // Convert the timeout from nanoseconds to ticks. -        // NOTE: Nintendo does not use this conversion logic in WaitSynchronization... - -        // Sleep. -        // NOTE: Nintendo does not check the result of this sleep. -        static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds)); -    } 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. -        ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds); -    } -} - -static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) { -    const auto nanoseconds = static_cast<s64>(u64{nanoseconds_low} | (u64{nanoseconds_high} << 32)); -    SleepThread(system, nanoseconds); -} - -/// Wait process wide key atomic -static Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag, -                                       s64 timeout_ns) { -    LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address, -              cv_key, tag, timeout_ns); - -    // Validate input. -    if (IsKernelAddress(address)) { -        LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address); -        return ResultInvalidCurrentMemory; -    } -    if (!Common::IsAligned(address, sizeof(s32))) { -        LOG_ERROR(Kernel_SVC, "Address must be 4 byte aligned (address={:08X})", address); -        return ResultInvalidAddress; -    } - -    // Convert timeout from nanoseconds to ticks. -    s64 timeout{}; -    if (timeout_ns > 0) { -        const s64 offset_tick(timeout_ns); -        if (offset_tick > 0) { -            timeout = offset_tick + 2; -            if (timeout <= 0) { -                timeout = std::numeric_limits<s64>::max(); -            } -        } else { -            timeout = std::numeric_limits<s64>::max(); -        } -    } else { -        timeout = timeout_ns; -    } - -    // Wait on the condition variable. -    return system.Kernel().CurrentProcess()->WaitConditionVariable( -        address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout); -} - -static Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, -                                         u32 timeout_ns_low, u32 timeout_ns_high) { -    const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); -    return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns); -} - -/// Signal process wide key -static void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) { -    LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count); - -    // Signal the condition variable. -    return system.Kernel().CurrentProcess()->SignalConditionVariable( -        Common::AlignDown(cv_key, sizeof(u32)), count); -} - -static void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) { -    SignalProcessWideKey(system, cv_key, count); -} - -namespace { - -constexpr bool IsValidSignalType(Svc::SignalType type) { -    switch (type) { -    case Svc::SignalType::Signal: -    case Svc::SignalType::SignalAndIncrementIfEqual: -    case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: -        return true; -    default: -        return false; -    } -} - -constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) { -    switch (type) { -    case Svc::ArbitrationType::WaitIfLessThan: -    case Svc::ArbitrationType::DecrementAndWaitIfLessThan: -    case Svc::ArbitrationType::WaitIfEqual: -        return true; -    default: -        return false; -    } -} - -} // namespace - -// Wait for an address (via Address Arbiter) -static Result WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type, -                             s32 value, s64 timeout_ns) { -    LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}", -              address, arb_type, value, timeout_ns); - -    // Validate input. -    if (IsKernelAddress(address)) { -        LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address); -        return ResultInvalidCurrentMemory; -    } -    if (!Common::IsAligned(address, sizeof(s32))) { -        LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address); -        return ResultInvalidAddress; -    } -    if (!IsValidArbitrationType(arb_type)) { -        LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type); -        return ResultInvalidEnumValue; -    } - -    // Convert timeout from nanoseconds to ticks. -    s64 timeout{}; -    if (timeout_ns > 0) { -        const s64 offset_tick(timeout_ns); -        if (offset_tick > 0) { -            timeout = offset_tick + 2; -            if (timeout <= 0) { -                timeout = std::numeric_limits<s64>::max(); -            } -        } else { -            timeout = std::numeric_limits<s64>::max(); -        } -    } else { -        timeout = timeout_ns; -    } - -    return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout); -} - -static Result WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type, -                               s32 value, u32 timeout_ns_low, u32 timeout_ns_high) { -    const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); -    return WaitForAddress(system, address, arb_type, value, timeout); -} - -// Signals to an address (via Address Arbiter) -static Result SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type, -                              s32 value, s32 count) { -    LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}", -              address, signal_type, value, count); - -    // Validate input. -    if (IsKernelAddress(address)) { -        LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address); -        return ResultInvalidCurrentMemory; -    } -    if (!Common::IsAligned(address, sizeof(s32))) { -        LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address); -        return ResultInvalidAddress; -    } -    if (!IsValidSignalType(signal_type)) { -        LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type); -        return ResultInvalidEnumValue; -    } - -    return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value, -                                                                  count); -} - -static 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 = system.Kernel().CurrentProcess(); -    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(core_id); -    } -} - -static Result SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type, -                                s32 value, s32 count) { -    return SignalToAddress(system, address, signal_type, value, count); -} - -static void KernelDebug([[maybe_unused]] Core::System& system, -                        [[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1, -                        [[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) { -    // Intentionally do nothing, as this does nothing in released kernel binaries. -} - -static void ChangeKernelTraceState([[maybe_unused]] Core::System& system, -                                   [[maybe_unused]] u32 trace_state) { -    // Intentionally do nothing, as this does nothing in released kernel binaries. -} - -/// This returns the total CPU ticks elapsed since the CPU was powered-on -static u64 GetSystemTick(Core::System& system) { -    LOG_TRACE(Kernel_SVC, "called"); - -    auto& core_timing = system.CoreTiming(); - -    // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) -    const u64 result{core_timing.GetClockTicks()}; - -    if (!system.Kernel().IsMulticore()) { -        core_timing.AddTicks(400U); -    } - -    return result; -} - -static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) { -    const auto time = GetSystemTick(system); -    *time_low = static_cast<u32>(time); -    *time_high = static_cast<u32>(time >> 32); -} - -/// Close a handle -static Result CloseHandle(Core::System& system, Handle handle) { -    LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); - -    // Remove the handle. -    R_UNLESS(system.Kernel().CurrentProcess()->GetHandleTable().Remove(handle), -             ResultInvalidHandle); - -    return ResultSuccess; -} - -static Result CloseHandle32(Core::System& system, Handle handle) { -    return CloseHandle(system, handle); -} - -/// Clears the signaled state of an event or process. -static 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 = system.Kernel().CurrentProcess()->GetHandleTable(); - -    // Try to reset as readable event. -    { -        KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle); -        if (readable_event.IsNotNull()) { -            return readable_event->Reset(); -        } -    } - -    // Try to reset as process. -    { -        KScopedAutoObject process = handle_table.GetObject<KProcess>(handle); -        if (process.IsNotNull()) { -            return process->Reset(); -        } -    } - -    LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle); - -    return ResultInvalidHandle; -} - -static Result ResetSignal32(Core::System& system, Handle handle) { -    return ResetSignal(system, handle); -} - -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 -static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr 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 = *kernel.CurrentProcess(); -    auto& handle_table = process.GetHandleTable(); - -    // Reserve a new transfer memory from the process resource limit. -    KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(), -                                                 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.PageTable().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_TRY(handle_table.Add(out, trmem)); - -    return ResultSuccess; -} - -static Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size, -                                     MemoryPermission map_perm) { -    return CreateTransferMemory(system, out, address, size, map_perm); -} - -static Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id, -                                u64* out_affinity_mask) { -    LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); - -    // Get the thread from its handle. -    KScopedAutoObject thread = -        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); -    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); - -    // Get the core mask. -    R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask)); - -    return ResultSuccess; -} - -static Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id, -                                  u32* out_affinity_mask_low, u32* out_affinity_mask_high) { -    u64 out_affinity_mask{}; -    const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask); -    *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32); -    *out_affinity_mask_low = static_cast<u32>(out_affinity_mask); -    return result; -} - -static 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 = system.Kernel().CurrentProcess()->GetIdealCoreId(); -        affinity_mask = (1ULL << core_id); -    } else { -        // Validate the affinity mask. -        const u64 process_core_mask = system.Kernel().CurrentProcess()->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 = -        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); -    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); - -    // Set the core mask. -    R_TRY(thread->SetCoreMask(core_id, affinity_mask)); - -    return ResultSuccess; -} - -static Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id, -                                  u32 affinity_mask_low, u32 affinity_mask_high) { -    const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32); -    return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask); -} - -static Result SignalEvent(Core::System& system, Handle event_handle) { -    LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); - -    // Get the current handle table. -    const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); - -    // Get the event. -    KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle); -    R_UNLESS(event.IsNotNull(), ResultInvalidHandle); - -    return event->Signal(); -} - -static Result SignalEvent32(Core::System& system, Handle event_handle) { -    return SignalEvent(system, event_handle); -} - -static Result ClearEvent(Core::System& system, Handle event_handle) { -    LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); - -    // Get the current handle table. -    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); - -    // Try to clear the writable event. -    { -        KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle); -        if (event.IsNotNull()) { -            return event->Clear(); -        } -    } - -    // Try to clear the readable event. -    { -        KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle); -        if (readable_event.IsNotNull()) { -            return readable_event->Clear(); -        } -    } - -    LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle); - -    return ResultInvalidHandle; -} - -static Result ClearEvent32(Core::System& system, Handle event_handle) { -    return ClearEvent(system, event_handle); -} - -static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) { -    LOG_DEBUG(Kernel_SVC, "called"); - -    // Get the kernel reference and handle table. -    auto& kernel = system.Kernel(); -    auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); - -    // Reserve a new event from the process resource limit -    KScopedResourceReservation event_reservation(kernel.CurrentProcess(), -                                                 LimitableResource::EventCountMax); -    R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); - -    // Create a new event. -    KEvent* event = KEvent::Create(kernel); -    R_UNLESS(event != nullptr, ResultOutOfResource); - -    // Initialize the event. -    event->Initialize(kernel.CurrentProcess()); - -    // Commit the thread reservation. -    event_reservation.Commit(); - -    // Ensure that we clean up the event (and its only references are handle table) on function end. -    SCOPE_EXIT({ -        event->GetReadableEvent().Close(); -        event->Close(); -    }); - -    // Register the event. -    KEvent::Register(kernel, event); - -    // Add the event to the handle table. -    R_TRY(handle_table.Add(out_write, event)); - -    // Ensure that we maintaing a clean handle state on exit. -    auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); }); - -    // Add the readable event to the handle table. -    R_TRY(handle_table.Add(out_read, std::addressof(event->GetReadableEvent()))); - -    // We succeeded. -    handle_guard.Cancel(); -    return ResultSuccess; -} - -static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) { -    return CreateEvent(system, out_write, out_read); -} - -static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { -    LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); - -    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); -    KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); -    if (process.IsNull()) { -        LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", -                  process_handle); -        return ResultInvalidHandle; -    } - -    const auto info_type = static_cast<ProcessInfoType>(type); -    if (info_type != ProcessInfoType::ProcessState) { -        LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type); -        return ResultInvalidEnumValue; -    } - -    *out = static_cast<u64>(process->GetState()); -    return ResultSuccess; -} - -static Result CreateResourceLimit(Core::System& system, Handle* out_handle) { -    LOG_DEBUG(Kernel_SVC, "called"); - -    // Create a new resource limit. -    auto& kernel = system.Kernel(); -    KResourceLimit* resource_limit = KResourceLimit::Create(kernel); -    R_UNLESS(resource_limit != nullptr, ResultOutOfResource); - -    // Ensure we don't leak a reference to the limit. -    SCOPE_EXIT({ resource_limit->Close(); }); - -    // Initialize the resource limit. -    resource_limit->Initialize(&system.CoreTiming()); - -    // Register the limit. -    KResourceLimit::Register(kernel, resource_limit); - -    // Add the limit to the handle table. -    R_TRY(kernel.CurrentProcess()->GetHandleTable().Add(out_handle, resource_limit)); - -    return ResultSuccess; -} - -static Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value, -                                         Handle resource_limit_handle, LimitableResource which) { -    LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle, -              which); - -    // Validate the resource. -    R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); - -    // Get the resource limit. -    auto& kernel = system.Kernel(); -    KScopedAutoObject resource_limit = -        kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle); -    R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); - -    // Get the limit value. -    *out_limit_value = resource_limit->GetLimitValue(which); - -    return ResultSuccess; -} - -static Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value, -                                           Handle resource_limit_handle, LimitableResource which) { -    LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle, -              which); - -    // Validate the resource. -    R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); - -    // Get the resource limit. -    auto& kernel = system.Kernel(); -    KScopedAutoObject resource_limit = -        kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle); -    R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); - -    // Get the current value. -    *out_current_value = resource_limit->GetCurrentValue(which); - -    return ResultSuccess; -} - -static Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle, -                                         LimitableResource which, u64 limit_value) { -    LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}", -              resource_limit_handle, which, limit_value); - -    // Validate the resource. -    R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); - -    // Get the resource limit. -    auto& kernel = system.Kernel(); -    KScopedAutoObject resource_limit = -        kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle); -    R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); - -    // Set the limit value. -    R_TRY(resource_limit->SetLimitValue(which, limit_value)); - -    return ResultSuccess; -} - -static Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids, -                             u32 out_process_ids_size) { -    LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", -              out_process_ids, out_process_ids_size); - -    // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail. -    if ((out_process_ids_size & 0xF0000000) != 0) { -        LOG_ERROR(Kernel_SVC, -                  "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}", -                  out_process_ids_size); -        return ResultOutOfRange; -    } - -    const auto& kernel = system.Kernel(); -    const auto total_copy_size = out_process_ids_size * sizeof(u64); - -    if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace( -                                        out_process_ids, total_copy_size)) { -        LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", -                  out_process_ids, out_process_ids + total_copy_size); -        return ResultInvalidCurrentMemory; -    } - -    auto& memory = system.Memory(); -    const auto& process_list = kernel.GetProcessList(); -    const auto num_processes = process_list.size(); -    const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes); - -    for (std::size_t i = 0; i < copy_amount; ++i) { -        memory.Write64(out_process_ids, process_list[i]->GetProcessID()); -        out_process_ids += sizeof(u64); -    } - -    *out_num_processes = static_cast<u32>(num_processes); -    return ResultSuccess; -} - -static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, -                            u32 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); -        return ResultOutOfRange; -    } - -    auto* const current_process = system.Kernel().CurrentProcess(); -    const auto total_copy_size = out_thread_ids_size * sizeof(u64); - -    if (out_thread_ids_size > 0 && -        !current_process->PageTable().IsInsideAddressSpace(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); -        return ResultInvalidCurrentMemory; -    } - -    auto& memory = system.Memory(); -    const auto& thread_list = current_process->GetThreadList(); -    const auto num_threads = thread_list.size(); -    const auto copy_amount = std::min(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); -    return ResultSuccess; -} - -static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, -                                      u64 size) { -    // Validate address/size. -    R_UNLESS(size > 0, ResultInvalidSize); -    R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory); -    R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory); - -    // Get the process from its handle. -    KScopedAutoObject process = -        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle); -    R_UNLESS(process.IsNotNull(), ResultInvalidHandle); - -    // Verify the region is within range. -    auto& page_table = process->PageTable(); -    R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); - -    // Perform the operation. -    R_RETURN(system.Memory().FlushDataCache(*process, address, size)); -} - -namespace {  struct FunctionDef {      using Func = void(Core::System&); @@ -2699,6 +18,7 @@ struct FunctionDef {      Func* func;      const char* name;  }; +  } // namespace  static const FunctionDef SVC_Table_32[] = { diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index 13f061b83..b599f9a3d 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h @@ -4,6 +4,8 @@  #pragma once  #include "common/common_types.h" +#include "core/hle/kernel/svc_types.h" +#include "core/hle/result.h"  namespace Core {  class System; @@ -13,4 +15,158 @@ namespace Kernel::Svc {  void Call(Core::System& system, u32 immediate); +Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size); +Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, MemoryPermission perm); +Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, u32 attr); +Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size); +Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size); +Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, +                   VAddr query_address); +void ExitProcess(Core::System& system); +Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, +                    VAddr stack_bottom, u32 priority, s32 core_id); +Result StartThread(Core::System& system, Handle thread_handle); +void ExitThread(Core::System& system); +void SleepThread(Core::System& system, s64 nanoseconds); +Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle); +Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority); +Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id, +                         u64* out_affinity_mask); +Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id, +                         u64 affinity_mask); +u32 GetCurrentProcessorNumber(Core::System& system); +Result SignalEvent(Core::System& system, Handle event_handle); +Result ClearEvent(Core::System& system, Handle event_handle); +Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size, +                       MemoryPermission map_perm); +Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size); +Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, +                            MemoryPermission map_perm); +Result CloseHandle(Core::System& system, Handle handle); +Result ResetSignal(Core::System& system, Handle handle); +Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, s32 num_handles, +                           s64 nano_seconds); +Result CancelSynchronization(Core::System& system, Handle handle); +Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag); +Result ArbitrateUnlock(Core::System& system, VAddr address); +Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag, +                                s64 timeout_ns); +void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count); +u64 GetSystemTick(Core::System& system); +Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address); +Result SendSyncRequest(Core::System& system, Handle handle); +Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle); +Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle); +void Break(Core::System& system, u32 reason, u64 info1, u64 info2); +void OutputDebugString(Core::System& system, VAddr address, u64 len); +Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, u64 info_sub_id); +Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size); +Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size); +Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value, +                                  Handle resource_limit_handle, LimitableResource which); +Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value, +                                    Handle resource_limit_handle, LimitableResource which); +Result SetThreadActivity(Core::System& system, Handle thread_handle, +                         ThreadActivity thread_activity); +Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle); +Result WaitForAddress(Core::System& system, VAddr address, ArbitrationType arb_type, s32 value, +                      s64 timeout_ns); +Result SignalToAddress(Core::System& system, VAddr address, SignalType signal_type, s32 value, +                       s32 count); +void SynchronizePreemptionState(Core::System& system); +void KernelDebug(Core::System& system, u32 kernel_debug_type, u64 param1, u64 param2, u64 param3); +void ChangeKernelTraceState(Core::System& system, u32 trace_state); +Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u32 is_light, +                     u64 name); +Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles, s32 num_handles, +                       Handle reply_target, s64 timeout_ns); +Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read); +Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size); +Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, +                         VAddr address, size_t size, MemoryPermission perm); +Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids, +                      u32 out_process_ids_size); +Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, +                     u32 out_thread_ids_size, Handle debug_handle); +Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address, +                                  u64 size, MemoryPermission perm); +Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, +                        VAddr src_address, u64 size); +Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, +                          VAddr src_address, u64 size); +Result QueryProcessMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, +                          Handle process_handle, VAddr address); +Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, +                            u64 src_address, u64 size); +Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, +                              u64 src_address, u64 size); +Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type); +Result CreateResourceLimit(Core::System& system, Handle* out_handle); +Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle, +                                  LimitableResource which, u64 limit_value); + +// + +Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size); +Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, u32 attr); +Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size); +Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size); +Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address, +                     u32 query_address); +void ExitProcess32(Core::System& system); +Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority, u32 entry_point, +                      u32 arg, u32 stack_top, s32 processor_id); +Result StartThread32(Core::System& system, Handle thread_handle); +void ExitThread32(Core::System& system); +void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high); +Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle); +Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority); +Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id, +                           u32* out_affinity_mask_low, u32* out_affinity_mask_high); +Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id, +                           u32 affinity_mask_low, u32 affinity_mask_high); +u32 GetCurrentProcessorNumber32(Core::System& system); +Result SignalEvent32(Core::System& system, Handle event_handle); +Result ClearEvent32(Core::System& system, Handle event_handle); +Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size, +                         MemoryPermission map_perm); +Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size); +Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size, +                              MemoryPermission map_perm); +Result CloseHandle32(Core::System& system, Handle handle); +Result ResetSignal32(Core::System& system, Handle handle); +Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, +                             s32 num_handles, u32 timeout_high, s32* index); +Result CancelSynchronization32(Core::System& system, Handle handle); +Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag); +Result ArbitrateUnlock32(Core::System& system, u32 address); +Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, +                                  u32 timeout_ns_low, u32 timeout_ns_high); +void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count); +void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high); +Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, u32 port_name_address); +Result SendSyncRequest32(Core::System& system, Handle handle); +Result GetProcessId32(Core::System& system, u32* out_process_id_low, u32* out_process_id_high, +                      Handle handle); +Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high, +                     Handle thread_handle); +void Break32(Core::System& system, u32 reason, u32 info1, u32 info2); +void OutputDebugString32(Core::System& system, u32 address, u32 len); +Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low, +                 u32 info_id, u32 handle, u32 sub_id_high); +Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size); +Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size); +Result SetThreadActivity32(Core::System& system, Handle thread_handle, +                           ThreadActivity thread_activity); +Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle); +Result WaitForAddress32(Core::System& system, u32 address, ArbitrationType arb_type, s32 value, +                        u32 timeout_ns_low, u32 timeout_ns_high); +Result SignalToAddress32(Core::System& system, u32 address, SignalType signal_type, s32 value, +                         s32 count); +Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read); +Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size); +Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation, +                           u64 address, u64 size, MemoryPermission perm); +Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, u64 size); +  } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_activity.cpp b/src/core/hle/kernel/svc/svc_activity.cpp new file mode 100644 index 000000000..8774a5c98 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_activity.cpp @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/kernel/svc_results.h" + +namespace Kernel::Svc { + +/// Sets the thread activity +Result SetThreadActivity(Core::System& system, Handle thread_handle, +                         ThreadActivity thread_activity) { +    LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle, +              thread_activity); + +    // Validate the activity. +    constexpr auto IsValidThreadActivity = [](ThreadActivity activity) { +        return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused; +    }; +    R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue); + +    // Get the thread from its handle. +    KScopedAutoObject thread = +        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); +    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + +    // Check that the activity is being set on a non-current thread for the current process. +    R_UNLESS(thread->GetOwnerProcess() == system.Kernel().CurrentProcess(), ResultInvalidHandle); +    R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy); + +    // Set the activity. +    R_TRY(thread->SetActivity(thread_activity)); + +    return ResultSuccess; +} + +Result SetThreadActivity32(Core::System& system, Handle thread_handle, +                           ThreadActivity thread_activity) { +    return SetThreadActivity(system, thread_handle, thread_activity); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_address_arbiter.cpp b/src/core/hle/kernel/svc/svc_address_arbiter.cpp new file mode 100644 index 000000000..842107726 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_address_arbiter.cpp @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_memory_layout.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/kernel/svc_results.h" +#include "core/hle/kernel/svc_types.h" + +namespace Kernel::Svc { +namespace { + +constexpr bool IsValidSignalType(Svc::SignalType type) { +    switch (type) { +    case Svc::SignalType::Signal: +    case Svc::SignalType::SignalAndIncrementIfEqual: +    case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: +        return true; +    default: +        return false; +    } +} + +constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) { +    switch (type) { +    case Svc::ArbitrationType::WaitIfLessThan: +    case Svc::ArbitrationType::DecrementAndWaitIfLessThan: +    case Svc::ArbitrationType::WaitIfEqual: +        return true; +    default: +        return false; +    } +} + +} // namespace + +// Wait for an address (via Address Arbiter) +Result WaitForAddress(Core::System& system, VAddr address, ArbitrationType arb_type, s32 value, +                      s64 timeout_ns) { +    LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}", +              address, arb_type, value, timeout_ns); + +    // Validate input. +    if (IsKernelAddress(address)) { +        LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address); +        return ResultInvalidCurrentMemory; +    } +    if (!Common::IsAligned(address, sizeof(s32))) { +        LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address); +        return ResultInvalidAddress; +    } +    if (!IsValidArbitrationType(arb_type)) { +        LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type); +        return ResultInvalidEnumValue; +    } + +    // Convert timeout from nanoseconds to ticks. +    s64 timeout{}; +    if (timeout_ns > 0) { +        const s64 offset_tick(timeout_ns); +        if (offset_tick > 0) { +            timeout = offset_tick + 2; +            if (timeout <= 0) { +                timeout = std::numeric_limits<s64>::max(); +            } +        } else { +            timeout = std::numeric_limits<s64>::max(); +        } +    } else { +        timeout = timeout_ns; +    } + +    return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout); +} + +Result WaitForAddress32(Core::System& system, u32 address, ArbitrationType arb_type, s32 value, +                        u32 timeout_ns_low, u32 timeout_ns_high) { +    const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); +    return WaitForAddress(system, address, arb_type, value, timeout); +} + +// Signals to an address (via Address Arbiter) +Result SignalToAddress(Core::System& system, VAddr address, SignalType signal_type, s32 value, +                       s32 count) { +    LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}", +              address, signal_type, value, count); + +    // Validate input. +    if (IsKernelAddress(address)) { +        LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address); +        return ResultInvalidCurrentMemory; +    } +    if (!Common::IsAligned(address, sizeof(s32))) { +        LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address); +        return ResultInvalidAddress; +    } +    if (!IsValidSignalType(signal_type)) { +        LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type); +        return ResultInvalidEnumValue; +    } + +    return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value, +                                                                  count); +} + +Result SignalToAddress32(Core::System& system, u32 address, SignalType signal_type, s32 value, +                         s32 count) { +    return SignalToAddress(system, address, signal_type, value, count); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_address_translation.cpp b/src/core/hle/kernel/svc/svc_address_translation.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_address_translation.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_cache.cpp b/src/core/hle/kernel/svc/svc_cache.cpp new file mode 100644 index 000000000..42167d35b --- /dev/null +++ b/src/core/hle/kernel/svc/svc_cache.cpp @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/kernel/svc_results.h" +#include "core/hle/kernel/svc_types.h" + +namespace Kernel::Svc { + +Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, u64 size) { +    // Validate address/size. +    R_UNLESS(size > 0, ResultInvalidSize); +    R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory); +    R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory); + +    // Get the process from its handle. +    KScopedAutoObject process = +        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle); +    R_UNLESS(process.IsNotNull(), ResultInvalidHandle); + +    // Verify the region is within range. +    auto& page_table = process->PageTable(); +    R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); + +    // Perform the operation. +    R_RETURN(system.Memory().FlushDataCache(*process, address, size)); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_code_memory.cpp b/src/core/hle/kernel/svc/svc_code_memory.cpp new file mode 100644 index 000000000..4cb21e101 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_code_memory.cpp @@ -0,0 +1,154 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_code_memory.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { +namespace { + +constexpr bool IsValidMapCodeMemoryPermission(MemoryPermission perm) { +    return perm == MemoryPermission::ReadWrite; +} + +constexpr bool IsValidMapToOwnerCodeMemoryPermission(MemoryPermission perm) { +    return perm == MemoryPermission::Read || perm == MemoryPermission::ReadExecute; +} + +constexpr bool IsValidUnmapCodeMemoryPermission(MemoryPermission perm) { +    return perm == MemoryPermission::None; +} + +constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(MemoryPermission perm) { +    return perm == MemoryPermission::None; +} + +} // namespace + +Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { +    LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size); + +    // Get kernel instance. +    auto& kernel = system.Kernel(); + +    // Validate 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); + +    // Create the code memory. + +    KCodeMemory* code_mem = KCodeMemory::Create(kernel); +    R_UNLESS(code_mem != nullptr, ResultOutOfResource); + +    // Verify that the region is in range. +    R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size), +             ResultInvalidCurrentMemory); + +    // Initialize the code memory. +    R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size)); + +    // Register the code memory. +    KCodeMemory::Register(kernel, code_mem); + +    // Add the code memory to the handle table. +    R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem)); + +    code_mem->Close(); + +    return ResultSuccess; +} + +Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) { +    return CreateCodeMemory(system, out, address, size); +} + +Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, +                         VAddr address, size_t size, MemoryPermission perm) { + +    LOG_TRACE(Kernel_SVC, +              "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, " +              "permission=0x{:X}", +              code_memory_handle, operation, address, size, 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); + +    // Get the code memory from its handle. +    KScopedAutoObject code_mem = +        system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle); +    R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle); + +    // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process. +    // This enables homebrew usage of these SVCs for JIT. + +    // Perform the operation. +    switch (static_cast<CodeMemoryOperation>(operation)) { +    case CodeMemoryOperation::Map: { +        // Check that the region is in range. +        R_UNLESS( +            system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), +            ResultInvalidMemoryRegion); + +        // Check the memory permission. +        R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); + +        // Map the memory. +        R_TRY(code_mem->Map(address, size)); +    } break; +    case CodeMemoryOperation::Unmap: { +        // Check that the region is in range. +        R_UNLESS( +            system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut), +            ResultInvalidMemoryRegion); + +        // Check the memory permission. +        R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); + +        // Unmap the memory. +        R_TRY(code_mem->Unmap(address, size)); +    } break; +    case CodeMemoryOperation::MapToOwner: { +        // Check that the region is in range. +        R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, +                                                              KMemoryState::GeneratedCode), +                 ResultInvalidMemoryRegion); + +        // Check the memory permission. +        R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); + +        // Map the memory to its owner. +        R_TRY(code_mem->MapToOwner(address, size, perm)); +    } break; +    case CodeMemoryOperation::UnmapFromOwner: { +        // Check that the region is in range. +        R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, +                                                              KMemoryState::GeneratedCode), +                 ResultInvalidMemoryRegion); + +        // Check the memory permission. +        R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); + +        // Unmap the memory from its owner. +        R_TRY(code_mem->UnmapFromOwner(address, size)); +    } break; +    default: +        return ResultInvalidEnumValue; +    } + +    return ResultSuccess; +} + +Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation, +                           u64 address, u64 size, MemoryPermission perm) { +    return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_condition_variable.cpp b/src/core/hle/kernel/svc/svc_condition_variable.cpp new file mode 100644 index 000000000..d6cfc87c5 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_condition_variable.cpp @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_memory_layout.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/kernel/svc_results.h" + +namespace Kernel::Svc { + +/// Wait process wide key atomic +Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag, +                                s64 timeout_ns) { +    LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address, +              cv_key, tag, timeout_ns); + +    // Validate input. +    if (IsKernelAddress(address)) { +        LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address); +        return ResultInvalidCurrentMemory; +    } +    if (!Common::IsAligned(address, sizeof(s32))) { +        LOG_ERROR(Kernel_SVC, "Address must be 4 byte aligned (address={:08X})", address); +        return ResultInvalidAddress; +    } + +    // Convert timeout from nanoseconds to ticks. +    s64 timeout{}; +    if (timeout_ns > 0) { +        const s64 offset_tick(timeout_ns); +        if (offset_tick > 0) { +            timeout = offset_tick + 2; +            if (timeout <= 0) { +                timeout = std::numeric_limits<s64>::max(); +            } +        } else { +            timeout = std::numeric_limits<s64>::max(); +        } +    } else { +        timeout = timeout_ns; +    } + +    // Wait on the condition variable. +    return system.Kernel().CurrentProcess()->WaitConditionVariable( +        address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout); +} + +Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, +                                  u32 timeout_ns_low, u32 timeout_ns_high) { +    const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); +    return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns); +} + +/// Signal process wide key +void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) { +    LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count); + +    // Signal the condition variable. +    return system.Kernel().CurrentProcess()->SignalConditionVariable( +        Common::AlignDown(cv_key, sizeof(u32)), count); +} + +void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) { +    SignalProcessWideKey(system, cv_key, count); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_debug.cpp b/src/core/hle/kernel/svc/svc_debug.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_debug.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_debug_string.cpp b/src/core/hle/kernel/svc/svc_debug_string.cpp new file mode 100644 index 000000000..486e62cc4 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_debug_string.cpp @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/svc.h" +#include "core/memory.h" + +namespace Kernel::Svc { + +/// Used to output a message on a debug hardware unit - does nothing on a retail unit +void OutputDebugString(Core::System& system, VAddr address, u64 len) { +    if (len == 0) { +        return; +    } + +    std::string str(len, '\0'); +    system.Memory().ReadBlock(address, str.data(), str.size()); +    LOG_DEBUG(Debug_Emulated, "{}", str); +} + +void OutputDebugString32(Core::System& system, u32 address, u32 len) { +    OutputDebugString(system, address, len); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_device_address_space.cpp b/src/core/hle/kernel/svc/svc_device_address_space.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_device_address_space.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_event.cpp b/src/core/hle/kernel/svc/svc_event.cpp new file mode 100644 index 000000000..885f02f50 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_event.cpp @@ -0,0 +1,111 @@ +// 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_event.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +Result SignalEvent(Core::System& system, Handle event_handle) { +    LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); + +    // Get the current handle table. +    const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + +    // Get the event. +    KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle); +    R_UNLESS(event.IsNotNull(), ResultInvalidHandle); + +    return event->Signal(); +} + +Result SignalEvent32(Core::System& system, Handle event_handle) { +    return SignalEvent(system, event_handle); +} + +Result ClearEvent(Core::System& system, Handle event_handle) { +    LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); + +    // Get the current handle table. +    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + +    // Try to clear the writable event. +    { +        KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle); +        if (event.IsNotNull()) { +            return event->Clear(); +        } +    } + +    // Try to clear the readable event. +    { +        KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle); +        if (readable_event.IsNotNull()) { +            return readable_event->Clear(); +        } +    } + +    LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle); + +    return ResultInvalidHandle; +} + +Result ClearEvent32(Core::System& system, Handle event_handle) { +    return ClearEvent(system, event_handle); +} + +Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) { +    LOG_DEBUG(Kernel_SVC, "called"); + +    // Get the kernel reference and handle table. +    auto& kernel = system.Kernel(); +    auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); + +    // Reserve a new event from the process resource limit +    KScopedResourceReservation event_reservation(kernel.CurrentProcess(), +                                                 LimitableResource::EventCountMax); +    R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); + +    // Create a new event. +    KEvent* event = KEvent::Create(kernel); +    R_UNLESS(event != nullptr, ResultOutOfResource); + +    // Initialize the event. +    event->Initialize(kernel.CurrentProcess()); + +    // Commit the thread reservation. +    event_reservation.Commit(); + +    // Ensure that we clean up the event (and its only references are handle table) on function end. +    SCOPE_EXIT({ +        event->GetReadableEvent().Close(); +        event->Close(); +    }); + +    // Register the event. +    KEvent::Register(kernel, event); + +    // Add the event to the handle table. +    R_TRY(handle_table.Add(out_write, event)); + +    // Ensure that we maintaing a clean handle state on exit. +    auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); }); + +    // Add the readable event to the handle table. +    R_TRY(handle_table.Add(out_read, std::addressof(event->GetReadableEvent()))); + +    // We succeeded. +    handle_guard.Cancel(); +    return ResultSuccess; +} + +Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) { +    return CreateEvent(system, out_write, out_read); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_exception.cpp b/src/core/hle/kernel/svc/svc_exception.cpp new file mode 100644 index 000000000..fb9f133c1 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_exception.cpp @@ -0,0 +1,121 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/debugger/debugger.h" +#include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/kernel/svc_types.h" +#include "core/memory.h" +#include "core/reporter.h" + +namespace Kernel::Svc { + +/// Break program execution +void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { +    BreakReason break_reason = +        static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag)); +    bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0; + +    bool has_dumped_buffer{}; +    std::vector<u8> debug_buffer; + +    const auto handle_debug_buffer = [&](VAddr addr, u64 sz) { +        if (sz == 0 || addr == 0 || has_dumped_buffer) { +            return; +        } + +        auto& memory = system.Memory(); + +        // This typically is an error code so we're going to assume this is the case +        if (sz == sizeof(u32)) { +            LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr)); +        } else { +            // We don't know what's in here so we'll hexdump it +            debug_buffer.resize(sz); +            memory.ReadBlock(addr, debug_buffer.data(), sz); +            std::string hexdump; +            for (std::size_t i = 0; i < debug_buffer.size(); i++) { +                hexdump += fmt::format("{:02X} ", debug_buffer[i]); +                if (i != 0 && i % 16 == 0) { +                    hexdump += '\n'; +                } +            } +            LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump); +        } +        has_dumped_buffer = true; +    }; +    switch (break_reason) { +    case BreakReason::Panic: +        LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1, +                     info2); +        handle_debug_buffer(info1, info2); +        break; +    case BreakReason::Assert: +        LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}", +                     info1, info2); +        handle_debug_buffer(info1, info2); +        break; +    case BreakReason::User: +        LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2); +        handle_debug_buffer(info1, info2); +        break; +    case BreakReason::PreLoadDll: +        LOG_INFO(Debug_Emulated, +                 "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1, +                 info2); +        break; +    case BreakReason::PostLoadDll: +        LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, +                 info2); +        break; +    case BreakReason::PreUnloadDll: +        LOG_INFO(Debug_Emulated, +                 "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1, +                 info2); +        break; +    case BreakReason::PostUnloadDll: +        LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}", +                 info1, info2); +        break; +    case BreakReason::CppException: +        LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered."); +        break; +    default: +        LOG_WARNING( +            Debug_Emulated, +            "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}", +            reason, info1, info2); +        handle_debug_buffer(info1, info2); +        break; +    } + +    system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2, +                                            has_dumped_buffer ? std::make_optional(debug_buffer) +                                                              : std::nullopt); + +    if (!notification_only) { +        LOG_CRITICAL( +            Debug_Emulated, +            "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", +            reason, info1, info2); + +        handle_debug_buffer(info1, info2); + +        auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); +        const auto thread_processor_id = current_thread->GetActiveCore(); +        system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); +    } + +    if (system.DebuggerEnabled()) { +        auto* thread = system.Kernel().GetCurrentEmuThread(); +        system.GetDebugger().NotifyThreadStopped(thread); +        thread->RequestSuspend(Kernel::SuspendType::Debug); +    } +} + +void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) { +    Break(system, reason, info1, info2); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp new file mode 100644 index 000000000..df5dd85a4 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_info.cpp @@ -0,0 +1,282 @@ +// 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/k_process.h" +#include "core/hle/kernel/k_resource_limit.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Gets system/memory information for the current process +Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, u64 info_sub_id) { +    LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, +              info_sub_id, handle); + +    const auto info_id_type = static_cast<InfoType>(info_id); + +    switch (info_id_type) { +    case InfoType::CoreMask: +    case InfoType::PriorityMask: +    case InfoType::AliasRegionAddress: +    case InfoType::AliasRegionSize: +    case InfoType::HeapRegionAddress: +    case InfoType::HeapRegionSize: +    case InfoType::AslrRegionAddress: +    case InfoType::AslrRegionSize: +    case InfoType::StackRegionAddress: +    case InfoType::StackRegionSize: +    case InfoType::TotalMemorySize: +    case InfoType::UsedMemorySize: +    case InfoType::SystemResourceSizeTotal: +    case InfoType::SystemResourceSizeUsed: +    case InfoType::ProgramId: +    case InfoType::UserExceptionContextAddress: +    case InfoType::TotalNonSystemMemorySize: +    case InfoType::UsedNonSystemMemorySize: +    case InfoType::IsApplication: +    case InfoType::FreeThreadCount: { +        if (info_sub_id != 0) { +            LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, +                      info_sub_id); +            return ResultInvalidEnumValue; +        } + +        const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); +        KScopedAutoObject process = handle_table.GetObject<KProcess>(handle); +        if (process.IsNull()) { +            LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}", +                      info_id, info_sub_id, handle); +            return ResultInvalidHandle; +        } + +        switch (info_id_type) { +        case InfoType::CoreMask: +            *result = process->GetCoreMask(); +            return ResultSuccess; + +        case InfoType::PriorityMask: +            *result = process->GetPriorityMask(); +            return ResultSuccess; + +        case InfoType::AliasRegionAddress: +            *result = process->PageTable().GetAliasRegionStart(); +            return ResultSuccess; + +        case InfoType::AliasRegionSize: +            *result = process->PageTable().GetAliasRegionSize(); +            return ResultSuccess; + +        case InfoType::HeapRegionAddress: +            *result = process->PageTable().GetHeapRegionStart(); +            return ResultSuccess; + +        case InfoType::HeapRegionSize: +            *result = process->PageTable().GetHeapRegionSize(); +            return ResultSuccess; + +        case InfoType::AslrRegionAddress: +            *result = process->PageTable().GetAliasCodeRegionStart(); +            return ResultSuccess; + +        case InfoType::AslrRegionSize: +            *result = process->PageTable().GetAliasCodeRegionSize(); +            return ResultSuccess; + +        case InfoType::StackRegionAddress: +            *result = process->PageTable().GetStackRegionStart(); +            return ResultSuccess; + +        case InfoType::StackRegionSize: +            *result = process->PageTable().GetStackRegionSize(); +            return ResultSuccess; + +        case InfoType::TotalMemorySize: +            *result = process->GetTotalPhysicalMemoryAvailable(); +            return ResultSuccess; + +        case InfoType::UsedMemorySize: +            *result = process->GetTotalPhysicalMemoryUsed(); +            return ResultSuccess; + +        case InfoType::SystemResourceSizeTotal: +            *result = process->GetSystemResourceSize(); +            return ResultSuccess; + +        case InfoType::SystemResourceSizeUsed: +            LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); +            *result = process->GetSystemResourceUsage(); +            return ResultSuccess; + +        case InfoType::ProgramId: +            *result = process->GetProgramID(); +            return ResultSuccess; + +        case InfoType::UserExceptionContextAddress: +            *result = process->GetProcessLocalRegionAddress(); +            return ResultSuccess; + +        case InfoType::TotalNonSystemMemorySize: +            *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource(); +            return ResultSuccess; + +        case InfoType::UsedNonSystemMemorySize: +            *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource(); +            return ResultSuccess; + +        case InfoType::FreeThreadCount: +            *result = process->GetFreeThreadCount(); +            return ResultSuccess; + +        default: +            break; +        } + +        LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id); +        return ResultInvalidEnumValue; +    } + +    case InfoType::DebuggerAttached: +        *result = 0; +        return ResultSuccess; + +    case InfoType::ResourceLimit: { +        if (handle != 0) { +            LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle); +            return ResultInvalidHandle; +        } + +        if (info_sub_id != 0) { +            LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, +                      info_sub_id); +            return ResultInvalidCombination; +        } + +        KProcess* const current_process = system.Kernel().CurrentProcess(); +        KHandleTable& handle_table = current_process->GetHandleTable(); +        const auto resource_limit = current_process->GetResourceLimit(); +        if (!resource_limit) { +            *result = Svc::InvalidHandle; +            // Yes, the kernel considers this a successful operation. +            return ResultSuccess; +        } + +        Handle resource_handle{}; +        R_TRY(handle_table.Add(&resource_handle, resource_limit)); + +        *result = resource_handle; +        return ResultSuccess; +    } + +    case InfoType::RandomEntropy: +        if (handle != 0) { +            LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}", +                      handle); +            return ResultInvalidHandle; +        } + +        if (info_sub_id >= KProcess::RANDOM_ENTROPY_SIZE) { +            LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}", +                      KProcess::RANDOM_ENTROPY_SIZE, info_sub_id); +            return ResultInvalidCombination; +        } + +        *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id); +        return ResultSuccess; + +    case InfoType::InitialProcessIdRange: +        LOG_WARNING(Kernel_SVC, +                    "(STUBBED) Attempted to query privileged process id bounds, returned 0"); +        *result = 0; +        return ResultSuccess; + +    case InfoType::ThreadTickCount: { +        constexpr u64 num_cpus = 4; +        if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { +            LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, +                      info_sub_id); +            return ResultInvalidCombination; +        } + +        KScopedAutoObject thread = +            system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>( +                static_cast<Handle>(handle)); +        if (thread.IsNull()) { +            LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", +                      static_cast<Handle>(handle)); +            return ResultInvalidHandle; +        } + +        const auto& core_timing = system.CoreTiming(); +        const auto& scheduler = *system.Kernel().CurrentScheduler(); +        const auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); +        const bool same_thread = current_thread == thread.GetPointerUnsafe(); + +        const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime(); +        u64 out_ticks = 0; +        if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { +            const u64 thread_ticks = current_thread->GetCpuTime(); + +            out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); +        } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) { +            out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; +        } + +        *result = out_ticks; +        return ResultSuccess; +    } +    case InfoType::IdleTickCount: { +        // Verify the input handle is invalid. +        R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); + +        // Verify the requested core is valid. +        const bool core_valid = +            (info_sub_id == 0xFFFFFFFFFFFFFFFF) || +            (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex())); +        R_UNLESS(core_valid, ResultInvalidCombination); + +        // Get the idle tick count. +        *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); +        return ResultSuccess; +    } +    case InfoType::MesosphereCurrentProcess: { +        // Verify the input handle is invalid. +        R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); + +        // Verify the sub-type is valid. +        R_UNLESS(info_sub_id == 0, ResultInvalidCombination); + +        // Get the handle table. +        KProcess* current_process = system.Kernel().CurrentProcess(); +        KHandleTable& handle_table = current_process->GetHandleTable(); + +        // Get a new handle for the current process. +        Handle tmp; +        R_TRY(handle_table.Add(&tmp, current_process)); + +        // Set the output. +        *result = tmp; + +        // We succeeded. +        return ResultSuccess; +    } +    default: +        LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id); +        return ResultInvalidEnumValue; +    } +} + +Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low, +                 u32 info_id, u32 handle, u32 sub_id_high) { +    const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)}; +    u64 res_value{}; + +    const Result result{GetInfo(system, &res_value, info_id, handle, sub_id)}; +    *result_high = static_cast<u32>(res_value >> 32); +    *result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max()); + +    return result; +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_interrupt_event.cpp b/src/core/hle/kernel/svc/svc_interrupt_event.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_interrupt_event.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_io_pool.cpp b/src/core/hle/kernel/svc/svc_io_pool.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_io_pool.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp new file mode 100644 index 000000000..dbb68e89a --- /dev/null +++ b/src/core/hle/kernel/svc/svc_ipc.cpp @@ -0,0 +1,89 @@ +// 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_client_session.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_server_session.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Makes a blocking IPC call to a service. +Result SendSyncRequest(Core::System& system, Handle handle) { +    auto& kernel = system.Kernel(); + +    // Get the client session from its handle. +    KScopedAutoObject session = +        kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle); +    R_UNLESS(session.IsNotNull(), ResultInvalidHandle); + +    LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); + +    return session->SendSyncRequest(); +} + +Result SendSyncRequest32(Core::System& system, Handle handle) { +    return SendSyncRequest(system, handle); +} + +Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles, s32 num_handles, +                       Handle reply_target, s64 timeout_ns) { +    auto& kernel = system.Kernel(); +    auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable(); + +    // Convert handle list to object table. +    std::vector<KSynchronizationObject*> objs(num_handles); +    R_UNLESS( +        handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, num_handles), +        ResultInvalidHandle); + +    // Ensure handles are closed when we're done. +    SCOPE_EXIT({ +        for (auto i = 0; i < num_handles; ++i) { +            objs[i]->Close(); +        } +    }); + +    // Reply to the target, if one is specified. +    if (reply_target != InvalidHandle) { +        KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target); +        R_UNLESS(session.IsNotNull(), ResultInvalidHandle); + +        // If we fail to reply, we want to set the output index to -1. +        ON_RESULT_FAILURE { +            *out_index = -1; +        }; + +        // Send the reply. +        R_TRY(session->SendReply()); +    } + +    // Wait for a message. +    while (true) { +        // Wait for an object. +        s32 index; +        Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(), +                                                     static_cast<s32>(objs.size()), timeout_ns); +        if (result == ResultTimedOut) { +            return result; +        } + +        // Receive the request. +        if (R_SUCCEEDED(result)) { +            KServerSession* session = objs[index]->DynamicCast<KServerSession*>(); +            if (session != nullptr) { +                result = session->ReceiveRequest(); +                if (result == ResultNotFound) { +                    continue; +                } +            } +        } + +        *out_index = index; +        return result; +    } +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_kernel_debug.cpp b/src/core/hle/kernel/svc/svc_kernel_debug.cpp new file mode 100644 index 000000000..454255e7a --- /dev/null +++ b/src/core/hle/kernel/svc/svc_kernel_debug.cpp @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +void KernelDebug([[maybe_unused]] Core::System& system, [[maybe_unused]] u32 kernel_debug_type, +                 [[maybe_unused]] u64 param1, [[maybe_unused]] u64 param2, +                 [[maybe_unused]] u64 param3) { +    // Intentionally do nothing, as this does nothing in released kernel binaries. +} + +void ChangeKernelTraceState([[maybe_unused]] Core::System& system, +                            [[maybe_unused]] u32 trace_state) { +    // Intentionally do nothing, as this does nothing in released kernel binaries. +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_light_ipc.cpp b/src/core/hle/kernel/svc/svc_light_ipc.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_light_ipc.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_lock.cpp b/src/core/hle/kernel/svc/svc_lock.cpp new file mode 100644 index 000000000..45f2a6553 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_lock.cpp @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_memory_layout.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Attempts to locks a mutex +Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) { +    LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}", +              thread_handle, address, tag); + +    // Validate the input address. +    if (IsKernelAddress(address)) { +        LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})", +                  address); +        return ResultInvalidCurrentMemory; +    } +    if (!Common::IsAligned(address, sizeof(u32))) { +        LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address); +        return ResultInvalidAddress; +    } + +    return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag); +} + +Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) { +    return ArbitrateLock(system, thread_handle, address, tag); +} + +/// Unlock a mutex +Result ArbitrateUnlock(Core::System& system, VAddr address) { +    LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address); + +    // Validate the input address. +    if (IsKernelAddress(address)) { +        LOG_ERROR(Kernel_SVC, +                  "Attempting to arbitrate an unlock on a kernel address (address={:08X})", +                  address); +        return ResultInvalidCurrentMemory; +    } +    if (!Common::IsAligned(address, sizeof(u32))) { +        LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address); +        return ResultInvalidAddress; +    } + +    return system.Kernel().CurrentProcess()->SignalToAddress(address); +} + +Result ArbitrateUnlock32(Core::System& system, u32 address) { +    return ArbitrateUnlock(system, address); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_memory.cpp b/src/core/hle/kernel/svc/svc_memory.cpp new file mode 100644 index 000000000..f78b1239b --- /dev/null +++ b/src/core/hle/kernel/svc/svc_memory.cpp @@ -0,0 +1,189 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { +namespace { + +constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) { +    switch (perm) { +    case MemoryPermission::None: +    case MemoryPermission::Read: +    case MemoryPermission::ReadWrite: +        return true; +    default: +        return false; +    } +} + +// Checks if address + size is greater than the given address +// This can return false if the size causes an overflow of a 64-bit type +// or if the given size is zero. +constexpr bool IsValidAddressRange(VAddr address, u64 size) { +    return address + size > address; +} + +// Helper function that performs the common sanity checks for svcMapMemory +// and svcUnmapMemory. This is doable, as both functions perform their sanitizing +// in the same order. +Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr, +                                  u64 size) { +    if (!Common::Is4KBAligned(dst_addr)) { +        LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr); +        return ResultInvalidAddress; +    } + +    if (!Common::Is4KBAligned(src_addr)) { +        LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr); +        return ResultInvalidSize; +    } + +    if (size == 0) { +        LOG_ERROR(Kernel_SVC, "Size is 0"); +        return ResultInvalidSize; +    } + +    if (!Common::Is4KBAligned(size)) { +        LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size); +        return ResultInvalidSize; +    } + +    if (!IsValidAddressRange(dst_addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}", +                  dst_addr, size); +        return ResultInvalidCurrentMemory; +    } + +    if (!IsValidAddressRange(src_addr, size)) { +        LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}", +                  src_addr, size); +        return ResultInvalidCurrentMemory; +    } + +    if (!manager.IsInsideAddressSpace(src_addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", +                  src_addr, size); +        return ResultInvalidCurrentMemory; +    } + +    if (manager.IsOutsideStackRegion(dst_addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}", +                  dst_addr, size); +        return ResultInvalidMemoryRegion; +    } + +    if (manager.IsInsideHeapRegion(dst_addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Destination does not fit within the heap region, addr=0x{:016X}, " +                  "size=0x{:016X}", +                  dst_addr, size); +        return ResultInvalidMemoryRegion; +    } + +    if (manager.IsInsideAliasRegion(dst_addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Destination does not fit within the map region, addr=0x{:016X}, " +                  "size=0x{:016X}", +                  dst_addr, size); +        return ResultInvalidMemoryRegion; +    } + +    return ResultSuccess; +} + +} // namespace + +Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, MemoryPermission perm) { +    LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size, +              perm); + +    // Validate 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(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission); + +    // Validate that the region is in range for the current process. +    auto& page_table = system.Kernel().CurrentProcess()->PageTable(); +    R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); + +    // Set the memory attribute. +    return page_table.SetMemoryPermission(address, size, perm); +} + +Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, u32 attr) { +    LOG_DEBUG(Kernel_SVC, +              "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, +              size, mask, attr); + +    // Validate 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 attribute and mask. +    constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached); +    R_UNLESS((mask | attr) == mask, ResultInvalidCombination); +    R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination); + +    // Validate that the region is in range for the current process. +    auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; +    R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); + +    // Set the memory attribute. +    return page_table.SetMemoryAttribute(address, size, mask, attr); +} + +Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, u32 attr) { +    return SetMemoryAttribute(system, address, size, mask, attr); +} + +/// Maps a memory range into a different range. +Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { +    LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, +              src_addr, size); + +    auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; + +    if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; +        result.IsError()) { +        return result; +    } + +    return page_table.MapMemory(dst_addr, src_addr, size); +} + +Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { +    return MapMemory(system, dst_addr, src_addr, size); +} + +/// Unmaps a region that was previously mapped with svcMapMemory +Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { +    LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, +              src_addr, size); + +    auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; + +    if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; +        result.IsError()) { +        return result; +    } + +    return page_table.UnmapMemory(dst_addr, src_addr, size); +} + +Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { +    return UnmapMemory(system, dst_addr, src_addr, size); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_physical_memory.cpp b/src/core/hle/kernel/svc/svc_physical_memory.cpp new file mode 100644 index 000000000..0fc262203 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_physical_memory.cpp @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Set the process heap to a given Size. It can both extend and shrink the heap. +Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) { +    LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size); + +    // Validate size. +    R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize); +    R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize); + +    // Set the heap size. +    R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size)); + +    return ResultSuccess; +} + +Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) { +    VAddr temp_heap_addr{}; +    const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)}; +    *heap_addr = static_cast<u32>(temp_heap_addr); +    return result; +} + +/// Maps memory at a desired address +Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { +    LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); + +    if (!Common::Is4KBAligned(addr)) { +        LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); +        return ResultInvalidAddress; +    } + +    if (!Common::Is4KBAligned(size)) { +        LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); +        return ResultInvalidSize; +    } + +    if (size == 0) { +        LOG_ERROR(Kernel_SVC, "Size is zero"); +        return ResultInvalidSize; +    } + +    if (!(addr < addr + size)) { +        LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); +        return ResultInvalidMemoryRegion; +    } + +    KProcess* const current_process{system.Kernel().CurrentProcess()}; +    auto& page_table{current_process->PageTable()}; + +    if (current_process->GetSystemResourceSize() == 0) { +        LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); +        return ResultInvalidState; +    } + +    if (!page_table.IsInsideAddressSpace(addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, +                  size); +        return ResultInvalidMemoryRegion; +    } + +    if (page_table.IsOutsideAliasRegion(addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, +                  size); +        return ResultInvalidMemoryRegion; +    } + +    return page_table.MapPhysicalMemory(addr, size); +} + +Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { +    return MapPhysicalMemory(system, addr, size); +} + +/// Unmaps memory previously mapped via MapPhysicalMemory +Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { +    LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); + +    if (!Common::Is4KBAligned(addr)) { +        LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); +        return ResultInvalidAddress; +    } + +    if (!Common::Is4KBAligned(size)) { +        LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); +        return ResultInvalidSize; +    } + +    if (size == 0) { +        LOG_ERROR(Kernel_SVC, "Size is zero"); +        return ResultInvalidSize; +    } + +    if (!(addr < addr + size)) { +        LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); +        return ResultInvalidMemoryRegion; +    } + +    KProcess* const current_process{system.Kernel().CurrentProcess()}; +    auto& page_table{current_process->PageTable()}; + +    if (current_process->GetSystemResourceSize() == 0) { +        LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); +        return ResultInvalidState; +    } + +    if (!page_table.IsInsideAddressSpace(addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, +                  size); +        return ResultInvalidMemoryRegion; +    } + +    if (page_table.IsOutsideAliasRegion(addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, +                  size); +        return ResultInvalidMemoryRegion; +    } + +    return page_table.UnmapPhysicalMemory(addr, size); +} + +Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { +    return UnmapPhysicalMemory(system, addr, size); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_port.cpp b/src/core/hle/kernel/svc/svc_port.cpp new file mode 100644 index 000000000..cdfe0dd16 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_port.cpp @@ -0,0 +1,71 @@ +// 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_client_port.h" +#include "core/hle/kernel/k_client_session.h" +#include "core/hle/kernel/k_port.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Connect to an OS service given the port name, returns the handle to the port to out +Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) { +    auto& memory = system.Memory(); +    if (!memory.IsValidVirtualAddress(port_name_address)) { +        LOG_ERROR(Kernel_SVC, +                  "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", +                  port_name_address); +        return ResultNotFound; +    } + +    static constexpr std::size_t PortNameMaxLength = 11; +    // Read 1 char beyond the max allowed port name to detect names that are too long. +    const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1); +    if (port_name.size() > PortNameMaxLength) { +        LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength, +                  port_name.size()); +        return ResultOutOfRange; +    } + +    LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); + +    // Get the current handle table. +    auto& kernel = system.Kernel(); +    auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); + +    // Find the client port. +    auto port = kernel.CreateNamedServicePort(port_name); +    if (!port) { +        LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name); +        return ResultNotFound; +    } + +    // Reserve a handle for the port. +    // NOTE: Nintendo really does write directly to the output handle here. +    R_TRY(handle_table.Reserve(out)); +    auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); }); + +    // Create a session. +    KClientSession* session{}; +    R_TRY(port->CreateSession(std::addressof(session))); + +    kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort()); + +    // Register the session in the table, close the extra reference. +    handle_table.Register(*out, session); +    session->Close(); + +    // We succeeded. +    handle_guard.Cancel(); +    return ResultSuccess; +} + +Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, u32 port_name_address) { + +    return ConnectToNamedPort(system, out_handle, port_name_address); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_power_management.cpp b/src/core/hle/kernel/svc/svc_power_management.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_power_management.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_process.cpp b/src/core/hle/kernel/svc/svc_process.cpp new file mode 100644 index 000000000..d6c8b4561 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_process.cpp @@ -0,0 +1,124 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Exits the current process +void ExitProcess(Core::System& system) { +    auto* current_process = system.Kernel().CurrentProcess(); + +    LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); +    ASSERT_MSG(current_process->GetState() == KProcess::State::Running, +               "Process has already exited"); + +    system.Exit(); +} + +void ExitProcess32(Core::System& system) { +    ExitProcess(system); +} + +/// Gets the ID of the specified process or a specified thread's owning process. +Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) { +    LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle); + +    // Get the object from the handle table. +    KScopedAutoObject obj = +        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KAutoObject>( +            static_cast<Handle>(handle)); +    R_UNLESS(obj.IsNotNull(), ResultInvalidHandle); + +    // Get the process from the object. +    KProcess* process = nullptr; +    if (KProcess* p = obj->DynamicCast<KProcess*>(); p != nullptr) { +        // The object is a process, so we can use it directly. +        process = p; +    } else if (KThread* t = obj->DynamicCast<KThread*>(); t != nullptr) { +        // The object is a thread, so we want to use its parent. +        process = reinterpret_cast<KThread*>(obj.GetPointerUnsafe())->GetOwnerProcess(); +    } else { +        // TODO(bunnei): This should also handle debug objects before returning. +        UNIMPLEMENTED_MSG("Debug objects not implemented"); +    } + +    // Make sure the target process exists. +    R_UNLESS(process != nullptr, ResultInvalidHandle); + +    // Get the process id. +    *out_process_id = process->GetId(); + +    return ResultSuccess; +} + +Result GetProcessId32(Core::System& system, u32* out_process_id_low, u32* out_process_id_high, +                      Handle handle) { +    u64 out_process_id{}; +    const auto result = GetProcessId(system, &out_process_id, handle); +    *out_process_id_low = static_cast<u32>(out_process_id); +    *out_process_id_high = static_cast<u32>(out_process_id >> 32); +    return result; +} + +Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids, +                      u32 out_process_ids_size) { +    LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", +              out_process_ids, out_process_ids_size); + +    // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail. +    if ((out_process_ids_size & 0xF0000000) != 0) { +        LOG_ERROR(Kernel_SVC, +                  "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}", +                  out_process_ids_size); +        return ResultOutOfRange; +    } + +    const auto& kernel = system.Kernel(); +    const auto total_copy_size = out_process_ids_size * sizeof(u64); + +    if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace( +                                        out_process_ids, total_copy_size)) { +        LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", +                  out_process_ids, out_process_ids + total_copy_size); +        return ResultInvalidCurrentMemory; +    } + +    auto& memory = system.Memory(); +    const auto& process_list = kernel.GetProcessList(); +    const auto num_processes = process_list.size(); +    const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes); + +    for (std::size_t i = 0; i < copy_amount; ++i) { +        memory.Write64(out_process_ids, process_list[i]->GetProcessID()); +        out_process_ids += sizeof(u64); +    } + +    *out_num_processes = static_cast<u32>(num_processes); +    return ResultSuccess; +} + +Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { +    LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); + +    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); +    KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); +    if (process.IsNull()) { +        LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", +                  process_handle); +        return ResultInvalidHandle; +    } + +    const auto info_type = static_cast<ProcessInfoType>(type); +    if (info_type != ProcessInfoType::ProcessState) { +        LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type); +        return ResultInvalidEnumValue; +    } + +    *out = static_cast<u64>(process->GetState()); +    return ResultSuccess; +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_process_memory.cpp b/src/core/hle/kernel/svc/svc_process_memory.cpp new file mode 100644 index 000000000..b6ac43af2 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_process_memory.cpp @@ -0,0 +1,274 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { +namespace { + +constexpr bool IsValidAddressRange(VAddr address, u64 size) { +    return address + size > address; +} + +constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) { +    switch (perm) { +    case Svc::MemoryPermission::None: +    case Svc::MemoryPermission::Read: +    case Svc::MemoryPermission::ReadWrite: +    case Svc::MemoryPermission::ReadExecute: +        return true; +    default: +        return false; +    } +} + +} // namespace + +Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address, +                                  u64 size, Svc::MemoryPermission perm) { +    LOG_TRACE(Kernel_SVC, +              "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", +              process_handle, address, size, 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); +    R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory); +    R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory); + +    // Validate the memory permission. +    R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission); + +    // Get the process from its handle. +    KScopedAutoObject process = +        system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle); +    R_UNLESS(process.IsNotNull(), ResultInvalidHandle); + +    // Validate that the address is in range. +    auto& page_table = process->PageTable(); +    R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); + +    // Set the memory permission. +    return page_table.SetProcessMemoryPermission(address, size, perm); +} + +Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, +                        VAddr src_address, u64 size) { +    LOG_TRACE(Kernel_SVC, +              "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", +              dst_address, process_handle, src_address, size); + +    // Validate the address/size. +    R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); +    R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); +    R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); +    R_UNLESS(size > 0, ResultInvalidSize); +    R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); +    R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); + +    // Get the processes. +    KProcess* dst_process = system.CurrentProcess(); +    KScopedAutoObject src_process = +        dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); +    R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); + +    // Get the page tables. +    auto& dst_pt = dst_process->PageTable(); +    auto& src_pt = src_process->PageTable(); + +    // Validate that the mapping is in range. +    R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); +    R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), +             ResultInvalidMemoryRegion); + +    // Create a new page group. +    KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()}; +    R_TRY(src_pt.MakeAndOpenPageGroup( +        std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess, +        KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None, +        KMemoryAttribute::All, KMemoryAttribute::None)); + +    // Map the group. +    R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode, +                              KMemoryPermission::UserReadWrite)); + +    return ResultSuccess; +} + +Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, +                          VAddr src_address, u64 size) { +    LOG_TRACE(Kernel_SVC, +              "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}", +              dst_address, process_handle, src_address, size); + +    // Validate the address/size. +    R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); +    R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); +    R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); +    R_UNLESS(size > 0, ResultInvalidSize); +    R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory); +    R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory); + +    // Get the processes. +    KProcess* dst_process = system.CurrentProcess(); +    KScopedAutoObject src_process = +        dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); +    R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); + +    // Get the page tables. +    auto& dst_pt = dst_process->PageTable(); +    auto& src_pt = src_process->PageTable(); + +    // Validate that the mapping is in range. +    R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); +    R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), +             ResultInvalidMemoryRegion); + +    // Unmap the memory. +    R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address)); + +    return ResultSuccess; +} + +Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, +                            u64 src_address, u64 size) { +    LOG_DEBUG(Kernel_SVC, +              "called. process_handle=0x{:08X}, dst_address=0x{:016X}, " +              "src_address=0x{:016X}, size=0x{:016X}", +              process_handle, dst_address, src_address, size); + +    if (!Common::Is4KBAligned(src_address)) { +        LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", +                  src_address); +        return ResultInvalidAddress; +    } + +    if (!Common::Is4KBAligned(dst_address)) { +        LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", +                  dst_address); +        return ResultInvalidAddress; +    } + +    if (size == 0 || !Common::Is4KBAligned(size)) { +        LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size); +        return ResultInvalidSize; +    } + +    if (!IsValidAddressRange(dst_address, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Destination address range overflows the address space (dst_address=0x{:016X}, " +                  "size=0x{:016X}).", +                  dst_address, size); +        return ResultInvalidCurrentMemory; +    } + +    if (!IsValidAddressRange(src_address, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Source address range overflows the address space (src_address=0x{:016X}, " +                  "size=0x{:016X}).", +                  src_address, size); +        return ResultInvalidCurrentMemory; +    } + +    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); +    KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); +    if (process.IsNull()) { +        LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", +                  process_handle); +        return ResultInvalidHandle; +    } + +    auto& page_table = process->PageTable(); +    if (!page_table.IsInsideAddressSpace(src_address, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Source address range is not within the address space (src_address=0x{:016X}, " +                  "size=0x{:016X}).", +                  src_address, size); +        return ResultInvalidCurrentMemory; +    } + +    if (!page_table.IsInsideASLRRegion(dst_address, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " +                  "size=0x{:016X}).", +                  dst_address, size); +        return ResultInvalidMemoryRegion; +    } + +    return page_table.MapCodeMemory(dst_address, src_address, size); +} + +Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, +                              u64 src_address, u64 size) { +    LOG_DEBUG(Kernel_SVC, +              "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " +              "size=0x{:016X}", +              process_handle, dst_address, src_address, size); + +    if (!Common::Is4KBAligned(dst_address)) { +        LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", +                  dst_address); +        return ResultInvalidAddress; +    } + +    if (!Common::Is4KBAligned(src_address)) { +        LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", +                  src_address); +        return ResultInvalidAddress; +    } + +    if (size == 0 || !Common::Is4KBAligned(size)) { +        LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); +        return ResultInvalidSize; +    } + +    if (!IsValidAddressRange(dst_address, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Destination address range overflows the address space (dst_address=0x{:016X}, " +                  "size=0x{:016X}).", +                  dst_address, size); +        return ResultInvalidCurrentMemory; +    } + +    if (!IsValidAddressRange(src_address, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Source address range overflows the address space (src_address=0x{:016X}, " +                  "size=0x{:016X}).", +                  src_address, size); +        return ResultInvalidCurrentMemory; +    } + +    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); +    KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); +    if (process.IsNull()) { +        LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", +                  process_handle); +        return ResultInvalidHandle; +    } + +    auto& page_table = process->PageTable(); +    if (!page_table.IsInsideAddressSpace(src_address, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Source address range is not within the address space (src_address=0x{:016X}, " +                  "size=0x{:016X}).", +                  src_address, size); +        return ResultInvalidCurrentMemory; +    } + +    if (!page_table.IsInsideASLRRegion(dst_address, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " +                  "size=0x{:016X}).", +                  dst_address, size); +        return ResultInvalidMemoryRegion; +    } + +    return page_table.UnmapCodeMemory(dst_address, src_address, size, +                                      KPageTable::ICacheInvalidationStrategy::InvalidateAll); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_processor.cpp b/src/core/hle/kernel/svc/svc_processor.cpp new file mode 100644 index 000000000..8561cf74f --- /dev/null +++ b/src/core/hle/kernel/svc/svc_processor.cpp @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/kernel/physical_core.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +/// Get which CPU core is executing the current thread +u32 GetCurrentProcessorNumber(Core::System& system) { +    LOG_TRACE(Kernel_SVC, "called"); +    return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex()); +} + +u32 GetCurrentProcessorNumber32(Core::System& system) { +    return GetCurrentProcessorNumber(system); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_query_memory.cpp b/src/core/hle/kernel/svc/svc_query_memory.cpp new file mode 100644 index 000000000..aac3b2eca --- /dev/null +++ b/src/core/hle/kernel/svc/svc_query_memory.cpp @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, +                   VAddr query_address) { +    LOG_TRACE(Kernel_SVC, +              "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " +              "query_address=0x{:016X}", +              memory_info_address, page_info_address, query_address); + +    return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess, +                              query_address); +} + +Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address, +                     u32 query_address) { +    return QueryMemory(system, memory_info_address, page_info_address, query_address); +} + +Result QueryProcessMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, +                          Handle process_handle, VAddr address) { +    LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); +    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); +    KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); +    if (process.IsNull()) { +        LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", +                  process_handle); +        return ResultInvalidHandle; +    } + +    auto& memory{system.Memory()}; +    const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; + +    memory.Write64(memory_info_address + 0x00, memory_info.base_address); +    memory.Write64(memory_info_address + 0x08, memory_info.size); +    memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff); +    memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute)); +    memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission)); +    memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count); +    memory.Write32(memory_info_address + 0x20, memory_info.device_count); +    memory.Write32(memory_info_address + 0x24, 0); + +    // Page info appears to be currently unused by the kernel and is always set to zero. +    memory.Write32(page_info_address, 0); + +    return ResultSuccess; +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_register.cpp b/src/core/hle/kernel/svc/svc_register.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_register.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_resource_limit.cpp b/src/core/hle/kernel/svc/svc_resource_limit.cpp new file mode 100644 index 000000000..679ba10fa --- /dev/null +++ b/src/core/hle/kernel/svc/svc_resource_limit.cpp @@ -0,0 +1,95 @@ +// 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_resource_limit.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { + +Result CreateResourceLimit(Core::System& system, Handle* out_handle) { +    LOG_DEBUG(Kernel_SVC, "called"); + +    // Create a new resource limit. +    auto& kernel = system.Kernel(); +    KResourceLimit* resource_limit = KResourceLimit::Create(kernel); +    R_UNLESS(resource_limit != nullptr, ResultOutOfResource); + +    // Ensure we don't leak a reference to the limit. +    SCOPE_EXIT({ resource_limit->Close(); }); + +    // Initialize the resource limit. +    resource_limit->Initialize(&system.CoreTiming()); + +    // Register the limit. +    KResourceLimit::Register(kernel, resource_limit); + +    // Add the limit to the handle table. +    R_TRY(kernel.CurrentProcess()->GetHandleTable().Add(out_handle, resource_limit)); + +    return ResultSuccess; +} + +Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value, +                                  Handle resource_limit_handle, LimitableResource which) { +    LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle, +              which); + +    // Validate the resource. +    R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); + +    // Get the resource limit. +    auto& kernel = system.Kernel(); +    KScopedAutoObject resource_limit = +        kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle); +    R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); + +    // Get the limit value. +    *out_limit_value = resource_limit->GetLimitValue(which); + +    return ResultSuccess; +} + +Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value, +                                    Handle resource_limit_handle, LimitableResource which) { +    LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle, +              which); + +    // Validate the resource. +    R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); + +    // Get the resource limit. +    auto& kernel = system.Kernel(); +    KScopedAutoObject resource_limit = +        kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle); +    R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); + +    // Get the current value. +    *out_current_value = resource_limit->GetCurrentValue(which); + +    return ResultSuccess; +} + +Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle, +                                  LimitableResource which, u64 limit_value) { +    LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}", +              resource_limit_handle, which, limit_value); + +    // Validate the resource. +    R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); + +    // Get the resource limit. +    auto& kernel = system.Kernel(); +    KScopedAutoObject resource_limit = +        kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle); +    R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle); + +    // Set the limit value. +    R_TRY(resource_limit->SetLimitValue(which, limit_value)); + +    return ResultSuccess; +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp b/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_session.cpp b/src/core/hle/kernel/svc/svc_session.cpp new file mode 100644 index 000000000..dac8ce33c --- /dev/null +++ b/src/core/hle/kernel/svc/svc_session.cpp @@ -0,0 +1,103 @@ +// 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_session.h" +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc { +namespace { + +template <typename T> +Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) { +    auto& process = *system.CurrentProcess(); +    auto& handle_table = process.GetHandleTable(); + +    // Declare the session we're going to allocate. +    T* session; + +    // Reserve a new session from the process resource limit. +    // FIXME: LimitableResource_SessionCountMax +    KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax); +    if (session_reservation.Succeeded()) { +        session = T::Create(system.Kernel()); +    } else { +        return ResultLimitReached; + +        // // We couldn't reserve a session. Check that we support dynamically expanding the +        // // resource limit. +        // R_UNLESS(process.GetResourceLimit() == +        //          &system.Kernel().GetSystemResourceLimit(), ResultLimitReached); +        // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached()); + +        // // Try to allocate a session from unused slab memory. +        // session = T::CreateFromUnusedSlabMemory(); +        // R_UNLESS(session != nullptr, ResultLimitReached); +        // ON_RESULT_FAILURE { session->Close(); }; + +        // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to +        // // prevent request exhaustion. +        // // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's +        // // no reason to not do this statically. +        // if constexpr (std::same_as<T, KSession>) { +        //     for (size_t i = 0; i < 2; i++) { +        //         KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory(); +        //         R_UNLESS(request != nullptr, ResultLimitReached); +        //         request->Close(); +        //     } +        // } + +        // We successfully allocated a session, so add the object we allocated to the resource +        // limit. +        // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1); +    } + +    // Check that we successfully created a session. +    R_UNLESS(session != nullptr, ResultOutOfResource); + +    // Initialize the session. +    session->Initialize(nullptr, fmt::format("{}", name)); + +    // Commit the session reservation. +    session_reservation.Commit(); + +    // Ensure that we clean up the session (and its only references are handle table) on function +    // end. +    SCOPE_EXIT({ +        session->GetClientSession().Close(); +        session->GetServerSession().Close(); +    }); + +    // Register the session. +    T::Register(system.Kernel(), session); + +    // Add the server session to the handle table. +    R_TRY(handle_table.Add(out_server, &session->GetServerSession())); + +    // Add the client session to the handle table. +    const auto result = handle_table.Add(out_client, &session->GetClientSession()); + +    if (!R_SUCCEEDED(result)) { +        // Ensure that we maintaing a clean handle state on exit. +        handle_table.Remove(*out_server); +    } + +    return result; +} + +} // namespace + +Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u32 is_light, +                     u64 name) { +    if (is_light) { +        // return CreateSession<KLightSession>(system, out_server, out_client, name); +        return ResultUnknown; +    } else { +        return CreateSession<KSession>(system, out_server, out_client, name); +    } +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_shared_memory.cpp b/src/core/hle/kernel/svc/svc_shared_memory.cpp new file mode 100644 index 000000000..d465bcbe7 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_shared_memory.cpp @@ -0,0 +1,106 @@ +// 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, VAddr 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 = *system.Kernel().CurrentProcess(); +    auto& page_table = process.PageTable(); + +    // 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. +    auto guard = +        SCOPE_GUARD({ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); }); + +    // Map the shared memory. +    R_TRY(shmem->Map(process, address, size, map_perm)); + +    // We succeeded. +    guard.Cancel(); +    return ResultSuccess; +} + +Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size, +                         Svc::MemoryPermission map_perm) { +    return MapSharedMemory(system, shmem_handle, address, size, map_perm); +} + +Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr 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 = *system.Kernel().CurrentProcess(); +    auto& page_table = process.PageTable(); + +    // 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); + +    return ResultSuccess; +} + +Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size) { +    return UnmapSharedMemory(system, shmem_handle, address, size); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp new file mode 100644 index 000000000..1bf6a612a --- /dev/null +++ b/src/core/hle/kernel/svc/svc_synchronization.cpp @@ -0,0 +1,139 @@ +// 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_readable_event.h" +#include "core/hle/kernel/svc.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(system.Kernel().CurrentProcess()->GetHandleTable().Remove(handle), +             ResultInvalidHandle); + +    return ResultSuccess; +} + +Result CloseHandle32(Core::System& system, Handle handle) { +    return CloseHandle(system, handle); +} + +/// 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 = system.Kernel().CurrentProcess()->GetHandleTable(); + +    // Try to reset as readable event. +    { +        KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle); +        if (readable_event.IsNotNull()) { +            return readable_event->Reset(); +        } +    } + +    // Try to reset as process. +    { +        KScopedAutoObject process = handle_table.GetObject<KProcess>(handle); +        if (process.IsNotNull()) { +            return process->Reset(); +        } +    } + +    LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle); + +    return ResultInvalidHandle; +} + +Result ResetSignal32(Core::System& system, Handle handle) { +    return ResetSignal(system, handle); +} + +/// Wait for the given handles to synchronize, timeout after the specified nanoseconds +Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, s32 num_handles, +                           s64 nano_seconds) { +    LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}", +              handles_address, num_handles, nano_seconds); + +    // Ensure number of handles is valid. +    R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange); + +    auto& kernel = system.Kernel(); +    std::vector<KSynchronizationObject*> objs(num_handles); +    const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); +    Handle* handles = system.Memory().GetPointer<Handle>(handles_address); + +    // Copy user handles. +    if (num_handles > 0) { +        // Convert the handles to objects. +        R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, +                                                                         num_handles), +                 ResultInvalidHandle); +        for (const auto& obj : objs) { +            kernel.RegisterInUseObject(obj); +        } +    } + +    // Ensure handles are closed when we're done. +    SCOPE_EXIT({ +        for (s32 i = 0; i < num_handles; ++i) { +            kernel.UnregisterInUseObject(objs[i]); +            objs[i]->Close(); +        } +    }); + +    return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast<s32>(objs.size()), +                                        nano_seconds); +} + +Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, +                             s32 num_handles, u32 timeout_high, s32* index) { +    const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)}; +    return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds); +} + +/// 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 = +        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle); +    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + +    // Cancel the thread's wait. +    thread->WaitCancel(); +    return ResultSuccess; +} + +Result CancelSynchronization32(Core::System& system, Handle handle) { +    return CancelSynchronization(system, handle); +} + +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 = system.Kernel().CurrentProcess(); +    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(core_id); +    } +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp new file mode 100644 index 000000000..dd9f8e8b1 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_thread.cpp @@ -0,0 +1,396 @@ +// 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_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, VAddr entry_point, u64 arg, +                    VAddr stack_bottom, u32 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 = *kernel.CurrentProcess(); +    if (core_id == IdealCoreUseProcessValue) { +        core_id = process.GetIdealCoreId(); +    } + +    // Validate arguments. +    if (!IsValidVirtualCoreId(core_id)) { +        LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id); +        return ResultInvalidCoreId; +    } +    if (((1ULL << core_id) & process.GetCoreMask()) == 0) { +        LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id); +        return ResultInvalidCoreId; +    } + +    if (HighestThreadPriority > priority || priority > LowestThreadPriority) { +        LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority); +        return ResultInvalidPriority; +    } +    if (!process.CheckThreadPriority(priority)) { +        LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority); +        return ResultInvalidPriority; +    } + +    // Reserve a new thread from the process resource limit (waiting up to 100ms). +    KScopedResourceReservation thread_reservation( +        kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1, +        system.CoreTiming().GetGlobalTimeNs().count() + 100000000); +    if (!thread_reservation.Succeeded()) { +        LOG_ERROR(Kernel_SVC, "Could not reserve a new thread"); +        return ResultLimitReached; +    } + +    // Create the thread. +    KThread* thread = KThread::Create(kernel); +    if (!thread) { +        LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached."); +        return 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, &process)); +    } + +    // Set the thread name for debugging purposes. +    thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle)); + +    // Commit the thread reservation. +    thread_reservation.Commit(); + +    // Register the new thread. +    KThread::Register(kernel, thread); + +    // Add the thread to the handle table. +    R_TRY(process.GetHandleTable().Add(out_handle, thread)); + +    return ResultSuccess; +} + +Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority, u32 entry_point, +                      u32 arg, u32 stack_top, s32 processor_id) { +    return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id); +} + +/// 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 = +        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); +    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + +    // Try to start the thread. +    R_TRY(thread->Run()); + +    // If we succeeded, persist a reference to the thread. +    thread->Open(); +    system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe()); + +    return ResultSuccess; +} + +Result StartThread32(Core::System& system, Handle thread_handle) { +    return StartThread(system, thread_handle); +} + +/// Called when a thread exits +void ExitThread(Core::System& system) { +    LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); + +    auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); +    system.GlobalSchedulerContext().RemoveThread(current_thread); +    current_thread->Exit(); +    system.Kernel().UnregisterInUseObject(current_thread); +} + +void ExitThread32(Core::System& system) { +    ExitThread(system); +} + +/// Sleep the current thread +void SleepThread(Core::System& system, s64 nanoseconds) { +    auto& kernel = system.Kernel(); +    const auto yield_type = static_cast<Svc::YieldType>(nanoseconds); + +    LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); + +    // When the input tick is positive, sleep. +    if (nanoseconds > 0) { +        // Convert the timeout from nanoseconds to ticks. +        // NOTE: Nintendo does not use this conversion logic in WaitSynchronization... + +        // Sleep. +        // NOTE: Nintendo does not check the result of this sleep. +        static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds)); +    } 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. +        ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds); +    } +} + +void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) { +    const auto nanoseconds = static_cast<s64>(u64{nanoseconds_low} | (u64{nanoseconds_high} << 32)); +    SleepThread(system, nanoseconds); +} + +/// Gets the thread context +Result GetThreadContext(Core::System& system, VAddr 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 = +        kernel.CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); +    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + +    // Require the handle be to a non-current thread in the current process. +    const auto* current_process = kernel.CurrentProcess(); +    R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId); + +    // Verify that the thread isn't terminated. +    R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested); + +    /// Check that the thread is not the current one. +    /// NOTE: Nintendo does not check this, and thus the following loop will deadlock. +    R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId); + +    // Try to get the thread context until the thread isn't current on any core. +    while (true) { +        KScopedSchedulerLock sl{kernel}; + +        // TODO(bunnei): Enforce that thread is suspended for debug here. + +        // If the thread's raw state isn't runnable, check if it's current on some core. +        if (thread->GetRawState() != ThreadState::Runnable) { +            bool current = false; +            for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { +                if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) { +                    current = true; +                    break; +                } +            } + +            // If the thread is current, retry until it isn't. +            if (current) { +                continue; +            } +        } + +        // Get the thread context. +        std::vector<u8> context; +        R_TRY(thread->GetThreadContext3(context)); + +        // Copy the thread context to user space. +        system.Memory().WriteBlock(out_context, context.data(), context.size()); + +        return ResultSuccess; +    } + +    return ResultSuccess; +} + +Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) { +    return GetThreadContext(system, out_context, thread_handle); +} + +/// Gets the priority for the specified thread +Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) { +    LOG_TRACE(Kernel_SVC, "called"); + +    // Get the thread from its handle. +    KScopedAutoObject thread = +        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle); +    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + +    // Get the thread's priority. +    *out_priority = thread->GetPriority(); +    return ResultSuccess; +} + +Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) { +    return GetThreadPriority(system, out_priority, handle); +} + +/// Sets the priority for the specified thread +Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) { +    // Get the current process. +    KProcess& process = *system.Kernel().CurrentProcess(); + +    // 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); +    return ResultSuccess; +} + +Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) { +    return SetThreadPriority(system, thread_handle, priority); +} + +Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, +                     u32 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); +        return ResultOutOfRange; +    } + +    auto* const current_process = system.Kernel().CurrentProcess(); +    const auto total_copy_size = out_thread_ids_size * sizeof(u64); + +    if (out_thread_ids_size > 0 && +        !current_process->PageTable().IsInsideAddressSpace(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); +        return ResultInvalidCurrentMemory; +    } + +    auto& memory = system.Memory(); +    const auto& thread_list = current_process->GetThreadList(); +    const auto num_threads = thread_list.size(); +    const auto copy_amount = std::min(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); +    return ResultSuccess; +} + +Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id, +                         u64* out_affinity_mask) { +    LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); + +    // Get the thread from its handle. +    KScopedAutoObject thread = +        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); +    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + +    // Get the core mask. +    R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask)); + +    return ResultSuccess; +} + +Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id, +                           u32* out_affinity_mask_low, u32* out_affinity_mask_high) { +    u64 out_affinity_mask{}; +    const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask); +    *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32); +    *out_affinity_mask_low = static_cast<u32>(out_affinity_mask); +    return result; +} + +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 = system.Kernel().CurrentProcess()->GetIdealCoreId(); +        affinity_mask = (1ULL << core_id); +    } else { +        // Validate the affinity mask. +        const u64 process_core_mask = system.Kernel().CurrentProcess()->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 = +        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); +    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + +    // Set the core mask. +    R_TRY(thread->SetCoreMask(core_id, affinity_mask)); + +    return ResultSuccess; +} + +Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id, +                           u32 affinity_mask_low, u32 affinity_mask_high) { +    const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32); +    return SetThreadCoreMask(system, thread_handle, 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 = +        system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); +    R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); + +    // Get the thread's id. +    *out_thread_id = thread->GetId(); +    return ResultSuccess; +} + +Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high, +                     Handle thread_handle) { +    u64 out_thread_id{}; +    const Result result{GetThreadId(system, &out_thread_id, thread_handle)}; + +    *out_thread_id_low = static_cast<u32>(out_thread_id >> 32); +    *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max()); + +    return result; +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_thread_profiler.cpp b/src/core/hle/kernel/svc/svc_thread_profiler.cpp new file mode 100644 index 000000000..299e22ae6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_thread_profiler.cpp @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/svc.h" + +namespace Kernel::Svc {} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_tick.cpp b/src/core/hle/kernel/svc/svc_tick.cpp new file mode 100644 index 000000000..e9b4fd5a6 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_tick.cpp @@ -0,0 +1,33 @@ +// 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 +u64 GetSystemTick(Core::System& system) { +    LOG_TRACE(Kernel_SVC, "called"); + +    auto& core_timing = system.CoreTiming(); + +    // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) +    const u64 result{core_timing.GetClockTicks()}; + +    if (!system.Kernel().IsMulticore()) { +        core_timing.AddTicks(400U); +    } + +    return result; +} + +void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) { +    const auto time = GetSystemTick(system); +    *time_low = static_cast<u32>(time); +    *time_high = static_cast<u32>(time >> 32); +} + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp new file mode 100644 index 000000000..b14ae24a1 --- /dev/null +++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp @@ -0,0 +1,79 @@ +// 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, VAddr 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 = *kernel.CurrentProcess(); +    auto& handle_table = process.GetHandleTable(); + +    // Reserve a new transfer memory from the process resource limit. +    KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(), +                                                 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.PageTable().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_TRY(handle_table.Add(out, trmem)); + +    return ResultSuccess; +} + +Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size, +                              MemoryPermission map_perm) { +    return CreateTransferMemory(system, out, address, size, map_perm); +} +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 1ea8c7fbc..052be40dd 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -172,11 +172,11 @@ void SvcWrap64(Core::System& system) {  }  // Used by GetResourceLimitLimitValue. -template <Result func(Core::System&, u64*, Handle, LimitableResource)> +template <Result func(Core::System&, u64*, Handle, Svc::LimitableResource)>  void SvcWrap64(Core::System& system) {      u64 param_1 = 0;      const u32 retval = func(system, ¶m_1, static_cast<Handle>(Param(system, 1)), -                            static_cast<LimitableResource>(Param(system, 2))) +                            static_cast<Svc::LimitableResource>(Param(system, 2)))                             .raw;      system.CurrentArmInterface().SetReg(1, param_1); @@ -189,10 +189,10 @@ void SvcWrap64(Core::System& system) {  }  // Used by SetResourceLimitLimitValue -template <Result func(Core::System&, Handle, LimitableResource, u64)> +template <Result func(Core::System&, Handle, Svc::LimitableResource, u64)>  void SvcWrap64(Core::System& system) {      FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), -                            static_cast<LimitableResource>(Param(system, 1)), Param(system, 2)) +                            static_cast<Svc::LimitableResource>(Param(system, 1)), Param(system, 2))                             .raw);  } | 
