mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-27 05:04:17 +01: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
78356fa789
commit
c97f5a9315
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
|
// SPDX-License-Identifier: MIT OR MPL-2.0
|
||||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
// 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"
|
#include "nvmap.h"
|
||||||
|
|
||||||
namespace skyline::service::nvdrv::device {
|
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) {}
|
PosixResult NvMap::Create(In<u32> size, Out<NvMapCore::Handle::Id> handle) {
|
||||||
|
auto h{core.nvMap.CreateHandle(util::AlignUp(size, PAGE_SIZE))};
|
||||||
NvStatus NvMap::Create(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
if (h) {
|
||||||
struct Data {
|
(*h)->origSize = size; // Orig size is the unaligned size
|
||||||
u32 size; // In
|
handle = (*h)->id;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NvStatus NvMap::FromId(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
return h;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state.logger->Warn("Handle not found for ID: 0x{:X}", data.id);
|
PosixResult NvMap::FromId(In<NvMapCore::Handle::Id> id, Out<NvMapCore::Handle::Id> handle) {
|
||||||
return NvStatus::BadValue;
|
// 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) {
|
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) {
|
||||||
struct Data {
|
if (!handle) [[unlikely]]
|
||||||
u32 handle; // In
|
return PosixResult::InvalidArgument;
|
||||||
u32 heapMask; // In
|
|
||||||
u32 flags; // In
|
|
||||||
u32 align; // In
|
|
||||||
u8 kind; // In
|
|
||||||
u8 _pad0_[7];
|
|
||||||
u8 *ptr; // InOut
|
|
||||||
} &data = buffer.as<Data>();
|
|
||||||
|
|
||||||
try {
|
if (!std::ispow2(align)) [[unlikely]]
|
||||||
auto object{GetObject(data.handle)};
|
return PosixResult::InvalidArgument;
|
||||||
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;
|
|
||||||
|
|
||||||
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);
|
// Force page size alignment at a minimum
|
||||||
return NvStatus::Success;
|
if (align < PAGE_SIZE) [[unlikely]]
|
||||||
} catch (const std::out_of_range &) {
|
align = PAGE_SIZE;
|
||||||
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.handle);
|
|
||||||
return NvStatus::BadParameter;
|
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) {
|
PosixResult NvMap::Free(In<NvMapCore::Handle::Id> handle, Out<u64> address, Out<u32> size, Out<NvMapCore::Handle::Flags> flags) {
|
||||||
struct Data {
|
if (!handle) [[unlikely]]
|
||||||
u32 handle; // In
|
return PosixResult::Success;
|
||||||
u32 _pad0_;
|
|
||||||
u8 *ptr; // Out
|
|
||||||
u32 size; // Out
|
|
||||||
u32 flags; // Out
|
|
||||||
} &data = buffer.as<Data>();
|
|
||||||
|
|
||||||
std::unique_lock lock(mapMutex);
|
if (auto freeInfo{core.nvMap.FreeHandle(handle, ctx.internalSession)}) {
|
||||||
try {
|
address = freeInfo->address;
|
||||||
auto &object{maps.at(data.handle - 1)};
|
size = static_cast<u32>(freeInfo->size);
|
||||||
if (object.use_count() > 1) {
|
flags = NvMapCore::Handle::Flags{ .mapUncached = freeInfo->wasUncached };
|
||||||
data.ptr = object->ptr;
|
|
||||||
data.flags = 0x0;
|
|
||||||
} else {
|
|
||||||
data.ptr = nullptr;
|
|
||||||
data.flags = 0x1; // Not free yet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data.size = object->size;
|
return PosixResult::Success;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NvStatus NvMap::Param(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
PosixResult NvMap::Param(In<NvMapCore::Handle::Id> handle, In<HandleParameterType> param, Out<u32> result) {
|
||||||
// https://android.googlesource.com/kernel/tegra/+/refs/heads/android-tegra-flounder-3.10-marshmallow/include/linux/nvmap.h#102
|
if (!handle)
|
||||||
enum class Parameter : u32 {
|
return PosixResult::InvalidArgument;
|
||||||
Size = 1,
|
|
||||||
Alignment = 2,
|
|
||||||
Base = 3,
|
|
||||||
HeapMask = 4,
|
|
||||||
Kind = 5,
|
|
||||||
Compr = 6,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Data {
|
auto h{core.nvMap.GetHandle(handle)};
|
||||||
u32 handle; // In
|
if (!h) [[unlikely]]
|
||||||
Parameter parameter; // In
|
return PosixResult::InvalidArgument;
|
||||||
u32 result; // Out
|
|
||||||
} &data = buffer.as<Data>();
|
|
||||||
|
|
||||||
try {
|
switch (param) {
|
||||||
auto object{GetObject(data.handle)};
|
case HandleParameterType::Size:
|
||||||
|
result = h->origSize;
|
||||||
switch (data.parameter) {
|
return PosixResult::Success;
|
||||||
case Parameter::Size:
|
case HandleParameterType::Alignment:
|
||||||
data.result = object->size;
|
result = h->align;
|
||||||
break;
|
return PosixResult::Success;
|
||||||
|
case HandleParameterType::Base:
|
||||||
case Parameter::Alignment:
|
result = -static_cast<i32>(PosixResult::InvalidArgument);
|
||||||
data.result = object->align;
|
return PosixResult::Success;
|
||||||
break;
|
case HandleParameterType::Heap:
|
||||||
|
if (h->allocated)
|
||||||
case Parameter::HeapMask:
|
result = 0x40000000;
|
||||||
data.result = object->heapMask;
|
else
|
||||||
break;
|
result = 0;
|
||||||
|
|
||||||
case Parameter::Kind:
|
|
||||||
data.result = object->kind;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Parameter::Compr:
|
|
||||||
data.result = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
return PosixResult::Success;
|
||||||
|
case HandleParameterType::Kind:
|
||||||
|
result = h->kind;
|
||||||
|
return PosixResult::Success;
|
||||||
|
case HandleParameterType::IsSharedMemMapped:
|
||||||
|
result = h->isSharedMemMapped;
|
||||||
|
return PosixResult::Success;
|
||||||
default:
|
default:
|
||||||
state.logger->Warn("Parameter not implemented: 0x{:X}", data.parameter);
|
return PosixResult::InvalidArgument;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NvStatus NvMap::GetId(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
PosixResult NvMap::GetId(Out<NvMapCore::Handle::Id> id, In<NvMapCore::Handle::Id> handle) {
|
||||||
struct Data {
|
// See the comment in FromId for extra info on this function
|
||||||
u32 id; // Out
|
if (!handle) [[unlikely]]
|
||||||
u32 handle; // In
|
return PosixResult::InvalidArgument;
|
||||||
} &data = buffer.as<Data>();
|
|
||||||
|
|
||||||
try {
|
auto h{core.nvMap.GetHandle(handle)};
|
||||||
data.id = GetObject(data.handle)->id;
|
if (!h) [[unlikely]]
|
||||||
state.logger->Debug("Handle: 0x{:X} -> ID: 0x{:X}", data.handle, data.id);
|
return PosixResult::NotPermitted; // This will always return EPERM irrespective of if the handle exists or not
|
||||||
return NvStatus::Success;
|
|
||||||
} catch (const std::out_of_range &) {
|
id = h->id;
|
||||||
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.handle);
|
return PosixResult::Success;
|
||||||
return NvStatus::BadParameter;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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
|
// SPDX-License-Identifier: MIT OR MPL-2.0
|
||||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -7,91 +7,60 @@
|
|||||||
|
|
||||||
namespace skyline::service::nvdrv::device {
|
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
|
* @url https://android.googlesource.com/kernel/tegra/+/refs/heads/android-tegra-flounder-3.10-marshmallow/include/linux/nvmap.h
|
||||||
*/
|
*/
|
||||||
class NvMap : public NvDevice {
|
class NvMap : public NvDevice {
|
||||||
public:
|
public:
|
||||||
/**
|
using NvMapCore = core::NvMap;
|
||||||
* @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
|
|
||||||
|
|
||||||
enum class Status {
|
enum class HandleParameterType : u32 {
|
||||||
Created, //!< The object has been created but memory has not been allocated
|
Size = 1,
|
||||||
Allocated //!< The object has been allocated
|
Alignment = 2,
|
||||||
} status{Status::Created}; //!< This holds the status of the object
|
Base = 3,
|
||||||
|
Heap = 4,
|
||||||
NvMapObject(u32 id, u32 size);
|
Kind = 5,
|
||||||
|
IsSharedMemMapped = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_mutex mapMutex; //!< Synchronizes mutations and accesses of the mappings
|
NvMap(const DeviceState &state, Core &core, const SessionContext &ctx);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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
|
* @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
|
* @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
|
* @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
|
* @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
|
* @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
|
* @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(
|
PosixResult Ioctl(IoctlDescriptor cmd, span<u8> buffer) override;
|
||||||
NVFUNC(0x0101, NvMap, Create),
|
|
||||||
NVFUNC(0x0103, NvMap, FromId),
|
|
||||||
NVFUNC(0x0104, NvMap, Alloc),
|
|
||||||
NVFUNC(0x0105, NvMap, Free),
|
|
||||||
NVFUNC(0x0109, NvMap, Param),
|
|
||||||
NVFUNC(0x010E, NvMap, GetId)
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user