summaryrefslogblamecommitdiffstats
path: root/src/core/hle/kernel/svc/svc_shared_memory.cpp
blob: d465bcbe7cd9ef1c813e4b3c918d438314c9d61f (plain) (tree)









































































































                                                                                                    
// 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