summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/svc/svc_transfer_memory.cpp
blob: 2ea0d44215741a01d1ba4c426b79636d9c080fc0 (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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// 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