// SPDX-FileCopyrightText: 2022 yuzu Emulator Project // SPDX-FileCopyrightText: 2022 Skyline Team and Contributors // SPDX-License-Identifier: GPL-3.0-or-later #pragma once #include #include #include #include #include #include #include #include "common/bit_field.h" #include "common/common_types.h" #include "core/hle/service/nvdrv/nvdata.h" namespace Tegra { namespace Host1x { class Host1x; } // namespace Host1x } // namespace Tegra namespace Service::Nvidia::NvCore { /** * @brief The nvmap core class holds the global state for nvmap and provides methods to manage * handles */ class NvMap { public: /** * @brief A handle to a contiguous block of memory in an application's address space */ struct Handle { std::mutex mutex; u64 align{}; //!< The alignment to use when pinning the handle onto the SMMU u64 size; //!< Page-aligned size of the memory the handle refers to u64 aligned_size; //!< `align`-aligned size of the memory the handle refers to u64 orig_size; //!< Original unaligned size of the memory this handle refers to s32 dupes{1}; //!< How many guest references there are to this handle s32 internal_dupes{0}; //!< How many emulator-internal references there are to this handle using Id = u32; Id id; //!< A globally unique identifier for this handle s32 pins{}; u32 pin_virt_address{}; std::optional>::iterator> unmap_queue_entry{}; union Flags { u32 raw; BitField<0, 1, u32> map_uncached; //!< If the handle should be mapped as uncached BitField<2, 1, u32> keep_uncached_after_free; //!< Only applicable when the handle was //!< allocated with a fixed address BitField<4, 1, u32> _unk0_; //!< Passed to IOVMM for pins } flags{}; static_assert(sizeof(Flags) == sizeof(u32)); u64 address{}; //!< The memory location in the guest's AS that this handle corresponds to, //!< this can also be in the nvdrv tmem bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC //!< call u8 kind{}; //!< Used for memory compression bool allocated{}; //!< If the handle has been allocated with `Alloc` u64 dma_map_addr{}; //! remove me after implementing pinning. Handle(u64 size, Id id); /** * @brief Sets up the handle with the given memory config, can allocate memory from the tmem * if a 0 address is passed */ [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress); /** * @brief Increases the dupe counter of the handle for the given session */ [[nodiscard]] NvResult Duplicate(bool internal_session); /** * @brief Obtains a pointer to the handle's memory and marks the handle it as having been * mapped */ u8* GetPointer() { if (!address) { return nullptr; } is_shared_mem_mapped = true; return reinterpret_cast(address); } }; /** * @brief Encapsulates the result of a FreeHandle operation */ struct FreeInfo { u64 address; //!< Address the handle referred to before deletion u64 size; //!< Page-aligned handle size bool was_uncached; //!< If the handle was allocated as uncached bool can_unlock; //!< If the address region is ready to be unlocked }; explicit NvMap(Tegra::Host1x::Host1x& host1x); /** * @brief Creates an unallocated handle of the given size */ [[nodiscard]] NvResult CreateHandle(u64 size, std::shared_ptr& result_out); std::shared_ptr GetHandle(Handle::Id handle); VAddr GetHandleAddress(Handle::Id handle); /** * @brief Maps a handle into the SMMU address space * @note This operation is refcounted, the number of calls to this must eventually match the * number of calls to `UnpinHandle` * @return The SMMU virtual address that the handle has been mapped to */ u32 PinHandle(Handle::Id handle); /** * @brief When this has been called an equal number of times to `PinHandle` for the supplied * handle it will be added to a list of handles to be freed when necessary */ void UnpinHandle(Handle::Id handle); /** * @brief Tries to duplicate a handle */ void DuplicateHandle(Handle::Id handle, bool internal_session = false); /** * @brief Tries to free a handle and remove a single dupe * @note If a handle has no dupes left and has no other users a FreeInfo struct will be returned * describing the prior state of the handle */ std::optional FreeHandle(Handle::Id handle, bool internal_session); private: std::list> unmap_queue{}; std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue` std::unordered_map> handles{}; //!< Main owning map of handles std::mutex handles_lock; //!< Protects access to `handles` static constexpr u32 HandleIdIncrement{ 4}; //!< Each new handle ID is an increment of 4 from the previous std::atomic next_handle_id{HandleIdIncrement}; Tegra::Host1x::Host1x& host1x; void AddHandle(std::shared_ptr handle); /** * @brief Unmaps and frees the SMMU memory region a handle is mapped to * @note Both `unmap_queue_lock` and `handle_description.mutex` MUST be locked when calling this */ void UnmapHandle(Handle& handle_description); /** * @brief Removes a handle from the map taking its dupes into account * @note handle_description.mutex MUST be locked when calling this * @return If the handle was removed from the map */ bool TryRemoveHandle(const Handle& handle_description); }; } // namespace Service::Nvidia::NvCore