diff options
Diffstat (limited to 'src/core')
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 | ||
| 671 | Core::Hardware::InterruptManager& System::InterruptManager() { | 672 | Tegra::Host1x::Host1x& System::Host1x() { |
| 672 | return *impl->interrupt_manager; | 673 | return *impl->host1x_core; |
| 673 | } | 674 | } |
| 674 | 675 | ||
| 675 | const Core::Hardware::InterruptManager& System::InterruptManager() const { | 676 | const Tegra::Host1x::Host1x& System::Host1x() const { |
| 676 | return *impl->interrupt_manager; | 677 | return *impl->host1x_core; |
| 677 | } | 678 | } |
| 678 | 679 | ||
| 679 | VideoCore::RendererBase& System::Renderer() { | 680 | VideoCore::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; | |||
| 74 | namespace Tegra { | 74 | namespace Tegra { |
| 75 | class DebugContext; | 75 | class DebugContext; |
| 76 | class GPU; | 76 | class GPU; |
| 77 | namespace Host1x { | ||
| 78 | class Host1x; | ||
| 79 | } // namespace Host1x | ||
| 77 | } // namespace Tegra | 80 | } // namespace Tegra |
| 78 | 81 | ||
| 79 | namespace VideoCore { | 82 | namespace VideoCore { |
| @@ -88,10 +91,6 @@ namespace Core::Timing { | |||
| 88 | class CoreTiming; | 91 | class CoreTiming; |
| 89 | } | 92 | } |
| 90 | 93 | ||
| 91 | namespace Core::Hardware { | ||
| 92 | class InterruptManager; | ||
| 93 | } | ||
| 94 | |||
| 95 | namespace Core::HID { | 94 | namespace Core::HID { |
| 96 | class HIDCore; | 95 | class 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 | |||
| 10 | namespace Core::Hardware { | ||
| 11 | |||
| 12 | InterruptManager::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 | |||
| 25 | InterruptManager::~InterruptManager() = default; | ||
| 26 | |||
| 27 | void 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 | |||
| 10 | namespace Core { | ||
| 11 | class System; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace Core::Timing { | ||
| 15 | struct EventType; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace Core::Hardware { | ||
| 19 | |||
| 20 | class InterruptManager { | ||
| 21 | public: | ||
| 22 | explicit InterruptManager(Core::System& system); | ||
| 23 | ~InterruptManager(); | ||
| 24 | |||
| 25 | void GPUInterruptSyncpt(u32 syncpoint_id, u32 value); | ||
| 26 | |||
| 27 | private: | ||
| 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 | |||
| 10 | namespace Service::Nvidia::NvCore { | ||
| 11 | |||
| 12 | struct 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 | |||
| 20 | Container::Container(Tegra::Host1x::Host1x& host1x_) { | ||
| 21 | impl = std::make_unique<ContainerImpl>(host1x_); | ||
| 22 | } | ||
| 23 | |||
| 24 | Container::~Container() = default; | ||
| 25 | |||
| 26 | NvMap& Container::GetNvMapFile() { | ||
| 27 | return impl->file; | ||
| 28 | } | ||
| 29 | |||
| 30 | const NvMap& Container::GetNvMapFile() const { | ||
| 31 | return impl->file; | ||
| 32 | } | ||
| 33 | |||
| 34 | Container::Host1xDeviceFileData& Container::Host1xDeviceFile() { | ||
| 35 | return impl->device_file_data; | ||
| 36 | } | ||
| 37 | |||
| 38 | const Container::Host1xDeviceFileData& Container::Host1xDeviceFile() const { | ||
| 39 | return impl->device_file_data; | ||
| 40 | } | ||
| 41 | |||
| 42 | SyncpointManager& Container::GetSyncpointManager() { | ||
| 43 | return impl->manager; | ||
| 44 | } | ||
| 45 | |||
| 46 | const 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 | |||
| 13 | namespace Tegra::Host1x { | ||
| 14 | class Host1x; | ||
| 15 | } // namespace Tegra::Host1x | ||
| 16 | |||
| 17 | namespace Service::Nvidia::NvCore { | ||
| 18 | |||
| 19 | class NvMap; | ||
| 20 | class SyncpointManager; | ||
| 21 | |||
| 22 | struct ContainerImpl; | ||
| 23 | |||
| 24 | class Container { | ||
| 25 | public: | ||
| 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 | |||
| 48 | private: | ||
| 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 | |||
| 12 | using Core::Memory::YUZU_PAGESIZE; | ||
| 13 | |||
| 14 | namespace Service::Nvidia::NvCore { | ||
| 15 | NvMap::Handle::Handle(u64 size_, Id id_) | ||
| 16 | : size(size_), aligned_size(size), orig_size(size), id(id_) { | ||
| 17 | flags.raw = 0; | ||
| 18 | } | ||
| 19 | |||
| 20 | NvResult 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 | |||
| 48 | NvResult 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 | |||
| 66 | NvMap::NvMap(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {} | ||
| 67 | |||
| 68 | void 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 | |||
| 74 | void 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 | |||
| 89 | bool 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 | |||
| 105 | NvResult 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 | |||
| 118 | std::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 | |||
| 127 | VAddr 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 | |||
| 136 | u32 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 | |||
| 187 | void 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 | |||
| 205 | void 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 | |||
| 218 | std::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 | |||
| 19 | namespace Tegra { | ||
| 20 | |||
| 21 | namespace Host1x { | ||
| 22 | class Host1x; | ||
| 23 | } // namespace Host1x | ||
| 24 | |||
| 25 | } // namespace Tegra | ||
| 26 | |||
| 27 | namespace Service::Nvidia::NvCore { | ||
| 28 | /** | ||
| 29 | * @brief The nvmap core class holds the global state for nvmap and provides methods to manage | ||
| 30 | * handles | ||
| 31 | */ | ||
| 32 | class NvMap { | ||
| 33 | public: | ||
| 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 | |||
| 147 | private: | ||
| 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 | |||
| 9 | namespace Service::Nvidia::NvCore { | ||
| 10 | |||
| 11 | SyncpointManager::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 | |||
| 28 | SyncpointManager::~SyncpointManager() = default; | ||
| 29 | |||
| 30 | u32 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 | |||
| 42 | u32 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 | |||
| 52 | u32 SyncpointManager::AllocateSyncpoint(bool client_managed) { | ||
| 53 | std::lock_guard lock(reservation_lock); | ||
| 54 | return ReserveSyncpoint(FindFreeSyncpoint(), client_managed); | ||
| 55 | } | ||
| 56 | |||
| 57 | void 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 | |||
| 63 | bool SyncpointManager::IsSyncpointAllocated(u32 id) { | ||
| 64 | return (id <= SyncpointCount) && syncpoints[id].reserved; | ||
| 65 | } | ||
| 66 | |||
| 67 | bool 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 | |||
| 84 | u32 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 | |||
| 93 | u32 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 | |||
| 102 | u32 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 | |||
| 112 | NvFence 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 | |||
| 14 | namespace Tegra::Host1x { | ||
| 15 | class Host1x; | ||
| 16 | } // namespace Tegra::Host1x | ||
| 17 | |||
| 18 | namespace Service::Nvidia::NvCore { | ||
| 19 | |||
| 20 | enum 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 | */ | ||
| 39 | class SyncpointManager final { | ||
| 40 | public: | ||
| 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 | |||
| 102 | private: | ||
| 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 { | |||
| 11 | class System; | 11 | class System; |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | namespace Kernel { | ||
| 15 | class KEvent; | ||
| 16 | } | ||
| 17 | |||
| 14 | namespace Service::Nvidia::Devices { | 18 | namespace 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 | |||
| 67 | protected: | 75 | protected: |
| 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 | ||
| 13 | namespace Service::Nvidia::Devices { | 14 | namespace Service::Nvidia::Devices { |
| 14 | 15 | ||
| 15 | nvdisp_disp0::nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_) | 16 | nvdisp_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()} {} |
| 17 | nvdisp_disp0::~nvdisp_disp0() = default; | 18 | nvdisp_disp0::~nvdisp_disp0() = default; |
| 18 | 19 | ||
| 19 | NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 20 | NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, |
| @@ -39,8 +40,9 @@ void nvdisp_disp0::OnClose(DeviceFD fd) {} | |||
| 39 | 40 | ||
| 40 | void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, | 41 | void 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 | ||
| 59 | Kernel::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 | ||
| 14 | namespace Service::Nvidia::NvCore { | ||
| 15 | class Container; | ||
| 16 | class NvMap; | ||
| 17 | } // namespace Service::Nvidia::NvCore | ||
| 18 | |||
| 14 | namespace Service::Nvidia::Devices { | 19 | namespace Service::Nvidia::Devices { |
| 15 | 20 | ||
| 16 | class nvmap; | 21 | class nvmap; |
| 17 | 22 | ||
| 18 | class nvdisp_disp0 final : public nvdevice { | 23 | class nvdisp_disp0 final : public nvdevice { |
| 19 | public: | 24 | public: |
| 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 | ||
| 38 | private: | 46 | private: |
| 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 | ||
| 15 | namespace Service::Nvidia::Devices { | 22 | namespace Service::Nvidia::Devices { |
| 16 | 23 | ||
| 17 | nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_) | 24 | nvhost_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 | |||
| 19 | nvhost_as_gpu::~nvhost_as_gpu() = default; | 28 | nvhost_as_gpu::~nvhost_as_gpu() = default; |
| 20 | 29 | ||
| 21 | NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 30 | NvResult 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(¶ms, input.data(), input.size()); | 92 | std::memcpy(¶ms, 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(), ¶ms, output.size()); | 197 | std::memcpy(output.data(), ¶ms, output.size()); |
| 116 | return result; | 198 | return NvResult::Success; |
| 199 | } | ||
| 200 | |||
| 201 | void 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 | ||
| 119 | NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) { | 223 | NvResult 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(), ¶ms, output.size()); | 265 | std::memcpy(output.data(), ¶ms, 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 | ||
| 169 | NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { | 322 | NvResult 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(), ¶ms, 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(), ¶ms, output.size()); | ||
| 210 | return NvResult::InvalidState; | ||
| 211 | } | 348 | } |
| 212 | 349 | ||
| 213 | std::memcpy(output.data(), ¶ms, 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(), ¶ms, 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(), ¶ms, output.size()); | 419 | std::memcpy(output.data(), ¶ms, output.size()); |
| 248 | return result; | 420 | return NvResult::Success; |
| 249 | } | 421 | } |
| 250 | 422 | ||
| 251 | NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { | 423 | NvResult 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(), ¶ms, output.size()); | ||
| 264 | return NvResult::Success; | 459 | return NvResult::Success; |
| 265 | } | 460 | } |
| 266 | 461 | ||
| 267 | NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { | 462 | NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { |
| 268 | IoctlBindChannel params{}; | 463 | IoctlBindChannel params{}; |
| 269 | std::memcpy(¶ms, input.data(), input.size()); | 464 | std::memcpy(¶ms, 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 | ||
| 472 | void 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 | |||
| 276 | NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { | 491 | NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { |
| 277 | IoctlGetVaRegions params{}; | 492 | IoctlGetVaRegions params{}; |
| 278 | std::memcpy(¶ms, input.data(), input.size()); | 493 | std::memcpy(¶ms, 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(), ¶ms, output.size()); | 506 | std::memcpy(output.data(), ¶ms, 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(¶ms, input.data(), input.size()); | 513 | std::memcpy(¶ms, 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(), ¶ms, output.size()); | 526 | std::memcpy(output.data(), ¶ms, output.size()); |
| 328 | std::memcpy(inline_output.data(), ¶ms.small, sizeof(IoctlVaRegion)); | 527 | std::memcpy(inline_output.data(), ¶ms.regions[0], sizeof(VaRegion)); |
| 329 | std::memcpy(inline_output.data() + sizeof(IoctlVaRegion), ¶ms.big, sizeof(IoctlVaRegion)); | 528 | std::memcpy(inline_output.data() + sizeof(VaRegion), ¶ms.regions[1], sizeof(VaRegion)); |
| 330 | 529 | ||
| 331 | return NvResult::Success; | 530 | return NvResult::Success; |
| 332 | } | 531 | } |
| 333 | 532 | ||
| 334 | std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const { | 533 | Kernel::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 | |||
| 345 | void 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 | |||
| 350 | std::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 | ||
| 16 | namespace Service::Nvidia::Devices { | 22 | namespace Tegra { |
| 23 | class MemoryManager; | ||
| 24 | } // namespace Tegra | ||
| 25 | |||
| 26 | namespace Service::Nvidia { | ||
| 27 | class Module; | ||
| 28 | } | ||
| 17 | 29 | ||
| 18 | constexpr u32 DEFAULT_BIG_PAGE_SIZE = 1 << 16; | 30 | namespace Service::Nvidia::NvCore { |
| 19 | constexpr u32 DEFAULT_SMALL_PAGE_SIZE = 1 << 12; | 31 | class Container; |
| 32 | class NvMap; | ||
| 33 | } // namespace Service::Nvidia::NvCore | ||
| 20 | 34 | ||
| 21 | class nvmap; | 35 | namespace Service::Nvidia::Devices { |
| 22 | 36 | ||
| 23 | enum class AddressSpaceFlags : u32 { | 37 | enum 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 | }; |
| 28 | DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags); | 43 | DECLARE_ENUM_FLAG_OPERATORS(MappingFlags); |
| 29 | 44 | ||
| 30 | class nvhost_as_gpu final : public nvdevice { | 45 | class nvhost_as_gpu final : public nvdevice { |
| 31 | public: | 46 | public: |
| 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 | ||
| 45 | private: | 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 | ||
| 70 | private: | ||
| 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 | ||
| 15 | namespace Service::Nvidia::Devices { | 22 | namespace Service::Nvidia::Devices { |
| 16 | 23 | ||
| 17 | nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, | 24 | nvhost_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()} {} |
| 21 | nvhost_ctrl::~nvhost_ctrl() = default; | 28 | |
| 29 | nvhost_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 | ||
| 23 | NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 38 | NvResult 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 | ||
| 62 | void nvhost_ctrl::OnOpen(DeviceFD fd) {} | 79 | void nvhost_ctrl::OnOpen(DeviceFD fd) {} |
| 80 | |||
| 63 | void nvhost_ctrl::OnClose(DeviceFD fd) {} | 81 | void nvhost_ctrl::OnClose(DeviceFD fd) {} |
| 64 | 82 | ||
| 65 | NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { | 83 | NvResult 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 | ||
| 73 | NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, | 91 | NvResult 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(¶ms, input.data(), sizeof(params)); | 94 | std::memcpy(¶ms, 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(), ¶ms, 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(), ¶ms, 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(), ¶ms, 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(), ¶ms, 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(), ¶ms, 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(), ¶ms, 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(), ¶ms, 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(), ¶ms, 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(), ¶ms, 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 | ||
| 216 | NvResult 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 | |||
| 166 | NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { | 235 | NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { |
| 167 | IocCtrlEventRegisterParams params{}; | 236 | IocCtrlEventRegisterParams params{}; |
| 168 | std::memcpy(¶ms, input.data(), sizeof(params)); | 237 | std::memcpy(¶ms, 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(¶ms, input.data(), sizeof(params)); | 259 | std::memcpy(¶ms, 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 | |
| 267 | NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector<u8>& input, | ||
| 268 | std::vector<u8>& output) { | ||
| 269 | IocCtrlEventUnregisterBatchParams params{}; | ||
| 270 | std::memcpy(¶ms, 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 | ||
| 203 | NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { | 286 | NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { |
| 204 | IocCtrlEventSignalParams params{}; | 287 | IocCtrlEventClearParams params{}; |
| 205 | std::memcpy(¶ms, input.data(), sizeof(params)); | 288 | std::memcpy(¶ms, 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 | ||
| 314 | Kernel::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 | |||
| 340 | std::unique_lock<std::mutex> nvhost_ctrl::NvEventsLock() { | ||
| 341 | return std::unique_lock<std::mutex>(events_mutex); | ||
| 342 | } | ||
| 343 | |||
| 344 | void 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 | |||
| 358 | void 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 | |||
| 371 | u32 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 | |||
| 15 | namespace Service::Nvidia::NvCore { | ||
| 16 | class Container; | ||
| 17 | class SyncpointManager; | ||
| 18 | } // namespace Service::Nvidia::NvCore | ||
| 11 | 19 | ||
| 12 | namespace Service::Nvidia::Devices { | 20 | namespace Service::Nvidia::Devices { |
| 13 | 21 | ||
| 14 | class nvhost_ctrl final : public nvdevice { | 22 | class nvhost_ctrl final : public nvdevice { |
| 15 | public: | 23 | public: |
| 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 | |||
| 30 | private: | 58 | private: |
| 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 | ||
| 11 | namespace Service::Nvidia::Devices { | 12 | namespace Service::Nvidia::Devices { |
| 12 | 13 | ||
| 13 | nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_) : nvdevice{system_} {} | 14 | nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_) |
| 14 | nvhost_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 | } | ||
| 19 | nvhost_ctrl_gpu::~nvhost_ctrl_gpu() { | ||
| 20 | events_interface.FreeEvent(error_notifier_event); | ||
| 21 | events_interface.FreeEvent(unknown_event); | ||
| 22 | } | ||
| 15 | 23 | ||
| 16 | NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 24 | NvResult 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 | ||
| 297 | Kernel::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 | ||
| 13 | namespace Service::Nvidia { | ||
| 14 | class EventInterface; | ||
| 15 | } | ||
| 16 | |||
| 13 | namespace Service::Nvidia::Devices { | 17 | namespace Service::Nvidia::Devices { |
| 14 | 18 | ||
| 15 | class nvhost_ctrl_gpu final : public nvdevice { | 19 | class nvhost_ctrl_gpu final : public nvdevice { |
| 16 | public: | 20 | public: |
| 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 | |||
| 30 | private: | 36 | private: |
| 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 | ||
| 13 | namespace Service::Nvidia::Devices { | 19 | namespace Service::Nvidia::Devices { |
| 14 | namespace { | 20 | namespace { |
| 15 | Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoint_id) { | 21 | Tegra::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 | ||
| 23 | nvhost_gpu::nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, | 29 | nvhost_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 | ||
| 30 | nvhost_gpu::~nvhost_gpu() = default; | 42 | nvhost_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 | ||
| 32 | NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 49 | NvResult 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(), ¶ms, output.size()); | 196 | std::memcpy(output.data(), ¶ms, 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 | ||
| 189 | static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) { | 211 | static 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 | ||
| 200 | static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence, | 222 | static 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 | ||
| 216 | static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence, | 238 | static 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(), ¶ms, sizeof(IoctlSubmitGpfifo)); | 292 | std::memcpy(output.data(), ¶ms, 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 | ||
| 359 | Kernel::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 | ||
| 16 | namespace Tegra { | ||
| 17 | namespace Control { | ||
| 18 | struct ChannelState; | ||
| 19 | } | ||
| 20 | } // namespace Tegra | ||
| 21 | |||
| 16 | namespace Service::Nvidia { | 22 | namespace Service::Nvidia { |
| 23 | |||
| 24 | namespace NvCore { | ||
| 25 | class Container; | ||
| 26 | class NvMap; | ||
| 17 | class SyncpointManager; | 27 | class SyncpointManager; |
| 18 | } | 28 | } // namespace NvCore |
| 29 | |||
| 30 | class EventInterface; | ||
| 31 | } // namespace Service::Nvidia | ||
| 19 | 32 | ||
| 20 | namespace Service::Nvidia::Devices { | 33 | namespace Service::Nvidia::Devices { |
| 21 | 34 | ||
| 35 | class nvhost_as_gpu; | ||
| 22 | class nvmap; | 36 | class nvmap; |
| 23 | class nvhost_gpu final : public nvdevice { | 37 | class nvhost_gpu final : public nvdevice { |
| 24 | public: | 38 | public: |
| 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 | |||
| 39 | private: | 55 | private: |
| 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 | ||
| 11 | namespace Service::Nvidia::Devices { | 12 | namespace Service::Nvidia::Devices { |
| 12 | 13 | ||
| 13 | nvhost_nvdec::nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, | 14 | nvhost_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_} {} | ||
| 16 | nvhost_nvdec::~nvhost_nvdec() = default; | 16 | nvhost_nvdec::~nvhost_nvdec() = default; |
| 17 | 17 | ||
| 18 | NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | 18 | NvResult 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 | ||
| 74 | void nvhost_nvdec::OnClose(DeviceFD fd) { | 75 | void 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 | ||
| 11 | class nvhost_nvdec final : public nvhost_nvdec_common { | 11 | class nvhost_nvdec final : public nvhost_nvdec_common { |
| 12 | public: | 12 | public: |
| 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 | |||
| 27 | private: | ||
| 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 | ||
| 47 | nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, | 49 | nvhost_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()}, |
| 50 | nvhost_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 | |||
| 62 | nvhost_nvdec_common::~nvhost_nvdec_common() { | ||
| 63 | core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint); | ||
| 64 | } | ||
| 51 | 65 | ||
| 52 | NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) { | 66 | NvResult 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(), ¶ms, sizeof(IoctlSubmit)); | 112 | std::memcpy(output.data(), ¶ms, 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(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); | 126 | std::memcpy(¶ms, 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(), ¶ms, sizeof(IoctlGetSyncpoint)); | 131 | std::memcpy(output.data(), ¶ms, 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 | ||
| 124 | NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { | 136 | NvResult 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(¶ms, input.data(), sizeof(IoctlGetWaitbase)); | 139 | std::memcpy(¶ms, 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(), ¶ms, sizeof(IoctlGetWaitbase)); | 141 | std::memcpy(output.data(), ¶ms, 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(), ¶ms, 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(), ¶ms, sizeof(IoctlMapBuffer)); | 155 | std::memcpy(output.data(), ¶ms, 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 | ||
| 169 | NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { | 162 | NvResult 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(¶ms, 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 | ||
| 183 | Kernel::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 | ||
| 11 | namespace Service::Nvidia { | 13 | namespace Service::Nvidia { |
| 12 | class SyncpointManager; | 14 | |
| 15 | namespace NvCore { | ||
| 16 | class Container; | ||
| 17 | class NvMap; | ||
| 18 | } // namespace NvCore | ||
| 13 | 19 | ||
| 14 | namespace Devices { | 20 | namespace Devices { |
| 15 | class nvmap; | ||
| 16 | 21 | ||
| 17 | class nvhost_nvdec_common : public nvdevice { | 22 | class nvhost_nvdec_common : public nvdevice { |
| 18 | public: | 23 | public: |
| 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 | ||
| 23 | protected: | 28 | protected: |
| @@ -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 | ||
| 10 | namespace Service::Nvidia::Devices { | 11 | namespace Service::Nvidia::Devices { |
| 11 | nvhost_vic::nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_, | 12 | |
| 12 | SyncpointManager& syncpoint_manager_) | 13 | nvhost_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 | ||
| 15 | nvhost_vic::~nvhost_vic() = default; | 16 | nvhost_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 | |||
| 67 | void nvhost_vic::OnOpen(DeviceFD fd) {} | 70 | void nvhost_vic::OnOpen(DeviceFD fd) {} |
| 68 | 71 | ||
| 69 | void nvhost_vic::OnClose(DeviceFD fd) { | 72 | void 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 | ||
| 10 | class nvhost_vic final : public nvhost_nvdec_common { | 10 | class nvhost_vic final : public nvhost_nvdec_common { |
| 11 | public: | 11 | public: |
| 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 | |||
| 26 | private: | ||
| 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 | |||
| 19 | using Core::Memory::YUZU_PAGESIZE; | ||
| 10 | 20 | ||
| 11 | namespace Service::Nvidia::Devices { | 21 | namespace Service::Nvidia::Devices { |
| 12 | 22 | ||
| 13 | nvmap::nvmap(Core::System& system_) : nvdevice{system_} { | 23 | nvmap::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 | ||
| 19 | nvmap::~nvmap() = default; | 26 | nvmap::~nvmap() = default; |
| 20 | 27 | ||
| @@ -62,39 +69,21 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input, | |||
| 62 | void nvmap::OnOpen(DeviceFD fd) {} | 69 | void nvmap::OnOpen(DeviceFD fd) {} |
| 63 | void nvmap::OnClose(DeviceFD fd) {} | 70 | void nvmap::OnClose(DeviceFD fd) {} |
| 64 | 71 | ||
| 65 | VAddr 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 | |||
| 72 | u32 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 | |||
| 87 | NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { | 72 | NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { |
| 88 | IocCreateParams params; | 73 | IocCreateParams params; |
| 89 | std::memcpy(¶ms, input.data(), sizeof(params)); | 74 | std::memcpy(¶ms, 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(), ¶ms, sizeof(params)); | 88 | std::memcpy(output.data(), ¶ms, 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) | |||
| 103 | NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { | 92 | NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { |
| 104 | IocAllocParams params; | 93 | IocAllocParams params; |
| 105 | std::memcpy(¶ms, input.data(), sizeof(params)); | 94 | std::memcpy(¶ms, 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(), ¶ms, sizeof(params)); | 133 | std::memcpy(output.data(), ¶ms, sizeof(params)); |
| 141 | return NvResult::Success; | 134 | return result; |
| 142 | } | 135 | } |
| 143 | 136 | ||
| 144 | NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { | 137 | NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { |
| 145 | IocGetIdParams params; | 138 | IocGetIdParams params; |
| 146 | std::memcpy(¶ms, input.data(), sizeof(params)); | 139 | std::memcpy(¶ms, 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(), ¶ms, sizeof(params)); | 157 | std::memcpy(output.data(), ¶ms, 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(¶ms, input.data(), sizeof(params)); | 163 | std::memcpy(¶ms, 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(), ¶ms, sizeof(params)); | 188 | std::memcpy(output.data(), ¶ms, 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(¶ms, input.data(), sizeof(params)); | 196 | std::memcpy(¶ms, 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(), ¶ms, sizeof(params)); | 237 | std::memcpy(output.data(), ¶ms, sizeof(params)); |
| @@ -234,46 +239,29 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) | |||
| 234 | } | 239 | } |
| 235 | 240 | ||
| 236 | NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) { | 241 | NvResult 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(¶ms, input.data(), sizeof(params)); | 243 | std::memcpy(¶ms, 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(), ¶ms, sizeof(params)); | 265 | std::memcpy(output.data(), ¶ms, 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 | ||
| 15 | namespace Service::Nvidia::NvCore { | ||
| 16 | class Container; | ||
| 17 | } // namespace Service::Nvidia::NvCore | ||
| 18 | |||
| 14 | namespace Service::Nvidia::Devices { | 19 | namespace Service::Nvidia::Devices { |
| 15 | 20 | ||
| 16 | class nvmap final : public nvdevice { | 21 | class nvmap final : public nvdevice { |
| 17 | public: | 22 | public: |
| 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 | |||
| 56 | private: | 48 | private: |
| 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 | ||
| 81 | enum class EventState { | 84 | enum 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 | ||
| 88 | union Ioctl { | 93 | union 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 | ||
| 27 | namespace Service::Nvidia { | 30 | namespace Service::Nvidia { |
| 28 | 31 | ||
| 32 | EventInterface::EventInterface(Module& module_) : module{module_}, guard{}, on_signal{} {} | ||
| 33 | |||
| 34 | EventInterface::~EventInterface() = default; | ||
| 35 | |||
| 36 | Kernel::KEvent* EventInterface::CreateEvent(std::string name) { | ||
| 37 | Kernel::KEvent* new_event = module.service_context.CreateEvent(std::move(name)); | ||
| 38 | return new_event; | ||
| 39 | } | ||
| 40 | |||
| 41 | void EventInterface::FreeEvent(Kernel::KEvent* event) { | ||
| 42 | module.service_context.CloseEvent(event); | ||
| 43 | } | ||
| 44 | |||
| 29 | void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, | 45 | void 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 | ||
| 40 | Module::Module(Core::System& system) | 56 | Module::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 | ||
| 64 | Module::~Module() { | 104 | Module::~Module() {} |
| 65 | for (u32 i = 0; i < MaxNvEvents; i++) { | ||
| 66 | service_context.CloseEvent(events_interface.events[i].event); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | 105 | ||
| 70 | NvResult Module::VerifyFD(DeviceFD fd) const { | 106 | NvResult 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 | ||
| 84 | DeviceFD Module::Open(const std::string& device_name) { | 120 | DeviceFD 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 | ||
| 171 | void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { | 207 | NvResult 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 | ||
| 181 | Kernel::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 | ||
| 185 | Kernel::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 | ||
| 29 | namespace Service::Nvidia { | 33 | namespace Service::Nvidia { |
| 30 | 34 | ||
| 35 | namespace NvCore { | ||
| 36 | class Container; | ||
| 31 | class SyncpointManager; | 37 | class SyncpointManager; |
| 38 | } // namespace NvCore | ||
| 32 | 39 | ||
| 33 | namespace Devices { | 40 | namespace Devices { |
| 34 | class nvdevice; | 41 | class nvdevice; |
| 35 | } | 42 | class nvhost_ctrl; |
| 43 | } // namespace Devices | ||
| 36 | 44 | ||
| 37 | /// Represents an Nvidia event | 45 | class Module; |
| 38 | struct NvEvent { | ||
| 39 | Kernel::KEvent* event{}; | ||
| 40 | NvFence fence{}; | ||
| 41 | }; | ||
| 42 | 46 | ||
| 43 | struct EventInterface { | 47 | class EventInterface { |
| 44 | // Mask representing currently busy events | 48 | public: |
| 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. | 56 | private: |
| 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 | ||
| 108 | class Module final { | 62 | class 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 | ||
| 146 | private: | 96 | private: |
| 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 | ||
| 13 | namespace Service::Nvidia { | 15 | namespace Service::Nvidia { |
| 14 | 16 | ||
| 15 | void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { | ||
| 16 | nvdrv->SignalSyncpt(syncpoint_id, value); | ||
| 17 | } | ||
| 18 | |||
| 19 | void NVDRV::Open(Kernel::HLERequestContext& ctx) { | 17 | void 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) { | |||
| 164 | void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { | 162 | void 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 | |||
| 23 | private: | 21 | private: |
| 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 | |||
| 8 | namespace Service::Nvidia { | ||
| 9 | |||
| 10 | SyncpointManager::SyncpointManager(Tegra::GPU& gpu_) : gpu{gpu_} {} | ||
| 11 | |||
| 12 | SyncpointManager::~SyncpointManager() = default; | ||
| 13 | |||
| 14 | u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) { | ||
| 15 | syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id); | ||
| 16 | return GetSyncpointMin(syncpoint_id); | ||
| 17 | } | ||
| 18 | |||
| 19 | u32 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 | |||
| 30 | u32 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 | |||
| 12 | namespace Tegra { | ||
| 13 | class GPU; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace Service::Nvidia { | ||
| 17 | |||
| 18 | class SyncpointManager final { | ||
| 19 | public: | ||
| 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 | |||
| 72 | private: | ||
| 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 | ||
| 13 | namespace Service::android { | 15 | namespace Service::android { |
| 14 | 16 | ||
| 15 | BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_) | 17 | BufferQueueConsumer::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 | ||
| 18 | BufferQueueConsumer::~BufferQueueConsumer() = default; | 21 | BufferQueueConsumer::~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 | ||
| 16 | namespace Service::Nvidia::NvCore { | ||
| 17 | class NvMap; | ||
| 18 | } // namespace Service::Nvidia::NvCore | ||
| 19 | |||
| 16 | namespace Service::android { | 20 | namespace Service::android { |
| 17 | 21 | ||
| 18 | class BufferItem; | 22 | class BufferItem; |
| @@ -21,7 +25,8 @@ class IConsumerListener; | |||
| 21 | 25 | ||
| 22 | class BufferQueueConsumer final { | 26 | class BufferQueueConsumer final { |
| 23 | public: | 27 | public: |
| 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: | |||
| 32 | private: | 37 | private: |
| 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 @@ | |||
| 26 | namespace Service::android { | 26 | namespace Service::android { |
| 27 | 27 | ||
| 28 | BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, | 28 | BufferQueueProducer::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 { | |||
| 31 | class ServiceContext; | 31 | class ServiceContext; |
| 32 | } // namespace Service::KernelHelpers | 32 | } // namespace Service::KernelHelpers |
| 33 | 33 | ||
| 34 | namespace Service::Nvidia::NvCore { | ||
| 35 | class NvMap; | ||
| 36 | } // namespace Service::Nvidia::NvCore | ||
| 37 | |||
| 34 | namespace Service::android { | 38 | namespace Service::android { |
| 35 | 39 | ||
| 36 | class BufferQueueCore; | 40 | class BufferQueueCore; |
| @@ -39,7 +43,8 @@ class IProducerListener; | |||
| 39 | class BufferQueueProducer final : public IBinder { | 43 | class BufferQueueProducer final : public IBinder { |
| 40 | public: | 44 | public: |
| 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 | ||
| 28 | namespace Service::NVFlinger { | 30 | namespace 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 | ||
| 110 | void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { | 116 | void 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 | ||
| 114 | std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { | 121 | std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { |
| @@ -142,7 +149,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { | |||
| 142 | 149 | ||
| 143 | void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { | 150 | void 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 | ||
| 148 | void NVFlinger::CloseLayer(u64 layer_id) { | 155 | void 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 | ||
| 32 | static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context) { | 33 | static 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 | ||
| 39 | Display::Display(u64 id, std::string name_, | 42 | Display::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 | ||
| 77 | void Display::CreateLayer(u64 layer_id, u32 binder_id) { | 80 | void 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 { | |||
| 27 | class HosBinderDriverServer; | 27 | class HosBinderDriverServer; |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | namespace Service::Nvidia::NvCore { | ||
| 31 | class Container; | ||
| 32 | class NvMap; | ||
| 33 | } // namespace Service::Nvidia::NvCore | ||
| 34 | |||
| 30 | namespace Service::VI { | 35 | namespace Service::VI { |
| 31 | 36 | ||
| 32 | class Layer; | 37 | class 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"); | |||
| 58 | class NativeWindow final { | 58 | class NativeWindow final { |
| 59 | public: | 59 | public: |
| 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 | ||
| 62 | private: | 63 | private: |
| 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 | ||
| 694 | u8* Memory::GetPointerSilent(VAddr vaddr) { | ||
| 695 | return impl->GetPointerSilent(vaddr); | ||
| 696 | } | ||
| 697 | |||
| 689 | const u8* Memory::GetPointer(VAddr vaddr) const { | 698 | const 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) { |