mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-27 16:15:30 +03:00
Entirely rewrite nvmap to separate global and per device state.
This will be required later for NVDEC/SMMU support and fixes many significant issues in the previous implementation. Based off of my 2.0.0/12.0.0 nvdrv REs.
This commit is contained in:
parent
d159605caf
commit
5123be6604
144
app/src/main/cpp/skyline/services/nvdrv/core/nvmap.cpp
Normal file
144
app/src/main/cpp/skyline/services/nvdrv/core/nvmap.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
// SPDX-License-Identifier: MIT OR MPL-2.0
|
||||
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include "nvmap.h"
|
||||
|
||||
namespace skyline::service::nvdrv::core {
|
||||
NvMap::Handle::Handle(u64 size, Id id) : size(size), alignedSize(size), origSize(size), id(id) {}
|
||||
|
||||
PosixResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) {
|
||||
std::scoped_lock lock(mutex);
|
||||
|
||||
// Handles cannot be allocated twice
|
||||
if (allocated) [[unlikely]]
|
||||
return PosixResult::NotPermitted;
|
||||
|
||||
flags = pFlags;
|
||||
kind = pKind;
|
||||
align = pAlign < PAGE_SIZE ? PAGE_SIZE : pAlign;
|
||||
|
||||
// This flag is only applicable for handles with an address passed
|
||||
if (pAddress)
|
||||
flags.keepUncachedAfterFree = false;
|
||||
else
|
||||
throw exception("Mapping nvmap handles without a cpu side address is unimplemented!");
|
||||
|
||||
size = util::AlignUp(size, PAGE_SIZE);
|
||||
alignedSize = util::AlignUp(size, align);
|
||||
address = pAddress;
|
||||
|
||||
// TODO: pin init
|
||||
|
||||
allocated = true;
|
||||
|
||||
return PosixResult::Success;
|
||||
}
|
||||
|
||||
PosixResult NvMap::Handle::Duplicate(bool internalSession) {
|
||||
// Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS)
|
||||
if (!allocated) [[unlikely]]
|
||||
return PosixResult::InvalidArgument;
|
||||
|
||||
std::scoped_lock lock(mutex);
|
||||
|
||||
// If we internally use FromId the duplication tracking of handles won't work accurately due to us not implementing
|
||||
// per-process handle refs.
|
||||
if (internalSession)
|
||||
internalDupes++;
|
||||
else
|
||||
dupes++;
|
||||
|
||||
return PosixResult::Success;
|
||||
}
|
||||
|
||||
NvMap::NvMap(const DeviceState &state) : state(state) {}
|
||||
|
||||
void NvMap::AddHandle(std::shared_ptr<Handle> handle) {
|
||||
std::scoped_lock lock(handlesLock);
|
||||
|
||||
handles.emplace(handle->id, std::move(handle));
|
||||
}
|
||||
|
||||
bool NvMap::TryRemoveHandle(const std::shared_ptr<Handle> &h) {
|
||||
// No dupes left, we can remove from handle map
|
||||
if (h->dupes == 0 && h->internalDupes == 0) {
|
||||
std::scoped_lock lock(handlesLock);
|
||||
|
||||
auto it{handles.find(h->id)};
|
||||
if (it != handles.end())
|
||||
handles.erase(it);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
PosixResultValue<std::shared_ptr<NvMap::Handle>> NvMap::CreateHandle(u64 size) {
|
||||
if (!size) [[unlikely]]
|
||||
return PosixResult::InvalidArgument;
|
||||
|
||||
u32 id{nextHandleId.fetch_add(HandleIdIncrement, std::memory_order_relaxed)};
|
||||
auto h{std::make_shared<Handle>(size, id)};
|
||||
AddHandle(h);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) {
|
||||
std::scoped_lock lock(handlesLock);
|
||||
|
||||
try {
|
||||
return handles.at(handle);
|
||||
} catch (std::out_of_range &e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool internalSession) {
|
||||
std::weak_ptr<Handle> hWeak{GetHandle(handle)};
|
||||
FreeInfo freeInfo;
|
||||
|
||||
// We use a weak ptr here so we can tell when the handle has been freed and report that back to guest
|
||||
if (auto h = hWeak.lock()) {
|
||||
if (!h) [[unlikely]]
|
||||
return std::nullopt;
|
||||
|
||||
std::scoped_lock lock(h->mutex);
|
||||
|
||||
if (internalSession) {
|
||||
if (--h->internalDupes < 0)
|
||||
state.logger->Warn("Internal duplicate count inbalance detected!");
|
||||
} else {
|
||||
if (--h->dupes < 0) {
|
||||
state.logger->Warn("User duplicate count inbalance detected!");
|
||||
} else if (h->dupes == 0) {
|
||||
// TODO: unpin
|
||||
}
|
||||
}
|
||||
|
||||
// Try to remove the shared ptr to the handle from the map, if nothing else is using the handle
|
||||
// then it will now be freed when `h` goes out of scope
|
||||
if (TryRemoveHandle(h))
|
||||
state.logger->Debug("Removed nvmap handle: {}", handle);
|
||||
else
|
||||
state.logger->Debug("Tried to free nvmap handle: {} but didn't as it still has duplicates", handle);
|
||||
|
||||
freeInfo = {
|
||||
.address = h->address,
|
||||
.size = h->size,
|
||||
.wasUncached = h->flags.mapUncached,
|
||||
};
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Handle hasn't been freed from memory, set address to 0 to mark that the handle wasn't freed
|
||||
if (!hWeak.expired()) {
|
||||
state.logger->Debug("nvmap handle: {} wasn't freed as it is still in use", handle);
|
||||
freeInfo.address = 0;
|
||||
}
|
||||
|
||||
return freeInfo;
|
||||
}
|
||||
}
|
116
app/src/main/cpp/skyline/services/nvdrv/core/nvmap.h
Normal file
116
app/src/main/cpp/skyline/services/nvdrv/core/nvmap.h
Normal file
@ -0,0 +1,116 @@
|
||||
// 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:
|
||||
NvMap(const DeviceState &state);
|
||||
|
||||
/**
|
||||
* @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
|
||||
};
|
||||
|
||||
/**
|
||||
* @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
|
||||
* 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);
|
||||
};
|
||||
}
|
@ -1,172 +1,134 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
// SPDX-License-Identifier: MIT OR MPL-2.0
|
||||
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <services/nvdrv/devices/deserialisation/deserialisation.h>
|
||||
#include <services/nvdrv/core/nvmap.h>
|
||||
#include "nvmap.h"
|
||||
|
||||
namespace skyline::service::nvdrv::device {
|
||||
NvMap::NvMapObject::NvMapObject(u32 id, u32 size) : id(id), size(size) {}
|
||||
NvMap::NvMap(const DeviceState &state, Core &core, const SessionContext &ctx) : NvDevice(state, core, ctx) {}
|
||||
|
||||
NvMap::NvMap(const DeviceState &state) : NvDevice(state) {}
|
||||
|
||||
NvStatus NvMap::Create(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
||||
struct Data {
|
||||
u32 size; // In
|
||||
u32 handle; // Out
|
||||
} &data = buffer.as<Data>();
|
||||
|
||||
std::unique_lock lock(mapMutex);
|
||||
maps.push_back(std::make_shared<NvMapObject>(idIndex++, data.size));
|
||||
data.handle = maps.size();
|
||||
|
||||
state.logger->Debug("Size: 0x{:X} -> Handle: 0x{:X}", data.size, data.handle);
|
||||
return NvStatus::Success;
|
||||
PosixResult NvMap::Create(In<u32> size, Out<NvMapCore::Handle::Id> handle) {
|
||||
auto h{core.nvMap.CreateHandle(util::AlignUp(size, PAGE_SIZE))};
|
||||
if (h) {
|
||||
(*h)->origSize = size; // Orig size is the unaligned size
|
||||
handle = (*h)->id;
|
||||
}
|
||||
|
||||
NvStatus NvMap::FromId(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
||||
struct Data {
|
||||
u32 id; // In
|
||||
u32 handle; // Out
|
||||
} &data = buffer.as<Data>();
|
||||
|
||||
std::shared_lock lock(mapMutex);
|
||||
for (auto it{maps.begin()}; it < maps.end(); it++) {
|
||||
if ((*it)->id == data.id) {
|
||||
data.handle = (it - maps.begin()) + 1;
|
||||
state.logger->Debug("ID: 0x{:X} -> Handle: 0x{:X}", data.id, data.handle);
|
||||
return NvStatus::Success;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
state.logger->Warn("Handle not found for ID: 0x{:X}", data.id);
|
||||
return NvStatus::BadValue;
|
||||
PosixResult NvMap::FromId(In<NvMapCore::Handle::Id> id, Out<NvMapCore::Handle::Id> handle) {
|
||||
// Handles and IDs are always the same value in nvmap however IDs can be used globally given the right permissions.
|
||||
// Since we don't plan on ever supporting multiprocess we can skip implementing handle refs and so this function
|
||||
// just does simple validation and passes through the handle id.
|
||||
if (!id) [[unlikely]]
|
||||
return PosixResult::InvalidArgument;
|
||||
|
||||
auto h{core.nvMap.GetHandle(id)};
|
||||
if (!h) [[unlikely]]
|
||||
return PosixResult::InvalidArgument;
|
||||
|
||||
return h->Duplicate(ctx.internalSession);
|
||||
}
|
||||
|
||||
NvStatus NvMap::Alloc(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
||||
struct Data {
|
||||
u32 handle; // In
|
||||
u32 heapMask; // In
|
||||
u32 flags; // In
|
||||
u32 align; // In
|
||||
u8 kind; // In
|
||||
u8 _pad0_[7];
|
||||
u8 *ptr; // InOut
|
||||
} &data = buffer.as<Data>();
|
||||
PosixResult NvMap::Alloc(In<NvMapCore::Handle::Id> handle, In<u32> heapMask, In<NvMapCore::Handle::Flags> flags, InOut<u32> align, In<u8> kind, In<u64> address) {
|
||||
if (!handle) [[unlikely]]
|
||||
return PosixResult::InvalidArgument;
|
||||
|
||||
try {
|
||||
auto object{GetObject(data.handle)};
|
||||
object->heapMask = data.heapMask;
|
||||
object->flags = data.flags;
|
||||
object->align = data.align;
|
||||
object->kind = data.kind;
|
||||
object->ptr = data.ptr;
|
||||
object->status = NvMapObject::Status::Allocated;
|
||||
if (!std::ispow2(align)) [[unlikely]]
|
||||
return PosixResult::InvalidArgument;
|
||||
|
||||
state.logger->Debug("Handle: 0x{:X}, HeapMask: 0x{:X}, Flags: {}, Align: 0x{:X}, Kind: {}, Pointer: 0x{:X}", data.handle, data.heapMask, data.flags, data.align, data.kind, data.ptr);
|
||||
return NvStatus::Success;
|
||||
} catch (const std::out_of_range &) {
|
||||
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.handle);
|
||||
return NvStatus::BadParameter;
|
||||
}
|
||||
// Force page size alignment at a minimum
|
||||
if (align < PAGE_SIZE) [[unlikely]]
|
||||
align = PAGE_SIZE;
|
||||
|
||||
auto h{core.nvMap.GetHandle(handle)};
|
||||
if (!h) [[unlikely]]
|
||||
return PosixResult::InvalidArgument;
|
||||
|
||||
return h->Alloc(flags, align, kind, address);
|
||||
}
|
||||
|
||||
NvStatus NvMap::Free(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
||||
struct Data {
|
||||
u32 handle; // In
|
||||
u32 _pad0_;
|
||||
u8 *ptr; // Out
|
||||
u32 size; // Out
|
||||
u32 flags; // Out
|
||||
} &data = buffer.as<Data>();
|
||||
PosixResult NvMap::Free(In<NvMapCore::Handle::Id> handle, Out<u64> address, Out<u32> size, Out<NvMapCore::Handle::Flags> flags) {
|
||||
if (!handle) [[unlikely]]
|
||||
return PosixResult::Success;
|
||||
|
||||
std::unique_lock lock(mapMutex);
|
||||
try {
|
||||
auto &object{maps.at(data.handle - 1)};
|
||||
if (object.use_count() > 1) {
|
||||
data.ptr = object->ptr;
|
||||
data.flags = 0x0;
|
||||
} else {
|
||||
data.ptr = nullptr;
|
||||
data.flags = 0x1; // Not free yet
|
||||
if (auto freeInfo{core.nvMap.FreeHandle(handle, ctx.internalSession)}) {
|
||||
address = freeInfo->address;
|
||||
size = static_cast<u32>(freeInfo->size);
|
||||
flags = NvMapCore::Handle::Flags{ .mapUncached = freeInfo->wasUncached };
|
||||
}
|
||||
|
||||
data.size = object->size;
|
||||
object = nullptr;
|
||||
|
||||
state.logger->Debug("Handle: 0x{:X} -> Pointer: 0x{:X}, Size: 0x{:X}, Flags: 0x{:X}", data.handle, data.ptr, data.size, data.flags);
|
||||
return NvStatus::Success;
|
||||
} catch (const std::out_of_range &) {
|
||||
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.handle);
|
||||
return NvStatus::BadParameter;
|
||||
}
|
||||
return PosixResult::Success;
|
||||
}
|
||||
|
||||
NvStatus NvMap::Param(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
||||
// https://android.googlesource.com/kernel/tegra/+/refs/heads/android-tegra-flounder-3.10-marshmallow/include/linux/nvmap.h#102
|
||||
enum class Parameter : u32 {
|
||||
Size = 1,
|
||||
Alignment = 2,
|
||||
Base = 3,
|
||||
HeapMask = 4,
|
||||
Kind = 5,
|
||||
Compr = 6,
|
||||
};
|
||||
PosixResult NvMap::Param(In<NvMapCore::Handle::Id> handle, In<HandleParameterType> param, Out<u32> result) {
|
||||
if (!handle)
|
||||
return PosixResult::InvalidArgument;
|
||||
|
||||
struct Data {
|
||||
u32 handle; // In
|
||||
Parameter parameter; // In
|
||||
u32 result; // Out
|
||||
} &data = buffer.as<Data>();
|
||||
auto h{core.nvMap.GetHandle(handle)};
|
||||
if (!h) [[unlikely]]
|
||||
return PosixResult::InvalidArgument;
|
||||
|
||||
try {
|
||||
auto object{GetObject(data.handle)};
|
||||
|
||||
switch (data.parameter) {
|
||||
case Parameter::Size:
|
||||
data.result = object->size;
|
||||
break;
|
||||
|
||||
case Parameter::Alignment:
|
||||
data.result = object->align;
|
||||
break;
|
||||
|
||||
case Parameter::HeapMask:
|
||||
data.result = object->heapMask;
|
||||
break;
|
||||
|
||||
case Parameter::Kind:
|
||||
data.result = object->kind;
|
||||
break;
|
||||
|
||||
case Parameter::Compr:
|
||||
data.result = 0;
|
||||
break;
|
||||
switch (param) {
|
||||
case HandleParameterType::Size:
|
||||
result = h->origSize;
|
||||
return PosixResult::Success;
|
||||
case HandleParameterType::Alignment:
|
||||
result = h->align;
|
||||
return PosixResult::Success;
|
||||
case HandleParameterType::Base:
|
||||
result = -static_cast<i32>(PosixResult::InvalidArgument);
|
||||
return PosixResult::Success;
|
||||
case HandleParameterType::Heap:
|
||||
if (h->allocated)
|
||||
result = 0x40000000;
|
||||
else
|
||||
result = 0;
|
||||
|
||||
return PosixResult::Success;
|
||||
case HandleParameterType::Kind:
|
||||
result = h->kind;
|
||||
return PosixResult::Success;
|
||||
case HandleParameterType::IsSharedMemMapped:
|
||||
result = h->isSharedMemMapped;
|
||||
return PosixResult::Success;
|
||||
default:
|
||||
state.logger->Warn("Parameter not implemented: 0x{:X}", data.parameter);
|
||||
return NvStatus::NotImplemented;
|
||||
}
|
||||
|
||||
state.logger->Debug("Handle: 0x{:X}, Parameter: {} -> Result: 0x{:X}", data.handle, data.parameter, data.result);
|
||||
return NvStatus::Success;
|
||||
} catch (const std::out_of_range &) {
|
||||
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.handle);
|
||||
return NvStatus::BadParameter;
|
||||
return PosixResult::InvalidArgument;
|
||||
}
|
||||
}
|
||||
|
||||
NvStatus NvMap::GetId(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
||||
struct Data {
|
||||
u32 id; // Out
|
||||
u32 handle; // In
|
||||
} &data = buffer.as<Data>();
|
||||
PosixResult NvMap::GetId(Out<NvMapCore::Handle::Id> id, In<NvMapCore::Handle::Id> handle) {
|
||||
// See the comment in FromId for extra info on this function
|
||||
if (!handle) [[unlikely]]
|
||||
return PosixResult::InvalidArgument;
|
||||
|
||||
try {
|
||||
data.id = GetObject(data.handle)->id;
|
||||
state.logger->Debug("Handle: 0x{:X} -> ID: 0x{:X}", data.handle, data.id);
|
||||
return NvStatus::Success;
|
||||
} catch (const std::out_of_range &) {
|
||||
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.handle);
|
||||
return NvStatus::BadParameter;
|
||||
}
|
||||
auto h{core.nvMap.GetHandle(handle)};
|
||||
if (!h) [[unlikely]]
|
||||
return PosixResult::NotPermitted; // This will always return EPERM irrespective of if the handle exists or not
|
||||
|
||||
id = h->id;
|
||||
return PosixResult::Success;
|
||||
}
|
||||
|
||||
#include "deserialisation/macro_def.h"
|
||||
static constexpr u32 NvMapMagic{1};
|
||||
|
||||
IOCTL_HANDLER_FUNC(NvMap, ({
|
||||
IOCTL_CASE_ARGS(INOUT, SIZE(0x8), MAGIC(NvMapMagic), FUNC(0x1),
|
||||
Create, ARGS(In<u32>, Out<NvMapCore::Handle::Id>))
|
||||
IOCTL_CASE_ARGS(INOUT, SIZE(0x8), MAGIC(NvMapMagic), FUNC(0x3),
|
||||
FromId, ARGS(In<NvMapCore::Handle::Id>, Out<NvMapCore::Handle::Id>))
|
||||
IOCTL_CASE_ARGS(INOUT, SIZE(0x20), MAGIC(NvMapMagic), FUNC(0x4),
|
||||
Alloc, ARGS(In<NvMapCore::Handle::Id>, In<u32>, In<NvMapCore::Handle::Flags>, InOut<u32>, In<u8>, Pad<u8, 0x7>, In<u64>))
|
||||
IOCTL_CASE_ARGS(INOUT, SIZE(0x18), MAGIC(NvMapMagic), FUNC(0x5),
|
||||
Free, ARGS(In<NvMapCore::Handle::Id>, Pad<u32>, Out<u64>, Out<u32>, Out<NvMapCore::Handle::Flags>))
|
||||
IOCTL_CASE_ARGS(INOUT, SIZE(0xC), MAGIC(NvMapMagic), FUNC(0x9),
|
||||
Param, ARGS(In<NvMapCore::Handle::Id>, In<HandleParameterType>, Out<u32>))
|
||||
IOCTL_CASE_ARGS(INOUT, SIZE(0x8), MAGIC(NvMapMagic), FUNC(0xE),
|
||||
GetId, ARGS(Out<NvMapCore::Handle::Id>, In<NvMapCore::Handle::Id>))
|
||||
}))
|
||||
#include "deserialisation/macro_undef.h"
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
// SPDX-License-Identifier: MIT OR MPL-2.0
|
||||
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
@ -7,91 +7,60 @@
|
||||
|
||||
namespace skyline::service::nvdrv::device {
|
||||
/**
|
||||
* @brief NvMap (/dev/nvmap) is used to map certain CPU memory as GPU memory (https://switchbrew.org/wiki/NV_services)
|
||||
* @brief NvMap (/dev/nvmap) is used to keep track of buffers and map them onto the SMMU (https://switchbrew.org/wiki/NV_services)
|
||||
* @url https://android.googlesource.com/kernel/tegra/+/refs/heads/android-tegra-flounder-3.10-marshmallow/include/linux/nvmap.h
|
||||
*/
|
||||
class NvMap : public NvDevice {
|
||||
public:
|
||||
/**
|
||||
* @brief NvMapObject is used to hold the state of held objects
|
||||
*/
|
||||
struct NvMapObject {
|
||||
u32 id;
|
||||
u32 size;
|
||||
u8 *ptr{};
|
||||
u32 flags{}; //!< The flag of the memory (0 = Read Only, 1 = Read-Write)
|
||||
u32 align{};
|
||||
u32 heapMask{}; //!< This is set during Alloc and returned during Param
|
||||
u8 kind{}; //!< This is same as heapMask
|
||||
using NvMapCore = core::NvMap;
|
||||
|
||||
enum class Status {
|
||||
Created, //!< The object has been created but memory has not been allocated
|
||||
Allocated //!< The object has been allocated
|
||||
} status{Status::Created}; //!< This holds the status of the object
|
||||
|
||||
NvMapObject(u32 id, u32 size);
|
||||
enum class HandleParameterType : u32 {
|
||||
Size = 1,
|
||||
Alignment = 2,
|
||||
Base = 3,
|
||||
Heap = 4,
|
||||
Kind = 5,
|
||||
IsSharedMemMapped = 6
|
||||
};
|
||||
|
||||
std::shared_mutex mapMutex; //!< Synchronizes mutations and accesses of the mappings
|
||||
std::vector<std::shared_ptr<NvMapObject>> maps;
|
||||
|
||||
u32 idIndex{1}; //!< This is used to keep track of the next ID to allocate
|
||||
|
||||
NvMap(const DeviceState &state);
|
||||
|
||||
std::shared_ptr<NvMapObject> GetObject(u32 handle) {
|
||||
if (handle-- == 0)
|
||||
throw std::out_of_range("0 is an invalid nvmap handle");
|
||||
std::shared_lock lock(mapMutex);
|
||||
auto &object{maps.at(handle)};
|
||||
if (!object)
|
||||
throw std::out_of_range("A freed nvmap handle was requested");
|
||||
return object;
|
||||
}
|
||||
NvMap(const DeviceState &state, Core &core, const SessionContext &ctx);
|
||||
|
||||
/**
|
||||
* @brief Creates an NvMapObject and returns an handle to it
|
||||
* @brief Creates an nvmap handle for the given size
|
||||
* @url https://switchbrew.org/wiki/NV_services#NVMAP_IOC_CREATE
|
||||
*/
|
||||
NvStatus Create(IoctlType type, span<u8> buffer, span<u8> inlineBuffer);
|
||||
PosixResult Create(In<u32> size, Out<NvMapCore::Handle::Id> handle);
|
||||
|
||||
/**
|
||||
* @brief Returns the handle of an NvMapObject from its ID
|
||||
* @brief Creates a new ref to the handle of the given ID
|
||||
* @url https://switchbrew.org/wiki/NV_services#NVMAP_IOC_FROM_ID
|
||||
*/
|
||||
NvStatus FromId(IoctlType type, span<u8> buffer, span<u8> inlineBuffer);
|
||||
PosixResult FromId(In<NvMapCore::Handle::Id> id, Out<NvMapCore::Handle::Id> handle);
|
||||
|
||||
/**
|
||||
* @brief Allocates memory for an NvMapObject
|
||||
* @brief Adds the given backing memory to the nvmap handle
|
||||
* @url https://switchbrew.org/wiki/NV_services#NVMAP_IOC_ALLOC
|
||||
*/
|
||||
NvStatus Alloc(IoctlType type, span<u8> buffer, span<u8> inlineBuffer);
|
||||
PosixResult Alloc(In<NvMapCore::Handle::Id> handle, In<u32> heapMask, In<NvMapCore::Handle::Flags> flags, InOut<u32> align, In<u8> kind, In<u64> address);
|
||||
|
||||
/**
|
||||
* @brief Frees previously allocated memory
|
||||
* @brief Attempts to free a handle and unpin it from SMMU memory
|
||||
* @url https://switchbrew.org/wiki/NV_services#NVMAP_IOC_FREE
|
||||
*/
|
||||
NvStatus Free(IoctlType type, span<u8> buffer, span<u8> inlineBuffer);
|
||||
PosixResult Free(In<NvMapCore::Handle::Id> handle, Out<u64> address, Out<u32> size, Out<NvMapCore::Handle::Flags> flags);
|
||||
|
||||
/**
|
||||
* @brief Returns a particular parameter from an NvMapObject
|
||||
* @brief Returns info about a property of the nvmap handle
|
||||
* @url https://switchbrew.org/wiki/NV_services#NVMAP_IOC_PARAM
|
||||
*/
|
||||
NvStatus Param(IoctlType type, span<u8> buffer, span<u8> inlineBuffer);
|
||||
PosixResult Param(In<NvMapCore::Handle::Id> handle, In<HandleParameterType> param, Out<u32> result);
|
||||
|
||||
/**
|
||||
* @brief Returns the ID of an NvMapObject from its handle
|
||||
* @brief Returns a global ID for the given nvmap handle
|
||||
* @url https://switchbrew.org/wiki/NV_services#NVMAP_IOC_GET_ID
|
||||
*/
|
||||
NvStatus GetId(IoctlType type, span<u8> buffer, span<u8> inlineBuffer);
|
||||
PosixResult GetId(Out<NvMapCore::Handle::Id> id, In<NvMapCore::Handle::Id> handle);
|
||||
|
||||
NVDEVICE_DECL(
|
||||
NVFUNC(0x0101, NvMap, Create),
|
||||
NVFUNC(0x0103, NvMap, FromId),
|
||||
NVFUNC(0x0104, NvMap, Alloc),
|
||||
NVFUNC(0x0105, NvMap, Free),
|
||||
NVFUNC(0x0109, NvMap, Param),
|
||||
NVFUNC(0x010E, NvMap, GetId)
|
||||
)
|
||||
PosixResult Ioctl(IoctlDescriptor cmd, span<u8> buffer) override;
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user