summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/svc/svc_code_memory.cpp
blob: ec256b757c9b46adb79dde87782be6950b1359e1 (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
162
163
164
165
166
167
168
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc.h"

namespace Kernel::Svc {
namespace {

constexpr bool IsValidMapCodeMemoryPermission(MemoryPermission perm) {
    return perm == MemoryPermission::ReadWrite;
}

constexpr bool IsValidMapToOwnerCodeMemoryPermission(MemoryPermission perm) {
    return perm == MemoryPermission::Read || perm == MemoryPermission::ReadExecute;
}

constexpr bool IsValidUnmapCodeMemoryPermission(MemoryPermission perm) {
    return perm == MemoryPermission::None;
}

constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(MemoryPermission perm) {
    return perm == MemoryPermission::None;
}

} // namespace

Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
    LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);

    // Get kernel instance.
    auto& kernel = system.Kernel();

    // Validate 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);

    // Create the code memory.

    KCodeMemory* code_mem = KCodeMemory::Create(kernel);
    R_UNLESS(code_mem != nullptr, ResultOutOfResource);

    // Verify that the region is in range.
    R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size),
             ResultInvalidCurrentMemory);

    // Initialize the code memory.
    R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size));

    // Register the code memory.
    KCodeMemory::Register(kernel, code_mem);

    // Add the code memory to the handle table.
    R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem));

    code_mem->Close();

    return ResultSuccess;
}

Result ControlCodeMemory(Core::System& system, Handle code_memory_handle,
                         CodeMemoryOperation operation, VAddr address, size_t size,
                         MemoryPermission perm) {

    LOG_TRACE(Kernel_SVC,
              "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
              "permission=0x{:X}",
              code_memory_handle, operation, address, size, 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);

    // Get the code memory from its handle.
    KScopedAutoObject code_mem =
        system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
    R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);

    // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.
    // This enables homebrew usage of these SVCs for JIT.

    // Perform the operation.
    switch (operation) {
    case CodeMemoryOperation::Map: {
        // Check that the region is in range.
        R_UNLESS(
            system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
            ResultInvalidMemoryRegion);

        // Check the memory permission.
        R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);

        // Map the memory.
        R_TRY(code_mem->Map(address, size));
    } break;
    case CodeMemoryOperation::Unmap: {
        // Check that the region is in range.
        R_UNLESS(
            system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
            ResultInvalidMemoryRegion);

        // Check the memory permission.
        R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);

        // Unmap the memory.
        R_TRY(code_mem->Unmap(address, size));
    } break;
    case CodeMemoryOperation::MapToOwner: {
        // Check that the region is in range.
        R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
                                                              KMemoryState::GeneratedCode),
                 ResultInvalidMemoryRegion);

        // Check the memory permission.
        R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);

        // Map the memory to its owner.
        R_TRY(code_mem->MapToOwner(address, size, perm));
    } break;
    case CodeMemoryOperation::UnmapFromOwner: {
        // Check that the region is in range.
        R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
                                                              KMemoryState::GeneratedCode),
                 ResultInvalidMemoryRegion);

        // Check the memory permission.
        R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);

        // Unmap the memory from its owner.
        R_TRY(code_mem->UnmapFromOwner(address, size));
    } break;
    default:
        return ResultInvalidEnumValue;
    }

    return ResultSuccess;
}

Result CreateCodeMemory64(Core::System& system, Handle* out_handle, uint64_t address,
                          uint64_t size) {
    R_RETURN(CreateCodeMemory(system, out_handle, address, size));
}

Result ControlCodeMemory64(Core::System& system, Handle code_memory_handle,
                           CodeMemoryOperation operation, uint64_t address, uint64_t size,
                           MemoryPermission perm) {
    R_RETURN(ControlCodeMemory(system, code_memory_handle, operation, address, size, perm));
}

Result CreateCodeMemory64From32(Core::System& system, Handle* out_handle, uint32_t address,
                                uint32_t size) {
    R_RETURN(CreateCodeMemory(system, out_handle, address, size));
}

Result ControlCodeMemory64From32(Core::System& system, Handle code_memory_handle,
                                 CodeMemoryOperation operation, uint64_t address, uint64_t size,
                                 MemoryPermission perm) {
    R_RETURN(ControlCodeMemory(system, code_memory_handle, operation, address, size, perm));
}

} // namespace Kernel::Svc