skyline/app/src/main/cpp/skyline/services/nvdrv/core/nvmap.h
2021-09-21 21:51:53 +01:00

116 lines
4.6 KiB
C++

// SPDX-License-Identifier: MIT OR MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common.h>
#include <services/common/result.h>
namespace skyline::service::nvdrv::core {
/**
* @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 alignedSize; //!< `align`-aligned size of the memory the handle refers to
u64 origSize; //!< Original unaligned size of the memory this handle refers to
i32 dupes{1}; //!< How many guest references there are to this handle
i32 internalDupes{0}; //!< How many emulator-internal references there are to this handle
using Id = u32;
Id id; //!< A globally unique identifier for this handle
struct Flags {
bool mapUncached : 1; //!< If the handle should be mapped as uncached
bool _pad0_ : 1;
bool keepUncachedAfterFree : 1; //!< Only applicable when the handle was allocated with a fixed address
bool _pad1_ : 1;
bool _unk0_ : 1; //!< Passed to IOVMM for pins
u32 _pad2_ : 27;
} 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 isSharedMemMapped{}; //!< 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`
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]] PosixResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress);
/**
* @brief Increases the dupe counter of the handle for the given session
*/
[[nodiscard]] PosixResult Duplicate(bool internalSession);
/**
* @brief Obtains a pointer to the handle's memory and marks the handle it as having been mapped
*/
u8 *GetPointer() {
if (!address)
throw exception("Cannot get a pointer to the memory of an unallocated handle!");
isSharedMemMapped = true;
return reinterpret_cast<u8 *>(address);
}
};
private:
const DeviceState &state;
std::unordered_map<Handle::Id, std::shared_ptr<Handle>> handles; //!< Main owning map of handles
std::mutex handlesLock; //!< Protects access to `handles`
static constexpr u32 HandleIdIncrement{4}; //!< Each new handle ID is an increment of 4 from the previous
std::atomic<u32> nextHandleId{HandleIdIncrement};
void AddHandle(std::shared_ptr<Handle> handle);
/**
* @brief Removes a handle from the map taking its dupes into account
* @note h->mutex MUST be locked when calling this
* @return If the handle was removed from the map
*/
bool TryRemoveHandle(const std::shared_ptr<Handle> &h);
public:
/**
* @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 wasUncached; //!< If the handle was allocated as uncached
};
NvMap(const DeviceState &state);
/**
* @brief Creates an unallocated handle of the given size
*/
[[nodiscard]] PosixResultValue<std::shared_ptr<Handle>> CreateHandle(u64 size);
std::shared_ptr<Handle> GetHandle(Handle::Id handle);
/**
* @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<FreeInfo> FreeHandle(Handle::Id handle, bool internalSession);
};
}