yuzu/src/core/hle/kernel/svc/svc_transfer_memory.cpp

160 lines
6.0 KiB
C++
Executable File

// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/svc.h"
namespace Kernel::Svc {
namespace {
constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
switch (perm) {
case MemoryPermission::None:
case MemoryPermission::Read:
case MemoryPermission::ReadWrite:
return true;
default:
return false;
}
}
} // Anonymous namespace
/// Creates a TransferMemory object
Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64 size,
MemoryPermission map_perm) {
auto& kernel = system.Kernel();
// Validate the size.
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
R_UNLESS(size > 0, ResultInvalidSize);
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
// Validate the permissions.
R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
// Get the current process and handle table.
auto& process = GetCurrentProcess(kernel);
auto& handle_table = process.GetHandleTable();
// Reserve a new transfer memory from the process resource limit.
KScopedResourceReservation trmem_reservation(std::addressof(process),
LimitableResource::TransferMemoryCountMax);
R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
// Create the transfer memory.
KTransferMemory* trmem = KTransferMemory::Create(kernel);
R_UNLESS(trmem != nullptr, ResultOutOfResource);
// Ensure the only reference is in the handle table when we're done.
SCOPE_EXIT({ trmem->Close(); });
// Ensure that the region is in range.
R_UNLESS(process.GetPageTable().Contains(address, size), ResultInvalidCurrentMemory);
// Initialize the transfer memory.
R_TRY(trmem->Initialize(address, size, map_perm));
// Commit the reservation.
trmem_reservation.Commit();
// Register the transfer memory.
KTransferMemory::Register(kernel, trmem);
// Add the transfer memory to the handle table.
R_RETURN(handle_table.Add(out, trmem));
}
Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size,
MemoryPermission map_perm) {
// Validate the address/size.
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
R_UNLESS(size > 0, ResultInvalidSize);
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
// Validate the permission.
R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidState);
// Get the transfer memory.
KScopedAutoObject trmem = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KTransferMemory>(trmem_handle);
R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle);
// Verify that the mapping is in range.
R_UNLESS(GetCurrentProcess(system.Kernel())
.GetPageTable()
.CanContain(address, size, KMemoryState::Transferred),
ResultInvalidMemoryRegion);
// Map the transfer memory.
R_TRY(trmem->Map(address, size, map_perm));
// We succeeded.
R_SUCCEED();
}
Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address,
uint64_t size) {
// Validate the address/size.
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
R_UNLESS(size > 0, ResultInvalidSize);
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
// Get the transfer memory.
KScopedAutoObject trmem = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KTransferMemory>(trmem_handle);
R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle);
// Verify that the mapping is in range.
R_UNLESS(GetCurrentProcess(system.Kernel())
.GetPageTable()
.CanContain(address, size, KMemoryState::Transferred),
ResultInvalidMemoryRegion);
// Unmap the transfer memory.
R_TRY(trmem->Unmap(address, size));
R_SUCCEED();
}
Result MapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address,
uint64_t size, MemoryPermission owner_perm) {
R_RETURN(MapTransferMemory(system, trmem_handle, address, size, owner_perm));
}
Result UnmapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address,
uint64_t size) {
R_RETURN(UnmapTransferMemory(system, trmem_handle, address, size));
}
Result CreateTransferMemory64(Core::System& system, Handle* out_handle, uint64_t address,
uint64_t size, MemoryPermission map_perm) {
R_RETURN(CreateTransferMemory(system, out_handle, address, size, map_perm));
}
Result MapTransferMemory64From32(Core::System& system, Handle trmem_handle, uint32_t address,
uint32_t size, MemoryPermission owner_perm) {
R_RETURN(MapTransferMemory(system, trmem_handle, address, size, owner_perm));
}
Result UnmapTransferMemory64From32(Core::System& system, Handle trmem_handle, uint32_t address,
uint32_t size) {
R_RETURN(UnmapTransferMemory(system, trmem_handle, address, size));
}
Result CreateTransferMemory64From32(Core::System& system, Handle* out_handle, uint32_t address,
uint32_t size, MemoryPermission map_perm) {
R_RETURN(CreateTransferMemory(system, out_handle, address, size, map_perm));
}
} // namespace Kernel::Svc