summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
blob: 7d0a99988812f73d7a74ea223daff42c122733b9 (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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <bit>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <vector>

#include "common/address_space.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/scratch_buffer.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"

namespace Tegra {
class MemoryManager;
} // namespace Tegra

namespace Service::Nvidia {
class Module;
}

namespace Service::Nvidia::NvCore {
class Container;
class NvMap;
} // namespace Service::Nvidia::NvCore

namespace Service::Nvidia::Devices {

enum class MappingFlags : u32 {
    None = 0,
    Fixed = 1 << 0,
    Sparse = 1 << 1,
    Remap = 1 << 8,
};
DECLARE_ENUM_FLAG_OPERATORS(MappingFlags);

class nvhost_as_gpu final : public nvdevice {
public:
    explicit nvhost_as_gpu(Core::System& system_, Module& module, NvCore::Container& core);
    ~nvhost_as_gpu() override;

    NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
                    std::span<u8> output) override;
    NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
                    std::span<const u8> inline_input, std::span<u8> output) override;
    NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
                    std::span<u8> inline_output) override;

    void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override;
    void OnClose(DeviceFD fd) override;

    Kernel::KEvent* QueryEvent(u32 event_id) override;

    struct VaRegion {
        u64 offset;
        u32 page_size;
        u32 _pad0_;
        u64 pages;
    };
    static_assert(sizeof(VaRegion) == 0x18);

private:
    struct IoctlAllocAsEx {
        u32_le flags{}; // usually passes 1
        s32_le as_fd{}; // ignored; passes 0
        u32_le big_page_size{};
        u32_le reserved{}; // ignored; passes 0
        u64_le va_range_start{};
        u64_le va_range_end{};
        u64_le va_range_split{};
    };
    static_assert(sizeof(IoctlAllocAsEx) == 40, "IoctlAllocAsEx is incorrect size");

    struct IoctlAllocSpace {
        u32_le pages{};
        u32_le page_size{};
        MappingFlags flags{};
        INSERT_PADDING_WORDS(1);
        union {
            u64_le offset;
            u64_le align;
        };
    };
    static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitializeEx is incorrect size");

    struct IoctlFreeSpace {
        u64_le offset{};
        u32_le pages{};
        u32_le page_size{};
    };
    static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size");

    struct IoctlRemapEntry {
        u16 flags;
        u16 kind;
        NvCore::NvMap::Handle::Id handle;
        u32 handle_offset_big_pages;
        u32 as_offset_big_pages;
        u32 big_pages;
    };
    static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");

    struct IoctlMapBufferEx {
        MappingFlags flags{}; // bit0: fixed_offset, bit2: cacheable
        u32_le kind{};        // -1 is default
        NvCore::NvMap::Handle::Id handle;
        u32_le page_size{}; // 0 means don't care
        s64_le buffer_offset{};
        u64_le mapping_size{};
        s64_le offset{};
    };
    static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size");

    struct IoctlUnmapBuffer {
        s64_le offset{};
    };
    static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size");

    struct IoctlBindChannel {
        s32_le fd{};
    };
    static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size");

    struct IoctlGetVaRegions {
        u64_le buf_addr{}; // (contained output user ptr on linux, ignored)
        u32_le buf_size{}; // forced to 2*sizeof(struct va_region)
        u32_le reserved{};
        std::array<VaRegion, 2> regions{};
    };
    static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2,
                  "IoctlGetVaRegions is incorrect size");

    NvResult AllocAsEx(IoctlAllocAsEx& params);
    NvResult AllocateSpace(IoctlAllocSpace& params);
    NvResult Remap(std::span<IoctlRemapEntry> params);
    NvResult MapBufferEx(IoctlMapBufferEx& params);
    NvResult UnmapBuffer(IoctlUnmapBuffer& params);
    NvResult FreeSpace(IoctlFreeSpace& params);
    NvResult BindChannel(IoctlBindChannel& params);

    void GetVARegionsImpl(IoctlGetVaRegions& params);
    NvResult GetVARegions1(IoctlGetVaRegions& params);
    NvResult GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions);

    void FreeMappingLocked(u64 offset);

    Module& module;

    NvCore::Container& container;
    NvCore::NvMap& nvmap;

    struct Mapping {
        NvCore::NvMap::Handle::Id handle;
        DAddr ptr;
        u64 offset;
        u64 size;
        bool fixed;
        bool big_page; // Only valid if fixed == false
        bool sparse_alloc;

        Mapping(NvCore::NvMap::Handle::Id handle_, DAddr ptr_, u64 offset_, u64 size_, bool fixed_,
                bool big_page_, bool sparse_alloc_)
            : handle(handle_), ptr(ptr_), offset(offset_), size(size_), fixed(fixed_),
              big_page(big_page_), sparse_alloc(sparse_alloc_) {}
    };

    struct Allocation {
        u64 size;
        std::list<std::shared_ptr<Mapping>> mappings;
        u32 page_size;
        bool sparse;
        bool big_pages;
    };

    std::map<u64, std::shared_ptr<Mapping>>
        mapping_map; //!< This maps the base addresses of mapped buffers to their total sizes and
                     //!< mapping type, this is needed as what was originally a single buffer may
                     //!< have been split into multiple GPU side buffers with the remap flag.
    std::map<u64, Allocation> allocation_map; //!< Holds allocations created by AllocSpace from
                                              //!< which fixed buffers can be mapped into
    std::mutex mutex;                         //!< Locks all AS operations

    struct VM {
        static constexpr u32 YUZU_PAGESIZE{0x1000};
        static constexpr u32 PAGE_SIZE_BITS{std::countr_zero(YUZU_PAGESIZE)};

        static constexpr u32 SUPPORTED_BIG_PAGE_SIZES{0x30000};
        static constexpr u32 DEFAULT_BIG_PAGE_SIZE{0x20000};
        u32 big_page_size{DEFAULT_BIG_PAGE_SIZE};
        u32 big_page_size_bits{std::countr_zero(DEFAULT_BIG_PAGE_SIZE)};

        static constexpr u32 VA_START_SHIFT{10};
        static constexpr u64 DEFAULT_VA_SPLIT{1ULL << 34};
        static constexpr u64 DEFAULT_VA_RANGE{1ULL << 37};
        u64 va_range_start{DEFAULT_BIG_PAGE_SIZE << VA_START_SHIFT};
        u64 va_range_split{DEFAULT_VA_SPLIT};
        u64 va_range_end{DEFAULT_VA_RANGE};

        using Allocator = Common::FlatAllocator<u32, 0, 32>;

        std::unique_ptr<Allocator> big_page_allocator;
        std::shared_ptr<Allocator>
            small_page_allocator; //! Shared as this is also used by nvhost::GpuChannel

        bool initialised{};
    } vm;
    std::shared_ptr<Tegra::MemoryManager> gmmu;
};

} // namespace Service::Nvidia::Devices