Merge pull request #11686 from liamwhite/trmem
kernel: implement transfer memory
This commit is contained in:
commit
8ac8d703b9
|
@ -2949,6 +2949,23 @@ Result KPageTable::UnlockForIpcUserBuffer(KProcessAddress address, size_t size)
|
||||||
KMemoryAttribute::Locked, nullptr));
|
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) {
|
Result KPageTable::LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size) {
|
||||||
R_RETURN(this->LockMemoryAndOpen(
|
R_RETURN(this->LockMemoryAndOpen(
|
||||||
out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
|
out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
|
||||||
|
|
|
@ -104,6 +104,9 @@ public:
|
||||||
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
|
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
|
||||||
Result CleanupForIpcClient(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 LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size);
|
||||||
Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg);
|
Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg);
|
||||||
Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
|
Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// 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_process.h"
|
||||||
#include "core/hle/kernel/k_resource_limit.h"
|
#include "core/hle/kernel/k_resource_limit.h"
|
||||||
#include "core/hle/kernel/k_transfer_memory.h"
|
#include "core/hle/kernel/k_transfer_memory.h"
|
||||||
|
@ -9,28 +10,50 @@
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
KTransferMemory::KTransferMemory(KernelCore& kernel)
|
KTransferMemory::KTransferMemory(KernelCore& kernel)
|
||||||
: KAutoObjectWithSlabHeapAndContainer{kernel} {}
|
: KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock{kernel} {}
|
||||||
|
|
||||||
KTransferMemory::~KTransferMemory() = default;
|
KTransferMemory::~KTransferMemory() = default;
|
||||||
|
|
||||||
Result KTransferMemory::Initialize(KProcessAddress address, std::size_t size,
|
Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size,
|
||||||
Svc::MemoryPermission owner_perm) {
|
Svc::MemoryPermission own_perm) {
|
||||||
// Set members.
|
// Set members.
|
||||||
m_owner = GetCurrentProcessPointer(m_kernel);
|
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.
|
// Set remaining tracking members.
|
||||||
m_owner->Open();
|
m_owner->Open();
|
||||||
m_owner_perm = owner_perm;
|
m_owner_perm = own_perm;
|
||||||
m_address = address;
|
m_address = addr;
|
||||||
m_size = size;
|
|
||||||
m_is_initialized = true;
|
m_is_initialized = true;
|
||||||
|
m_is_mapped = false;
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
pg_guard.Cancel();
|
||||||
R_SUCCEED();
|
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) {
|
void KTransferMemory::PostDestroy(uintptr_t arg) {
|
||||||
KProcess* owner = reinterpret_cast<KProcess*>(arg);
|
KProcess* owner = reinterpret_cast<KProcess*>(arg);
|
||||||
|
@ -38,4 +61,54 @@ void KTransferMemory::PostDestroy(uintptr_t arg) {
|
||||||
owner->Close();
|
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
|
} // namespace Kernel
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "core/hle/kernel/k_page_group.h"
|
||||||
#include "core/hle/kernel/slab_helpers.h"
|
#include "core/hle/kernel/slab_helpers.h"
|
||||||
#include "core/hle/kernel/svc_types.h"
|
#include "core/hle/kernel/svc_types.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
@ -48,16 +51,19 @@ public:
|
||||||
return m_address;
|
return m_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t GetSize() const {
|
size_t GetSize() const;
|
||||||
return m_is_initialized ? m_size : 0;
|
|
||||||
}
|
Result Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm);
|
||||||
|
Result Unmap(KProcessAddress address, size_t size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::optional<KPageGroup> m_page_group{};
|
||||||
KProcess* m_owner{};
|
KProcess* m_owner{};
|
||||||
KProcessAddress m_address{};
|
KProcessAddress m_address{};
|
||||||
|
KLightLock m_lock;
|
||||||
Svc::MemoryPermission m_owner_perm{};
|
Svc::MemoryPermission m_owner_perm{};
|
||||||
size_t m_size{};
|
|
||||||
bool m_is_initialized{};
|
bool m_is_initialized{};
|
||||||
|
bool m_is_mapped{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Kernel
|
} // 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,
|
Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size,
|
||||||
MemoryPermission owner_perm) {
|
MemoryPermission map_perm) {
|
||||||
UNIMPLEMENTED();
|
// Validate the address/size.
|
||||||
R_THROW(ResultNotImplemented);
|
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,
|
Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address,
|
||||||
uint64_t size) {
|
uint64_t size) {
|
||||||
UNIMPLEMENTED();
|
// Validate the address/size.
|
||||||
R_THROW(ResultNotImplemented);
|
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,
|
Result MapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address,
|
||||||
|
|
Loading…
Reference in a new issue