summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/core.cpp15
-rw-r--r--src/core/core.h19
-rw-r--r--src/core/hardware_interrupt_manager.cpp32
-rw-r--r--src/core/hardware_interrupt_manager.h32
-rw-r--r--src/core/hle/service/nvdrv/core/container.cpp50
-rw-r--r--src/core/hle/service/nvdrv/core/container.h52
-rw-r--r--src/core/hle/service/nvdrv/core/nvmap.cpp272
-rw-r--r--src/core/hle/service/nvdrv/core/nvmap.h175
-rw-r--r--src/core/hle/service/nvdrv/core/syncpoint_manager.cpp121
-rw-r--r--src/core/hle/service/nvdrv/core/syncpoint_manager.h134
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp19
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h15
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp492
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h191
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp363
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h114
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp25
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp129
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h54
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp16
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp81
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h23
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp230
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h56
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h17
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp130
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h125
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp31
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.h2
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.cpp38
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.h84
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.cpp9
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.h8
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp10
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.h9
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp29
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h1
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp16
-rw-r--r--src/core/hle/service/vi/display/vi_display.h7
-rw-r--r--src/core/hle/service/vi/vi.cpp1
-rw-r--r--src/core/memory.cpp9
-rw-r--r--src/core/memory.h1
48 files changed, 2255 insertions, 1046 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 8e3fd4505..95302c419 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -138,8 +138,6 @@ add_library(core STATIC
138 frontend/emu_window.h 138 frontend/emu_window.h
139 frontend/framebuffer_layout.cpp 139 frontend/framebuffer_layout.cpp
140 frontend/framebuffer_layout.h 140 frontend/framebuffer_layout.h
141 hardware_interrupt_manager.cpp
142 hardware_interrupt_manager.h
143 hid/emulated_console.cpp 141 hid/emulated_console.cpp
144 hid/emulated_console.h 142 hid/emulated_console.h
145 hid/emulated_controller.cpp 143 hid/emulated_controller.cpp
@@ -550,6 +548,12 @@ add_library(core STATIC
550 hle/service/ns/ns.h 548 hle/service/ns/ns.h
551 hle/service/ns/pdm_qry.cpp 549 hle/service/ns/pdm_qry.cpp
552 hle/service/ns/pdm_qry.h 550 hle/service/ns/pdm_qry.h
551 hle/service/nvdrv/core/container.cpp
552 hle/service/nvdrv/core/container.h
553 hle/service/nvdrv/core/nvmap.cpp
554 hle/service/nvdrv/core/nvmap.h
555 hle/service/nvdrv/core/syncpoint_manager.cpp
556 hle/service/nvdrv/core/syncpoint_manager.h
553 hle/service/nvdrv/devices/nvdevice.h 557 hle/service/nvdrv/devices/nvdevice.h
554 hle/service/nvdrv/devices/nvdisp_disp0.cpp 558 hle/service/nvdrv/devices/nvdisp_disp0.cpp
555 hle/service/nvdrv/devices/nvdisp_disp0.h 559 hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -578,8 +582,6 @@ add_library(core STATIC
578 hle/service/nvdrv/nvdrv_interface.h 582 hle/service/nvdrv/nvdrv_interface.h
579 hle/service/nvdrv/nvmemp.cpp 583 hle/service/nvdrv/nvmemp.cpp
580 hle/service/nvdrv/nvmemp.h 584 hle/service/nvdrv/nvmemp.h
581 hle/service/nvdrv/syncpoint_manager.cpp
582 hle/service/nvdrv/syncpoint_manager.h
583 hle/service/nvflinger/binder.h 585 hle/service/nvflinger/binder.h
584 hle/service/nvflinger/buffer_item.h 586 hle/service/nvflinger/buffer_item.h
585 hle/service/nvflinger/buffer_item_consumer.cpp 587 hle/service/nvflinger/buffer_item_consumer.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 121092868..1deeee154 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -27,7 +27,6 @@
27#include "core/file_sys/savedata_factory.h" 27#include "core/file_sys/savedata_factory.h"
28#include "core/file_sys/vfs_concat.h" 28#include "core/file_sys/vfs_concat.h"
29#include "core/file_sys/vfs_real.h" 29#include "core/file_sys/vfs_real.h"
30#include "core/hardware_interrupt_manager.h"
31#include "core/hid/hid_core.h" 30#include "core/hid/hid_core.h"
32#include "core/hle/kernel/k_memory_manager.h" 31#include "core/hle/kernel/k_memory_manager.h"
33#include "core/hle/kernel/k_process.h" 32#include "core/hle/kernel/k_process.h"
@@ -51,6 +50,7 @@
51#include "core/telemetry_session.h" 50#include "core/telemetry_session.h"
52#include "core/tools/freezer.h" 51#include "core/tools/freezer.h"
53#include "network/network.h" 52#include "network/network.h"
53#include "video_core/host1x/host1x.h"
54#include "video_core/renderer_base.h" 54#include "video_core/renderer_base.h"
55#include "video_core/video_core.h" 55#include "video_core/video_core.h"
56 56
@@ -215,6 +215,7 @@ struct System::Impl {
215 215
216 telemetry_session = std::make_unique<Core::TelemetrySession>(); 216 telemetry_session = std::make_unique<Core::TelemetrySession>();
217 217
218 host1x_core = std::make_unique<Tegra::Host1x::Host1x>(system);
218 gpu_core = VideoCore::CreateGPU(emu_window, system); 219 gpu_core = VideoCore::CreateGPU(emu_window, system);
219 if (!gpu_core) { 220 if (!gpu_core) {
220 return SystemResultStatus::ErrorVideoCore; 221 return SystemResultStatus::ErrorVideoCore;
@@ -224,7 +225,6 @@ struct System::Impl {
224 225
225 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); 226 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
226 services = std::make_unique<Service::Services>(service_manager, system); 227 services = std::make_unique<Service::Services>(service_manager, system);
227 interrupt_manager = std::make_unique<Hardware::InterruptManager>(system);
228 228
229 // Initialize time manager, which must happen after kernel is created 229 // Initialize time manager, which must happen after kernel is created
230 time_manager.Initialize(); 230 time_manager.Initialize();
@@ -373,6 +373,7 @@ struct System::Impl {
373 app_loader.reset(); 373 app_loader.reset();
374 audio_core.reset(); 374 audio_core.reset();
375 gpu_core.reset(); 375 gpu_core.reset();
376 host1x_core.reset();
376 perf_stats.reset(); 377 perf_stats.reset();
377 kernel.Shutdown(); 378 kernel.Shutdown();
378 memory.Reset(); 379 memory.Reset();
@@ -450,7 +451,7 @@ struct System::Impl {
450 /// AppLoader used to load the current executing application 451 /// AppLoader used to load the current executing application
451 std::unique_ptr<Loader::AppLoader> app_loader; 452 std::unique_ptr<Loader::AppLoader> app_loader;
452 std::unique_ptr<Tegra::GPU> gpu_core; 453 std::unique_ptr<Tegra::GPU> gpu_core;
453 std::unique_ptr<Hardware::InterruptManager> interrupt_manager; 454 std::unique_ptr<Tegra::Host1x::Host1x> host1x_core;
454 std::unique_ptr<Core::DeviceMemory> device_memory; 455 std::unique_ptr<Core::DeviceMemory> device_memory;
455 std::unique_ptr<AudioCore::AudioCore> audio_core; 456 std::unique_ptr<AudioCore::AudioCore> audio_core;
456 Core::Memory::Memory memory; 457 Core::Memory::Memory memory;
@@ -668,12 +669,12 @@ const Tegra::GPU& System::GPU() const {
668 return *impl->gpu_core; 669 return *impl->gpu_core;
669} 670}
670 671
671Core::Hardware::InterruptManager& System::InterruptManager() { 672Tegra::Host1x::Host1x& System::Host1x() {
672 return *impl->interrupt_manager; 673 return *impl->host1x_core;
673} 674}
674 675
675const Core::Hardware::InterruptManager& System::InterruptManager() const { 676const Tegra::Host1x::Host1x& System::Host1x() const {
676 return *impl->interrupt_manager; 677 return *impl->host1x_core;
677} 678}
678 679
679VideoCore::RendererBase& System::Renderer() { 680VideoCore::RendererBase& System::Renderer() {
diff --git a/src/core/core.h b/src/core/core.h
index 0ce3b1d60..7843cc8ad 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -74,6 +74,9 @@ class TimeManager;
74namespace Tegra { 74namespace Tegra {
75class DebugContext; 75class DebugContext;
76class GPU; 76class GPU;
77namespace Host1x {
78class Host1x;
79} // namespace Host1x
77} // namespace Tegra 80} // namespace Tegra
78 81
79namespace VideoCore { 82namespace VideoCore {
@@ -88,10 +91,6 @@ namespace Core::Timing {
88class CoreTiming; 91class CoreTiming;
89} 92}
90 93
91namespace Core::Hardware {
92class InterruptManager;
93}
94
95namespace Core::HID { 94namespace Core::HID {
96class HIDCore; 95class HIDCore;
97} 96}
@@ -260,6 +259,12 @@ public:
260 /// Gets an immutable reference to the GPU interface. 259 /// Gets an immutable reference to the GPU interface.
261 [[nodiscard]] const Tegra::GPU& GPU() const; 260 [[nodiscard]] const Tegra::GPU& GPU() const;
262 261
262 /// Gets a mutable reference to the Host1x interface
263 [[nodiscard]] Tegra::Host1x::Host1x& Host1x();
264
265 /// Gets an immutable reference to the Host1x interface.
266 [[nodiscard]] const Tegra::Host1x::Host1x& Host1x() const;
267
263 /// Gets a mutable reference to the renderer. 268 /// Gets a mutable reference to the renderer.
264 [[nodiscard]] VideoCore::RendererBase& Renderer(); 269 [[nodiscard]] VideoCore::RendererBase& Renderer();
265 270
@@ -296,12 +301,6 @@ public:
296 /// Provides a constant reference to the core timing instance. 301 /// Provides a constant reference to the core timing instance.
297 [[nodiscard]] const Timing::CoreTiming& CoreTiming() const; 302 [[nodiscard]] const Timing::CoreTiming& CoreTiming() const;
298 303
299 /// Provides a reference to the interrupt manager instance.
300 [[nodiscard]] Core::Hardware::InterruptManager& InterruptManager();
301
302 /// Provides a constant reference to the interrupt manager instance.
303 [[nodiscard]] const Core::Hardware::InterruptManager& InterruptManager() const;
304
305 /// Provides a reference to the kernel instance. 304 /// Provides a reference to the kernel instance.
306 [[nodiscard]] Kernel::KernelCore& Kernel(); 305 [[nodiscard]] Kernel::KernelCore& Kernel();
307 306
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp
deleted file mode 100644
index d08cc3315..000000000
--- a/src/core/hardware_interrupt_manager.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hardware_interrupt_manager.h"
7#include "core/hle/service/nvdrv/nvdrv_interface.h"
8#include "core/hle/service/sm/sm.h"
9
10namespace Core::Hardware {
11
12InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
13 gpu_interrupt_event = Core::Timing::CreateEvent(
14 "GPUInterrupt",
15 [this](std::uintptr_t message, u64 time,
16 std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
17 auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
18 const u32 syncpt = static_cast<u32>(message >> 32);
19 const u32 value = static_cast<u32>(message);
20 nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
21 return std::nullopt;
22 });
23}
24
25InterruptManager::~InterruptManager() = default;
26
27void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
28 const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value;
29 system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{10}, gpu_interrupt_event, msg);
30}
31
32} // namespace Core::Hardware
diff --git a/src/core/hardware_interrupt_manager.h b/src/core/hardware_interrupt_manager.h
deleted file mode 100644
index 5665c5918..000000000
--- a/src/core/hardware_interrupt_manager.h
+++ /dev/null
@@ -1,32 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_types.h"
9
10namespace Core {
11class System;
12}
13
14namespace Core::Timing {
15struct EventType;
16}
17
18namespace Core::Hardware {
19
20class InterruptManager {
21public:
22 explicit InterruptManager(Core::System& system);
23 ~InterruptManager();
24
25 void GPUInterruptSyncpt(u32 syncpoint_id, u32 value);
26
27private:
28 Core::System& system;
29 std::shared_ptr<Core::Timing::EventType> gpu_interrupt_event;
30};
31
32} // namespace Core::Hardware
diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp
new file mode 100644
index 000000000..37ca24f5d
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/container.cpp
@@ -0,0 +1,50 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "core/hle/service/nvdrv/core/container.h"
6#include "core/hle/service/nvdrv/core/nvmap.h"
7#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
8#include "video_core/host1x/host1x.h"
9
10namespace Service::Nvidia::NvCore {
11
12struct ContainerImpl {
13 explicit ContainerImpl(Tegra::Host1x::Host1x& host1x_)
14 : file{host1x_}, manager{host1x_}, device_file_data{} {}
15 NvMap file;
16 SyncpointManager manager;
17 Container::Host1xDeviceFileData device_file_data;
18};
19
20Container::Container(Tegra::Host1x::Host1x& host1x_) {
21 impl = std::make_unique<ContainerImpl>(host1x_);
22}
23
24Container::~Container() = default;
25
26NvMap& Container::GetNvMapFile() {
27 return impl->file;
28}
29
30const NvMap& Container::GetNvMapFile() const {
31 return impl->file;
32}
33
34Container::Host1xDeviceFileData& Container::Host1xDeviceFile() {
35 return impl->device_file_data;
36}
37
38const Container::Host1xDeviceFileData& Container::Host1xDeviceFile() const {
39 return impl->device_file_data;
40}
41
42SyncpointManager& Container::GetSyncpointManager() {
43 return impl->manager;
44}
45
46const SyncpointManager& Container::GetSyncpointManager() const {
47 return impl->manager;
48}
49
50} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h
new file mode 100644
index 000000000..b4b63ac90
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/container.h
@@ -0,0 +1,52 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#pragma once
6
7#include <deque>
8#include <memory>
9#include <unordered_map>
10
11#include "core/hle/service/nvdrv/nvdata.h"
12
13namespace Tegra::Host1x {
14class Host1x;
15} // namespace Tegra::Host1x
16
17namespace Service::Nvidia::NvCore {
18
19class NvMap;
20class SyncpointManager;
21
22struct ContainerImpl;
23
24class Container {
25public:
26 explicit Container(Tegra::Host1x::Host1x& host1x);
27 ~Container();
28
29 NvMap& GetNvMapFile();
30
31 const NvMap& GetNvMapFile() const;
32
33 SyncpointManager& GetSyncpointManager();
34
35 const SyncpointManager& GetSyncpointManager() const;
36
37 struct Host1xDeviceFileData {
38 std::unordered_map<DeviceFD, u32> fd_to_id{};
39 std::deque<u32> syncpts_accumulated{};
40 u32 nvdec_next_id{};
41 u32 vic_next_id{};
42 };
43
44 Host1xDeviceFileData& Host1xDeviceFile();
45
46 const Host1xDeviceFileData& Host1xDeviceFile() const;
47
48private:
49 std::unique_ptr<ContainerImpl> impl;
50};
51
52} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp
new file mode 100644
index 000000000..fbd8a74a5
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/nvmap.cpp
@@ -0,0 +1,272 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "common/alignment.h"
6#include "common/assert.h"
7#include "common/logging/log.h"
8#include "core/hle/service/nvdrv/core/nvmap.h"
9#include "core/memory.h"
10#include "video_core/host1x/host1x.h"
11
12using Core::Memory::YUZU_PAGESIZE;
13
14namespace Service::Nvidia::NvCore {
15NvMap::Handle::Handle(u64 size_, Id id_)
16 : size(size_), aligned_size(size), orig_size(size), id(id_) {
17 flags.raw = 0;
18}
19
20NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) {
21 std::scoped_lock lock(mutex);
22
23 // Handles cannot be allocated twice
24 if (allocated) {
25 return NvResult::AccessDenied;
26 }
27
28 flags = pFlags;
29 kind = pKind;
30 align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign;
31
32 // This flag is only applicable for handles with an address passed
33 if (pAddress) {
34 flags.keep_uncached_after_free.Assign(0);
35 } else {
36 LOG_CRITICAL(Service_NVDRV,
37 "Mapping nvmap handles without a CPU side address is unimplemented!");
38 }
39
40 size = Common::AlignUp(size, YUZU_PAGESIZE);
41 aligned_size = Common::AlignUp(size, align);
42 address = pAddress;
43 allocated = true;
44
45 return NvResult::Success;
46}
47
48NvResult NvMap::Handle::Duplicate(bool internal_session) {
49 std::scoped_lock lock(mutex);
50 // Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS)
51 if (!allocated) [[unlikely]] {
52 return NvResult::BadValue;
53 }
54
55 // If we internally use FromId the duplication tracking of handles won't work accurately due to
56 // us not implementing per-process handle refs.
57 if (internal_session) {
58 internal_dupes++;
59 } else {
60 dupes++;
61 }
62
63 return NvResult::Success;
64}
65
66NvMap::NvMap(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {}
67
68void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) {
69 std::scoped_lock lock(handles_lock);
70
71 handles.emplace(handle_description->id, std::move(handle_description));
72}
73
74void NvMap::UnmapHandle(Handle& handle_description) {
75 // Remove pending unmap queue entry if needed
76 if (handle_description.unmap_queue_entry) {
77 unmap_queue.erase(*handle_description.unmap_queue_entry);
78 handle_description.unmap_queue_entry.reset();
79 }
80
81 // Free and unmap the handle from the SMMU
82 host1x.MemoryManager().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address),
83 handle_description.aligned_size);
84 host1x.Allocator().Free(handle_description.pin_virt_address,
85 static_cast<u32>(handle_description.aligned_size));
86 handle_description.pin_virt_address = 0;
87}
88
89bool NvMap::TryRemoveHandle(const Handle& handle_description) {
90 // No dupes left, we can remove from handle map
91 if (handle_description.dupes == 0 && handle_description.internal_dupes == 0) {
92 std::scoped_lock lock(handles_lock);
93
94 auto it{handles.find(handle_description.id)};
95 if (it != handles.end()) {
96 handles.erase(it);
97 }
98
99 return true;
100 } else {
101 return false;
102 }
103}
104
105NvResult NvMap::CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out) {
106 if (!size) [[unlikely]] {
107 return NvResult::BadValue;
108 }
109
110 u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)};
111 auto handle_description{std::make_shared<Handle>(size, id)};
112 AddHandle(handle_description);
113
114 result_out = handle_description;
115 return NvResult::Success;
116}
117
118std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) {
119 std::scoped_lock lock(handles_lock);
120 try {
121 return handles.at(handle);
122 } catch (std::out_of_range&) {
123 return nullptr;
124 }
125}
126
127VAddr NvMap::GetHandleAddress(Handle::Id handle) {
128 std::scoped_lock lock(handles_lock);
129 try {
130 return handles.at(handle)->address;
131 } catch (std::out_of_range&) {
132 return 0;
133 }
134}
135
136u32 NvMap::PinHandle(NvMap::Handle::Id handle) {
137 auto handle_description{GetHandle(handle)};
138 if (!handle_description) [[unlikely]] {
139 return 0;
140 }
141
142 std::scoped_lock lock(handle_description->mutex);
143 if (!handle_description->pins) {
144 // If we're in the unmap queue we can just remove ourselves and return since we're already
145 // mapped
146 {
147 // Lock now to prevent our queue entry from being removed for allocation in-between the
148 // following check and erase
149 std::scoped_lock queueLock(unmap_queue_lock);
150 if (handle_description->unmap_queue_entry) {
151 unmap_queue.erase(*handle_description->unmap_queue_entry);
152 handle_description->unmap_queue_entry.reset();
153
154 handle_description->pins++;
155 return handle_description->pin_virt_address;
156 }
157 }
158
159 // If not then allocate some space and map it
160 u32 address{};
161 auto& smmu_allocator = host1x.Allocator();
162 auto& smmu_memory_manager = host1x.MemoryManager();
163 while (!(address =
164 smmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size)))) {
165 // Free handles until the allocation succeeds
166 std::scoped_lock queueLock(unmap_queue_lock);
167 if (auto freeHandleDesc{unmap_queue.front()}) {
168 // Handles in the unmap queue are guaranteed not to be pinned so don't bother
169 // checking if they are before unmapping
170 std::scoped_lock freeLock(freeHandleDesc->mutex);
171 if (handle_description->pin_virt_address)
172 UnmapHandle(*freeHandleDesc);
173 } else {
174 LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!");
175 }
176 }
177
178 smmu_memory_manager.Map(static_cast<GPUVAddr>(address), handle_description->address,
179 handle_description->aligned_size);
180 handle_description->pin_virt_address = address;
181 }
182
183 handle_description->pins++;
184 return handle_description->pin_virt_address;
185}
186
187void NvMap::UnpinHandle(Handle::Id handle) {
188 auto handle_description{GetHandle(handle)};
189 if (!handle_description) {
190 return;
191 }
192
193 std::scoped_lock lock(handle_description->mutex);
194 if (--handle_description->pins < 0) {
195 LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!");
196 } else if (!handle_description->pins) {
197 std::scoped_lock queueLock(unmap_queue_lock);
198
199 // Add to the unmap queue allowing this handle's memory to be freed if needed
200 unmap_queue.push_back(handle_description);
201 handle_description->unmap_queue_entry = std::prev(unmap_queue.end());
202 }
203}
204
205void NvMap::DuplicateHandle(Handle::Id handle, bool internal_session) {
206 auto handle_description{GetHandle(handle)};
207 if (!handle_description) {
208 LOG_CRITICAL(Service_NVDRV, "Unregistered handle!");
209 return;
210 }
211
212 auto result = handle_description->Duplicate(internal_session);
213 if (result != NvResult::Success) {
214 LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!");
215 }
216}
217
218std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool internal_session) {
219 std::weak_ptr<Handle> hWeak{GetHandle(handle)};
220 FreeInfo freeInfo;
221
222 // We use a weak ptr here so we can tell when the handle has been freed and report that back to
223 // guest
224 if (auto handle_description = hWeak.lock()) {
225 std::scoped_lock lock(handle_description->mutex);
226
227 if (internal_session) {
228 if (--handle_description->internal_dupes < 0)
229 LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!");
230 } else {
231 if (--handle_description->dupes < 0) {
232 LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!");
233 } else if (handle_description->dupes == 0) {
234 // Force unmap the handle
235 if (handle_description->pin_virt_address) {
236 std::scoped_lock queueLock(unmap_queue_lock);
237 UnmapHandle(*handle_description);
238 }
239
240 handle_description->pins = 0;
241 }
242 }
243
244 // Try to remove the shared ptr to the handle from the map, if nothing else is using the
245 // handle then it will now be freed when `handle_description` goes out of scope
246 if (TryRemoveHandle(*handle_description)) {
247 LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle);
248 } else {
249 LOG_DEBUG(Service_NVDRV,
250 "Tried to free nvmap handle: {} but didn't as it still has duplicates",
251 handle);
252 }
253
254 freeInfo = {
255 .address = handle_description->address,
256 .size = handle_description->size,
257 .was_uncached = handle_description->flags.map_uncached.Value() != 0,
258 };
259 } else {
260 return std::nullopt;
261 }
262
263 // Handle hasn't been freed from memory, set address to 0 to mark that the handle wasn't freed
264 if (!hWeak.expired()) {
265 LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle);
266 freeInfo.address = 0;
267 }
268
269 return freeInfo;
270}
271
272} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h
new file mode 100644
index 000000000..b9dd3801f
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/nvmap.h
@@ -0,0 +1,175 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#pragma once
6
7#include <atomic>
8#include <list>
9#include <memory>
10#include <mutex>
11#include <optional>
12#include <unordered_map>
13#include <assert.h>
14
15#include "common/bit_field.h"
16#include "common/common_types.h"
17#include "core/hle/service/nvdrv/nvdata.h"
18
19namespace Tegra {
20
21namespace Host1x {
22class Host1x;
23} // namespace Host1x
24
25} // namespace Tegra
26
27namespace Service::Nvidia::NvCore {
28/**
29 * @brief The nvmap core class holds the global state for nvmap and provides methods to manage
30 * handles
31 */
32class NvMap {
33public:
34 /**
35 * @brief A handle to a contiguous block of memory in an application's address space
36 */
37 struct Handle {
38 std::mutex mutex;
39
40 u64 align{}; //!< The alignment to use when pinning the handle onto the SMMU
41 u64 size; //!< Page-aligned size of the memory the handle refers to
42 u64 aligned_size; //!< `align`-aligned size of the memory the handle refers to
43 u64 orig_size; //!< Original unaligned size of the memory this handle refers to
44
45 s32 dupes{1}; //!< How many guest references there are to this handle
46 s32 internal_dupes{0}; //!< How many emulator-internal references there are to this handle
47
48 using Id = u32;
49 Id id; //!< A globally unique identifier for this handle
50
51 s32 pins{};
52 u32 pin_virt_address{};
53 std::optional<typename std::list<std::shared_ptr<Handle>>::iterator> unmap_queue_entry{};
54
55 union Flags {
56 u32 raw;
57 BitField<0, 1, u32> map_uncached; //!< If the handle should be mapped as uncached
58 BitField<2, 1, u32> keep_uncached_after_free; //!< Only applicable when the handle was
59 //!< allocated with a fixed address
60 BitField<4, 1, u32> _unk0_; //!< Passed to IOVMM for pins
61 } flags{};
62 static_assert(sizeof(Flags) == sizeof(u32));
63
64 u64 address{}; //!< The memory location in the guest's AS that this handle corresponds to,
65 //!< this can also be in the nvdrv tmem
66 bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC
67 //!< call
68
69 u8 kind{}; //!< Used for memory compression
70 bool allocated{}; //!< If the handle has been allocated with `Alloc`
71
72 u64 dma_map_addr{}; //! remove me after implementing pinning.
73
74 Handle(u64 size, Id id);
75
76 /**
77 * @brief Sets up the handle with the given memory config, can allocate memory from the tmem
78 * if a 0 address is passed
79 */
80 [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress);
81
82 /**
83 * @brief Increases the dupe counter of the handle for the given session
84 */
85 [[nodiscard]] NvResult Duplicate(bool internal_session);
86
87 /**
88 * @brief Obtains a pointer to the handle's memory and marks the handle it as having been
89 * mapped
90 */
91 u8* GetPointer() {
92 if (!address) {
93 return nullptr;
94 }
95
96 is_shared_mem_mapped = true;
97 return reinterpret_cast<u8*>(address);
98 }
99 };
100
101 /**
102 * @brief Encapsulates the result of a FreeHandle operation
103 */
104 struct FreeInfo {
105 u64 address; //!< Address the handle referred to before deletion
106 u64 size; //!< Page-aligned handle size
107 bool was_uncached; //!< If the handle was allocated as uncached
108 };
109
110 explicit NvMap(Tegra::Host1x::Host1x& host1x);
111
112 /**
113 * @brief Creates an unallocated handle of the given size
114 */
115 [[nodiscard]] NvResult CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out);
116
117 std::shared_ptr<Handle> GetHandle(Handle::Id handle);
118
119 VAddr GetHandleAddress(Handle::Id handle);
120
121 /**
122 * @brief Maps a handle into the SMMU address space
123 * @note This operation is refcounted, the number of calls to this must eventually match the
124 * number of calls to `UnpinHandle`
125 * @return The SMMU virtual address that the handle has been mapped to
126 */
127 u32 PinHandle(Handle::Id handle);
128
129 /**
130 * @brief When this has been called an equal number of times to `PinHandle` for the supplied
131 * handle it will be added to a list of handles to be freed when necessary
132 */
133 void UnpinHandle(Handle::Id handle);
134
135 /**
136 * @brief Tries to duplicate a handle
137 */
138 void DuplicateHandle(Handle::Id handle, bool internal_session = false);
139
140 /**
141 * @brief Tries to free a handle and remove a single dupe
142 * @note If a handle has no dupes left and has no other users a FreeInfo struct will be returned
143 * describing the prior state of the handle
144 */
145 std::optional<FreeInfo> FreeHandle(Handle::Id handle, bool internal_session);
146
147private:
148 std::list<std::shared_ptr<Handle>> unmap_queue{};
149 std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue`
150
151 std::unordered_map<Handle::Id, std::shared_ptr<Handle>>
152 handles{}; //!< Main owning map of handles
153 std::mutex handles_lock; //!< Protects access to `handles`
154
155 static constexpr u32 HandleIdIncrement{
156 4}; //!< Each new handle ID is an increment of 4 from the previous
157 std::atomic<u32> next_handle_id{HandleIdIncrement};
158 Tegra::Host1x::Host1x& host1x;
159
160 void AddHandle(std::shared_ptr<Handle> handle);
161
162 /**
163 * @brief Unmaps and frees the SMMU memory region a handle is mapped to
164 * @note Both `unmap_queue_lock` and `handle_description.mutex` MUST be locked when calling this
165 */
166 void UnmapHandle(Handle& handle_description);
167
168 /**
169 * @brief Removes a handle from the map taking its dupes into account
170 * @note handle_description.mutex MUST be locked when calling this
171 * @return If the handle was removed from the map
172 */
173 bool TryRemoveHandle(const Handle& handle_description);
174};
175} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp
new file mode 100644
index 000000000..eda2041a0
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp
@@ -0,0 +1,121 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "common/assert.h"
6#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
7#include "video_core/host1x/host1x.h"
8
9namespace Service::Nvidia::NvCore {
10
11SyncpointManager::SyncpointManager(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {
12 constexpr u32 VBlank0SyncpointId{26};
13 constexpr u32 VBlank1SyncpointId{27};
14
15 // Reserve both vblank syncpoints as client managed as they use Continuous Mode
16 // Refer to section 14.3.5.3 of the TRM for more information on Continuous Mode
17 // https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/drm/dc.c#L660
18 ReserveSyncpoint(VBlank0SyncpointId, true);
19 ReserveSyncpoint(VBlank1SyncpointId, true);
20
21 for (u32 syncpoint_id : channel_syncpoints) {
22 if (syncpoint_id) {
23 ReserveSyncpoint(syncpoint_id, false);
24 }
25 }
26}
27
28SyncpointManager::~SyncpointManager() = default;
29
30u32 SyncpointManager::ReserveSyncpoint(u32 id, bool client_managed) {
31 if (syncpoints.at(id).reserved) {
32 ASSERT_MSG(false, "Requested syncpoint is in use");
33 return 0;
34 }
35
36 syncpoints.at(id).reserved = true;
37 syncpoints.at(id).interface_managed = client_managed;
38
39 return id;
40}
41
42u32 SyncpointManager::FindFreeSyncpoint() {
43 for (u32 i{1}; i < syncpoints.size(); i++) {
44 if (!syncpoints[i].reserved) {
45 return i;
46 }
47 }
48 ASSERT_MSG(false, "Failed to find a free syncpoint!");
49 return 0;
50}
51
52u32 SyncpointManager::AllocateSyncpoint(bool client_managed) {
53 std::lock_guard lock(reservation_lock);
54 return ReserveSyncpoint(FindFreeSyncpoint(), client_managed);
55}
56
57void SyncpointManager::FreeSyncpoint(u32 id) {
58 std::lock_guard lock(reservation_lock);
59 ASSERT(syncpoints.at(id).reserved);
60 syncpoints.at(id).reserved = false;
61}
62
63bool SyncpointManager::IsSyncpointAllocated(u32 id) {
64 return (id <= SyncpointCount) && syncpoints[id].reserved;
65}
66
67bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) const {
68 const SyncpointInfo& syncpoint{syncpoints.at(id)};
69
70 if (!syncpoint.reserved) {
71 ASSERT(false);
72 return 0;
73 }
74
75 // If the interface manages counters then we don't keep track of the maximum value as it handles
76 // sanity checking the values then
77 if (syncpoint.interface_managed) {
78 return static_cast<s32>(syncpoint.counter_min - threshold) >= 0;
79 } else {
80 return (syncpoint.counter_max - threshold) >= (syncpoint.counter_min - threshold);
81 }
82}
83
84u32 SyncpointManager::IncrementSyncpointMaxExt(u32 id, u32 amount) {
85 if (!syncpoints.at(id).reserved) {
86 ASSERT(false);
87 return 0;
88 }
89
90 return syncpoints.at(id).counter_max += amount;
91}
92
93u32 SyncpointManager::ReadSyncpointMinValue(u32 id) {
94 if (!syncpoints.at(id).reserved) {
95 ASSERT(false);
96 return 0;
97 }
98
99 return syncpoints.at(id).counter_min;
100}
101
102u32 SyncpointManager::UpdateMin(u32 id) {
103 if (!syncpoints.at(id).reserved) {
104 ASSERT(false);
105 return 0;
106 }
107
108 syncpoints.at(id).counter_min = host1x.GetSyncpointManager().GetHostSyncpointValue(id);
109 return syncpoints.at(id).counter_min;
110}
111
112NvFence SyncpointManager::GetSyncpointFence(u32 id) {
113 if (!syncpoints.at(id).reserved) {
114 ASSERT(false);
115 return NvFence{};
116 }
117
118 return {.id = static_cast<s32>(id), .value = syncpoints.at(id).counter_max};
119}
120
121} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h
new file mode 100644
index 000000000..b76ef9032
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h
@@ -0,0 +1,134 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#pragma once
6
7#include <array>
8#include <atomic>
9#include <mutex>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvdrv/nvdata.h"
13
14namespace Tegra::Host1x {
15class Host1x;
16} // namespace Tegra::Host1x
17
18namespace Service::Nvidia::NvCore {
19
20enum class ChannelType : u32 {
21 MsEnc = 0,
22 VIC = 1,
23 GPU = 2,
24 NvDec = 3,
25 Display = 4,
26 NvJpg = 5,
27 TSec = 6,
28 Max = 7
29};
30
31/**
32 * @brief SyncpointManager handles allocating and accessing host1x syncpoints, these are cached
33 * versions of the HW syncpoints which are intermittently synced
34 * @note Refer to Chapter 14 of the Tegra X1 TRM for an exhaustive overview of them
35 * @url https://http.download.nvidia.com/tegra-public-appnotes/host1x.html
36 * @url
37 * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/jetson-tx1/drivers/video/tegra/host/nvhost_syncpt.c
38 */
39class SyncpointManager final {
40public:
41 explicit SyncpointManager(Tegra::Host1x::Host1x& host1x);
42 ~SyncpointManager();
43
44 /**
45 * @brief Checks if the given syncpoint is both allocated and below the number of HW syncpoints
46 */
47 bool IsSyncpointAllocated(u32 id);
48
49 /**
50 * @brief Finds a free syncpoint and reserves it
51 * @return The ID of the reserved syncpoint
52 */
53 u32 AllocateSyncpoint(bool client_managed);
54
55 /**
56 * @url
57 * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/syncpt.c#L259
58 */
59 bool HasSyncpointExpired(u32 id, u32 threshold) const;
60
61 bool IsFenceSignalled(NvFence fence) const {
62 return HasSyncpointExpired(fence.id, fence.value);
63 }
64
65 /**
66 * @brief Atomically increments the maximum value of a syncpoint by the given amount
67 * @return The new max value of the syncpoint
68 */
69 u32 IncrementSyncpointMaxExt(u32 id, u32 amount);
70
71 /**
72 * @return The minimum value of the syncpoint
73 */
74 u32 ReadSyncpointMinValue(u32 id);
75
76 /**
77 * @brief Synchronises the minimum value of the syncpoint to with the GPU
78 * @return The new minimum value of the syncpoint
79 */
80 u32 UpdateMin(u32 id);
81
82 /**
83 * @brief Frees the usage of a syncpoint.
84 */
85 void FreeSyncpoint(u32 id);
86
87 /**
88 * @return A fence that will be signalled once this syncpoint hits its maximum value
89 */
90 NvFence GetSyncpointFence(u32 id);
91
92 static constexpr std::array<u32, static_cast<u32>(ChannelType::Max)> channel_syncpoints{
93 0x0, // `MsEnc` is unimplemented
94 0xC, // `VIC`
95 0x0, // `GPU` syncpoints are allocated per-channel instead
96 0x36, // `NvDec`
97 0x0, // `Display` is unimplemented
98 0x37, // `NvJpg`
99 0x0, // `TSec` is unimplemented
100 }; //!< Maps each channel ID to a constant syncpoint
101
102private:
103 /**
104 * @note reservation_lock should be locked when calling this
105 */
106 u32 ReserveSyncpoint(u32 id, bool client_managed);
107
108 /**
109 * @return The ID of the first free syncpoint
110 */
111 u32 FindFreeSyncpoint();
112
113 struct SyncpointInfo {
114 std::atomic<u32> counter_min; //!< The least value the syncpoint can be (The value it was
115 //!< when it was last synchronized with host1x)
116 std::atomic<u32> counter_max; //!< The maximum value the syncpoint can reach according to
117 //!< the current usage
118 bool interface_managed; //!< If the syncpoint is managed by a host1x client interface, a
119 //!< client interface is a HW block that can handle host1x
120 //!< transactions on behalf of a host1x client (Which would
121 //!< otherwise need to be manually synced using PIO which is
122 //!< synchronous and requires direct cooperation of the CPU)
123 bool reserved; //!< If the syncpoint is reserved or not, not to be confused with a reserved
124 //!< value
125 };
126
127 constexpr static std::size_t SyncpointCount{192};
128 std::array<SyncpointInfo, SyncpointCount> syncpoints{};
129 std::mutex reservation_lock;
130
131 Tegra::Host1x::Host1x& host1x;
132};
133
134} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 696e8121e..204b0e757 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -11,6 +11,10 @@ namespace Core {
11class System; 11class System;
12} 12}
13 13
14namespace Kernel {
15class KEvent;
16}
17
14namespace Service::Nvidia::Devices { 18namespace Service::Nvidia::Devices {
15 19
16/// Represents an abstract nvidia device node. It is to be subclassed by concrete device nodes to 20/// Represents an abstract nvidia device node. It is to be subclassed by concrete device nodes to
@@ -64,6 +68,10 @@ public:
64 */ 68 */
65 virtual void OnClose(DeviceFD fd) = 0; 69 virtual void OnClose(DeviceFD fd) = 0;
66 70
71 virtual Kernel::KEvent* QueryEvent(u32 event_id) {
72 return nullptr;
73 }
74
67protected: 75protected:
68 Core::System& system; 76 Core::System& system;
69}; 77};
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 604711914..4122fc98d 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -5,15 +5,16 @@
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hle/service/nvdrv/core/container.h"
9#include "core/hle/service/nvdrv/core/nvmap.h"
8#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" 10#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
9#include "core/hle/service/nvdrv/devices/nvmap.h"
10#include "core/perf_stats.h" 11#include "core/perf_stats.h"
11#include "video_core/gpu.h" 12#include "video_core/gpu.h"
12 13
13namespace Service::Nvidia::Devices { 14namespace Service::Nvidia::Devices {
14 15
15nvdisp_disp0::nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_) 16nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
16 : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} 17 : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
17nvdisp_disp0::~nvdisp_disp0() = default; 18nvdisp_disp0::~nvdisp_disp0() = default;
18 19
19NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 20NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -39,8 +40,9 @@ void nvdisp_disp0::OnClose(DeviceFD fd) {}
39 40
40void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, 41void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width,
41 u32 height, u32 stride, android::BufferTransformFlags transform, 42 u32 height, u32 stride, android::BufferTransformFlags transform,
42 const Common::Rectangle<int>& crop_rect) { 43 const Common::Rectangle<int>& crop_rect,
43 const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); 44 std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) {
45 const VAddr addr = nvmap.GetHandleAddress(buffer_handle);
44 LOG_TRACE(Service, 46 LOG_TRACE(Service,
45 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", 47 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
46 addr, offset, width, height, stride, format); 48 addr, offset, width, height, stride, format);
@@ -48,10 +50,15 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat form
48 const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, 50 const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
49 stride, format, transform, crop_rect}; 51 stride, format, transform, crop_rect};
50 52
53 system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences);
51 system.GetPerfStats().EndSystemFrame(); 54 system.GetPerfStats().EndSystemFrame();
52 system.GPU().SwapBuffers(&framebuffer);
53 system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); 55 system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs());
54 system.GetPerfStats().BeginSystemFrame(); 56 system.GetPerfStats().BeginSystemFrame();
55} 57}
56 58
59Kernel::KEvent* nvdisp_disp0::QueryEvent(u32 event_id) {
60 LOG_CRITICAL(Service_NVDRV, "Unknown DISP Event {}", event_id);
61 return nullptr;
62}
63
57} // namespace Service::Nvidia::Devices 64} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index 67b105e02..04217ab12 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -11,13 +11,18 @@
11#include "core/hle/service/nvflinger/buffer_transform_flags.h" 11#include "core/hle/service/nvflinger/buffer_transform_flags.h"
12#include "core/hle/service/nvflinger/pixel_format.h" 12#include "core/hle/service/nvflinger/pixel_format.h"
13 13
14namespace Service::Nvidia::NvCore {
15class Container;
16class NvMap;
17} // namespace Service::Nvidia::NvCore
18
14namespace Service::Nvidia::Devices { 19namespace Service::Nvidia::Devices {
15 20
16class nvmap; 21class nvmap;
17 22
18class nvdisp_disp0 final : public nvdevice { 23class nvdisp_disp0 final : public nvdevice {
19public: 24public:
20 explicit nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_); 25 explicit nvdisp_disp0(Core::System& system_, NvCore::Container& core);
21 ~nvdisp_disp0() override; 26 ~nvdisp_disp0() override;
22 27
23 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 28 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -33,10 +38,14 @@ public:
33 /// Performs a screen flip, drawing the buffer pointed to by the handle. 38 /// Performs a screen flip, drawing the buffer pointed to by the handle.
34 void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, 39 void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height,
35 u32 stride, android::BufferTransformFlags transform, 40 u32 stride, android::BufferTransformFlags transform,
36 const Common::Rectangle<int>& crop_rect); 41 const Common::Rectangle<int>& crop_rect,
42 std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences);
43
44 Kernel::KEvent* QueryEvent(u32 event_id) override;
37 45
38private: 46private:
39 std::shared_ptr<nvmap> nvmap_dev; 47 NvCore::Container& container;
48 NvCore::NvMap& nvmap;
40}; 49};
41 50
42} // namespace Service::Nvidia::Devices 51} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 9867a648d..6411dbf43 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -1,21 +1,30 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#include <cstring> 5#include <cstring>
5#include <utility> 6#include <utility>
6 7
8#include "common/alignment.h"
7#include "common/assert.h" 9#include "common/assert.h"
8#include "common/logging/log.h" 10#include "common/logging/log.h"
9#include "core/core.h" 11#include "core/core.h"
12#include "core/hle/service/nvdrv/core/container.h"
13#include "core/hle/service/nvdrv/core/nvmap.h"
10#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" 14#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
11#include "core/hle/service/nvdrv/devices/nvmap.h" 15#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
16#include "core/hle/service/nvdrv/nvdrv.h"
17#include "video_core/control/channel_state.h"
18#include "video_core/gpu.h"
12#include "video_core/memory_manager.h" 19#include "video_core/memory_manager.h"
13#include "video_core/rasterizer_interface.h" 20#include "video_core/rasterizer_interface.h"
14 21
15namespace Service::Nvidia::Devices { 22namespace Service::Nvidia::Devices {
16 23
17nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_) 24nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Container& core)
18 : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {} 25 : nvdevice{system_}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, vm{},
26 gmmu{} {}
27
19nvhost_as_gpu::~nvhost_as_gpu() = default; 28nvhost_as_gpu::~nvhost_as_gpu() = default;
20 29
21NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 30NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -82,12 +91,52 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector<u8>& input, std::vector<u8>&
82 IoctlAllocAsEx params{}; 91 IoctlAllocAsEx params{};
83 std::memcpy(&params, input.data(), input.size()); 92 std::memcpy(&params, input.data(), input.size());
84 93
85 LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); 94 LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size);
86 if (params.big_page_size == 0) { 95
87 params.big_page_size = DEFAULT_BIG_PAGE_SIZE; 96 std::scoped_lock lock(mutex);
97
98 if (vm.initialised) {
99 ASSERT_MSG(false, "Cannot initialise an address space twice!");
100 return NvResult::InvalidState;
101 }
102
103 if (params.big_page_size) {
104 if (!std::has_single_bit(params.big_page_size)) {
105 LOG_ERROR(Service_NVDRV, "Non power-of-2 big page size: 0x{:X}!", params.big_page_size);
106 return NvResult::BadValue;
107 }
108
109 if ((params.big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES) == 0) {
110 LOG_ERROR(Service_NVDRV, "Unsupported big page size: 0x{:X}!", params.big_page_size);
111 return NvResult::BadValue;
112 }
113
114 vm.big_page_size = params.big_page_size;
115 vm.big_page_size_bits = static_cast<u32>(std::countr_zero(params.big_page_size));
116
117 vm.va_range_start = params.big_page_size << VM::VA_START_SHIFT;
118 }
119
120 // If this is unspecified then default values should be used
121 if (params.va_range_start) {
122 vm.va_range_start = params.va_range_start;
123 vm.va_range_split = params.va_range_split;
124 vm.va_range_end = params.va_range_end;
88 } 125 }
89 126
90 big_page_size = params.big_page_size; 127 const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)};
128 const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)};
129 vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages);
130
131 const auto start_big_pages{static_cast<u32>(vm.va_range_split >> vm.big_page_size_bits)};
132 const auto end_big_pages{
133 static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)};
134 vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages);
135
136 gmmu = std::make_shared<Tegra::MemoryManager>(system, 40, vm.big_page_size_bits,
137 VM::PAGE_SIZE_BITS);
138 system.GPU().InitAddressSpace(*gmmu);
139 vm.initialised = true;
91 140
92 return NvResult::Success; 141 return NvResult::Success;
93} 142}
@@ -99,21 +148,76 @@ NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<
99 LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, 148 LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
100 params.page_size, params.flags); 149 params.page_size, params.flags);
101 150
102 const auto size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; 151 std::scoped_lock lock(mutex);
103 if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) { 152
104 params.offset = *system.GPU().MemoryManager().AllocateFixed(params.offset, size); 153 if (!vm.initialised) {
154 return NvResult::BadValue;
155 }
156
157 if (params.page_size != VM::YUZU_PAGESIZE && params.page_size != vm.big_page_size) {
158 return NvResult::BadValue;
159 }
160
161 if (params.page_size != vm.big_page_size &&
162 ((params.flags & MappingFlags::Sparse) != MappingFlags::None)) {
163 UNIMPLEMENTED_MSG("Sparse small pages are not implemented!");
164 return NvResult::NotImplemented;
165 }
166
167 const u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS
168 : vm.big_page_size_bits};
169
170 auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator
171 : *vm.big_page_allocator};
172
173 if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) {
174 allocator.AllocateFixed(static_cast<u32>(params.offset >> page_size_bits), params.pages);
105 } else { 175 } else {
106 params.offset = system.GPU().MemoryManager().Allocate(size, params.align); 176 params.offset = static_cast<u64>(allocator.Allocate(params.pages)) << page_size_bits;
177 if (!params.offset) {
178 ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!");
179 return NvResult::InsufficientMemory;
180 }
107 } 181 }
108 182
109 auto result = NvResult::Success; 183 u64 size{static_cast<u64>(params.pages) * params.page_size};
110 if (!params.offset) { 184
111 LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size); 185 if ((params.flags & MappingFlags::Sparse) != MappingFlags::None) {
112 result = NvResult::InsufficientMemory; 186 gmmu->MapSparse(params.offset, size);
113 } 187 }
114 188
189 allocation_map[params.offset] = {
190 .size = size,
191 .mappings{},
192 .page_size = params.page_size,
193 .sparse = (params.flags & MappingFlags::Sparse) != MappingFlags::None,
194 .big_pages = params.page_size != VM::YUZU_PAGESIZE,
195 };
196
115 std::memcpy(output.data(), &params, output.size()); 197 std::memcpy(output.data(), &params, output.size());
116 return result; 198 return NvResult::Success;
199}
200
201void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
202 auto mapping{mapping_map.at(offset)};
203
204 if (!mapping->fixed) {
205 auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
206 u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
207
208 allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits),
209 static_cast<u32>(mapping->size >> page_size_bits));
210 }
211
212 // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
213 // Only FreeSpace can unmap them fully
214 if (mapping->sparse_alloc) {
215 gmmu->MapSparse(offset, mapping->size, mapping->big_page);
216 } else {
217 gmmu->Unmap(offset, mapping->size);
218 }
219
220 mapping_map.erase(offset);
117} 221}
118 222
119NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) { 223NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -123,8 +227,40 @@ NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>&
123 LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, 227 LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset,
124 params.pages, params.page_size); 228 params.pages, params.page_size);
125 229
126 system.GPU().MemoryManager().Unmap(params.offset, 230 std::scoped_lock lock(mutex);
127 static_cast<std::size_t>(params.pages) * params.page_size); 231
232 if (!vm.initialised) {
233 return NvResult::BadValue;
234 }
235
236 try {
237 auto allocation{allocation_map[params.offset]};
238
239 if (allocation.page_size != params.page_size ||
240 allocation.size != (static_cast<u64>(params.pages) * params.page_size)) {
241 return NvResult::BadValue;
242 }
243
244 for (const auto& mapping : allocation.mappings) {
245 FreeMappingLocked(mapping->offset);
246 }
247
248 // Unset sparse flag if required
249 if (allocation.sparse) {
250 gmmu->Unmap(params.offset, allocation.size);
251 }
252
253 auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator
254 : *vm.big_page_allocator};
255 u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS
256 : vm.big_page_size_bits};
257
258 allocator.Free(static_cast<u32>(params.offset >> page_size_bits),
259 static_cast<u32>(allocation.size >> page_size_bits));
260 allocation_map.erase(params.offset);
261 } catch (const std::out_of_range&) {
262 return NvResult::BadValue;
263 }
128 264
129 std::memcpy(output.data(), &params, output.size()); 265 std::memcpy(output.data(), &params, output.size());
130 return NvResult::Success; 266 return NvResult::Success;
@@ -135,35 +271,52 @@ NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& out
135 271
136 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); 272 LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
137 273
138 auto result = NvResult::Success;
139 std::vector<IoctlRemapEntry> entries(num_entries); 274 std::vector<IoctlRemapEntry> entries(num_entries);
140 std::memcpy(entries.data(), input.data(), input.size()); 275 std::memcpy(entries.data(), input.data(), input.size());
141 276
277 std::scoped_lock lock(mutex);
278
279 if (!vm.initialised) {
280 return NvResult::BadValue;
281 }
282
142 for (const auto& entry : entries) { 283 for (const auto& entry : entries) {
143 LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", 284 GPUVAddr virtual_address{static_cast<u64>(entry.as_offset_big_pages)
144 entry.offset, entry.nvmap_handle, entry.pages); 285 << vm.big_page_size_bits};
286 u64 size{static_cast<u64>(entry.big_pages) << vm.big_page_size_bits};
145 287
146 const auto object{nvmap_dev->GetObject(entry.nvmap_handle)}; 288 auto alloc{allocation_map.upper_bound(virtual_address)};
147 if (!object) { 289
148 LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle); 290 if (alloc-- == allocation_map.begin() ||
149 result = NvResult::InvalidState; 291 (virtual_address - alloc->first) + size > alloc->second.size) {
150 break; 292 LOG_WARNING(Service_NVDRV, "Cannot remap into an unallocated region!");
293 return NvResult::BadValue;
151 } 294 }
152 295
153 const auto offset{static_cast<GPUVAddr>(entry.offset) << 0x10}; 296 if (!alloc->second.sparse) {
154 const auto size{static_cast<u64>(entry.pages) << 0x10}; 297 LOG_WARNING(Service_NVDRV, "Cannot remap a non-sparse mapping!");
155 const auto map_offset{static_cast<u64>(entry.map_offset) << 0x10}; 298 return NvResult::BadValue;
156 const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)}; 299 }
157 300
158 if (!addr) { 301 const bool use_big_pages = alloc->second.big_pages;
159 LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!"); 302 if (!entry.handle) {
160 result = NvResult::InvalidState; 303 gmmu->MapSparse(virtual_address, size, use_big_pages);
161 break; 304 } else {
305 auto handle{nvmap.GetHandle(entry.handle)};
306 if (!handle) {
307 return NvResult::BadValue;
308 }
309
310 VAddr cpu_address{static_cast<VAddr>(
311 handle->address +
312 (static_cast<u64>(entry.handle_offset_big_pages) << vm.big_page_size_bits))};
313
314 gmmu->Map(virtual_address, cpu_address, size, use_big_pages);
162 } 315 }
163 } 316 }
164 317
165 std::memcpy(output.data(), entries.data(), output.size()); 318 std::memcpy(output.data(), entries.data(), output.size());
166 return result; 319 return NvResult::Success;
167} 320}
168 321
169NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { 322NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -173,79 +326,98 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8
173 LOG_DEBUG(Service_NVDRV, 326 LOG_DEBUG(Service_NVDRV,
174 "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}" 327 "called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}"
175 ", offset={}", 328 ", offset={}",
176 params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, 329 params.flags, params.handle, params.buffer_offset, params.mapping_size,
177 params.offset); 330 params.offset);
178 331
179 const auto object{nvmap_dev->GetObject(params.nvmap_handle)}; 332 std::scoped_lock lock(mutex);
180 if (!object) {
181 LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
182 std::memcpy(output.data(), &params, output.size());
183 return NvResult::InvalidState;
184 }
185
186 // The real nvservices doesn't make a distinction between handles and ids, and
187 // object can only have one handle and it will be the same as its id. Assert that this is the
188 // case to prevent unexpected behavior.
189 ASSERT(object->id == params.nvmap_handle);
190 auto& gpu = system.GPU();
191 333
192 u64 page_size{params.page_size}; 334 if (!vm.initialised) {
193 if (!page_size) { 335 return NvResult::BadValue;
194 page_size = object->align;
195 } 336 }
196 337
197 if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) { 338 // Remaps a subregion of an existing mapping to a different PA
198 if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) { 339 if ((params.flags & MappingFlags::Remap) != MappingFlags::None) {
199 const auto cpu_addr{static_cast<VAddr>(buffer_map->CpuAddr() + params.buffer_offset)}; 340 try {
200 const auto gpu_addr{static_cast<GPUVAddr>(params.offset + params.buffer_offset)}; 341 auto mapping{mapping_map.at(params.offset)};
201 342
202 if (!gpu.MemoryManager().Map(cpu_addr, gpu_addr, params.mapping_size)) { 343 if (mapping->size < params.mapping_size) {
203 LOG_CRITICAL(Service_NVDRV, 344 LOG_WARNING(Service_NVDRV,
204 "remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, " 345 "Cannot remap a partially mapped GPU address space region: 0x{:X}",
205 "mapping_size = {}, offset={}", 346 params.offset);
206 params.flags, params.nvmap_handle, params.buffer_offset, 347 return NvResult::BadValue;
207 params.mapping_size, params.offset);
208
209 std::memcpy(output.data(), &params, output.size());
210 return NvResult::InvalidState;
211 } 348 }
212 349
213 std::memcpy(output.data(), &params, output.size()); 350 u64 gpu_address{static_cast<u64>(params.offset + params.buffer_offset)};
214 return NvResult::Success; 351 VAddr cpu_address{mapping->ptr + params.buffer_offset};
215 } else { 352
216 LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset); 353 gmmu->Map(gpu_address, cpu_address, params.mapping_size, mapping->big_page);
217 354
218 std::memcpy(output.data(), &params, output.size()); 355 return NvResult::Success;
219 return NvResult::InvalidState; 356 } catch (const std::out_of_range&) {
357 LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: 0x{:X}",
358 params.offset);
359 return NvResult::BadValue;
220 } 360 }
221 } 361 }
222 362
223 // We can only map objects that have already been assigned a CPU address. 363 auto handle{nvmap.GetHandle(params.handle)};
224 ASSERT(object->status == nvmap::Object::Status::Allocated); 364 if (!handle) {
225 365 return NvResult::BadValue;
226 const auto physical_address{object->addr + params.buffer_offset};
227 u64 size{params.mapping_size};
228 if (!size) {
229 size = object->size;
230 } 366 }
231 367
232 const bool is_alloc{(params.flags & AddressSpaceFlags::FixedOffset) == AddressSpaceFlags::None}; 368 VAddr cpu_address{static_cast<VAddr>(handle->address + params.buffer_offset)};
233 if (is_alloc) { 369 u64 size{params.mapping_size ? params.mapping_size : handle->orig_size};
234 params.offset = gpu.MemoryManager().MapAllocate(physical_address, size, page_size); 370
235 } else { 371 bool big_page{[&]() {
236 params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size); 372 if (Common::IsAligned(handle->align, vm.big_page_size)) {
237 } 373 return true;
374 } else if (Common::IsAligned(handle->align, VM::YUZU_PAGESIZE)) {
375 return false;
376 } else {
377 ASSERT(false);
378 return false;
379 }
380 }()};
381
382 if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) {
383 auto alloc{allocation_map.upper_bound(params.offset)};
238 384
239 auto result = NvResult::Success; 385 if (alloc-- == allocation_map.begin() ||
240 if (!params.offset) { 386 (params.offset - alloc->first) + size > alloc->second.size) {
241 LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size); 387 ASSERT_MSG(false, "Cannot perform a fixed mapping into an unallocated region!");
242 result = NvResult::InvalidState; 388 return NvResult::BadValue;
389 }
390
391 const bool use_big_pages = alloc->second.big_pages && big_page;
392 gmmu->Map(params.offset, cpu_address, size, use_big_pages);
393
394 auto mapping{std::make_shared<Mapping>(cpu_address, params.offset, size, true,
395 use_big_pages, alloc->second.sparse)};
396 alloc->second.mappings.push_back(mapping);
397 mapping_map[params.offset] = mapping;
243 } else { 398 } else {
244 AddBufferMap(params.offset, size, physical_address, is_alloc); 399
400 auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
401 u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE};
402 u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
403
404 params.offset = static_cast<u64>(allocator.Allocate(
405 static_cast<u32>(Common::AlignUp(size, page_size) >> page_size_bits)))
406 << page_size_bits;
407 if (!params.offset) {
408 ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!");
409 return NvResult::InsufficientMemory;
410 }
411
412 gmmu->Map(params.offset, cpu_address, Common::AlignUp(size, page_size), big_page);
413
414 auto mapping{
415 std::make_shared<Mapping>(cpu_address, params.offset, size, false, big_page, false)};
416 mapping_map[params.offset] = mapping;
245 } 417 }
246 418
247 std::memcpy(output.data(), &params, output.size()); 419 std::memcpy(output.data(), &params, output.size());
248 return result; 420 return NvResult::Success;
249} 421}
250 422
251NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 423NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -254,47 +426,82 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8
254 426
255 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); 427 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
256 428
257 if (const auto size{RemoveBufferMap(params.offset)}; size) { 429 std::scoped_lock lock(mutex);
258 system.GPU().MemoryManager().Unmap(params.offset, *size); 430
259 } else { 431 if (!vm.initialised) {
260 LOG_ERROR(Service_NVDRV, "invalid offset=0x{:X}", params.offset); 432 return NvResult::BadValue;
433 }
434
435 try {
436 auto mapping{mapping_map.at(params.offset)};
437
438 if (!mapping->fixed) {
439 auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
440 u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
441
442 allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits),
443 static_cast<u32>(mapping->size >> page_size_bits));
444 }
445
446 // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
447 // Only FreeSpace can unmap them fully
448 if (mapping->sparse_alloc) {
449 gmmu->MapSparse(params.offset, mapping->size, mapping->big_page);
450 } else {
451 gmmu->Unmap(params.offset, mapping->size);
452 }
453
454 mapping_map.erase(params.offset);
455 } catch (const std::out_of_range&) {
456 LOG_WARNING(Service_NVDRV, "Couldn't find region to unmap at 0x{:X}", params.offset);
261 } 457 }
262 458
263 std::memcpy(output.data(), &params, output.size());
264 return NvResult::Success; 459 return NvResult::Success;
265} 460}
266 461
267NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { 462NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
268 IoctlBindChannel params{}; 463 IoctlBindChannel params{};
269 std::memcpy(&params, input.data(), input.size()); 464 std::memcpy(&params, input.data(), input.size());
270 LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd); 465 LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
271 466
272 channel = params.fd; 467 auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd);
468 gpu_channel_device->channel_state->memory_manager = gmmu;
273 return NvResult::Success; 469 return NvResult::Success;
274} 470}
275 471
472void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {
473 params.buf_size = 2 * sizeof(VaRegion);
474
475 params.regions = std::array<VaRegion, 2>{
476 VaRegion{
477 .offset = vm.small_page_allocator->GetVAStart() << VM::PAGE_SIZE_BITS,
478 .page_size = VM::YUZU_PAGESIZE,
479 ._pad0_{},
480 .pages = vm.small_page_allocator->GetVALimit() - vm.small_page_allocator->GetVAStart(),
481 },
482 VaRegion{
483 .offset = vm.big_page_allocator->GetVAStart() << vm.big_page_size_bits,
484 .page_size = vm.big_page_size,
485 ._pad0_{},
486 .pages = vm.big_page_allocator->GetVALimit() - vm.big_page_allocator->GetVAStart(),
487 },
488 };
489}
490
276NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { 491NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
277 IoctlGetVaRegions params{}; 492 IoctlGetVaRegions params{};
278 std::memcpy(&params, input.data(), input.size()); 493 std::memcpy(&params, input.data(), input.size());
279 494
280 LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, 495 LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
281 params.buf_size); 496 params.buf_size);
282
283 params.buf_size = 0x30;
284 497
285 params.small = IoctlVaRegion{ 498 std::scoped_lock lock(mutex);
286 .offset = 0x04000000,
287 .page_size = DEFAULT_SMALL_PAGE_SIZE,
288 .pages = 0x3fbfff,
289 };
290 499
291 params.big = IoctlVaRegion{ 500 if (!vm.initialised) {
292 .offset = 0x04000000, 501 return NvResult::BadValue;
293 .page_size = big_page_size, 502 }
294 .pages = 0x1bffff,
295 };
296 503
297 // TODO(ogniK): This probably can stay stubbed but should add support way way later 504 GetVARegionsImpl(params);
298 505
299 std::memcpy(output.data(), &params, output.size()); 506 std::memcpy(output.data(), &params, output.size());
300 return NvResult::Success; 507 return NvResult::Success;
@@ -305,62 +512,27 @@ NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u
305 IoctlGetVaRegions params{}; 512 IoctlGetVaRegions params{};
306 std::memcpy(&params, input.data(), input.size()); 513 std::memcpy(&params, input.data(), input.size());
307 514
308 LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, 515 LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
309 params.buf_size); 516 params.buf_size);
310
311 params.buf_size = 0x30;
312 517
313 params.small = IoctlVaRegion{ 518 std::scoped_lock lock(mutex);
314 .offset = 0x04000000,
315 .page_size = 0x1000,
316 .pages = 0x3fbfff,
317 };
318 519
319 params.big = IoctlVaRegion{ 520 if (!vm.initialised) {
320 .offset = 0x04000000, 521 return NvResult::BadValue;
321 .page_size = big_page_size, 522 }
322 .pages = 0x1bffff,
323 };
324 523
325 // TODO(ogniK): This probably can stay stubbed but should add support way way later 524 GetVARegionsImpl(params);
326 525
327 std::memcpy(output.data(), &params, output.size()); 526 std::memcpy(output.data(), &params, output.size());
328 std::memcpy(inline_output.data(), &params.small, sizeof(IoctlVaRegion)); 527 std::memcpy(inline_output.data(), &params.regions[0], sizeof(VaRegion));
329 std::memcpy(inline_output.data() + sizeof(IoctlVaRegion), &params.big, sizeof(IoctlVaRegion)); 528 std::memcpy(inline_output.data() + sizeof(VaRegion), &params.regions[1], sizeof(VaRegion));
330 529
331 return NvResult::Success; 530 return NvResult::Success;
332} 531}
333 532
334std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const { 533Kernel::KEvent* nvhost_as_gpu::QueryEvent(u32 event_id) {
335 const auto end{buffer_mappings.upper_bound(gpu_addr)}; 534 LOG_CRITICAL(Service_NVDRV, "Unknown AS GPU Event {}", event_id);
336 for (auto iter{buffer_mappings.begin()}; iter != end; ++iter) { 535 return nullptr;
337 if (gpu_addr >= iter->second.StartAddr() && gpu_addr < iter->second.EndAddr()) {
338 return iter->second;
339 }
340 }
341
342 return std::nullopt;
343}
344
345void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
346 bool is_allocated) {
347 buffer_mappings[gpu_addr] = {gpu_addr, size, cpu_addr, is_allocated};
348}
349
350std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) {
351 if (const auto iter{buffer_mappings.find(gpu_addr)}; iter != buffer_mappings.end()) {
352 std::size_t size{};
353
354 if (iter->second.IsAllocated()) {
355 size = iter->second.Size();
356 }
357
358 buffer_mappings.erase(iter);
359
360 return size;
361 }
362
363 return std::nullopt;
364} 536}
365 537
366} // namespace Service::Nvidia::Devices 538} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 555843a6f..86fe71c75 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -1,35 +1,50 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#pragma once 5#pragma once
5 6
7#include <bit>
8#include <list>
6#include <map> 9#include <map>
7#include <memory> 10#include <memory>
11#include <mutex>
8#include <optional> 12#include <optional>
9#include <vector> 13#include <vector>
10 14
15#include "common/address_space.h"
11#include "common/common_funcs.h" 16#include "common/common_funcs.h"
12#include "common/common_types.h" 17#include "common/common_types.h"
13#include "common/swap.h" 18#include "common/swap.h"
19#include "core/hle/service/nvdrv/core/nvmap.h"
14#include "core/hle/service/nvdrv/devices/nvdevice.h" 20#include "core/hle/service/nvdrv/devices/nvdevice.h"
15 21
16namespace Service::Nvidia::Devices { 22namespace Tegra {
23class MemoryManager;
24} // namespace Tegra
25
26namespace Service::Nvidia {
27class Module;
28}
17 29
18constexpr u32 DEFAULT_BIG_PAGE_SIZE = 1 << 16; 30namespace Service::Nvidia::NvCore {
19constexpr u32 DEFAULT_SMALL_PAGE_SIZE = 1 << 12; 31class Container;
32class NvMap;
33} // namespace Service::Nvidia::NvCore
20 34
21class nvmap; 35namespace Service::Nvidia::Devices {
22 36
23enum class AddressSpaceFlags : u32 { 37enum class MappingFlags : u32 {
24 None = 0x0, 38 None = 0,
25 FixedOffset = 0x1, 39 Fixed = 1 << 0,
26 Remap = 0x100, 40 Sparse = 1 << 1,
41 Remap = 1 << 8,
27}; 42};
28DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags); 43DECLARE_ENUM_FLAG_OPERATORS(MappingFlags);
29 44
30class nvhost_as_gpu final : public nvdevice { 45class nvhost_as_gpu final : public nvdevice {
31public: 46public:
32 explicit nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_); 47 explicit nvhost_as_gpu(Core::System& system_, Module& module, NvCore::Container& core);
33 ~nvhost_as_gpu() override; 48 ~nvhost_as_gpu() override;
34 49
35 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 50 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -42,46 +57,17 @@ public:
42 void OnOpen(DeviceFD fd) override; 57 void OnOpen(DeviceFD fd) override;
43 void OnClose(DeviceFD fd) override; 58 void OnClose(DeviceFD fd) override;
44 59
45private: 60 Kernel::KEvent* QueryEvent(u32 event_id) override;
46 class BufferMap final { 61
47 public: 62 struct VaRegion {
48 constexpr BufferMap() = default; 63 u64 offset;
49 64 u32 page_size;
50 constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_) 65 u32 _pad0_;
51 : start_addr{start_addr_}, end_addr{start_addr_ + size_} {} 66 u64 pages;
52
53 constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_, VAddr cpu_addr_,
54 bool is_allocated_)
55 : start_addr{start_addr_}, end_addr{start_addr_ + size_}, cpu_addr{cpu_addr_},
56 is_allocated{is_allocated_} {}
57
58 constexpr VAddr StartAddr() const {
59 return start_addr;
60 }
61
62 constexpr VAddr EndAddr() const {
63 return end_addr;
64 }
65
66 constexpr std::size_t Size() const {
67 return end_addr - start_addr;
68 }
69
70 constexpr VAddr CpuAddr() const {
71 return cpu_addr;
72 }
73
74 constexpr bool IsAllocated() const {
75 return is_allocated;
76 }
77
78 private:
79 GPUVAddr start_addr{};
80 GPUVAddr end_addr{};
81 VAddr cpu_addr{};
82 bool is_allocated{};
83 }; 67 };
68 static_assert(sizeof(VaRegion) == 0x18);
84 69
70private:
85 struct IoctlAllocAsEx { 71 struct IoctlAllocAsEx {
86 u32_le flags{}; // usually passes 1 72 u32_le flags{}; // usually passes 1
87 s32_le as_fd{}; // ignored; passes 0 73 s32_le as_fd{}; // ignored; passes 0
@@ -96,7 +82,7 @@ private:
96 struct IoctlAllocSpace { 82 struct IoctlAllocSpace {
97 u32_le pages{}; 83 u32_le pages{};
98 u32_le page_size{}; 84 u32_le page_size{};
99 AddressSpaceFlags flags{}; 85 MappingFlags flags{};
100 INSERT_PADDING_WORDS(1); 86 INSERT_PADDING_WORDS(1);
101 union { 87 union {
102 u64_le offset; 88 u64_le offset;
@@ -113,19 +99,19 @@ private:
113 static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size"); 99 static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size");
114 100
115 struct IoctlRemapEntry { 101 struct IoctlRemapEntry {
116 u16_le flags{}; 102 u16 flags;
117 u16_le kind{}; 103 u16 kind;
118 u32_le nvmap_handle{}; 104 NvCore::NvMap::Handle::Id handle;
119 u32_le map_offset{}; 105 u32 handle_offset_big_pages;
120 u32_le offset{}; 106 u32 as_offset_big_pages;
121 u32_le pages{}; 107 u32 big_pages;
122 }; 108 };
123 static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); 109 static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
124 110
125 struct IoctlMapBufferEx { 111 struct IoctlMapBufferEx {
126 AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable 112 MappingFlags flags{}; // bit0: fixed_offset, bit2: cacheable
127 u32_le kind{}; // -1 is default 113 u32_le kind{}; // -1 is default
128 u32_le nvmap_handle{}; 114 NvCore::NvMap::Handle::Id handle;
129 u32_le page_size{}; // 0 means don't care 115 u32_le page_size{}; // 0 means don't care
130 s64_le buffer_offset{}; 116 s64_le buffer_offset{};
131 u64_le mapping_size{}; 117 u64_le mapping_size{};
@@ -143,27 +129,15 @@ private:
143 }; 129 };
144 static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size"); 130 static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size");
145 131
146 struct IoctlVaRegion {
147 u64_le offset{};
148 u32_le page_size{};
149 INSERT_PADDING_WORDS(1);
150 u64_le pages{};
151 };
152 static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size");
153
154 struct IoctlGetVaRegions { 132 struct IoctlGetVaRegions {
155 u64_le buf_addr{}; // (contained output user ptr on linux, ignored) 133 u64_le buf_addr{}; // (contained output user ptr on linux, ignored)
156 u32_le buf_size{}; // forced to 2*sizeof(struct va_region) 134 u32_le buf_size{}; // forced to 2*sizeof(struct va_region)
157 u32_le reserved{}; 135 u32_le reserved{};
158 IoctlVaRegion small{}; 136 std::array<VaRegion, 2> regions{};
159 IoctlVaRegion big{};
160 }; 137 };
161 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, 138 static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2,
162 "IoctlGetVaRegions is incorrect size"); 139 "IoctlGetVaRegions is incorrect size");
163 140
164 s32 channel{};
165 u32 big_page_size{DEFAULT_BIG_PAGE_SIZE};
166
167 NvResult AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output); 141 NvResult AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output);
168 NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); 142 NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
169 NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output); 143 NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output);
@@ -172,18 +146,75 @@ private:
172 NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output); 146 NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
173 NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output); 147 NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
174 148
149 void GetVARegionsImpl(IoctlGetVaRegions& params);
175 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); 150 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
176 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output, 151 NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
177 std::vector<u8>& inline_output); 152 std::vector<u8>& inline_output);
178 153
179 std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const; 154 void FreeMappingLocked(u64 offset);
180 void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); 155
181 std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr); 156 Module& module;
157
158 NvCore::Container& container;
159 NvCore::NvMap& nvmap;
182 160
183 std::shared_ptr<nvmap> nvmap_dev; 161 struct Mapping {
162 VAddr ptr;
163 u64 offset;
164 u64 size;
165 bool fixed;
166 bool big_page; // Only valid if fixed == false
167 bool sparse_alloc;
168
169 Mapping(VAddr ptr_, u64 offset_, u64 size_, bool fixed_, bool big_page_, bool sparse_alloc_)
170 : ptr(ptr_), offset(offset_), size(size_), fixed(fixed_), big_page(big_page_),
171 sparse_alloc(sparse_alloc_) {}
172 };
173
174 struct Allocation {
175 u64 size;
176 std::list<std::shared_ptr<Mapping>> mappings;
177 u32 page_size;
178 bool sparse;
179 bool big_pages;
180 };
184 181
185 // This is expected to be ordered, therefore we must use a map, not unordered_map 182 std::map<u64, std::shared_ptr<Mapping>>
186 std::map<GPUVAddr, BufferMap> buffer_mappings; 183 mapping_map; //!< This maps the base addresses of mapped buffers to their total sizes and
184 //!< mapping type, this is needed as what was originally a single buffer may
185 //!< have been split into multiple GPU side buffers with the remap flag.
186 std::map<u64, Allocation> allocation_map; //!< Holds allocations created by AllocSpace from
187 //!< which fixed buffers can be mapped into
188 std::mutex mutex; //!< Locks all AS operations
189
190 struct VM {
191 static constexpr u32 YUZU_PAGESIZE{0x1000};
192 static constexpr u32 PAGE_SIZE_BITS{std::countr_zero(YUZU_PAGESIZE)};
193
194 static constexpr u32 SUPPORTED_BIG_PAGE_SIZES{0x30000};
195 static constexpr u32 DEFAULT_BIG_PAGE_SIZE{0x20000};
196 u32 big_page_size{DEFAULT_BIG_PAGE_SIZE};
197 u32 big_page_size_bits{std::countr_zero(DEFAULT_BIG_PAGE_SIZE)};
198
199 static constexpr u32 VA_START_SHIFT{10};
200 static constexpr u64 DEFAULT_VA_SPLIT{1ULL << 34};
201 static constexpr u64 DEFAULT_VA_RANGE{1ULL << 37};
202 u64 va_range_start{DEFAULT_BIG_PAGE_SIZE << VA_START_SHIFT};
203 u64 va_range_split{DEFAULT_VA_SPLIT};
204 u64 va_range_end{DEFAULT_VA_RANGE};
205
206 using Allocator = Common::FlatAllocator<u32, 0, 32>;
207
208 std::unique_ptr<Allocator> big_page_allocator;
209 std::shared_ptr<Allocator>
210 small_page_allocator; //! Shared as this is also used by nvhost::GpuChannel
211
212 bool initialised{};
213 } vm;
214 std::shared_ptr<Tegra::MemoryManager> gmmu;
215
216 // s32 channel{};
217 // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE};
187}; 218};
188 219
189} // namespace Service::Nvidia::Devices 220} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 527531f29..5bee4a3d3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -1,24 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
5#include <bit>
4#include <cstdlib> 6#include <cstdlib>
5#include <cstring> 7#include <cstring>
6 8
9#include <fmt/format.h>
7#include "common/assert.h" 10#include "common/assert.h"
8#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/scope_exit.h"
9#include "core/core.h" 13#include "core/core.h"
10#include "core/hle/kernel/k_event.h" 14#include "core/hle/kernel/k_event.h"
11#include "core/hle/kernel/k_writable_event.h" 15#include "core/hle/kernel/k_writable_event.h"
16#include "core/hle/service/nvdrv/core/container.h"
17#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
12#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" 18#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
13#include "video_core/gpu.h" 19#include "video_core/gpu.h"
20#include "video_core/host1x/host1x.h"
14 21
15namespace Service::Nvidia::Devices { 22namespace Service::Nvidia::Devices {
16 23
17nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, 24nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_,
18 SyncpointManager& syncpoint_manager_) 25 NvCore::Container& core_)
19 : nvdevice{system_}, events_interface{events_interface_}, syncpoint_manager{ 26 : nvdevice{system_}, events_interface{events_interface_}, core{core_},
20 syncpoint_manager_} {} 27 syncpoint_manager{core_.GetSyncpointManager()} {}
21nvhost_ctrl::~nvhost_ctrl() = default; 28
29nvhost_ctrl::~nvhost_ctrl() {
30 for (auto& event : events) {
31 if (!event.registered) {
32 continue;
33 }
34 events_interface.FreeEvent(event.kevent);
35 }
36}
22 37
23NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 38NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
24 std::vector<u8>& output) { 39 std::vector<u8>& output) {
@@ -30,13 +45,15 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
30 case 0x1c: 45 case 0x1c:
31 return IocCtrlClearEventWait(input, output); 46 return IocCtrlClearEventWait(input, output);
32 case 0x1d: 47 case 0x1d:
33 return IocCtrlEventWait(input, output, false);
34 case 0x1e:
35 return IocCtrlEventWait(input, output, true); 48 return IocCtrlEventWait(input, output, true);
49 case 0x1e:
50 return IocCtrlEventWait(input, output, false);
36 case 0x1f: 51 case 0x1f:
37 return IocCtrlEventRegister(input, output); 52 return IocCtrlEventRegister(input, output);
38 case 0x20: 53 case 0x20:
39 return IocCtrlEventUnregister(input, output); 54 return IocCtrlEventUnregister(input, output);
55 case 0x21:
56 return IocCtrlEventUnregisterBatch(input, output);
40 } 57 }
41 break; 58 break;
42 default: 59 default:
@@ -60,6 +77,7 @@ NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
60} 77}
61 78
62void nvhost_ctrl::OnOpen(DeviceFD fd) {} 79void nvhost_ctrl::OnOpen(DeviceFD fd) {}
80
63void nvhost_ctrl::OnClose(DeviceFD fd) {} 81void nvhost_ctrl::OnClose(DeviceFD fd) {}
64 82
65NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { 83NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -71,116 +89,167 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector
71} 89}
72 90
73NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, 91NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
74 bool is_async) { 92 bool is_allocation) {
75 IocCtrlEventWaitParams params{}; 93 IocCtrlEventWaitParams params{};
76 std::memcpy(&params, input.data(), sizeof(params)); 94 std::memcpy(&params, input.data(), sizeof(params));
77 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", 95 LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}",
78 params.syncpt_id, params.threshold, params.timeout, is_async); 96 params.fence.id, params.fence.value, params.timeout, is_allocation);
79 97
80 if (params.syncpt_id >= MaxSyncPoints) { 98 bool must_unmark_fail = !is_allocation;
81 return NvResult::BadParameter; 99 const u32 event_id = params.value.raw;
82 } 100 SCOPE_EXIT({
101 std::memcpy(output.data(), &params, sizeof(params));
102 if (must_unmark_fail) {
103 events[event_id].fails = 0;
104 }
105 });
83 106
84 u32 event_id = params.value & 0x00FF; 107 const u32 fence_id = static_cast<u32>(params.fence.id);
85 108
86 if (event_id >= MaxNvEvents) { 109 if (fence_id >= MaxSyncPoints) {
87 std::memcpy(output.data(), &params, sizeof(params));
88 return NvResult::BadParameter; 110 return NvResult::BadParameter;
89 } 111 }
90 112
91 if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { 113 if (params.fence.value == 0) {
92 params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id); 114 if (!syncpoint_manager.IsSyncpointAllocated(params.fence.id)) {
93 std::memcpy(output.data(), &params, sizeof(params)); 115 LOG_WARNING(Service_NVDRV,
94 events_interface.failed[event_id] = false; 116 "Unallocated syncpt_id={}, threshold={}, timeout={}, is_allocation={}",
117 params.fence.id, params.fence.value, params.timeout, is_allocation);
118 } else {
119 params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id);
120 }
95 return NvResult::Success; 121 return NvResult::Success;
96 } 122 }
97 123
98 if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id); 124 if (syncpoint_manager.IsFenceSignalled(params.fence)) {
99 syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { 125 params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id);
100 params.value = new_value;
101 std::memcpy(output.data(), &params, sizeof(params));
102 events_interface.failed[event_id] = false;
103 return NvResult::Success; 126 return NvResult::Success;
104 } 127 }
105 128
106 auto& event = events_interface.events[event_id]; 129 if (const auto new_value = syncpoint_manager.UpdateMin(fence_id);
107 auto& gpu = system.GPU(); 130 syncpoint_manager.IsFenceSignalled(params.fence)) {
108 131 params.value.raw = new_value;
109 // This is mostly to take into account unimplemented features. As synced
110 // gpu is always synced.
111 if (!gpu.IsAsync()) {
112 event.event->GetWritableEvent().Signal();
113 return NvResult::Success;
114 }
115 const u32 current_syncpoint_value = event.fence.value;
116 const s32 diff = current_syncpoint_value - params.threshold;
117 if (diff >= 0) {
118 event.event->GetWritableEvent().Signal();
119 params.value = current_syncpoint_value;
120 std::memcpy(output.data(), &params, sizeof(params));
121 events_interface.failed[event_id] = false;
122 return NvResult::Success; 132 return NvResult::Success;
123 } 133 }
124 const u32 target_value = current_syncpoint_value - diff;
125 134
126 if (!is_async) { 135 auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager();
127 params.value = 0; 136 const u32 target_value = params.fence.value;
137
138 auto lock = NvEventsLock();
139
140 u32 slot = [&]() {
141 if (is_allocation) {
142 params.value.raw = 0;
143 return FindFreeNvEvent(fence_id);
144 } else {
145 return params.value.raw;
146 }
147 }();
148
149 must_unmark_fail = false;
150
151 const auto check_failing = [&]() {
152 if (events[slot].fails > 2) {
153 {
154 auto lk = system.StallProcesses();
155 host1x_syncpoint_manager.WaitHost(fence_id, target_value);
156 system.UnstallProcesses();
157 }
158 params.value.raw = target_value;
159 return true;
160 }
161 return false;
162 };
163
164 if (slot >= MaxNvEvents) {
165 return NvResult::BadParameter;
128 } 166 }
129 167
130 if (params.timeout == 0) { 168 if (params.timeout == 0) {
131 std::memcpy(output.data(), &params, sizeof(params)); 169 if (check_failing()) {
170 events[slot].fails = 0;
171 return NvResult::Success;
172 }
132 return NvResult::Timeout; 173 return NvResult::Timeout;
133 } 174 }
134 175
135 EventState status = events_interface.status[event_id]; 176 auto& event = events[slot];
136 const bool bad_parameter = status == EventState::Busy; 177
137 if (bad_parameter) { 178 if (!event.registered) {
138 std::memcpy(output.data(), &params, sizeof(params));
139 return NvResult::BadParameter; 179 return NvResult::BadParameter;
140 } 180 }
141 events_interface.SetEventStatus(event_id, EventState::Waiting); 181
142 events_interface.assigned_syncpt[event_id] = params.syncpt_id; 182 if (event.IsBeingUsed()) {
143 events_interface.assigned_value[event_id] = target_value; 183 return NvResult::BadParameter;
144 if (is_async) { 184 }
145 params.value = params.syncpt_id << 4; 185
146 } else { 186 if (check_failing()) {
147 params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; 187 event.fails = 0;
148 }
149 params.value |= event_id;
150 event.event->GetWritableEvent().Clear();
151 if (events_interface.failed[event_id]) {
152 {
153 auto lk = system.StallProcesses();
154 gpu.WaitFence(params.syncpt_id, target_value);
155 system.UnstallProcesses();
156 }
157 std::memcpy(output.data(), &params, sizeof(params));
158 events_interface.failed[event_id] = false;
159 return NvResult::Success; 188 return NvResult::Success;
160 } 189 }
161 gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); 190
162 std::memcpy(output.data(), &params, sizeof(params)); 191 params.value.raw = 0;
192
193 event.status.store(EventState::Waiting, std::memory_order_release);
194 event.assigned_syncpt = fence_id;
195 event.assigned_value = target_value;
196 if (is_allocation) {
197 params.value.syncpoint_id_for_allocation.Assign(static_cast<u16>(fence_id));
198 params.value.event_allocated.Assign(1);
199 } else {
200 params.value.syncpoint_id.Assign(fence_id);
201 }
202 params.value.raw |= slot;
203
204 event.wait_handle =
205 host1x_syncpoint_manager.RegisterHostAction(fence_id, target_value, [this, slot]() {
206 auto& event_ = events[slot];
207 if (event_.status.exchange(EventState::Signalling, std::memory_order_acq_rel) ==
208 EventState::Waiting) {
209 event_.kevent->GetWritableEvent().Signal();
210 }
211 event_.status.store(EventState::Signalled, std::memory_order_release);
212 });
163 return NvResult::Timeout; 213 return NvResult::Timeout;
164} 214}
165 215
216NvResult nvhost_ctrl::FreeEvent(u32 slot) {
217 if (slot >= MaxNvEvents) {
218 return NvResult::BadParameter;
219 }
220
221 auto& event = events[slot];
222
223 if (!event.registered) {
224 return NvResult::Success;
225 }
226
227 if (event.IsBeingUsed()) {
228 return NvResult::Busy;
229 }
230
231 FreeNvEvent(slot);
232 return NvResult::Success;
233}
234
166NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { 235NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
167 IocCtrlEventRegisterParams params{}; 236 IocCtrlEventRegisterParams params{};
168 std::memcpy(&params, input.data(), sizeof(params)); 237 std::memcpy(&params, input.data(), sizeof(params));
169 const u32 event_id = params.user_event_id & 0x00FF; 238 const u32 event_id = params.user_event_id;
170 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); 239 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
171 if (event_id >= MaxNvEvents) { 240 if (event_id >= MaxNvEvents) {
172 return NvResult::BadParameter; 241 return NvResult::BadParameter;
173 } 242 }
174 if (events_interface.registered[event_id]) { 243
175 const auto event_state = events_interface.status[event_id]; 244 auto lock = NvEventsLock();
176 if (event_state != EventState::Free) { 245
177 LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event"); 246 if (events[event_id].registered) {
178 events_interface.UnregisterEvent(event_id); 247 const auto result = FreeEvent(event_id);
179 } else { 248 if (result != NvResult::Success) {
180 return NvResult::BadParameter; 249 return result;
181 } 250 }
182 } 251 }
183 events_interface.RegisterEvent(event_id); 252 CreateNvEvent(event_id);
184 return NvResult::Success; 253 return NvResult::Success;
185} 254}
186 255
@@ -190,34 +259,142 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
190 std::memcpy(&params, input.data(), sizeof(params)); 259 std::memcpy(&params, input.data(), sizeof(params));
191 const u32 event_id = params.user_event_id & 0x00FF; 260 const u32 event_id = params.user_event_id & 0x00FF;
192 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); 261 LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
193 if (event_id >= MaxNvEvents) { 262
194 return NvResult::BadParameter; 263 auto lock = NvEventsLock();
195 } 264 return FreeEvent(event_id);
196 if (!events_interface.registered[event_id]) { 265}
197 return NvResult::BadParameter; 266
267NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector<u8>& input,
268 std::vector<u8>& output) {
269 IocCtrlEventUnregisterBatchParams params{};
270 std::memcpy(&params, input.data(), sizeof(params));
271 u64 event_mask = params.user_events;
272 LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask);
273
274 auto lock = NvEventsLock();
275 while (event_mask != 0) {
276 const u64 event_id = std::countr_zero(event_mask);
277 event_mask &= ~(1ULL << event_id);
278 const auto result = FreeEvent(static_cast<u32>(event_id));
279 if (result != NvResult::Success) {
280 return result;
281 }
198 } 282 }
199 events_interface.UnregisterEvent(event_id);
200 return NvResult::Success; 283 return NvResult::Success;
201} 284}
202 285
203NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { 286NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
204 IocCtrlEventSignalParams params{}; 287 IocCtrlEventClearParams params{};
205 std::memcpy(&params, input.data(), sizeof(params)); 288 std::memcpy(&params, input.data(), sizeof(params));
206 289
207 u32 event_id = params.event_id & 0x00FF; 290 u32 event_id = params.event_id.slot;
208 LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id); 291 LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id);
209 292
210 if (event_id >= MaxNvEvents) { 293 if (event_id >= MaxNvEvents) {
211 return NvResult::BadParameter; 294 return NvResult::BadParameter;
212 } 295 }
213 if (events_interface.status[event_id] == EventState::Waiting) {
214 events_interface.LiberateEvent(event_id);
215 }
216 events_interface.failed[event_id] = true;
217 296
218 syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id); 297 auto lock = NvEventsLock();
298
299 auto& event = events[event_id];
300 if (event.status.exchange(EventState::Cancelling, std::memory_order_acq_rel) ==
301 EventState::Waiting) {
302 auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager();
303 host1x_syncpoint_manager.DeregisterHostAction(event.assigned_syncpt, event.wait_handle);
304 syncpoint_manager.UpdateMin(event.assigned_syncpt);
305 event.wait_handle = {};
306 }
307 event.fails++;
308 event.status.store(EventState::Cancelled, std::memory_order_release);
309 event.kevent->GetWritableEvent().Clear();
219 310
220 return NvResult::Success; 311 return NvResult::Success;
221} 312}
222 313
314Kernel::KEvent* nvhost_ctrl::QueryEvent(u32 event_id) {
315 const auto desired_event = SyncpointEventValue{.raw = event_id};
316
317 const bool allocated = desired_event.event_allocated.Value() != 0;
318 const u32 slot{allocated ? desired_event.partial_slot.Value()
319 : static_cast<u32>(desired_event.slot)};
320 if (slot >= MaxNvEvents) {
321 ASSERT(false);
322 return nullptr;
323 }
324
325 const u32 syncpoint_id{allocated ? desired_event.syncpoint_id_for_allocation.Value()
326 : desired_event.syncpoint_id.Value()};
327
328 auto lock = NvEventsLock();
329
330 auto& event = events[slot];
331 if (event.registered && event.assigned_syncpt == syncpoint_id) {
332 ASSERT(event.kevent);
333 return event.kevent;
334 }
335 // Is this possible in hardware?
336 ASSERT_MSG(false, "Slot:{}, SyncpointID:{}, requested", slot, syncpoint_id);
337 return nullptr;
338}
339
340std::unique_lock<std::mutex> nvhost_ctrl::NvEventsLock() {
341 return std::unique_lock<std::mutex>(events_mutex);
342}
343
344void nvhost_ctrl::CreateNvEvent(u32 event_id) {
345 auto& event = events[event_id];
346 ASSERT(!event.kevent);
347 ASSERT(!event.registered);
348 ASSERT(!event.IsBeingUsed());
349 event.kevent = events_interface.CreateEvent(fmt::format("NVCTRL::NvEvent_{}", event_id));
350 event.status = EventState::Available;
351 event.registered = true;
352 const u64 mask = 1ULL << event_id;
353 event.fails = 0;
354 events_mask |= mask;
355 event.assigned_syncpt = 0;
356}
357
358void nvhost_ctrl::FreeNvEvent(u32 event_id) {
359 auto& event = events[event_id];
360 ASSERT(event.kevent);
361 ASSERT(event.registered);
362 ASSERT(!event.IsBeingUsed());
363 events_interface.FreeEvent(event.kevent);
364 event.kevent = nullptr;
365 event.status = EventState::Available;
366 event.registered = false;
367 const u64 mask = ~(1ULL << event_id);
368 events_mask &= mask;
369}
370
371u32 nvhost_ctrl::FindFreeNvEvent(u32 syncpoint_id) {
372 u32 slot{MaxNvEvents};
373 u32 free_slot{MaxNvEvents};
374 for (u32 i = 0; i < MaxNvEvents; i++) {
375 auto& event = events[i];
376 if (event.registered) {
377 if (!event.IsBeingUsed()) {
378 slot = i;
379 if (event.assigned_syncpt == syncpoint_id) {
380 return slot;
381 }
382 }
383 } else if (free_slot == MaxNvEvents) {
384 free_slot = i;
385 }
386 }
387 if (free_slot < MaxNvEvents) {
388 CreateNvEvent(free_slot);
389 return free_slot;
390 }
391
392 if (slot < MaxNvEvents) {
393 return slot;
394 }
395
396 LOG_CRITICAL(Service_NVDRV, "Failed to allocate an event");
397 return 0;
398}
399
223} // namespace Service::Nvidia::Devices 400} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 4fbb89b15..0b56d7070 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -1,20 +1,28 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#pragma once 5#pragma once
5 6
6#include <array> 7#include <array>
7#include <vector> 8#include <vector>
9#include "common/bit_field.h"
8#include "common/common_types.h" 10#include "common/common_types.h"
9#include "core/hle/service/nvdrv/devices/nvdevice.h" 11#include "core/hle/service/nvdrv/devices/nvdevice.h"
10#include "core/hle/service/nvdrv/nvdrv.h" 12#include "core/hle/service/nvdrv/nvdrv.h"
13#include "video_core/host1x/syncpoint_manager.h"
14
15namespace Service::Nvidia::NvCore {
16class Container;
17class SyncpointManager;
18} // namespace Service::Nvidia::NvCore
11 19
12namespace Service::Nvidia::Devices { 20namespace Service::Nvidia::Devices {
13 21
14class nvhost_ctrl final : public nvdevice { 22class nvhost_ctrl final : public nvdevice {
15public: 23public:
16 explicit nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, 24 explicit nvhost_ctrl(Core::System& system_, EventInterface& events_interface_,
17 SyncpointManager& syncpoint_manager_); 25 NvCore::Container& core);
18 ~nvhost_ctrl() override; 26 ~nvhost_ctrl() override;
19 27
20 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 28 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -27,7 +35,70 @@ public:
27 void OnOpen(DeviceFD fd) override; 35 void OnOpen(DeviceFD fd) override;
28 void OnClose(DeviceFD fd) override; 36 void OnClose(DeviceFD fd) override;
29 37
38 Kernel::KEvent* QueryEvent(u32 event_id) override;
39
40 union SyncpointEventValue {
41 u32 raw;
42
43 union {
44 BitField<0, 4, u32> partial_slot;
45 BitField<4, 28, u32> syncpoint_id;
46 };
47
48 struct {
49 u16 slot;
50 union {
51 BitField<0, 12, u16> syncpoint_id_for_allocation;
52 BitField<12, 1, u16> event_allocated;
53 };
54 };
55 };
56 static_assert(sizeof(SyncpointEventValue) == sizeof(u32));
57
30private: 58private:
59 struct InternalEvent {
60 // Mask representing registered events
61
62 // Each kernel event associated to an NV event
63 Kernel::KEvent* kevent{};
64 // The status of the current NVEvent
65 std::atomic<EventState> status{};
66
67 // Tells the NVEvent that it has failed.
68 u32 fails{};
69 // When an NVEvent is waiting on GPU interrupt, this is the sync_point
70 // associated with it.
71 u32 assigned_syncpt{};
72 // This is the value of the GPU interrupt for which the NVEvent is waiting
73 // for.
74 u32 assigned_value{};
75
76 // Tells if an NVEvent is registered or not
77 bool registered{};
78
79 // Used for waiting on a syncpoint & canceling it.
80 Tegra::Host1x::SyncpointManager::ActionHandle wait_handle{};
81
82 bool IsBeingUsed() const {
83 const auto current_status = status.load(std::memory_order_acquire);
84 return current_status == EventState::Waiting ||
85 current_status == EventState::Cancelling ||
86 current_status == EventState::Signalling;
87 }
88 };
89
90 std::unique_lock<std::mutex> NvEventsLock();
91
92 void CreateNvEvent(u32 event_id);
93
94 void FreeNvEvent(u32 event_id);
95
96 u32 FindFreeNvEvent(u32 syncpoint_id);
97
98 std::array<InternalEvent, MaxNvEvents> events{};
99 std::mutex events_mutex;
100 u64 events_mask{};
101
31 struct IocSyncptReadParams { 102 struct IocSyncptReadParams {
32 u32_le id{}; 103 u32_le id{};
33 u32_le value{}; 104 u32_le value{};
@@ -83,27 +154,18 @@ private:
83 }; 154 };
84 static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); 155 static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
85 156
86 struct IocCtrlEventSignalParams { 157 struct IocCtrlEventClearParams {
87 u32_le event_id{}; 158 SyncpointEventValue event_id{};
88 }; 159 };
89 static_assert(sizeof(IocCtrlEventSignalParams) == 4, 160 static_assert(sizeof(IocCtrlEventClearParams) == 4,
90 "IocCtrlEventSignalParams is incorrect size"); 161 "IocCtrlEventClearParams is incorrect size");
91 162
92 struct IocCtrlEventWaitParams { 163 struct IocCtrlEventWaitParams {
93 u32_le syncpt_id{}; 164 NvFence fence{};
94 u32_le threshold{};
95 s32_le timeout{};
96 u32_le value{};
97 };
98 static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
99
100 struct IocCtrlEventWaitAsyncParams {
101 u32_le syncpt_id{};
102 u32_le threshold{};
103 u32_le timeout{}; 165 u32_le timeout{};
104 u32_le value{}; 166 SyncpointEventValue value{};
105 }; 167 };
106 static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16, 168 static_assert(sizeof(IocCtrlEventWaitParams) == 16,
107 "IocCtrlEventWaitAsyncParams is incorrect size"); 169 "IocCtrlEventWaitAsyncParams is incorrect size");
108 170
109 struct IocCtrlEventRegisterParams { 171 struct IocCtrlEventRegisterParams {
@@ -118,19 +180,25 @@ private:
118 static_assert(sizeof(IocCtrlEventUnregisterParams) == 4, 180 static_assert(sizeof(IocCtrlEventUnregisterParams) == 4,
119 "IocCtrlEventUnregisterParams is incorrect size"); 181 "IocCtrlEventUnregisterParams is incorrect size");
120 182
121 struct IocCtrlEventKill { 183 struct IocCtrlEventUnregisterBatchParams {
122 u64_le user_events{}; 184 u64_le user_events{};
123 }; 185 };
124 static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size"); 186 static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8,
187 "IocCtrlEventKill is incorrect size");
125 188
126 NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); 189 NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
127 NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async); 190 NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
191 bool is_allocation);
128 NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); 192 NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
129 NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); 193 NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
194 NvResult IocCtrlEventUnregisterBatch(const std::vector<u8>& input, std::vector<u8>& output);
130 NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); 195 NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
131 196
197 NvResult FreeEvent(u32 slot);
198
132 EventInterface& events_interface; 199 EventInterface& events_interface;
133 SyncpointManager& syncpoint_manager; 200 NvCore::Container& core;
201 NvCore::SyncpointManager& syncpoint_manager;
134}; 202};
135 203
136} // namespace Service::Nvidia::Devices 204} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 2b3b7efea..ced57dfe6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -7,11 +7,19 @@
7#include "core/core.h" 7#include "core/core.h"
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" 9#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
10#include "core/hle/service/nvdrv/nvdrv.h"
10 11
11namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
12 13
13nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_) : nvdevice{system_} {} 14nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_)
14nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; 15 : nvdevice{system_}, events_interface{events_interface_} {
16 error_notifier_event = events_interface.CreateEvent("CtrlGpuErrorNotifier");
17 unknown_event = events_interface.CreateEvent("CtrlGpuUknownEvent");
18}
19nvhost_ctrl_gpu::~nvhost_ctrl_gpu() {
20 events_interface.FreeEvent(error_notifier_event);
21 events_interface.FreeEvent(unknown_event);
22}
15 23
16NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 24NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
17 std::vector<u8>& output) { 25 std::vector<u8>& output) {
@@ -286,4 +294,17 @@ NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u
286 return NvResult::Success; 294 return NvResult::Success;
287} 295}
288 296
297Kernel::KEvent* nvhost_ctrl_gpu::QueryEvent(u32 event_id) {
298 switch (event_id) {
299 case 1:
300 return error_notifier_event;
301 case 2:
302 return unknown_event;
303 default: {
304 LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
305 }
306 }
307 return nullptr;
308}
309
289} // namespace Service::Nvidia::Devices 310} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 97e9a90cb..1e8f254e2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -10,11 +10,15 @@
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/hle/service/nvdrv/devices/nvdevice.h" 11#include "core/hle/service/nvdrv/devices/nvdevice.h"
12 12
13namespace Service::Nvidia {
14class EventInterface;
15}
16
13namespace Service::Nvidia::Devices { 17namespace Service::Nvidia::Devices {
14 18
15class nvhost_ctrl_gpu final : public nvdevice { 19class nvhost_ctrl_gpu final : public nvdevice {
16public: 20public:
17 explicit nvhost_ctrl_gpu(Core::System& system_); 21 explicit nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_);
18 ~nvhost_ctrl_gpu() override; 22 ~nvhost_ctrl_gpu() override;
19 23
20 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 24 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -27,6 +31,8 @@ public:
27 void OnOpen(DeviceFD fd) override; 31 void OnOpen(DeviceFD fd) override;
28 void OnClose(DeviceFD fd) override; 32 void OnClose(DeviceFD fd) override;
29 33
34 Kernel::KEvent* QueryEvent(u32 event_id) override;
35
30private: 36private:
31 struct IoctlGpuCharacteristics { 37 struct IoctlGpuCharacteristics {
32 u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200) 38 u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200)
@@ -160,6 +166,12 @@ private:
160 NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output); 166 NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
161 NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output); 167 NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
162 NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output); 168 NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
169
170 EventInterface& events_interface;
171
172 // Events
173 Kernel::KEvent* error_notifier_event;
174 Kernel::KEvent* unknown_event;
163}; 175};
164 176
165} // namespace Service::Nvidia::Devices 177} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index b98e63011..45a759fa8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -5,29 +5,46 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hle/service/nvdrv/core/container.h"
9#include "core/hle/service/nvdrv/core/nvmap.h"
10#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
8#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" 11#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
9#include "core/hle/service/nvdrv/syncpoint_manager.h" 12#include "core/hle/service/nvdrv/nvdrv.h"
10#include "core/memory.h" 13#include "core/memory.h"
14#include "video_core/control/channel_state.h"
15#include "video_core/engines/puller.h"
11#include "video_core/gpu.h" 16#include "video_core/gpu.h"
17#include "video_core/host1x/host1x.h"
12 18
13namespace Service::Nvidia::Devices { 19namespace Service::Nvidia::Devices {
14namespace { 20namespace {
15Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoint_id) { 21Tegra::CommandHeader BuildFenceAction(Tegra::Engines::Puller::FenceOperation op, u32 syncpoint_id) {
16 Tegra::GPU::FenceAction result{}; 22 Tegra::Engines::Puller::FenceAction result{};
17 result.op.Assign(op); 23 result.op.Assign(op);
18 result.syncpoint_id.Assign(syncpoint_id); 24 result.syncpoint_id.Assign(syncpoint_id);
19 return {result.raw}; 25 return {result.raw};
20} 26}
21} // namespace 27} // namespace
22 28
23nvhost_gpu::nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 29nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_,
24 SyncpointManager& syncpoint_manager_) 30 NvCore::Container& core_)
25 : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} { 31 : nvdevice{system_}, events_interface{events_interface_}, core{core_},
26 channel_fence.id = syncpoint_manager_.AllocateSyncpoint(); 32 syncpoint_manager{core_.GetSyncpointManager()}, nvmap{core.GetNvMapFile()},
27 channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id); 33 channel_state{system.GPU().AllocateChannel()} {
34 channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false);
35 sm_exception_breakpoint_int_report_event =
36 events_interface.CreateEvent("GpuChannelSMExceptionBreakpointInt");
37 sm_exception_breakpoint_pause_report_event =
38 events_interface.CreateEvent("GpuChannelSMExceptionBreakpointPause");
39 error_notifier_event = events_interface.CreateEvent("GpuChannelErrorNotifier");
28} 40}
29 41
30nvhost_gpu::~nvhost_gpu() = default; 42nvhost_gpu::~nvhost_gpu() {
43 events_interface.FreeEvent(sm_exception_breakpoint_int_report_event);
44 events_interface.FreeEvent(sm_exception_breakpoint_pause_report_event);
45 events_interface.FreeEvent(error_notifier_event);
46 syncpoint_manager.FreeSyncpoint(channel_syncpoint);
47}
31 48
32NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 49NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
33 std::vector<u8>& output) { 50 std::vector<u8>& output) {
@@ -167,9 +184,14 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8
167 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, 184 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
168 params.unk3); 185 params.unk3);
169 186
170 channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id); 187 if (channel_state->initialized) {
188 LOG_CRITICAL(Service_NVDRV, "Already allocated!");
189 return NvResult::AlreadyAllocated;
190 }
191
192 system.GPU().InitChannel(*channel_state);
171 193
172 params.fence_out = channel_fence; 194 params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint);
173 195
174 std::memcpy(output.data(), &params, output.size()); 196 std::memcpy(output.data(), &params, output.size());
175 return NvResult::Success; 197 return NvResult::Success;
@@ -188,39 +210,37 @@ NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::ve
188 210
189static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) { 211static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) {
190 return { 212 return {
191 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, 213 Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,
192 Tegra::SubmissionMode::Increasing), 214 Tegra::SubmissionMode::Increasing),
193 {fence.value}, 215 {fence.value},
194 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, 216 Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1,
195 Tegra::SubmissionMode::Increasing), 217 Tegra::SubmissionMode::Increasing),
196 BuildFenceAction(Tegra::GPU::FenceOperation::Acquire, fence.id), 218 BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Acquire, fence.id),
197 }; 219 };
198} 220}
199 221
200static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence, 222static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence) {
201 u32 add_increment) {
202 std::vector<Tegra::CommandHeader> result{ 223 std::vector<Tegra::CommandHeader> result{
203 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, 224 Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,
204 Tegra::SubmissionMode::Increasing), 225 Tegra::SubmissionMode::Increasing),
205 {}}; 226 {}};
206 227
207 for (u32 count = 0; count < add_increment; ++count) { 228 for (u32 count = 0; count < 2; ++count) {
208 result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, 229 result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1,
209 Tegra::SubmissionMode::Increasing)); 230 Tegra::SubmissionMode::Increasing));
210 result.emplace_back(BuildFenceAction(Tegra::GPU::FenceOperation::Increment, fence.id)); 231 result.emplace_back(
232 BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id));
211 } 233 }
212 234
213 return result; 235 return result;
214} 236}
215 237
216static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence, 238static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence) {
217 u32 add_increment) {
218 std::vector<Tegra::CommandHeader> result{ 239 std::vector<Tegra::CommandHeader> result{
219 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1, 240 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1,
220 Tegra::SubmissionMode::Increasing), 241 Tegra::SubmissionMode::Increasing),
221 {}}; 242 {}};
222 const std::vector<Tegra::CommandHeader> increment{ 243 const std::vector<Tegra::CommandHeader> increment{BuildIncrementCommandList(fence)};
223 BuildIncrementCommandList(fence, add_increment)};
224 244
225 result.insert(result.end(), increment.begin(), increment.end()); 245 result.insert(result.end(), increment.begin(), increment.end());
226 246
@@ -234,33 +254,41 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>
234 254
235 auto& gpu = system.GPU(); 255 auto& gpu = system.GPU();
236 256
237 params.fence_out.id = channel_fence.id; 257 std::scoped_lock lock(channel_mutex);
238 258
239 if (params.flags.add_wait.Value() && 259 const auto bind_id = channel_state->bind_id;
240 !syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) {
241 gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)});
242 }
243 260
244 if (params.flags.add_increment.Value() || params.flags.increment.Value()) { 261 auto& flags = params.flags;
245 const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0; 262
246 params.fence_out.value = syncpoint_manager.IncreaseSyncpoint( 263 if (flags.fence_wait.Value()) {
247 params.fence_out.id, params.AddIncrementValue() + increment_value); 264 if (flags.increment_value.Value()) {
248 } else { 265 return NvResult::BadParameter;
249 params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id); 266 }
267
268 if (!syncpoint_manager.IsFenceSignalled(params.fence)) {
269 gpu.PushGPUEntries(bind_id, Tegra::CommandList{BuildWaitCommandList(params.fence)});
270 }
250 } 271 }
251 272
252 gpu.PushGPUEntries(std::move(entries)); 273 params.fence.id = channel_syncpoint;
274
275 u32 increment{(flags.fence_increment.Value() != 0 ? 2 : 0) +
276 (flags.increment_value.Value() != 0 ? params.fence.value : 0)};
277 params.fence.value = syncpoint_manager.IncrementSyncpointMaxExt(channel_syncpoint, increment);
278 gpu.PushGPUEntries(bind_id, std::move(entries));
253 279
254 if (params.flags.add_increment.Value()) { 280 if (flags.fence_increment.Value()) {
255 if (params.flags.suppress_wfi) { 281 if (flags.suppress_wfi.Value()) {
256 gpu.PushGPUEntries(Tegra::CommandList{ 282 gpu.PushGPUEntries(bind_id,
257 BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())}); 283 Tegra::CommandList{BuildIncrementCommandList(params.fence)});
258 } else { 284 } else {
259 gpu.PushGPUEntries(Tegra::CommandList{ 285 gpu.PushGPUEntries(bind_id,
260 BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())}); 286 Tegra::CommandList{BuildIncrementWithWfiCommandList(params.fence)});
261 } 287 }
262 } 288 }
263 289
290 flags.raw = 0;
291
264 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo)); 292 std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
265 return NvResult::Success; 293 return NvResult::Success;
266} 294}
@@ -328,4 +356,19 @@ NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vect
328 return NvResult::Success; 356 return NvResult::Success;
329} 357}
330 358
359Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) {
360 switch (event_id) {
361 case 1:
362 return sm_exception_breakpoint_int_report_event;
363 case 2:
364 return sm_exception_breakpoint_pause_report_event;
365 case 3:
366 return error_notifier_event;
367 default: {
368 LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
369 }
370 }
371 return nullptr;
372}
373
331} // namespace Service::Nvidia::Devices 374} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index 8a9f7775a..1e4ecd55b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -13,17 +13,31 @@
13#include "core/hle/service/nvdrv/nvdata.h" 13#include "core/hle/service/nvdrv/nvdata.h"
14#include "video_core/dma_pusher.h" 14#include "video_core/dma_pusher.h"
15 15
16namespace Tegra {
17namespace Control {
18struct ChannelState;
19}
20} // namespace Tegra
21
16namespace Service::Nvidia { 22namespace Service::Nvidia {
23
24namespace NvCore {
25class Container;
26class NvMap;
17class SyncpointManager; 27class SyncpointManager;
18} 28} // namespace NvCore
29
30class EventInterface;
31} // namespace Service::Nvidia
19 32
20namespace Service::Nvidia::Devices { 33namespace Service::Nvidia::Devices {
21 34
35class nvhost_as_gpu;
22class nvmap; 36class nvmap;
23class nvhost_gpu final : public nvdevice { 37class nvhost_gpu final : public nvdevice {
24public: 38public:
25 explicit nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 39 explicit nvhost_gpu(Core::System& system_, EventInterface& events_interface_,
26 SyncpointManager& syncpoint_manager_); 40 NvCore::Container& core);
27 ~nvhost_gpu() override; 41 ~nvhost_gpu() override;
28 42
29 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 43 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -36,7 +50,10 @@ public:
36 void OnOpen(DeviceFD fd) override; 50 void OnOpen(DeviceFD fd) override;
37 void OnClose(DeviceFD fd) override; 51 void OnClose(DeviceFD fd) override;
38 52
53 Kernel::KEvent* QueryEvent(u32 event_id) override;
54
39private: 55private:
56 friend class nvhost_as_gpu;
40 enum class CtxObjects : u32_le { 57 enum class CtxObjects : u32_le {
41 Ctx2D = 0x902D, 58 Ctx2D = 0x902D,
42 Ctx3D = 0xB197, 59 Ctx3D = 0xB197,
@@ -146,17 +163,13 @@ private:
146 u32_le num_entries{}; // number of fence objects being submitted 163 u32_le num_entries{}; // number of fence objects being submitted
147 union { 164 union {
148 u32_le raw; 165 u32_le raw;
149 BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list 166 BitField<0, 1, u32_le> fence_wait; // append a wait sync_point to the list
150 BitField<1, 1, u32_le> add_increment; // append an increment to the list 167 BitField<1, 1, u32_le> fence_increment; // append an increment to the list
151 BitField<2, 1, u32_le> new_hw_format; // mostly ignored 168 BitField<2, 1, u32_le> new_hw_format; // mostly ignored
152 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt 169 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
153 BitField<8, 1, u32_le> increment; // increment the returned fence 170 BitField<8, 1, u32_le> increment_value; // increment the returned fence
154 } flags; 171 } flags;
155 NvFence fence_out{}; // returned new fence object for others to wait on 172 NvFence fence{}; // returned new fence object for others to wait on
156
157 u32 AddIncrementValue() const {
158 return flags.add_increment.Value() << 1;
159 }
160 }; 173 };
161 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence), 174 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence),
162 "IoctlSubmitGpfifo is incorrect size"); 175 "IoctlSubmitGpfifo is incorrect size");
@@ -191,9 +204,18 @@ private:
191 NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); 204 NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
192 NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); 205 NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
193 206
194 std::shared_ptr<nvmap> nvmap_dev; 207 EventInterface& events_interface;
195 SyncpointManager& syncpoint_manager; 208 NvCore::Container& core;
196 NvFence channel_fence; 209 NvCore::SyncpointManager& syncpoint_manager;
210 NvCore::NvMap& nvmap;
211 std::shared_ptr<Tegra::Control::ChannelState> channel_state;
212 u32 channel_syncpoint;
213 std::mutex channel_mutex;
214
215 // Events
216 Kernel::KEvent* sm_exception_breakpoint_int_report_event;
217 Kernel::KEvent* sm_exception_breakpoint_pause_report_event;
218 Kernel::KEvent* error_notifier_event;
197}; 219};
198 220
199} // namespace Service::Nvidia::Devices 221} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index a7385fce8..1703f9cc3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -5,14 +5,14 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hle/service/nvdrv/core/container.h"
8#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" 9#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
9#include "video_core/renderer_base.h" 10#include "video_core/renderer_base.h"
10 11
11namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
12 13
13nvhost_nvdec::nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 14nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_)
14 SyncpointManager& syncpoint_manager_) 15 : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::NvDec} {}
15 : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {}
16nvhost_nvdec::~nvhost_nvdec() = default; 16nvhost_nvdec::~nvhost_nvdec() = default;
17 17
18NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 18NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -21,8 +21,9 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
21 case 0x0: 21 case 0x0:
22 switch (command.cmd) { 22 switch (command.cmd) {
23 case 0x1: { 23 case 0x1: {
24 if (!fd_to_id.contains(fd)) { 24 auto& host1x_file = core.Host1xDeviceFile();
25 fd_to_id[fd] = next_id++; 25 if (!host1x_file.fd_to_id.contains(fd)) {
26 host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++;
26 } 27 }
27 return Submit(fd, input, output); 28 return Submit(fd, input, output);
28 } 29 }
@@ -73,8 +74,9 @@ void nvhost_nvdec::OnOpen(DeviceFD fd) {
73 74
74void nvhost_nvdec::OnClose(DeviceFD fd) { 75void nvhost_nvdec::OnClose(DeviceFD fd) {
75 LOG_INFO(Service_NVDRV, "NVDEC video stream ended"); 76 LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
76 const auto iter = fd_to_id.find(fd); 77 auto& host1x_file = core.Host1xDeviceFile();
77 if (iter != fd_to_id.end()) { 78 const auto iter = host1x_file.fd_to_id.find(fd);
79 if (iter != host1x_file.fd_to_id.end()) {
78 system.GPU().ClearCdmaInstance(iter->second); 80 system.GPU().ClearCdmaInstance(iter->second);
79 } 81 }
80 system.AudioCore().SetNVDECActive(false); 82 system.AudioCore().SetNVDECActive(false);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 29b3e6a36..c1b4e53e8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -10,8 +10,7 @@ namespace Service::Nvidia::Devices {
10 10
11class nvhost_nvdec final : public nvhost_nvdec_common { 11class nvhost_nvdec final : public nvhost_nvdec_common {
12public: 12public:
13 explicit nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 13 explicit nvhost_nvdec(Core::System& system_, NvCore::Container& core);
14 SyncpointManager& syncpoint_manager_);
15 ~nvhost_nvdec() override; 14 ~nvhost_nvdec() override;
16 15
17 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 16 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -23,9 +22,6 @@ public:
23 22
24 void OnOpen(DeviceFD fd) override; 23 void OnOpen(DeviceFD fd) override;
25 void OnClose(DeviceFD fd) override; 24 void OnClose(DeviceFD fd) override;
26
27private:
28 u32 next_id{};
29}; 25};
30 26
31} // namespace Service::Nvidia::Devices 27} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index 8b2cd9bf1..99eede702 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -8,10 +8,12 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/hle/service/nvdrv/core/container.h"
12#include "core/hle/service/nvdrv/core/nvmap.h"
13#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
11#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" 14#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
12#include "core/hle/service/nvdrv/devices/nvmap.h"
13#include "core/hle/service/nvdrv/syncpoint_manager.h"
14#include "core/memory.h" 15#include "core/memory.h"
16#include "video_core/host1x/host1x.h"
15#include "video_core/memory_manager.h" 17#include "video_core/memory_manager.h"
16#include "video_core/renderer_base.h" 18#include "video_core/renderer_base.h"
17 19
@@ -44,10 +46,22 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s
44} 46}
45} // Anonymous namespace 47} // Anonymous namespace
46 48
47nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 49nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_,
48 SyncpointManager& syncpoint_manager_) 50 NvCore::ChannelType channel_type_)
49 : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} {} 51 : nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()},
50nvhost_nvdec_common::~nvhost_nvdec_common() = default; 52 nvmap{core.GetNvMapFile()}, channel_type{channel_type_} {
53 auto& syncpts_accumulated = core.Host1xDeviceFile().syncpts_accumulated;
54 if (syncpts_accumulated.empty()) {
55 channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false);
56 } else {
57 channel_syncpoint = syncpts_accumulated.front();
58 syncpts_accumulated.pop_front();
59 }
60}
61
62nvhost_nvdec_common::~nvhost_nvdec_common() {
63 core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint);
64}
51 65
52NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { 66NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
53 IoctlSetNvmapFD params{}; 67 IoctlSetNvmapFD params{};
@@ -84,16 +98,16 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input,
84 for (std::size_t i = 0; i < syncpt_increments.size(); i++) { 98 for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
85 const SyncptIncr& syncpt_incr = syncpt_increments[i]; 99 const SyncptIncr& syncpt_incr = syncpt_increments[i];
86 fence_thresholds[i] = 100 fence_thresholds[i] =
87 syncpoint_manager.IncreaseSyncpoint(syncpt_incr.id, syncpt_incr.increments); 101 syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments);
88 } 102 }
89 } 103 }
90 for (const auto& cmd_buffer : command_buffers) { 104 for (const auto& cmd_buffer : command_buffers) {
91 const auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); 105 const auto object = nvmap.GetHandle(cmd_buffer.memory_id);
92 ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); 106 ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
93 Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); 107 Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
94 system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(), 108 system.Memory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(),
95 cmdlist.size() * sizeof(u32)); 109 cmdlist.size() * sizeof(u32));
96 gpu.PushCommandBuffer(fd_to_id[fd], cmdlist); 110 gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist);
97 } 111 }
98 std::memcpy(output.data(), &params, sizeof(IoctlSubmit)); 112 std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
99 // Some games expect command_buffers to be written back 113 // Some games expect command_buffers to be written back
@@ -112,10 +126,8 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve
112 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint)); 126 std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
113 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param); 127 LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
114 128
115 if (device_syncpoints[params.param] == 0 && system.GPU().UseNvdec()) { 129 // const u32 id{NvCore::SyncpointManager::channel_syncpoints[static_cast<u32>(channel_type)]};
116 device_syncpoints[params.param] = syncpoint_manager.AllocateSyncpoint(); 130 params.value = channel_syncpoint;
117 }
118 params.value = device_syncpoints[params.param];
119 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint)); 131 std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
120 132
121 return NvResult::Success; 133 return NvResult::Success;
@@ -123,6 +135,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve
123 135
124NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { 136NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
125 IoctlGetWaitbase params{}; 137 IoctlGetWaitbase params{};
138 LOG_CRITICAL(Service_NVDRV, "called WAITBASE");
126 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase)); 139 std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
127 params.value = 0; // Seems to be hard coded at 0 140 params.value = 0; // Seems to be hard coded at 0
128 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase)); 141 std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
@@ -136,28 +149,8 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
136 149
137 SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); 150 SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
138 151
139 auto& gpu = system.GPU();
140
141 for (auto& cmd_buffer : cmd_buffer_handles) { 152 for (auto& cmd_buffer : cmd_buffer_handles) {
142 auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)}; 153 cmd_buffer.map_address = nvmap.PinHandle(cmd_buffer.map_handle);
143 if (!object) {
144 LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle);
145 std::memcpy(output.data(), &params, output.size());
146 return NvResult::InvalidState;
147 }
148 if (object->dma_map_addr == 0) {
149 // NVDEC and VIC memory is in the 32-bit address space
150 // MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space
151 const GPUVAddr low_addr = gpu.MemoryManager().MapAllocate32(object->addr, object->size);
152 object->dma_map_addr = static_cast<u32>(low_addr);
153 // Ensure that the dma_map_addr is indeed in the lower 32-bit address space.
154 ASSERT(object->dma_map_addr == low_addr);
155 }
156 if (!object->dma_map_addr) {
157 LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size);
158 } else {
159 cmd_buffer.map_address = object->dma_map_addr;
160 }
161 } 154 }
162 std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer)); 155 std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
163 std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(), 156 std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
@@ -167,11 +160,16 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
167} 160}
168 161
169NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { 162NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
170 // This is intntionally stubbed. 163 IoctlMapBuffer params{};
171 // Skip unmapping buffers here, as to not break the continuity of the VP9 reference frame 164 std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
172 // addresses, and risk invalidating data before the async GPU thread is done with it 165 std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
166
167 SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
168 for (auto& cmd_buffer : cmd_buffer_handles) {
169 nvmap.UnpinHandle(cmd_buffer.map_handle);
170 }
171
173 std::memset(output.data(), 0, output.size()); 172 std::memset(output.data(), 0, output.size());
174 LOG_DEBUG(Service_NVDRV, "(STUBBED) called");
175 return NvResult::Success; 173 return NvResult::Success;
176} 174}
177 175
@@ -182,4 +180,9 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
182 return NvResult::Success; 180 return NvResult::Success;
183} 181}
184 182
183Kernel::KEvent* nvhost_nvdec_common::QueryEvent(u32 event_id) {
184 LOG_CRITICAL(Service_NVDRV, "Unknown HOSTX1 Event {}", event_id);
185 return nullptr;
186}
187
185} // namespace Service::Nvidia::Devices 188} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index 12d39946d..fe76100c8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -3,21 +3,26 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <deque>
6#include <vector> 7#include <vector>
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "common/swap.h" 9#include "common/swap.h"
10#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
9#include "core/hle/service/nvdrv/devices/nvdevice.h" 11#include "core/hle/service/nvdrv/devices/nvdevice.h"
10 12
11namespace Service::Nvidia { 13namespace Service::Nvidia {
12class SyncpointManager; 14
15namespace NvCore {
16class Container;
17class NvMap;
18} // namespace NvCore
13 19
14namespace Devices { 20namespace Devices {
15class nvmap;
16 21
17class nvhost_nvdec_common : public nvdevice { 22class nvhost_nvdec_common : public nvdevice {
18public: 23public:
19 explicit nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 24 explicit nvhost_nvdec_common(Core::System& system_, NvCore::Container& core,
20 SyncpointManager& syncpoint_manager_); 25 NvCore::ChannelType channel_type);
21 ~nvhost_nvdec_common() override; 26 ~nvhost_nvdec_common() override;
22 27
23protected: 28protected:
@@ -110,11 +115,15 @@ protected:
110 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); 115 NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
111 NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output); 116 NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
112 117
113 std::unordered_map<DeviceFD, u32> fd_to_id{}; 118 Kernel::KEvent* QueryEvent(u32 event_id) override;
119
120 u32 channel_syncpoint;
114 s32_le nvmap_fd{}; 121 s32_le nvmap_fd{};
115 u32_le submit_timeout{}; 122 u32_le submit_timeout{};
116 std::shared_ptr<nvmap> nvmap_dev; 123 NvCore::Container& core;
117 SyncpointManager& syncpoint_manager; 124 NvCore::SyncpointManager& syncpoint_manager;
125 NvCore::NvMap& nvmap;
126 NvCore::ChannelType channel_type;
118 std::array<u32, MaxSyncPoints> device_syncpoints{}; 127 std::array<u32, MaxSyncPoints> device_syncpoints{};
119}; 128};
120}; // namespace Devices 129}; // namespace Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index f58e8bada..73f97136e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -4,13 +4,14 @@
4#include "common/assert.h" 4#include "common/assert.h"
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/hle/service/nvdrv/core/container.h"
7#include "core/hle/service/nvdrv/devices/nvhost_vic.h" 8#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
8#include "video_core/renderer_base.h" 9#include "video_core/renderer_base.h"
9 10
10namespace Service::Nvidia::Devices { 11namespace Service::Nvidia::Devices {
11nvhost_vic::nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 12
12 SyncpointManager& syncpoint_manager_) 13nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_)
13 : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {} 14 : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::VIC} {}
14 15
15nvhost_vic::~nvhost_vic() = default; 16nvhost_vic::~nvhost_vic() = default;
16 17
@@ -19,11 +20,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
19 switch (command.group) { 20 switch (command.group) {
20 case 0x0: 21 case 0x0:
21 switch (command.cmd) { 22 switch (command.cmd) {
22 case 0x1: 23 case 0x1: {
23 if (!fd_to_id.contains(fd)) { 24 auto& host1x_file = core.Host1xDeviceFile();
24 fd_to_id[fd] = next_id++; 25 if (!host1x_file.fd_to_id.contains(fd)) {
26 host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++;
25 } 27 }
26 return Submit(fd, input, output); 28 return Submit(fd, input, output);
29 }
27 case 0x2: 30 case 0x2:
28 return GetSyncpoint(input, output); 31 return GetSyncpoint(input, output);
29 case 0x3: 32 case 0x3:
@@ -67,8 +70,9 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i
67void nvhost_vic::OnOpen(DeviceFD fd) {} 70void nvhost_vic::OnOpen(DeviceFD fd) {}
68 71
69void nvhost_vic::OnClose(DeviceFD fd) { 72void nvhost_vic::OnClose(DeviceFD fd) {
70 const auto iter = fd_to_id.find(fd); 73 auto& host1x_file = core.Host1xDeviceFile();
71 if (iter != fd_to_id.end()) { 74 const auto iter = host1x_file.fd_to_id.find(fd);
75 if (iter != host1x_file.fd_to_id.end()) {
72 system.GPU().ClearCdmaInstance(iter->second); 76 system.GPU().ClearCdmaInstance(iter->second);
73 } 77 }
74} 78}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index b41b195ae..f164caafb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -9,8 +9,7 @@ namespace Service::Nvidia::Devices {
9 9
10class nvhost_vic final : public nvhost_nvdec_common { 10class nvhost_vic final : public nvhost_nvdec_common {
11public: 11public:
12 explicit nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, 12 explicit nvhost_vic(Core::System& system_, NvCore::Container& core);
13 SyncpointManager& syncpoint_manager_);
14 ~nvhost_vic(); 13 ~nvhost_vic();
15 14
16 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 15 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -22,8 +21,5 @@ public:
22 21
23 void OnOpen(DeviceFD fd) override; 22 void OnOpen(DeviceFD fd) override;
24 void OnClose(DeviceFD fd) override; 23 void OnClose(DeviceFD fd) override;
25
26private:
27 u32 next_id{};
28}; 24};
29} // namespace Service::Nvidia::Devices 25} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index d8518149d..ddf273b5e 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -2,19 +2,26 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include <algorithm>
5#include <bit>
5#include <cstring> 6#include <cstring>
6 7
8#include "common/alignment.h"
7#include "common/assert.h" 9#include "common/assert.h"
8#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "core/core.h"
12#include "core/hle/kernel/k_page_table.h"
13#include "core/hle/kernel/k_process.h"
14#include "core/hle/service/nvdrv/core/container.h"
15#include "core/hle/service/nvdrv/core/nvmap.h"
9#include "core/hle/service/nvdrv/devices/nvmap.h" 16#include "core/hle/service/nvdrv/devices/nvmap.h"
17#include "core/memory.h"
18
19using Core::Memory::YUZU_PAGESIZE;
10 20
11namespace Service::Nvidia::Devices { 21namespace Service::Nvidia::Devices {
12 22
13nvmap::nvmap(Core::System& system_) : nvdevice{system_} { 23nvmap::nvmap(Core::System& system_, NvCore::Container& container_)
14 // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to 24 : nvdevice{system_}, container{container_}, file{container.GetNvMapFile()} {}
15 // represent this.
16 CreateObject(0);
17}
18 25
19nvmap::~nvmap() = default; 26nvmap::~nvmap() = default;
20 27
@@ -62,39 +69,21 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
62void nvmap::OnOpen(DeviceFD fd) {} 69void nvmap::OnOpen(DeviceFD fd) {}
63void nvmap::OnClose(DeviceFD fd) {} 70void nvmap::OnClose(DeviceFD fd) {}
64 71
65VAddr nvmap::GetObjectAddress(u32 handle) const {
66 auto object = GetObject(handle);
67 ASSERT(object);
68 ASSERT(object->status == Object::Status::Allocated);
69 return object->addr;
70}
71
72u32 nvmap::CreateObject(u32 size) {
73 // Create a new nvmap object and obtain a handle to it.
74 auto object = std::make_shared<Object>();
75 object->id = next_id++;
76 object->size = size;
77 object->status = Object::Status::Created;
78 object->refcount = 1;
79
80 const u32 handle = next_handle++;
81
82 handles.insert_or_assign(handle, std::move(object));
83
84 return handle;
85}
86
87NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { 72NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
88 IocCreateParams params; 73 IocCreateParams params;
89 std::memcpy(&params, input.data(), sizeof(params)); 74 std::memcpy(&params, input.data(), sizeof(params));
90 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size); 75 LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
91 76
92 if (!params.size) { 77 std::shared_ptr<NvCore::NvMap::Handle> handle_description{};
93 LOG_ERROR(Service_NVDRV, "Size is 0"); 78 auto result =
94 return NvResult::BadValue; 79 file.CreateHandle(Common::AlignUp(params.size, YUZU_PAGESIZE), handle_description);
80 if (result != NvResult::Success) {
81 LOG_CRITICAL(Service_NVDRV, "Failed to create Object");
82 return result;
95 } 83 }
96 84 handle_description->orig_size = params.size; // Orig size is the unaligned size
97 params.handle = CreateObject(params.size); 85 params.handle = handle_description->id;
86 LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size);
98 87
99 std::memcpy(output.data(), &params, sizeof(params)); 88 std::memcpy(output.data(), &params, sizeof(params));
100 return NvResult::Success; 89 return NvResult::Success;
@@ -103,63 +92,68 @@ NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output)
103NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { 92NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
104 IocAllocParams params; 93 IocAllocParams params;
105 std::memcpy(&params, input.data(), sizeof(params)); 94 std::memcpy(&params, input.data(), sizeof(params));
106 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr); 95 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
107 96
108 if (!params.handle) { 97 if (!params.handle) {
109 LOG_ERROR(Service_NVDRV, "Handle is 0"); 98 LOG_CRITICAL(Service_NVDRV, "Handle is 0");
110 return NvResult::BadValue; 99 return NvResult::BadValue;
111 } 100 }
112 101
113 if ((params.align - 1) & params.align) { 102 if ((params.align - 1) & params.align) {
114 LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align); 103 LOG_CRITICAL(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
115 return NvResult::BadValue; 104 return NvResult::BadValue;
116 } 105 }
117 106
118 const u32 min_alignment = 0x1000; 107 // Force page size alignment at a minimum
119 if (params.align < min_alignment) { 108 if (params.align < YUZU_PAGESIZE) {
120 params.align = min_alignment; 109 params.align = YUZU_PAGESIZE;
121 } 110 }
122 111
123 auto object = GetObject(params.handle); 112 auto handle_description{file.GetHandle(params.handle)};
124 if (!object) { 113 if (!handle_description) {
125 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 114 LOG_CRITICAL(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
126 return NvResult::BadValue; 115 return NvResult::BadValue;
127 } 116 }
128 117
129 if (object->status == Object::Status::Allocated) { 118 if (handle_description->allocated) {
130 LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle); 119 LOG_CRITICAL(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
131 return NvResult::InsufficientMemory; 120 return NvResult::InsufficientMemory;
132 } 121 }
133 122
134 object->flags = params.flags; 123 const auto result =
135 object->align = params.align; 124 handle_description->Alloc(params.flags, params.align, params.kind, params.address);
136 object->kind = params.kind; 125 if (result != NvResult::Success) {
137 object->addr = params.addr; 126 LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle);
138 object->status = Object::Status::Allocated; 127 return result;
139 128 }
129 ASSERT(system.CurrentProcess()
130 ->PageTable()
131 .LockForDeviceAddressSpace(handle_description->address, handle_description->size)
132 .IsSuccess());
140 std::memcpy(output.data(), &params, sizeof(params)); 133 std::memcpy(output.data(), &params, sizeof(params));
141 return NvResult::Success; 134 return result;
142} 135}
143 136
144NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { 137NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
145 IocGetIdParams params; 138 IocGetIdParams params;
146 std::memcpy(&params, input.data(), sizeof(params)); 139 std::memcpy(&params, input.data(), sizeof(params));
147 140
148 LOG_WARNING(Service_NVDRV, "called"); 141 LOG_DEBUG(Service_NVDRV, "called");
149 142
143 // See the comment in FromId for extra info on this function
150 if (!params.handle) { 144 if (!params.handle) {
151 LOG_ERROR(Service_NVDRV, "Handle is zero"); 145 LOG_CRITICAL(Service_NVDRV, "Error!");
152 return NvResult::BadValue; 146 return NvResult::BadValue;
153 } 147 }
154 148
155 auto object = GetObject(params.handle); 149 auto handle_description{file.GetHandle(params.handle)};
156 if (!object) { 150 if (!handle_description) {
157 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 151 LOG_CRITICAL(Service_NVDRV, "Error!");
158 return NvResult::BadValue; 152 return NvResult::AccessDenied; // This will always return EPERM irrespective of if the
153 // handle exists or not
159 } 154 }
160 155
161 params.id = object->id; 156 params.id = handle_description->id;
162
163 std::memcpy(output.data(), &params, sizeof(params)); 157 std::memcpy(output.data(), &params, sizeof(params));
164 return NvResult::Success; 158 return NvResult::Success;
165} 159}
@@ -168,26 +162,29 @@ NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output)
168 IocFromIdParams params; 162 IocFromIdParams params;
169 std::memcpy(&params, input.data(), sizeof(params)); 163 std::memcpy(&params, input.data(), sizeof(params));
170 164
171 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 165 LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id);
172 166
173 auto itr = std::find_if(handles.begin(), handles.end(), 167 // Handles and IDs are always the same value in nvmap however IDs can be used globally given the
174 [&](const auto& entry) { return entry.second->id == params.id; }); 168 // right permissions.
175 if (itr == handles.end()) { 169 // Since we don't plan on ever supporting multiprocess we can skip implementing handle refs and
176 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 170 // so this function just does simple validation and passes through the handle id.
171 if (!params.id) {
172 LOG_CRITICAL(Service_NVDRV, "Zero Id is invalid!");
177 return NvResult::BadValue; 173 return NvResult::BadValue;
178 } 174 }
179 175
180 auto& object = itr->second; 176 auto handle_description{file.GetHandle(params.id)};
181 if (object->status != Object::Status::Allocated) { 177 if (!handle_description) {
182 LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); 178 LOG_CRITICAL(Service_NVDRV, "Unregistered handle!");
183 return NvResult::BadValue; 179 return NvResult::BadValue;
184 } 180 }
185 181
186 itr->second->refcount++; 182 auto result = handle_description->Duplicate(false);
187 183 if (result != NvResult::Success) {
188 // Return the existing handle instead of creating a new one. 184 LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!");
189 params.handle = itr->first; 185 return result;
190 186 }
187 params.handle = handle_description->id;
191 std::memcpy(output.data(), &params, sizeof(params)); 188 std::memcpy(output.data(), &params, sizeof(params));
192 return NvResult::Success; 189 return NvResult::Success;
193} 190}
@@ -198,35 +195,43 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output)
198 IocParamParams params; 195 IocParamParams params;
199 std::memcpy(&params, input.data(), sizeof(params)); 196 std::memcpy(&params, input.data(), sizeof(params));
200 197
201 LOG_DEBUG(Service_NVDRV, "(STUBBED) called type={}", params.param); 198 LOG_DEBUG(Service_NVDRV, "called type={}", params.param);
202 199
203 auto object = GetObject(params.handle); 200 if (!params.handle) {
204 if (!object) { 201 LOG_CRITICAL(Service_NVDRV, "Invalid handle!");
205 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
206 return NvResult::BadValue; 202 return NvResult::BadValue;
207 } 203 }
208 204
209 if (object->status != Object::Status::Allocated) { 205 auto handle_description{file.GetHandle(params.handle)};
210 LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle); 206 if (!handle_description) {
207 LOG_CRITICAL(Service_NVDRV, "Not registered handle!");
211 return NvResult::BadValue; 208 return NvResult::BadValue;
212 } 209 }
213 210
214 switch (static_cast<ParamTypes>(params.param)) { 211 switch (params.param) {
215 case ParamTypes::Size: 212 case HandleParameterType::Size:
216 params.result = object->size; 213 params.result = static_cast<u32_le>(handle_description->orig_size);
214 break;
215 case HandleParameterType::Alignment:
216 params.result = static_cast<u32_le>(handle_description->align);
217 break; 217 break;
218 case ParamTypes::Alignment: 218 case HandleParameterType::Base:
219 params.result = object->align; 219 params.result = static_cast<u32_le>(-22); // posix EINVAL
220 break; 220 break;
221 case ParamTypes::Heap: 221 case HandleParameterType::Heap:
222 // TODO(Subv): Seems to be a hardcoded value? 222 if (handle_description->allocated)
223 params.result = 0x40000000; 223 params.result = 0x40000000;
224 else
225 params.result = 0;
224 break; 226 break;
225 case ParamTypes::Kind: 227 case HandleParameterType::Kind:
226 params.result = object->kind; 228 params.result = handle_description->kind;
229 break;
230 case HandleParameterType::IsSharedMemMapped:
231 params.result = handle_description->is_shared_mem_mapped;
227 break; 232 break;
228 default: 233 default:
229 UNIMPLEMENTED(); 234 return NvResult::BadValue;
230 } 235 }
231 236
232 std::memcpy(output.data(), &params, sizeof(params)); 237 std::memcpy(output.data(), &params, sizeof(params));
@@ -234,46 +239,29 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output)
234} 239}
235 240
236NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { 241NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
237 // TODO(Subv): These flags are unconfirmed.
238 enum FreeFlags {
239 Freed = 0,
240 NotFreedYet = 1,
241 };
242
243 IocFreeParams params; 242 IocFreeParams params;
244 std::memcpy(&params, input.data(), sizeof(params)); 243 std::memcpy(&params, input.data(), sizeof(params));
245 244
246 LOG_DEBUG(Service_NVDRV, "(STUBBED) called"); 245 LOG_DEBUG(Service_NVDRV, "called");
247 246
248 auto itr = handles.find(params.handle); 247 if (!params.handle) {
249 if (itr == handles.end()) { 248 LOG_CRITICAL(Service_NVDRV, "Handle null freed?");
250 LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle); 249 return NvResult::Success;
251 return NvResult::BadValue;
252 }
253 if (!itr->second->refcount) {
254 LOG_ERROR(
255 Service_NVDRV,
256 "There is no references to this object. The object is already freed. handle={:08X}",
257 params.handle);
258 return NvResult::BadValue;
259 } 250 }
260 251
261 itr->second->refcount--; 252 if (auto freeInfo{file.FreeHandle(params.handle, false)}) {
262 253 ASSERT(system.CurrentProcess()
263 params.size = itr->second->size; 254 ->PageTable()
264 255 .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size)
265 if (itr->second->refcount == 0) { 256 .IsSuccess());
266 params.flags = Freed; 257 params.address = freeInfo->address;
267 // The address of the nvmap is written to the output if we're finally freeing it, otherwise 258 params.size = static_cast<u32>(freeInfo->size);
268 // 0 is written. 259 params.flags.raw = 0;
269 params.address = itr->second->addr; 260 params.flags.map_uncached.Assign(freeInfo->was_uncached);
270 } else { 261 } else {
271 params.flags = NotFreedYet; 262 // This is possible when there's internel dups or other duplicates.
272 params.address = 0;
273 } 263 }
274 264
275 handles.erase(params.handle);
276
277 std::memcpy(output.data(), &params, sizeof(params)); 265 std::memcpy(output.data(), &params, sizeof(params));
278 return NvResult::Success; 266 return NvResult::Success;
279} 267}
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index d5360d6e5..e9bfd0358 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -9,15 +9,23 @@
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/hle/service/nvdrv/core/nvmap.h"
12#include "core/hle/service/nvdrv/devices/nvdevice.h" 13#include "core/hle/service/nvdrv/devices/nvdevice.h"
13 14
15namespace Service::Nvidia::NvCore {
16class Container;
17} // namespace Service::Nvidia::NvCore
18
14namespace Service::Nvidia::Devices { 19namespace Service::Nvidia::Devices {
15 20
16class nvmap final : public nvdevice { 21class nvmap final : public nvdevice {
17public: 22public:
18 explicit nvmap(Core::System& system_); 23 explicit nvmap(Core::System& system_, NvCore::Container& container);
19 ~nvmap() override; 24 ~nvmap() override;
20 25
26 nvmap(const nvmap&) = delete;
27 nvmap& operator=(const nvmap&) = delete;
28
21 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 29 NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
22 std::vector<u8>& output) override; 30 std::vector<u8>& output) override;
23 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input, 31 NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -28,31 +36,15 @@ public:
28 void OnOpen(DeviceFD fd) override; 36 void OnOpen(DeviceFD fd) override;
29 void OnClose(DeviceFD fd) override; 37 void OnClose(DeviceFD fd) override;
30 38
31 /// Returns the allocated address of an nvmap object given its handle. 39 enum class HandleParameterType : u32_le {
32 VAddr GetObjectAddress(u32 handle) const; 40 Size = 1,
33 41 Alignment = 2,
34 /// Represents an nvmap object. 42 Base = 3,
35 struct Object { 43 Heap = 4,
36 enum class Status { Created, Allocated }; 44 Kind = 5,
37 u32 id; 45 IsSharedMemMapped = 6
38 u32 size;
39 u32 flags;
40 u32 align;
41 u8 kind;
42 VAddr addr;
43 Status status;
44 u32 refcount;
45 u32 dma_map_addr;
46 }; 46 };
47 47
48 std::shared_ptr<Object> GetObject(u32 handle) const {
49 auto itr = handles.find(handle);
50 if (itr != handles.end()) {
51 return itr->second;
52 }
53 return {};
54 }
55
56private: 48private:
57 /// Id to use for the next handle that is created. 49 /// Id to use for the next handle that is created.
58 u32 next_handle = 0; 50 u32 next_handle = 0;
@@ -60,9 +52,6 @@ private:
60 /// Id to use for the next object that is created. 52 /// Id to use for the next object that is created.
61 u32 next_id = 0; 53 u32 next_id = 0;
62 54
63 /// Mapping of currently allocated handles to the objects they represent.
64 std::unordered_map<u32, std::shared_ptr<Object>> handles;
65
66 struct IocCreateParams { 55 struct IocCreateParams {
67 // Input 56 // Input
68 u32_le size{}; 57 u32_le size{};
@@ -83,11 +72,11 @@ private:
83 // Input 72 // Input
84 u32_le handle{}; 73 u32_le handle{};
85 u32_le heap_mask{}; 74 u32_le heap_mask{};
86 u32_le flags{}; 75 NvCore::NvMap::Handle::Flags flags{};
87 u32_le align{}; 76 u32_le align{};
88 u8 kind{}; 77 u8 kind{};
89 INSERT_PADDING_BYTES(7); 78 INSERT_PADDING_BYTES(7);
90 u64_le addr{}; 79 u64_le address{};
91 }; 80 };
92 static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size"); 81 static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size");
93 82
@@ -96,14 +85,14 @@ private:
96 INSERT_PADDING_BYTES(4); 85 INSERT_PADDING_BYTES(4);
97 u64_le address{}; 86 u64_le address{};
98 u32_le size{}; 87 u32_le size{};
99 u32_le flags{}; 88 NvCore::NvMap::Handle::Flags flags{};
100 }; 89 };
101 static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size"); 90 static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size");
102 91
103 struct IocParamParams { 92 struct IocParamParams {
104 // Input 93 // Input
105 u32_le handle{}; 94 u32_le handle{};
106 u32_le param{}; 95 HandleParameterType param{};
107 // Output 96 // Output
108 u32_le result{}; 97 u32_le result{};
109 }; 98 };
@@ -117,14 +106,15 @@ private:
117 }; 106 };
118 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); 107 static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
119 108
120 u32 CreateObject(u32 size);
121
122 NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output); 109 NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
123 NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); 110 NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
124 NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output); 111 NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
125 NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output); 112 NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
126 NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output); 113 NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output);
127 NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output); 114 NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output);
115
116 NvCore::Container& container;
117 NvCore::NvMap& file;
128}; 118};
129 119
130} // namespace Service::Nvidia::Devices 120} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 1d00394c8..0e2f47075 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -1,5 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#pragma once 5#pragma once
5 6
@@ -78,11 +79,15 @@ enum class NvResult : u32 {
78 ModuleNotPresent = 0xA000E, 79 ModuleNotPresent = 0xA000E,
79}; 80};
80 81
82// obtained from
83// https://github.com/skyline-emu/skyline/blob/nvdec-dev/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/ctrl.h#L47
81enum class EventState { 84enum class EventState {
82 Free = 0, 85 Available = 0,
83 Registered = 1, 86 Waiting = 1,
84 Waiting = 2, 87 Cancelling = 2,
85 Busy = 3, 88 Signalling = 3,
89 Signalled = 4,
90 Cancelled = 5,
86}; 91};
87 92
88union Ioctl { 93union Ioctl {
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 756eb7453..5e7b7468f 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -1,5 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#include <utility> 5#include <utility>
5 6
@@ -8,6 +9,7 @@
8#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/k_event.h" 10#include "core/hle/kernel/k_event.h"
10#include "core/hle/kernel/k_writable_event.h" 11#include "core/hle/kernel/k_writable_event.h"
12#include "core/hle/service/nvdrv/core/container.h"
11#include "core/hle/service/nvdrv/devices/nvdevice.h" 13#include "core/hle/service/nvdrv/devices/nvdevice.h"
12#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" 14#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
13#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" 15#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
@@ -15,17 +17,31 @@
15#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" 17#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
16#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" 18#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
17#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" 19#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
20#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
18#include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" 21#include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h"
19#include "core/hle/service/nvdrv/devices/nvhost_vic.h" 22#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
20#include "core/hle/service/nvdrv/devices/nvmap.h" 23#include "core/hle/service/nvdrv/devices/nvmap.h"
21#include "core/hle/service/nvdrv/nvdrv.h" 24#include "core/hle/service/nvdrv/nvdrv.h"
22#include "core/hle/service/nvdrv/nvdrv_interface.h" 25#include "core/hle/service/nvdrv/nvdrv_interface.h"
23#include "core/hle/service/nvdrv/nvmemp.h" 26#include "core/hle/service/nvdrv/nvmemp.h"
24#include "core/hle/service/nvdrv/syncpoint_manager.h"
25#include "core/hle/service/nvflinger/nvflinger.h" 27#include "core/hle/service/nvflinger/nvflinger.h"
28#include "video_core/gpu.h"
26 29
27namespace Service::Nvidia { 30namespace Service::Nvidia {
28 31
32EventInterface::EventInterface(Module& module_) : module{module_}, guard{}, on_signal{} {}
33
34EventInterface::~EventInterface() = default;
35
36Kernel::KEvent* EventInterface::CreateEvent(std::string name) {
37 Kernel::KEvent* new_event = module.service_context.CreateEvent(std::move(name));
38 return new_event;
39}
40
41void EventInterface::FreeEvent(Kernel::KEvent* event) {
42 module.service_context.CloseEvent(event);
43}
44
29void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, 45void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
30 Core::System& system) { 46 Core::System& system) {
31 auto module_ = std::make_shared<Module>(system); 47 auto module_ = std::make_shared<Module>(system);
@@ -38,34 +54,54 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
38} 54}
39 55
40Module::Module(Core::System& system) 56Module::Module(Core::System& system)
41 : syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"} { 57 : service_context{system, "nvdrv"}, events_interface{*this}, container{system.Host1x()} {
42 for (u32 i = 0; i < MaxNvEvents; i++) { 58 builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) {
43 events_interface.events[i].event = 59 std::shared_ptr<Devices::nvdevice> device =
44 service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i)); 60 std::make_shared<Devices::nvhost_as_gpu>(system, *this, container);
45 events_interface.status[i] = EventState::Free; 61 return open_files.emplace(fd, device).first;
46 events_interface.registered[i] = false; 62 };
47 } 63 builders["/dev/nvhost-gpu"] = [this, &system](DeviceFD fd) {
48 auto nvmap_dev = std::make_shared<Devices::nvmap>(system); 64 std::shared_ptr<Devices::nvdevice> device =
49 devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev); 65 std::make_shared<Devices::nvhost_gpu>(system, events_interface, container);
50 devices["/dev/nvhost-gpu"] = 66 return open_files.emplace(fd, device).first;
51 std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev, syncpoint_manager); 67 };
52 devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system); 68 builders["/dev/nvhost-ctrl-gpu"] = [this, &system](DeviceFD fd) {
53 devices["/dev/nvmap"] = nvmap_dev; 69 std::shared_ptr<Devices::nvdevice> device =
54 devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev); 70 std::make_shared<Devices::nvhost_ctrl_gpu>(system, events_interface);
55 devices["/dev/nvhost-ctrl"] = 71 return open_files.emplace(fd, device).first;
56 std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager); 72 };
57 devices["/dev/nvhost-nvdec"] = 73 builders["/dev/nvmap"] = [this, &system](DeviceFD fd) {
58 std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev, syncpoint_manager); 74 std::shared_ptr<Devices::nvdevice> device =
59 devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system); 75 std::make_shared<Devices::nvmap>(system, container);
60 devices["/dev/nvhost-vic"] = 76 return open_files.emplace(fd, device).first;
61 std::make_shared<Devices::nvhost_vic>(system, nvmap_dev, syncpoint_manager); 77 };
78 builders["/dev/nvdisp_disp0"] = [this, &system](DeviceFD fd) {
79 std::shared_ptr<Devices::nvdevice> device =
80 std::make_shared<Devices::nvdisp_disp0>(system, container);
81 return open_files.emplace(fd, device).first;
82 };
83 builders["/dev/nvhost-ctrl"] = [this, &system](DeviceFD fd) {
84 std::shared_ptr<Devices::nvdevice> device =
85 std::make_shared<Devices::nvhost_ctrl>(system, events_interface, container);
86 return open_files.emplace(fd, device).first;
87 };
88 builders["/dev/nvhost-nvdec"] = [this, &system](DeviceFD fd) {
89 std::shared_ptr<Devices::nvdevice> device =
90 std::make_shared<Devices::nvhost_nvdec>(system, container);
91 return open_files.emplace(fd, device).first;
92 };
93 builders["/dev/nvhost-nvjpg"] = [this, &system](DeviceFD fd) {
94 std::shared_ptr<Devices::nvdevice> device = std::make_shared<Devices::nvhost_nvjpg>(system);
95 return open_files.emplace(fd, device).first;
96 };
97 builders["/dev/nvhost-vic"] = [this, &system](DeviceFD fd) {
98 std::shared_ptr<Devices::nvdevice> device =
99 std::make_shared<Devices::nvhost_vic>(system, container);
100 return open_files.emplace(fd, device).first;
101 };
62} 102}
63 103
64Module::~Module() { 104Module::~Module() {}
65 for (u32 i = 0; i < MaxNvEvents; i++) {
66 service_context.CloseEvent(events_interface.events[i].event);
67 }
68}
69 105
70NvResult Module::VerifyFD(DeviceFD fd) const { 106NvResult Module::VerifyFD(DeviceFD fd) const {
71 if (fd < 0) { 107 if (fd < 0) {
@@ -82,18 +118,18 @@ NvResult Module::VerifyFD(DeviceFD fd) const {
82} 118}
83 119
84DeviceFD Module::Open(const std::string& device_name) { 120DeviceFD Module::Open(const std::string& device_name) {
85 if (devices.find(device_name) == devices.end()) { 121 auto it = builders.find(device_name);
122 if (it == builders.end()) {
86 LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); 123 LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name);
87 return INVALID_NVDRV_FD; 124 return INVALID_NVDRV_FD;
88 } 125 }
89 126
90 auto device = devices[device_name];
91 const DeviceFD fd = next_fd++; 127 const DeviceFD fd = next_fd++;
128 auto& builder = it->second;
129 auto device = builder(fd)->second;
92 130
93 device->OnOpen(fd); 131 device->OnOpen(fd);
94 132
95 open_files[fd] = std::move(device);
96
97 return fd; 133 return fd;
98} 134}
99 135
@@ -168,22 +204,24 @@ NvResult Module::Close(DeviceFD fd) {
168 return NvResult::Success; 204 return NvResult::Success;
169} 205}
170 206
171void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { 207NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) {
172 for (u32 i = 0; i < MaxNvEvents; i++) { 208 if (fd < 0) {
173 if (events_interface.assigned_syncpt[i] == syncpoint_id && 209 LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
174 events_interface.assigned_value[i] == value) { 210 return NvResult::InvalidState;
175 events_interface.LiberateEvent(i);
176 events_interface.events[i].event->GetWritableEvent().Signal();
177 }
178 } 211 }
179}
180 212
181Kernel::KReadableEvent& Module::GetEvent(const u32 event_id) { 213 const auto itr = open_files.find(fd);
182 return events_interface.events[event_id].event->GetReadableEvent();
183}
184 214
185Kernel::KWritableEvent& Module::GetEventWriteable(const u32 event_id) { 215 if (itr == open_files.end()) {
186 return events_interface.events[event_id].event->GetWritableEvent(); 216 LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
217 return NvResult::NotImplemented;
218 }
219
220 event = itr->second->QueryEvent(event_id);
221 if (!event) {
222 return NvResult::BadParameter;
223 }
224 return NvResult::Success;
187} 225}
188 226
189} // namespace Service::Nvidia 227} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index c929e5106..146d046a9 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -1,16 +1,20 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#pragma once 5#pragma once
5 6
7#include <functional>
8#include <list>
6#include <memory> 9#include <memory>
10#include <string>
7#include <unordered_map> 11#include <unordered_map>
8#include <vector> 12#include <vector>
9 13
10#include "common/common_types.h" 14#include "common/common_types.h"
11#include "core/hle/service/kernel_helpers.h" 15#include "core/hle/service/kernel_helpers.h"
16#include "core/hle/service/nvdrv/core/container.h"
12#include "core/hle/service/nvdrv/nvdata.h" 17#include "core/hle/service/nvdrv/nvdata.h"
13#include "core/hle/service/nvdrv/syncpoint_manager.h"
14#include "core/hle/service/nvflinger/ui/fence.h" 18#include "core/hle/service/nvflinger/ui/fence.h"
15#include "core/hle/service/service.h" 19#include "core/hle/service/service.h"
16 20
@@ -28,81 +32,31 @@ class NVFlinger;
28 32
29namespace Service::Nvidia { 33namespace Service::Nvidia {
30 34
35namespace NvCore {
36class Container;
31class SyncpointManager; 37class SyncpointManager;
38} // namespace NvCore
32 39
33namespace Devices { 40namespace Devices {
34class nvdevice; 41class nvdevice;
35} 42class nvhost_ctrl;
43} // namespace Devices
36 44
37/// Represents an Nvidia event 45class Module;
38struct NvEvent {
39 Kernel::KEvent* event{};
40 NvFence fence{};
41};
42 46
43struct EventInterface { 47class EventInterface {
44 // Mask representing currently busy events 48public:
45 u64 events_mask{}; 49 explicit EventInterface(Module& module_);
46 // Each kernel event associated to an NV event 50 ~EventInterface();
47 std::array<NvEvent, MaxNvEvents> events; 51
48 // The status of the current NVEvent 52 Kernel::KEvent* CreateEvent(std::string name);
49 std::array<EventState, MaxNvEvents> status{}; 53
50 // Tells if an NVEvent is registered or not 54 void FreeEvent(Kernel::KEvent* event);
51 std::array<bool, MaxNvEvents> registered{}; 55
52 // Tells the NVEvent that it has failed. 56private:
53 std::array<bool, MaxNvEvents> failed{}; 57 Module& module;
54 // When an NVEvent is waiting on GPU interrupt, this is the sync_point 58 std::mutex guard;
55 // associated with it. 59 std::list<Devices::nvhost_ctrl*> on_signal;
56 std::array<u32, MaxNvEvents> assigned_syncpt{};
57 // This is the value of the GPU interrupt for which the NVEvent is waiting
58 // for.
59 std::array<u32, MaxNvEvents> assigned_value{};
60 // Constant to denote an unasigned syncpoint.
61 static constexpr u32 unassigned_syncpt = 0xFFFFFFFF;
62 std::optional<u32> GetFreeEvent() const {
63 u64 mask = events_mask;
64 for (u32 i = 0; i < MaxNvEvents; i++) {
65 const bool is_free = (mask & 0x1) == 0;
66 if (is_free) {
67 if (status[i] == EventState::Registered || status[i] == EventState::Free) {
68 return {i};
69 }
70 }
71 mask = mask >> 1;
72 }
73 return std::nullopt;
74 }
75 void SetEventStatus(const u32 event_id, EventState new_status) {
76 EventState old_status = status[event_id];
77 if (old_status == new_status) {
78 return;
79 }
80 status[event_id] = new_status;
81 if (new_status == EventState::Registered) {
82 registered[event_id] = true;
83 }
84 if (new_status == EventState::Waiting || new_status == EventState::Busy) {
85 events_mask |= (1ULL << event_id);
86 }
87 }
88 void RegisterEvent(const u32 event_id) {
89 registered[event_id] = true;
90 if (status[event_id] == EventState::Free) {
91 status[event_id] = EventState::Registered;
92 }
93 }
94 void UnregisterEvent(const u32 event_id) {
95 registered[event_id] = false;
96 if (status[event_id] == EventState::Registered) {
97 status[event_id] = EventState::Free;
98 }
99 }
100 void LiberateEvent(const u32 event_id) {
101 status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free;
102 events_mask &= ~(1ULL << event_id);
103 assigned_syncpt[event_id] = unassigned_syncpt;
104 assigned_value[event_id] = 0;
105 }
106}; 60};
107 61
108class Module final { 62class Module final {
@@ -112,9 +66,9 @@ public:
112 66
113 /// Returns a pointer to one of the available devices, identified by its name. 67 /// Returns a pointer to one of the available devices, identified by its name.
114 template <typename T> 68 template <typename T>
115 std::shared_ptr<T> GetDevice(const std::string& name) { 69 std::shared_ptr<T> GetDevice(DeviceFD fd) {
116 auto itr = devices.find(name); 70 auto itr = open_files.find(fd);
117 if (itr == devices.end()) 71 if (itr == open_files.end())
118 return nullptr; 72 return nullptr;
119 return std::static_pointer_cast<T>(itr->second); 73 return std::static_pointer_cast<T>(itr->second);
120 } 74 }
@@ -137,28 +91,27 @@ public:
137 /// Closes a device file descriptor and returns operation success. 91 /// Closes a device file descriptor and returns operation success.
138 NvResult Close(DeviceFD fd); 92 NvResult Close(DeviceFD fd);
139 93
140 void SignalSyncpt(const u32 syncpoint_id, const u32 value); 94 NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event);
141
142 Kernel::KReadableEvent& GetEvent(u32 event_id);
143
144 Kernel::KWritableEvent& GetEventWriteable(u32 event_id);
145 95
146private: 96private:
147 /// Manages syncpoints on the host 97 friend class EventInterface;
148 SyncpointManager syncpoint_manager; 98 friend class Service::NVFlinger::NVFlinger;
149 99
150 /// Id to use for the next open file descriptor. 100 /// Id to use for the next open file descriptor.
151 DeviceFD next_fd = 1; 101 DeviceFD next_fd = 1;
152 102
103 using FilesContainerType = std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>>;
153 /// Mapping of file descriptors to the devices they reference. 104 /// Mapping of file descriptors to the devices they reference.
154 std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>> open_files; 105 FilesContainerType open_files;
155 106
156 /// Mapping of device node names to their implementation. 107 KernelHelpers::ServiceContext service_context;
157 std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;
158 108
159 EventInterface events_interface; 109 EventInterface events_interface;
160 110
161 KernelHelpers::ServiceContext service_context; 111 /// Manages syncpoints on the host
112 NvCore::Container container;
113
114 std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders;
162}; 115};
163 116
164/// Registers all NVDRV services with the specified service manager. 117/// Registers all NVDRV services with the specified service manager.
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index b5a980384..edbdfee43 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
@@ -1,10 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
3// SPDX-License-Identifier: GPL-3.0-or-later
3 4
4#include <cinttypes> 5#include <cinttypes>
5#include "common/logging/log.h" 6#include "common/logging/log.h"
6#include "core/core.h" 7#include "core/core.h"
7#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/k_event.h"
8#include "core/hle/kernel/k_readable_event.h" 10#include "core/hle/kernel/k_readable_event.h"
9#include "core/hle/service/nvdrv/nvdata.h" 11#include "core/hle/service/nvdrv/nvdata.h"
10#include "core/hle/service/nvdrv/nvdrv.h" 12#include "core/hle/service/nvdrv/nvdrv.h"
@@ -12,10 +14,6 @@
12 14
13namespace Service::Nvidia { 15namespace Service::Nvidia {
14 16
15void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
16 nvdrv->SignalSyncpt(syncpoint_id, value);
17}
18
19void NVDRV::Open(Kernel::HLERequestContext& ctx) { 17void NVDRV::Open(Kernel::HLERequestContext& ctx) {
20 LOG_DEBUG(Service_NVDRV, "called"); 18 LOG_DEBUG(Service_NVDRV, "called");
21 IPC::ResponseBuilder rb{ctx, 4}; 19 IPC::ResponseBuilder rb{ctx, 4};
@@ -164,8 +162,7 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
164void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { 162void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
165 IPC::RequestParser rp{ctx}; 163 IPC::RequestParser rp{ctx};
166 const auto fd = rp.Pop<DeviceFD>(); 164 const auto fd = rp.Pop<DeviceFD>();
167 const auto event_id = rp.Pop<u32>() & 0x00FF; 165 const auto event_id = rp.Pop<u32>();
168 LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
169 166
170 if (!is_initialized) { 167 if (!is_initialized) {
171 ServiceError(ctx, NvResult::NotInitialized); 168 ServiceError(ctx, NvResult::NotInitialized);
@@ -173,24 +170,20 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
173 return; 170 return;
174 } 171 }
175 172
176 const auto nv_result = nvdrv->VerifyFD(fd); 173 Kernel::KEvent* event = nullptr;
177 if (nv_result != NvResult::Success) { 174 NvResult result = nvdrv->QueryEvent(fd, event_id, event);
178 LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd);
179 ServiceError(ctx, nv_result);
180 return;
181 }
182 175
183 if (event_id < MaxNvEvents) { 176 if (result == NvResult::Success) {
184 IPC::ResponseBuilder rb{ctx, 3, 1}; 177 IPC::ResponseBuilder rb{ctx, 3, 1};
185 rb.Push(ResultSuccess); 178 rb.Push(ResultSuccess);
186 auto& event = nvdrv->GetEvent(event_id); 179 auto& readable_event = event->GetReadableEvent();
187 event.Clear(); 180 rb.PushCopyObjects(readable_event);
188 rb.PushCopyObjects(event);
189 rb.PushEnum(NvResult::Success); 181 rb.PushEnum(NvResult::Success);
190 } else { 182 } else {
183 LOG_ERROR(Service_NVDRV, "Invalid event request!");
191 IPC::ResponseBuilder rb{ctx, 3}; 184 IPC::ResponseBuilder rb{ctx, 3};
192 rb.Push(ResultSuccess); 185 rb.Push(ResultSuccess);
193 rb.PushEnum(NvResult::BadParameter); 186 rb.PushEnum(result);
194 } 187 }
195} 188}
196 189
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h
index cbd37b52b..cd58a4f35 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.h
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.h
@@ -18,8 +18,6 @@ public:
18 explicit NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name); 18 explicit NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name);
19 ~NVDRV() override; 19 ~NVDRV() override;
20 20
21 void SignalGPUInterruptSyncpt(u32 syncpoint_id, u32 value);
22
23private: 21private:
24 void Open(Kernel::HLERequestContext& ctx); 22 void Open(Kernel::HLERequestContext& ctx);
25 void Ioctl1(Kernel::HLERequestContext& ctx); 23 void Ioctl1(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
deleted file mode 100644
index a6fa943e8..000000000
--- a/src/core/hle/service/nvdrv/syncpoint_manager.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "core/hle/service/nvdrv/syncpoint_manager.h"
6#include "video_core/gpu.h"
7
8namespace Service::Nvidia {
9
10SyncpointManager::SyncpointManager(Tegra::GPU& gpu_) : gpu{gpu_} {}
11
12SyncpointManager::~SyncpointManager() = default;
13
14u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) {
15 syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id);
16 return GetSyncpointMin(syncpoint_id);
17}
18
19u32 SyncpointManager::AllocateSyncpoint() {
20 for (u32 syncpoint_id = 1; syncpoint_id < MaxSyncPoints; syncpoint_id++) {
21 if (!syncpoints[syncpoint_id].is_allocated) {
22 syncpoints[syncpoint_id].is_allocated = true;
23 return syncpoint_id;
24 }
25 }
26 ASSERT_MSG(false, "No more available syncpoints!");
27 return {};
28}
29
30u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) {
31 for (u32 index = 0; index < value; ++index) {
32 syncpoints[syncpoint_id].max.fetch_add(1, std::memory_order_relaxed);
33 }
34
35 return GetSyncpointMax(syncpoint_id);
36}
37
38} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.h b/src/core/hle/service/nvdrv/syncpoint_manager.h
deleted file mode 100644
index 7f080f76e..000000000
--- a/src/core/hle/service/nvdrv/syncpoint_manager.h
+++ /dev/null
@@ -1,84 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <atomic>
8
9#include "common/common_types.h"
10#include "core/hle/service/nvdrv/nvdata.h"
11
12namespace Tegra {
13class GPU;
14}
15
16namespace Service::Nvidia {
17
18class SyncpointManager final {
19public:
20 explicit SyncpointManager(Tegra::GPU& gpu_);
21 ~SyncpointManager();
22
23 /**
24 * Returns true if the specified syncpoint is expired for the given value.
25 * @param syncpoint_id Syncpoint ID to check.
26 * @param value Value to check against the specified syncpoint.
27 * @returns True if the specified syncpoint is expired for the given value, otherwise False.
28 */
29 bool IsSyncpointExpired(u32 syncpoint_id, u32 value) const {
30 return (GetSyncpointMax(syncpoint_id) - value) >= (GetSyncpointMin(syncpoint_id) - value);
31 }
32
33 /**
34 * Gets the lower bound for the specified syncpoint.
35 * @param syncpoint_id Syncpoint ID to get the lower bound for.
36 * @returns The lower bound for the specified syncpoint.
37 */
38 u32 GetSyncpointMin(u32 syncpoint_id) const {
39 return syncpoints.at(syncpoint_id).min.load(std::memory_order_relaxed);
40 }
41
42 /**
43 * Gets the uper bound for the specified syncpoint.
44 * @param syncpoint_id Syncpoint ID to get the upper bound for.
45 * @returns The upper bound for the specified syncpoint.
46 */
47 u32 GetSyncpointMax(u32 syncpoint_id) const {
48 return syncpoints.at(syncpoint_id).max.load(std::memory_order_relaxed);
49 }
50
51 /**
52 * Refreshes the minimum value for the specified syncpoint.
53 * @param syncpoint_id Syncpoint ID to be refreshed.
54 * @returns The new syncpoint minimum value.
55 */
56 u32 RefreshSyncpoint(u32 syncpoint_id);
57
58 /**
59 * Allocates a new syncoint.
60 * @returns The syncpoint ID for the newly allocated syncpoint.
61 */
62 u32 AllocateSyncpoint();
63
64 /**
65 * Increases the maximum value for the specified syncpoint.
66 * @param syncpoint_id Syncpoint ID to be increased.
67 * @param value Value to increase the specified syncpoint by.
68 * @returns The new syncpoint maximum value.
69 */
70 u32 IncreaseSyncpoint(u32 syncpoint_id, u32 value);
71
72private:
73 struct Syncpoint {
74 std::atomic<u32> min;
75 std::atomic<u32> max;
76 std::atomic<bool> is_allocated;
77 };
78
79 std::array<Syncpoint, MaxSyncPoints> syncpoints{};
80
81 Tegra::GPU& gpu;
82};
83
84} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
index 4b3d5efd6..1ce67c771 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -5,15 +5,18 @@
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp 5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/service/nvdrv/core/nvmap.h"
8#include "core/hle/service/nvflinger/buffer_item.h" 9#include "core/hle/service/nvflinger/buffer_item.h"
9#include "core/hle/service/nvflinger/buffer_queue_consumer.h" 10#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
10#include "core/hle/service/nvflinger/buffer_queue_core.h" 11#include "core/hle/service/nvflinger/buffer_queue_core.h"
11#include "core/hle/service/nvflinger/producer_listener.h" 12#include "core/hle/service/nvflinger/producer_listener.h"
13#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
12 14
13namespace Service::android { 15namespace Service::android {
14 16
15BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_) 17BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_,
16 : core{std::move(core_)}, slots{core->slots} {} 18 Service::Nvidia::NvCore::NvMap& nvmap_)
19 : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {}
17 20
18BufferQueueConsumer::~BufferQueueConsumer() = default; 21BufferQueueConsumer::~BufferQueueConsumer() = default;
19 22
@@ -133,6 +136,8 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
133 136
134 slots[slot].buffer_state = BufferState::Free; 137 slots[slot].buffer_state = BufferState::Free;
135 138
139 nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true);
140
136 listener = core->connected_producer_listener; 141 listener = core->connected_producer_listener;
137 142
138 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); 143 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
index b598c314f..4ec06ca13 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_consumer.h
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
@@ -13,6 +13,10 @@
13#include "core/hle/service/nvflinger/buffer_queue_defs.h" 13#include "core/hle/service/nvflinger/buffer_queue_defs.h"
14#include "core/hle/service/nvflinger/status.h" 14#include "core/hle/service/nvflinger/status.h"
15 15
16namespace Service::Nvidia::NvCore {
17class NvMap;
18} // namespace Service::Nvidia::NvCore
19
16namespace Service::android { 20namespace Service::android {
17 21
18class BufferItem; 22class BufferItem;
@@ -21,7 +25,8 @@ class IConsumerListener;
21 25
22class BufferQueueConsumer final { 26class BufferQueueConsumer final {
23public: 27public:
24 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_); 28 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_,
29 Service::Nvidia::NvCore::NvMap& nvmap_);
25 ~BufferQueueConsumer(); 30 ~BufferQueueConsumer();
26 31
27 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); 32 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
@@ -32,6 +37,7 @@ public:
32private: 37private:
33 std::shared_ptr<BufferQueueCore> core; 38 std::shared_ptr<BufferQueueCore> core;
34 BufferQueueDefs::SlotsType& slots; 39 BufferQueueDefs::SlotsType& slots;
40 Service::Nvidia::NvCore::NvMap& nvmap;
35}; 41};
36 42
37} // namespace Service::android 43} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
index 337431488..d4ab23a10 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -14,7 +14,7 @@
14#include "core/hle/kernel/k_writable_event.h" 14#include "core/hle/kernel/k_writable_event.h"
15#include "core/hle/kernel/kernel.h" 15#include "core/hle/kernel/kernel.h"
16#include "core/hle/service/kernel_helpers.h" 16#include "core/hle/service/kernel_helpers.h"
17#include "core/hle/service/nvdrv/nvdrv.h" 17#include "core/hle/service/nvdrv/core/nvmap.h"
18#include "core/hle/service/nvflinger/buffer_queue_core.h" 18#include "core/hle/service/nvflinger/buffer_queue_core.h"
19#include "core/hle/service/nvflinger/buffer_queue_producer.h" 19#include "core/hle/service/nvflinger/buffer_queue_producer.h"
20#include "core/hle/service/nvflinger/consumer_listener.h" 20#include "core/hle/service/nvflinger/consumer_listener.h"
@@ -26,8 +26,10 @@
26namespace Service::android { 26namespace Service::android {
27 27
28BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, 28BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
29 std::shared_ptr<BufferQueueCore> buffer_queue_core_) 29 std::shared_ptr<BufferQueueCore> buffer_queue_core_,
30 : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots) { 30 Service::Nvidia::NvCore::NvMap& nvmap_)
31 : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots),
32 nvmap(nvmap_) {
31 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); 33 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
32} 34}
33 35
@@ -530,6 +532,8 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
530 item.is_droppable = core->dequeue_buffer_cannot_block || async; 532 item.is_droppable = core->dequeue_buffer_cannot_block || async;
531 item.swap_interval = swap_interval; 533 item.swap_interval = swap_interval;
532 534
535 nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true);
536
533 sticky_transform = sticky_transform_; 537 sticky_transform = sticky_transform_;
534 538
535 if (core->queue.empty()) { 539 if (core->queue.empty()) {
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h
index 42d4722dc..0ba03a568 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.h
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h
@@ -31,6 +31,10 @@ namespace Service::KernelHelpers {
31class ServiceContext; 31class ServiceContext;
32} // namespace Service::KernelHelpers 32} // namespace Service::KernelHelpers
33 33
34namespace Service::Nvidia::NvCore {
35class NvMap;
36} // namespace Service::Nvidia::NvCore
37
34namespace Service::android { 38namespace Service::android {
35 39
36class BufferQueueCore; 40class BufferQueueCore;
@@ -39,7 +43,8 @@ class IProducerListener;
39class BufferQueueProducer final : public IBinder { 43class BufferQueueProducer final : public IBinder {
40public: 44public:
41 explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, 45 explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
42 std::shared_ptr<BufferQueueCore> buffer_queue_core_); 46 std::shared_ptr<BufferQueueCore> buffer_queue_core_,
47 Service::Nvidia::NvCore::NvMap& nvmap_);
43 ~BufferQueueProducer(); 48 ~BufferQueueProducer();
44 49
45 void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override; 50 void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override;
@@ -78,6 +83,8 @@ private:
78 s32 next_callback_ticket{}; 83 s32 next_callback_ticket{};
79 s32 current_callback_ticket{}; 84 s32 current_callback_ticket{};
80 std::condition_variable_any callback_condition; 85 std::condition_variable_any callback_condition;
86
87 Service::Nvidia::NvCore::NvMap& nvmap;
81}; 88};
82 89
83} // namespace Service::android 90} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 4246e5e25..aa14d2cbc 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -24,6 +24,8 @@
24#include "core/hle/service/vi/layer/vi_layer.h" 24#include "core/hle/service/vi/layer/vi_layer.h"
25#include "core/hle/service/vi/vi_results.h" 25#include "core/hle/service/vi/vi_results.h"
26#include "video_core/gpu.h" 26#include "video_core/gpu.h"
27#include "video_core/host1x/host1x.h"
28#include "video_core/host1x/syncpoint_manager.h"
27 29
28namespace Service::NVFlinger { 30namespace Service::NVFlinger {
29 31
@@ -105,10 +107,15 @@ NVFlinger::~NVFlinger() {
105 display.GetLayer(layer).Core().NotifyShutdown(); 107 display.GetLayer(layer).Core().NotifyShutdown();
106 } 108 }
107 } 109 }
110
111 if (nvdrv) {
112 nvdrv->Close(disp_fd);
113 }
108} 114}
109 115
110void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 116void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
111 nvdrv = std::move(instance); 117 nvdrv = std::move(instance);
118 disp_fd = nvdrv->Open("/dev/nvdisp_disp0");
112} 119}
113 120
114std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { 121std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
@@ -142,7 +149,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
142 149
143void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { 150void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
144 const auto buffer_id = next_buffer_queue_id++; 151 const auto buffer_id = next_buffer_queue_id++;
145 display.CreateLayer(layer_id, buffer_id); 152 display.CreateLayer(layer_id, buffer_id, nvdrv->container);
146} 153}
147 154
148void NVFlinger::CloseLayer(u64 layer_id) { 155void NVFlinger::CloseLayer(u64 layer_id) {
@@ -262,30 +269,24 @@ void NVFlinger::Compose() {
262 return; // We are likely shutting down 269 return; // We are likely shutting down
263 } 270 }
264 271
265 auto& gpu = system.GPU();
266 const auto& multi_fence = buffer.fence;
267 guard->unlock();
268 for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) {
269 const auto& fence = multi_fence.fences[fence_id];
270 gpu.WaitFence(fence.id, fence.value);
271 }
272 guard->lock();
273
274 MicroProfileFlip();
275
276 // Now send the buffer to the GPU for drawing. 272 // Now send the buffer to the GPU for drawing.
277 // TODO(Subv): Support more than just disp0. The display device selection is probably based 273 // TODO(Subv): Support more than just disp0. The display device selection is probably based
278 // on which display we're drawing (Default, Internal, External, etc) 274 // on which display we're drawing (Default, Internal, External, etc)
279 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); 275 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
280 ASSERT(nvdisp); 276 ASSERT(nvdisp);
281 277
278 guard->unlock();
282 Common::Rectangle<int> crop_rect{ 279 Common::Rectangle<int> crop_rect{
283 static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()), 280 static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()),
284 static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())}; 281 static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())};
285 282
286 nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), 283 nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(),
287 igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), 284 igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(),
288 static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect); 285 static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect,
286 buffer.fence.fences, buffer.fence.num_fences);
287
288 MicroProfileFlip();
289 guard->lock();
289 290
290 swap_interval = buffer.swap_interval; 291 swap_interval = buffer.swap_interval;
291 292
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 3bbe5d92b..b62615de2 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -116,6 +116,7 @@ private:
116 void SplitVSync(std::stop_token stop_token); 116 void SplitVSync(std::stop_token stop_token);
117 117
118 std::shared_ptr<Nvidia::Module> nvdrv; 118 std::shared_ptr<Nvidia::Module> nvdrv;
119 s32 disp_fd;
119 120
120 std::list<VI::Display> displays; 121 std::list<VI::Display> displays;
121 122
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index aa49aa775..288aafaaf 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -12,6 +12,7 @@
12#include "core/hle/kernel/k_readable_event.h" 12#include "core/hle/kernel/k_readable_event.h"
13#include "core/hle/kernel/k_writable_event.h" 13#include "core/hle/kernel/k_writable_event.h"
14#include "core/hle/service/kernel_helpers.h" 14#include "core/hle/service/kernel_helpers.h"
15#include "core/hle/service/nvdrv/core/container.h"
15#include "core/hle/service/nvflinger/buffer_item_consumer.h" 16#include "core/hle/service/nvflinger/buffer_item_consumer.h"
16#include "core/hle/service/nvflinger/buffer_queue_consumer.h" 17#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
17#include "core/hle/service/nvflinger/buffer_queue_core.h" 18#include "core/hle/service/nvflinger/buffer_queue_core.h"
@@ -29,11 +30,13 @@ struct BufferQueue {
29 std::unique_ptr<android::BufferQueueConsumer> consumer; 30 std::unique_ptr<android::BufferQueueConsumer> consumer;
30}; 31};
31 32
32static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context) { 33static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context,
34 Service::Nvidia::NvCore::NvMap& nvmap) {
33 auto buffer_queue_core = std::make_shared<android::BufferQueueCore>(); 35 auto buffer_queue_core = std::make_shared<android::BufferQueueCore>();
34 return {buffer_queue_core, 36 return {
35 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core), 37 buffer_queue_core,
36 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)}; 38 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap),
39 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core, nvmap)};
37} 40}
38 41
39Display::Display(u64 id, std::string name_, 42Display::Display(u64 id, std::string name_,
@@ -74,10 +77,11 @@ void Display::SignalVSyncEvent() {
74 vsync_event->GetWritableEvent().Signal(); 77 vsync_event->GetWritableEvent().Signal();
75} 78}
76 79
77void Display::CreateLayer(u64 layer_id, u32 binder_id) { 80void Display::CreateLayer(u64 layer_id, u32 binder_id,
81 Service::Nvidia::NvCore::Container& nv_core) {
78 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); 82 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment");
79 83
80 auto [core, producer, consumer] = CreateBufferQueue(service_context); 84 auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile());
81 85
82 auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); 86 auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer));
83 buffer_item_consumer->Connect(false); 87 buffer_item_consumer->Connect(false);
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 8dbb0ef80..33d5f398c 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -27,6 +27,11 @@ namespace Service::NVFlinger {
27class HosBinderDriverServer; 27class HosBinderDriverServer;
28} 28}
29 29
30namespace Service::Nvidia::NvCore {
31class Container;
32class NvMap;
33} // namespace Service::Nvidia::NvCore
34
30namespace Service::VI { 35namespace Service::VI {
31 36
32class Layer; 37class Layer;
@@ -93,7 +98,7 @@ public:
93 /// @param layer_id The ID to assign to the created layer. 98 /// @param layer_id The ID to assign to the created layer.
94 /// @param binder_id The ID assigned to the buffer queue. 99 /// @param binder_id The ID assigned to the buffer queue.
95 /// 100 ///
96 void CreateLayer(u64 layer_id, u32 binder_id); 101 void CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& core);
97 102
98 /// Closes and removes a layer from this display with the given ID. 103 /// Closes and removes a layer from this display with the given ID.
99 /// 104 ///
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index f083811ec..9c917cacf 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -58,6 +58,7 @@ static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
58class NativeWindow final { 58class NativeWindow final {
59public: 59public:
60 constexpr explicit NativeWindow(u32 id_) : id{id_} {} 60 constexpr explicit NativeWindow(u32 id_) : id{id_} {}
61 constexpr explicit NativeWindow(const NativeWindow& other) = default;
61 62
62private: 63private:
63 const u32 magic = 2; 64 const u32 magic = 2;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 34ad7cadd..2ac792566 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -551,6 +551,11 @@ struct Memory::Impl {
551 []() {}); 551 []() {});
552 } 552 }
553 553
554 [[nodiscard]] u8* GetPointerSilent(const VAddr vaddr) const {
555 return GetPointerImpl(
556 vaddr, []() {}, []() {});
557 }
558
554 /** 559 /**
555 * Reads a particular data type out of memory at the given virtual address. 560 * Reads a particular data type out of memory at the given virtual address.
556 * 561 *
@@ -686,6 +691,10 @@ u8* Memory::GetPointer(VAddr vaddr) {
686 return impl->GetPointer(vaddr); 691 return impl->GetPointer(vaddr);
687} 692}
688 693
694u8* Memory::GetPointerSilent(VAddr vaddr) {
695 return impl->GetPointerSilent(vaddr);
696}
697
689const u8* Memory::GetPointer(VAddr vaddr) const { 698const u8* Memory::GetPointer(VAddr vaddr) const {
690 return impl->GetPointer(vaddr); 699 return impl->GetPointer(vaddr);
691} 700}
diff --git a/src/core/memory.h b/src/core/memory.h
index a11ff8766..81eac448b 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -114,6 +114,7 @@ public:
114 * If the address is not valid, nullptr will be returned. 114 * If the address is not valid, nullptr will be returned.
115 */ 115 */
116 u8* GetPointer(VAddr vaddr); 116 u8* GetPointer(VAddr vaddr);
117 u8* GetPointerSilent(VAddr vaddr);
117 118
118 template <typename T> 119 template <typename T>
119 T* GetPointer(VAddr vaddr) { 120 T* GetPointer(VAddr vaddr) {