summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/k_transfer_memory.cpp
blob: 0e2e11743b00b320bc55909e4e46dbb1bd673ae9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// 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"
#include "core/hle/kernel/kernel.h"

namespace Kernel {

KTransferMemory::KTransferMemory(KernelCore& kernel)
    : KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock{kernel} {}

KTransferMemory::~KTransferMemory() = default;

Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size,
                                   Svc::MemoryPermission own_perm) {
    // Set members.
    m_owner = GetCurrentProcessPointer(m_kernel);

    // 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 = own_perm;
    m_address = addr;
    m_is_initialized = true;
    m_is_mapped = false;

    // We succeeded.
    pg_guard.Cancel();
    R_SUCCEED();
}

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);
    owner->GetResourceLimit()->Release(LimitableResource::TransferMemoryCountMax, 1);
    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