kernel: implement transfer memory
This commit is contained in:
parent
7a0da729b4
commit
e797a917a9
5 changed files with 160 additions and 17 deletions
|
@ -2949,6 +2949,23 @@ Result KPageTable::UnlockForIpcUserBuffer(KProcessAddress address, size_t size)
|
|||
KMemoryAttribute::Locked, nullptr));
|
||||
}
|
||||
|
||||
Result KPageTable::LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
|
||||
KMemoryPermission perm) {
|
||||
R_RETURN(this->LockMemoryAndOpen(out, nullptr, address, size, KMemoryState::FlagCanTransfer,
|
||||
KMemoryState::FlagCanTransfer, KMemoryPermission::All,
|
||||
KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
|
||||
KMemoryAttribute::None, perm, KMemoryAttribute::Locked));
|
||||
}
|
||||
|
||||
Result KPageTable::UnlockForTransferMemory(KProcessAddress address, size_t size,
|
||||
const KPageGroup& pg) {
|
||||
R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanTransfer,
|
||||
KMemoryState::FlagCanTransfer, KMemoryPermission::None,
|
||||
KMemoryPermission::None, KMemoryAttribute::All,
|
||||
KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
|
||||
KMemoryAttribute::Locked, std::addressof(pg)));
|
||||
}
|
||||
|
||||
Result KPageTable::LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size) {
|
||||
R_RETURN(this->LockMemoryAndOpen(
|
||||
out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
|
||||
|
|
|
@ -104,6 +104,9 @@ public:
|
|||
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
|
||||
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state);
|
||||
|
||||
Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
|
||||
KMemoryPermission perm);
|
||||
Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
|
||||
Result LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size);
|
||||
Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg);
|
||||
Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
|
@ -9,28 +10,50 @@
|
|||
namespace Kernel {
|
||||
|
||||
KTransferMemory::KTransferMemory(KernelCore& kernel)
|
||||
: KAutoObjectWithSlabHeapAndContainer{kernel} {}
|
||||
: KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock{kernel} {}
|
||||
|
||||
KTransferMemory::~KTransferMemory() = default;
|
||||
|
||||
Result KTransferMemory::Initialize(KProcessAddress address, std::size_t size,
|
||||
Svc::MemoryPermission owner_perm) {
|
||||
Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size,
|
||||
Svc::MemoryPermission own_perm) {
|
||||
// Set members.
|
||||
m_owner = GetCurrentProcessPointer(m_kernel);
|
||||
|
||||
// TODO(bunnei): Lock for transfer memory
|
||||
// Get the owner page table.
|
||||
auto& page_table = m_owner->GetPageTable();
|
||||
|
||||
// Construct the page group, guarding to make sure our state is valid on exit.
|
||||
m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager());
|
||||
auto pg_guard = SCOPE_GUARD({ m_page_group.reset(); });
|
||||
|
||||
// Lock the memory.
|
||||
R_TRY(page_table.LockForTransferMemory(std::addressof(*m_page_group), addr, size,
|
||||
ConvertToKMemoryPermission(own_perm)));
|
||||
|
||||
// Set remaining tracking members.
|
||||
m_owner->Open();
|
||||
m_owner_perm = owner_perm;
|
||||
m_address = address;
|
||||
m_size = size;
|
||||
m_owner_perm = own_perm;
|
||||
m_address = addr;
|
||||
m_is_initialized = true;
|
||||
m_is_mapped = false;
|
||||
|
||||
// We succeeded.
|
||||
pg_guard.Cancel();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void KTransferMemory::Finalize() {}
|
||||
void KTransferMemory::Finalize() {
|
||||
// Unlock.
|
||||
if (!m_is_mapped) {
|
||||
const size_t size = m_page_group->GetNumPages() * PageSize;
|
||||
ASSERT(R_SUCCEEDED(
|
||||
m_owner->GetPageTable().UnlockForTransferMemory(m_address, size, *m_page_group)));
|
||||
}
|
||||
|
||||
// Close the page group.
|
||||
m_page_group->Close();
|
||||
m_page_group->Finalize();
|
||||
}
|
||||
|
||||
void KTransferMemory::PostDestroy(uintptr_t arg) {
|
||||
KProcess* owner = reinterpret_cast<KProcess*>(arg);
|
||||
|
@ -38,4 +61,54 @@ void KTransferMemory::PostDestroy(uintptr_t arg) {
|
|||
owner->Close();
|
||||
}
|
||||
|
||||
Result KTransferMemory::Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm) {
|
||||
// Validate the size.
|
||||
R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
||||
|
||||
// Validate the permission.
|
||||
R_UNLESS(m_owner_perm == map_perm, ResultInvalidState);
|
||||
|
||||
// Lock ourselves.
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
// Ensure we're not already mapped.
|
||||
R_UNLESS(!m_is_mapped, ResultInvalidState);
|
||||
|
||||
// Map the memory.
|
||||
const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None)
|
||||
? KMemoryState::Transfered
|
||||
: KMemoryState::SharedTransfered;
|
||||
R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup(
|
||||
address, *m_page_group, state, KMemoryPermission::UserReadWrite));
|
||||
|
||||
// Mark ourselves as mapped.
|
||||
m_is_mapped = true;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result KTransferMemory::Unmap(KProcessAddress address, size_t size) {
|
||||
// Validate the size.
|
||||
R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
|
||||
|
||||
// Lock ourselves.
|
||||
KScopedLightLock lk(m_lock);
|
||||
|
||||
// Unmap the memory.
|
||||
const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None)
|
||||
? KMemoryState::Transfered
|
||||
: KMemoryState::SharedTransfered;
|
||||
R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, state));
|
||||
|
||||
// Mark ourselves as unmapped.
|
||||
ASSERT(m_is_mapped);
|
||||
m_is_mapped = false;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
size_t KTransferMemory::GetSize() const {
|
||||
return m_is_initialized ? m_page_group->GetNumPages() * PageSize : 0;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "core/hle/kernel/k_page_group.h"
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
#include "core/hle/result.h"
|
||||
|
@ -48,16 +51,19 @@ public:
|
|||
return m_address;
|
||||
}
|
||||
|
||||
size_t GetSize() const {
|
||||
return m_is_initialized ? m_size : 0;
|
||||
}
|
||||
size_t GetSize() const;
|
||||
|
||||
Result Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm);
|
||||
Result Unmap(KProcessAddress address, size_t size);
|
||||
|
||||
private:
|
||||
std::optional<KPageGroup> m_page_group{};
|
||||
KProcess* m_owner{};
|
||||
KProcessAddress m_address{};
|
||||
KLightLock m_lock;
|
||||
Svc::MemoryPermission m_owner_perm{};
|
||||
size_t m_size{};
|
||||
bool m_is_initialized{};
|
||||
bool m_is_mapped{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
|
|
@ -71,15 +71,59 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64
|
|||
}
|
||||
|
||||
Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size,
|
||||
MemoryPermission owner_perm) {
|
||||
UNIMPLEMENTED();
|
||||
R_THROW(ResultNotImplemented);
|
||||
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::Transfered),
|
||||
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) {
|
||||
UNIMPLEMENTED();
|
||||
R_THROW(ResultNotImplemented);
|
||||
// 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::Transfered),
|
||||
ResultInvalidMemoryRegion);
|
||||
|
||||
// Unmap the transfer memory.
|
||||
R_TRY(trmem->Unmap(address, size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address,
|
||||
|
|
Loading…
Reference in a new issue