NVDRV: Implement new NvMap
This commit is contained in:
parent
3cbe352c18
commit
de0e8eff42
|
@ -17,7 +17,7 @@ NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress)
|
|||
std::scoped_lock lock(mutex);
|
||||
|
||||
// Handles cannot be allocated twice
|
||||
if (allocated) [[unlikely]]
|
||||
if (allocated)
|
||||
return NvResult::AccessDenied;
|
||||
|
||||
flags = pFlags;
|
||||
|
@ -61,33 +61,34 @@ NvResult NvMap::Handle::Duplicate(bool internal_session) {
|
|||
|
||||
NvMap::NvMap() = default;
|
||||
|
||||
void NvMap::AddHandle(std::shared_ptr<Handle> handleDesc) {
|
||||
void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) {
|
||||
std::scoped_lock lock(handles_lock);
|
||||
|
||||
handles.emplace(handleDesc->id, std::move(handleDesc));
|
||||
handles.emplace(handle_description->id, std::move(handle_description));
|
||||
}
|
||||
|
||||
void NvMap::UnmapHandle(Handle& handleDesc) {
|
||||
void NvMap::UnmapHandle(Handle& handle_description) {
|
||||
// Remove pending unmap queue entry if needed
|
||||
if (handleDesc.unmap_queue_entry) {
|
||||
unmap_queue.erase(*handleDesc.unmap_queue_entry);
|
||||
handleDesc.unmap_queue_entry.reset();
|
||||
if (handle_description.unmap_queue_entry) {
|
||||
unmap_queue.erase(*handle_description.unmap_queue_entry);
|
||||
handle_description.unmap_queue_entry.reset();
|
||||
}
|
||||
|
||||
// Free and unmap the handle from the SMMU
|
||||
/*
|
||||
state.soc->smmu.Unmap(handleDesc.pin_virt_address, static_cast<u32>(handleDesc.aligned_size));
|
||||
smmuAllocator.Free(handleDesc.pin_virt_address, static_cast<u32>(handleDesc.aligned_size));
|
||||
handleDesc.pin_virt_address = 0;
|
||||
state.soc->smmu.Unmap(handle_description.pin_virt_address,
|
||||
static_cast<u32>(handle_description.aligned_size));
|
||||
smmuAllocator.Free(handle_description.pin_virt_address,
|
||||
static_cast<u32>(handle_description.aligned_size)); handle_description.pin_virt_address = 0;
|
||||
*/
|
||||
}
|
||||
|
||||
bool NvMap::TryRemoveHandle(const Handle& handleDesc) {
|
||||
bool NvMap::TryRemoveHandle(const Handle& handle_description) {
|
||||
// No dupes left, we can remove from handle map
|
||||
if (handleDesc.dupes == 0 && handleDesc.internal_dupes == 0) {
|
||||
if (handle_description.dupes == 0 && handle_description.internal_dupes == 0) {
|
||||
std::scoped_lock lock(handles_lock);
|
||||
|
||||
auto it{handles.find(handleDesc.id)};
|
||||
auto it{handles.find(handle_description.id)};
|
||||
if (it != handles.end())
|
||||
handles.erase(it);
|
||||
|
||||
|
@ -102,10 +103,10 @@ NvResult NvMap::CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_ou
|
|||
return NvResult::BadValue;
|
||||
|
||||
u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)};
|
||||
auto handleDesc{std::make_shared<Handle>(size, id)};
|
||||
AddHandle(handleDesc);
|
||||
auto handle_description{std::make_shared<Handle>(size, id)};
|
||||
AddHandle(handle_description);
|
||||
|
||||
result_out = handleDesc;
|
||||
result_out = handle_description;
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
|
@ -118,73 +119,83 @@ std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) {
|
|||
}
|
||||
}
|
||||
|
||||
VAddr NvMap::GetHandleAddress(Handle::Id handle) {
|
||||
std::scoped_lock lock(handles_lock);
|
||||
try {
|
||||
return handles.at(handle)->address;
|
||||
} catch ([[maybe_unused]] std::out_of_range& e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
u32 NvMap::PinHandle(NvMap::Handle::Id handle) {
|
||||
UNIMPLEMENTED_MSG("pinning");
|
||||
return 0;
|
||||
/*
|
||||
auto handleDesc{GetHandle(handle)};
|
||||
if (!handleDesc)
|
||||
auto handle_description{GetHandle(handle)};
|
||||
if (!handle_description)
|
||||
[[unlikely]] return 0;
|
||||
|
||||
std::scoped_lock lock(handleDesc->mutex);
|
||||
if (!handleDesc->pins) {
|
||||
std::scoped_lock lock(handle_description->mutex);
|
||||
if (!handle_description->pins) {
|
||||
// If we're in the unmap queue we can just remove ourselves and return since we're already
|
||||
// mapped
|
||||
{
|
||||
// Lock now to prevent our queue entry from being removed for allocation in-between the
|
||||
// following check and erase
|
||||
std::scoped_lock queueLock(unmap_queue_lock);
|
||||
if (handleDesc->unmap_queue_entry) {
|
||||
unmap_queue.erase(*handleDesc->unmap_queue_entry);
|
||||
handleDesc->unmap_queue_entry.reset();
|
||||
if (handle_description->unmap_queue_entry) {
|
||||
unmap_queue.erase(*handle_description->unmap_queue_entry);
|
||||
handle_description->unmap_queue_entry.reset();
|
||||
|
||||
handleDesc->pins++;
|
||||
return handleDesc->pin_virt_address;
|
||||
handle_description->pins++;
|
||||
return handle_description->pin_virt_address;
|
||||
}
|
||||
}
|
||||
|
||||
// If not then allocate some space and map it
|
||||
u32 address{};
|
||||
while (!(address = smmuAllocator.Allocate(static_cast<u32>(handleDesc->aligned_size)))) {
|
||||
while (!(address =
|
||||
smmuAllocator.Allocate(static_cast<u32>(handle_description->aligned_size)))) {
|
||||
// Free handles until the allocation succeeds
|
||||
std::scoped_lock queueLock(unmap_queue_lock);
|
||||
if (auto freeHandleDesc{unmap_queue.front()}) {
|
||||
// Handles in the unmap queue are guaranteed not to be pinned so don't bother
|
||||
// checking if they are before unmapping
|
||||
std::scoped_lock freeLock(freeHandleDesc->mutex);
|
||||
if (handleDesc->pin_virt_address)
|
||||
if (handle_description->pin_virt_address)
|
||||
UnmapHandle(*freeHandleDesc);
|
||||
} else {
|
||||
LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!");
|
||||
}
|
||||
}
|
||||
|
||||
state.soc->smmu.Map(address, handleDesc->GetPointer(),
|
||||
static_cast<u32>(handleDesc->aligned_size));
|
||||
handleDesc->pin_virt_address = address;
|
||||
state.soc->smmu.Map(address, handle_description->GetPointer(),
|
||||
static_cast<u32>(handle_description->aligned_size));
|
||||
handle_description->pin_virt_address = address;
|
||||
}
|
||||
|
||||
handleDesc->pins++;
|
||||
return handleDesc->pin_virt_address;
|
||||
handle_description->pins++;
|
||||
return handle_description->pin_virt_address;
|
||||
*/
|
||||
}
|
||||
|
||||
void NvMap::UnpinHandle(Handle::Id handle) {
|
||||
UNIMPLEMENTED_MSG("Unpinning");
|
||||
/*
|
||||
auto handleDesc{GetHandle(handle)};
|
||||
if (!handleDesc)
|
||||
auto handle_description{GetHandle(handle)};
|
||||
if (!handle_description)
|
||||
return;
|
||||
|
||||
std::scoped_lock lock(handleDesc->mutex);
|
||||
if (--handleDesc->pins < 0) {
|
||||
std::scoped_lock lock(handle_description->mutex);
|
||||
if (--handle_description->pins < 0) {
|
||||
LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!");
|
||||
} else if (!handleDesc->pins) {
|
||||
} else if (!handle_description->pins) {
|
||||
std::scoped_lock queueLock(unmap_queue_lock);
|
||||
|
||||
// Add to the unmap queue allowing this handle's memory to be freed if needed
|
||||
unmap_queue.push_back(handleDesc);
|
||||
handleDesc->unmap_queue_entry = std::prev(unmap_queue.end());
|
||||
unmap_queue.push_back(handle_description);
|
||||
handle_description->unmap_queue_entry = std::prev(unmap_queue.end());
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -195,39 +206,39 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna
|
|||
|
||||
// We use a weak ptr here so we can tell when the handle has been freed and report that back to
|
||||
// guest
|
||||
if (auto handleDesc = hWeak.lock()) {
|
||||
std::scoped_lock lock(handleDesc->mutex);
|
||||
if (auto handle_description = hWeak.lock()) {
|
||||
std::scoped_lock lock(handle_description->mutex);
|
||||
|
||||
if (internal_session) {
|
||||
if (--handleDesc->internal_dupes < 0)
|
||||
if (--handle_description->internal_dupes < 0)
|
||||
LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!");
|
||||
} else {
|
||||
if (--handleDesc->dupes < 0) {
|
||||
if (--handle_description->dupes < 0) {
|
||||
LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!");
|
||||
} else if (handleDesc->dupes == 0) {
|
||||
} else if (handle_description->dupes == 0) {
|
||||
// Force unmap the handle
|
||||
if (handleDesc->pin_virt_address) {
|
||||
if (handle_description->pin_virt_address) {
|
||||
std::scoped_lock queueLock(unmap_queue_lock);
|
||||
UnmapHandle(*handleDesc);
|
||||
UnmapHandle(*handle_description);
|
||||
}
|
||||
|
||||
handleDesc->pins = 0;
|
||||
handle_description->pins = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 `handleDesc` goes out of scope
|
||||
if (TryRemoveHandle(*handleDesc))
|
||||
LOG_ERROR(Service_NVDRV, "Removed nvmap handle: {}", handle);
|
||||
// handle then it will now be freed when `handle_description` goes out of scope
|
||||
if (TryRemoveHandle(*handle_description))
|
||||
LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle);
|
||||
else
|
||||
LOG_ERROR(Service_NVDRV,
|
||||
LOG_DEBUG(Service_NVDRV,
|
||||
"Tried to free nvmap handle: {} but didn't as it still has duplicates",
|
||||
handle);
|
||||
|
||||
freeInfo = {
|
||||
.address = handleDesc->address,
|
||||
.size = handleDesc->size,
|
||||
.was_uncached = handleDesc->flags.map_uncached.Value() != 0,
|
||||
.address = handle_description->address,
|
||||
.size = handle_description->size,
|
||||
.was_uncached = handle_description->flags.map_uncached.Value() != 0,
|
||||
};
|
||||
} else {
|
||||
return std::nullopt;
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
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);
|
||||
|
||||
/**
|
||||
|
@ -101,16 +103,16 @@ private:
|
|||
|
||||
/**
|
||||
* @brief Unmaps and frees the SMMU memory region a handle is mapped to
|
||||
* @note Both `unmap_queue_lock` and `handleDesc.mutex` MUST be locked when calling this
|
||||
* @note Both `unmap_queue_lock` and `handle_description.mutex` MUST be locked when calling this
|
||||
*/
|
||||
void UnmapHandle(Handle& handleDesc);
|
||||
void UnmapHandle(Handle& handle_description);
|
||||
|
||||
/**
|
||||
* @brief Removes a handle from the map taking its dupes into account
|
||||
* @note handleDesc.mutex MUST be locked when calling this
|
||||
* @note handle_description.mutex MUST be locked when calling this
|
||||
* @return If the handle was removed from the map
|
||||
*/
|
||||
bool TryRemoveHandle(const Handle& handleDesc);
|
||||
bool TryRemoveHandle(const Handle& handle_description);
|
||||
|
||||
public:
|
||||
/**
|
||||
|
@ -131,6 +133,8 @@ public:
|
|||
|
||||
std::shared_ptr<Handle> 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
|
||||
|
|
|
@ -5,15 +5,16 @@
|
|||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/core/nvmap.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvmap.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
nvdisp_disp0::nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_)
|
||||
: nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {}
|
||||
nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
|
||||
: nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
|
||||
nvdisp_disp0::~nvdisp_disp0() = default;
|
||||
|
||||
NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
|
@ -40,7 +41,7 @@ void nvdisp_disp0::OnClose(DeviceFD fd) {}
|
|||
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width,
|
||||
u32 height, u32 stride, android::BufferTransformFlags transform,
|
||||
const Common::Rectangle<int>& crop_rect) {
|
||||
const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
|
||||
const VAddr addr = nvmap.GetHandleAddress(buffer_handle);
|
||||
LOG_TRACE(Service,
|
||||
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
|
||||
addr, offset, width, height, stride, format);
|
||||
|
@ -54,4 +55,9 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat form
|
|||
system.GetPerfStats().BeginSystemFrame();
|
||||
}
|
||||
|
||||
Kernel::KEvent* nvdisp_disp0::QueryEvent(u32 event_id) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Unknown DISP Event {}", event_id);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
|
|
@ -11,13 +11,18 @@
|
|||
#include "core/hle/service/nvflinger/buffer_transform_flags.h"
|
||||
#include "core/hle/service/nvflinger/pixel_format.h"
|
||||
|
||||
namespace Service::Nvidia::NvCore {
|
||||
class Container;
|
||||
class NvMap;
|
||||
} // namespace Service::Nvidia::NvCore
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
class nvmap;
|
||||
|
||||
class nvdisp_disp0 final : public nvdevice {
|
||||
public:
|
||||
explicit nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_);
|
||||
explicit nvdisp_disp0(Core::System& system_, NvCore::Container& core);
|
||||
~nvdisp_disp0() override;
|
||||
|
||||
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
|
@ -35,8 +40,11 @@ public:
|
|||
u32 stride, android::BufferTransformFlags transform,
|
||||
const Common::Rectangle<int>& crop_rect);
|
||||
|
||||
Kernel::KEvent* QueryEvent(u32 event_id) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
NvCore::Container& container;
|
||||
NvCore::NvMap& nvmap;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
|
|
@ -7,15 +7,16 @@
|
|||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/core/nvmap.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvmap.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_)
|
||||
: nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {}
|
||||
nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, NvCore::Container& core)
|
||||
: nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
|
||||
nvhost_as_gpu::~nvhost_as_gpu() = default;
|
||||
|
||||
NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
|
@ -143,7 +144,7 @@ NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& out
|
|||
LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
|
||||
entry.offset, entry.nvmap_handle, entry.pages);
|
||||
|
||||
const auto object{nvmap_dev->GetObject(entry.nvmap_handle)};
|
||||
const auto object{nvmap.GetHandle(entry.nvmap_handle)};
|
||||
if (!object) {
|
||||
LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle);
|
||||
result = NvResult::InvalidState;
|
||||
|
@ -153,7 +154,8 @@ NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& out
|
|||
const auto offset{static_cast<GPUVAddr>(entry.offset) << 0x10};
|
||||
const auto size{static_cast<u64>(entry.pages) << 0x10};
|
||||
const auto map_offset{static_cast<u64>(entry.map_offset) << 0x10};
|
||||
const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)};
|
||||
const auto addr{
|
||||
system.GPU().MemoryManager().Map(object->address + map_offset, offset, size)};
|
||||
|
||||
if (!addr) {
|
||||
LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!");
|
||||
|
@ -176,24 +178,7 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8
|
|||
params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size,
|
||||
params.offset);
|
||||
|
||||
const auto object{nvmap_dev->GetObject(params.nvmap_handle)};
|
||||
if (!object) {
|
||||
LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return NvResult::InvalidState;
|
||||
}
|
||||
|
||||
// The real nvservices doesn't make a distinction between handles and ids, and
|
||||
// object can only have one handle and it will be the same as its id. Assert that this is the
|
||||
// case to prevent unexpected behavior.
|
||||
ASSERT(object->id == params.nvmap_handle);
|
||||
auto& gpu = system.GPU();
|
||||
|
||||
u64 page_size{params.page_size};
|
||||
if (!page_size) {
|
||||
page_size = object->align;
|
||||
}
|
||||
|
||||
if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) {
|
||||
if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) {
|
||||
const auto cpu_addr{static_cast<VAddr>(buffer_map->CpuAddr() + params.buffer_offset)};
|
||||
|
@ -220,10 +205,24 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8
|
|||
}
|
||||
}
|
||||
|
||||
// We can only map objects that have already been assigned a CPU address.
|
||||
ASSERT(object->status == nvmap::Object::Status::Allocated);
|
||||
const auto object{nvmap.GetHandle(params.nvmap_handle)};
|
||||
if (!object) {
|
||||
LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return NvResult::InvalidState;
|
||||
}
|
||||
|
||||
const auto physical_address{object->addr + params.buffer_offset};
|
||||
// The real nvservices doesn't make a distinction between handles and ids, and
|
||||
// object can only have one handle and it will be the same as its id. Assert that this is the
|
||||
// case to prevent unexpected behavior.
|
||||
ASSERT(object->id == params.nvmap_handle);
|
||||
|
||||
u64 page_size{params.page_size};
|
||||
if (!page_size) {
|
||||
page_size = object->align;
|
||||
}
|
||||
|
||||
const auto physical_address{object->address + params.buffer_offset};
|
||||
u64 size{params.mapping_size};
|
||||
if (!size) {
|
||||
size = object->size;
|
||||
|
@ -363,4 +362,9 @@ std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
Kernel::KEvent* nvhost_as_gpu::QueryEvent(u32 event_id) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Unknown AS GPU Event {}", event_id);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
|
|
@ -13,6 +13,11 @@
|
|||
#include "common/swap.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
|
||||
namespace Service::Nvidia::NvCore {
|
||||
class Container;
|
||||
class NvMap;
|
||||
} // namespace Service::Nvidia::NvCore
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
constexpr u32 DEFAULT_BIG_PAGE_SIZE = 1 << 16;
|
||||
|
@ -29,7 +34,7 @@ DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags);
|
|||
|
||||
class nvhost_as_gpu final : public nvdevice {
|
||||
public:
|
||||
explicit nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_);
|
||||
explicit nvhost_as_gpu(Core::System& system_, NvCore::Container& core);
|
||||
~nvhost_as_gpu() override;
|
||||
|
||||
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
|
@ -42,6 +47,8 @@ public:
|
|||
void OnOpen(DeviceFD fd) override;
|
||||
void OnClose(DeviceFD fd) override;
|
||||
|
||||
Kernel::KEvent* QueryEvent(u32 event_id) override;
|
||||
|
||||
private:
|
||||
class BufferMap final {
|
||||
public:
|
||||
|
@ -180,7 +187,8 @@ private:
|
|||
void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
|
||||
std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
|
||||
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
NvCore::Container& container;
|
||||
NvCore::NvMap& nvmap;
|
||||
|
||||
// This is expected to be ordered, therefore we must use a map, not unordered_map
|
||||
std::map<GPUVAddr, BufferMap> buffer_mappings;
|
||||
|
|
|
@ -279,6 +279,8 @@ Kernel::KEvent* nvhost_ctrl::QueryEvent(u32 event_id) {
|
|||
ASSERT(events_interface.events[slot]);
|
||||
return events_interface.events[slot];
|
||||
}
|
||||
// Is this possible in hardware?
|
||||
ASSERT_MSG(false, "Slot:{}, SyncpointID:{}, requested", slot, syncpoint_id);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/core/nvmap.h"
|
||||
#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
|
@ -22,10 +23,10 @@ Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoi
|
|||
}
|
||||
} // namespace
|
||||
|
||||
nvhost_gpu::nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
|
||||
EventInterface& events_interface_, NvCore::Container& core_)
|
||||
: nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, events_interface{events_interface_},
|
||||
core{core_}, syncpoint_manager{core_.GetSyncpointManager()} {
|
||||
nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_,
|
||||
NvCore::Container& core_)
|
||||
: nvdevice{system_}, events_interface{events_interface_}, core{core_},
|
||||
syncpoint_manager{core_.GetSyncpointManager()}, nvmap{core.GetNvMapFile()} {
|
||||
channel_fence.id = syncpoint_manager.AllocateSyncpoint();
|
||||
channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id);
|
||||
sm_exception_breakpoint_int_report_event =
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Service::Nvidia {
|
|||
|
||||
namespace NvCore {
|
||||
class Container;
|
||||
class NvMap;
|
||||
class SyncpointManager;
|
||||
} // namespace NvCore
|
||||
|
||||
|
@ -28,8 +29,8 @@ namespace Service::Nvidia::Devices {
|
|||
class nvmap;
|
||||
class nvhost_gpu final : public nvdevice {
|
||||
public:
|
||||
explicit nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
|
||||
EventInterface& events_interface_, NvCore::Container& core);
|
||||
explicit nvhost_gpu(Core::System& system_, EventInterface& events_interface_,
|
||||
NvCore::Container& core);
|
||||
~nvhost_gpu() override;
|
||||
|
||||
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
|
@ -199,10 +200,10 @@ private:
|
|||
NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
EventInterface& events_interface;
|
||||
NvCore::Container& core;
|
||||
NvCore::SyncpointManager& syncpoint_manager;
|
||||
NvCore::NvMap& nvmap;
|
||||
NvFence channel_fence;
|
||||
|
||||
// Events
|
||||
|
|
|
@ -10,9 +10,8 @@
|
|||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
nvhost_nvdec::nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
|
||||
NvCore::Container& core)
|
||||
: nvhost_nvdec_common{system_, std::move(nvmap_dev_), core} {}
|
||||
nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core)
|
||||
: nvhost_nvdec_common{system_, core} {}
|
||||
nvhost_nvdec::~nvhost_nvdec() = default;
|
||||
|
||||
NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
|
|
|
@ -10,8 +10,7 @@ namespace Service::Nvidia::Devices {
|
|||
|
||||
class nvhost_nvdec final : public nvhost_nvdec_common {
|
||||
public:
|
||||
explicit nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
|
||||
NvCore::Container& core);
|
||||
explicit nvhost_nvdec(Core::System& system_, NvCore::Container& core);
|
||||
~nvhost_nvdec() override;
|
||||
|
||||
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/core/nvmap.h"
|
||||
#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvmap.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
|
@ -45,10 +45,9 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s
|
|||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
|
||||
NvCore::Container& core_)
|
||||
: nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, core{core_},
|
||||
syncpoint_manager{core.GetSyncpointManager()} {}
|
||||
nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_)
|
||||
: nvdevice{system_}, core{core_},
|
||||
syncpoint_manager{core.GetSyncpointManager()}, nvmap{core.GetNvMapFile()} {}
|
||||
nvhost_nvdec_common::~nvhost_nvdec_common() = default;
|
||||
|
||||
NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
|
||||
|
@ -90,10 +89,10 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input,
|
|||
}
|
||||
}
|
||||
for (const auto& cmd_buffer : command_buffers) {
|
||||
const auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
|
||||
const auto object = nvmap.GetHandle(cmd_buffer.memory_id);
|
||||
ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
|
||||
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
|
||||
system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(),
|
||||
system.Memory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(),
|
||||
cmdlist.size() * sizeof(u32));
|
||||
gpu.PushCommandBuffer(fd_to_id[fd], cmdlist);
|
||||
}
|
||||
|
@ -125,6 +124,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve
|
|||
|
||||
NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IoctlGetWaitbase params{};
|
||||
LOG_CRITICAL(Service_NVDRV, "called WAITBASE");
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase));
|
||||
params.value = 0; // Seems to be hard coded at 0
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase));
|
||||
|
@ -141,7 +141,7 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
|
|||
auto& gpu = system.GPU();
|
||||
|
||||
for (auto& cmd_buffer : cmd_buffer_handles) {
|
||||
auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)};
|
||||
auto object{nvmap.GetHandle(cmd_buffer.map_handle)};
|
||||
if (!object) {
|
||||
LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle);
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
|
@ -150,7 +150,8 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
|
|||
if (object->dma_map_addr == 0) {
|
||||
// NVDEC and VIC memory is in the 32-bit address space
|
||||
// MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space
|
||||
const GPUVAddr low_addr = gpu.MemoryManager().MapAllocate32(object->addr, object->size);
|
||||
const GPUVAddr low_addr =
|
||||
gpu.MemoryManager().MapAllocate32(object->address, object->size);
|
||||
object->dma_map_addr = static_cast<u32>(low_addr);
|
||||
// Ensure that the dma_map_addr is indeed in the lower 32-bit address space.
|
||||
ASSERT(object->dma_map_addr == low_addr);
|
||||
|
@ -158,7 +159,7 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
|
|||
if (!object->dma_map_addr) {
|
||||
LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size);
|
||||
} else {
|
||||
cmd_buffer.map_address = object->dma_map_addr;
|
||||
cmd_buffer.map_address = static_cast<u32_le>(object->dma_map_addr);
|
||||
}
|
||||
}
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer));
|
||||
|
@ -184,4 +185,9 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
|
|||
return NvResult::Success;
|
||||
}
|
||||
|
||||
Kernel::KEvent* nvhost_nvdec_common::QueryEvent(u32 event_id) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Unknown HOSTX1 Event {}", event_id);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
|
|
@ -11,17 +11,16 @@
|
|||
namespace Service::Nvidia {
|
||||
|
||||
namespace NvCore {
|
||||
class SyncpointManager;
|
||||
class Container;
|
||||
class NvMap;
|
||||
class SyncpointManager;
|
||||
} // namespace NvCore
|
||||
|
||||
namespace Devices {
|
||||
class nvmap;
|
||||
|
||||
class nvhost_nvdec_common : public nvdevice {
|
||||
public:
|
||||
explicit nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
|
||||
NvCore::Container& core);
|
||||
explicit nvhost_nvdec_common(Core::System& system_, NvCore::Container& core);
|
||||
~nvhost_nvdec_common() override;
|
||||
|
||||
protected:
|
||||
|
@ -114,12 +113,14 @@ protected:
|
|||
NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
Kernel::KEvent* QueryEvent(u32 event_id) override;
|
||||
|
||||
std::unordered_map<DeviceFD, u32> fd_to_id{};
|
||||
s32_le nvmap_fd{};
|
||||
u32_le submit_timeout{};
|
||||
std::shared_ptr<nvmap> nvmap_dev;
|
||||
NvCore::Container& core;
|
||||
NvCore::SyncpointManager& syncpoint_manager;
|
||||
NvCore::NvMap& nvmap;
|
||||
std::array<u32, MaxSyncPoints> device_syncpoints{};
|
||||
};
|
||||
}; // namespace Devices
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
#include "video_core/renderer_base.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
nvhost_vic::nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
|
||||
NvCore::Container& core)
|
||||
: nvhost_nvdec_common{system_, std::move(nvmap_dev_), core} {}
|
||||
nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core)
|
||||
: nvhost_nvdec_common{system_, core} {}
|
||||
|
||||
nvhost_vic::~nvhost_vic() = default;
|
||||
|
||||
|
|
|
@ -9,8 +9,7 @@ namespace Service::Nvidia::Devices {
|
|||
|
||||
class nvhost_vic final : public nvhost_nvdec_common {
|
||||
public:
|
||||
explicit nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
|
||||
NvCore::Container& core);
|
||||
explicit nvhost_vic(Core::System& system_, NvCore::Container& core);
|
||||
~nvhost_vic();
|
||||
|
||||
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
|
|
|
@ -2,19 +2,24 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/core/nvmap.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvmap.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
using Core::Memory::YUZU_PAGESIZE;
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
nvmap::nvmap(Core::System& system_) : nvdevice{system_} {
|
||||
// Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to
|
||||
// represent this.
|
||||
CreateObject(0);
|
||||
}
|
||||
nvmap::nvmap(Core::System& system_, NvCore::Container& container_)
|
||||
: nvdevice{system_}, container{container_}, file{container.GetNvMapFile()} {}
|
||||
|
||||
nvmap::~nvmap() = default;
|
||||
|
||||
|
@ -63,38 +68,32 @@ void nvmap::OnOpen(DeviceFD fd) {}
|
|||
void nvmap::OnClose(DeviceFD fd) {}
|
||||
|
||||
VAddr nvmap::GetObjectAddress(u32 handle) const {
|
||||
auto object = GetObject(handle);
|
||||
ASSERT(object);
|
||||
ASSERT(object->status == Object::Status::Allocated);
|
||||
return object->addr;
|
||||
auto obj = file.GetHandle(handle);
|
||||
if (obj) {
|
||||
return obj->address;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvmap::CreateObject(u32 size) {
|
||||
// Create a new nvmap object and obtain a handle to it.
|
||||
auto object = std::make_shared<Object>();
|
||||
object->id = next_id++;
|
||||
object->size = size;
|
||||
object->status = Object::Status::Created;
|
||||
object->refcount = 1;
|
||||
|
||||
const u32 handle = next_handle++;
|
||||
|
||||
handles.insert_or_assign(handle, std::move(object));
|
||||
|
||||
return handle;
|
||||
std::shared_ptr<NvCore::NvMap::Handle> nvmap::GetObject(u32 handle) const {
|
||||
return file.GetHandle(handle);
|
||||
}
|
||||
|
||||
NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IocCreateParams params;
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
|
||||
LOG_WARNING(Service_NVDRV, "called, size=0x{:08X}", params.size);
|
||||
|
||||
if (!params.size) {
|
||||
LOG_ERROR(Service_NVDRV, "Size is 0");
|
||||
return NvResult::BadValue;
|
||||
std::shared_ptr<NvCore::NvMap::Handle> handle_description{};
|
||||
auto result =
|
||||
file.CreateHandle(Common::AlignUp(params.size, YUZU_PAGESIZE), handle_description);
|
||||
if (result != NvResult::Success) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Failed to create Object");
|
||||
return result;
|
||||
}
|
||||
|
||||
params.handle = CreateObject(params.size);
|
||||
handle_description->orig_size = params.size; // Orig size is the unaligned size
|
||||
params.handle = handle_description->id;
|
||||
LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size);
|
||||
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::Success;
|
||||
|
@ -103,42 +102,42 @@ NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output)
|
|||
NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IocAllocParams params;
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
|
||||
LOG_WARNING(Service_NVDRV, "called, addr={:X}", params.address);
|
||||
|
||||
if (!params.handle) {
|
||||
LOG_ERROR(Service_NVDRV, "Handle is 0");
|
||||
LOG_CRITICAL(Service_NVDRV, "Handle is 0");
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
if ((params.align - 1) & params.align) {
|
||||
LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
|
||||
LOG_CRITICAL(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
const u32 min_alignment = 0x1000;
|
||||
if (params.align < min_alignment) {
|
||||
params.align = min_alignment;
|
||||
// Force page size alignment at a minimum
|
||||
if (params.align < YUZU_PAGESIZE) {
|
||||
params.align = YUZU_PAGESIZE;
|
||||
}
|
||||
|
||||
auto object = GetObject(params.handle);
|
||||
if (!object) {
|
||||
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
|
||||
auto handle_description{file.GetHandle(params.handle)};
|
||||
if (!handle_description) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
if (object->status == Object::Status::Allocated) {
|
||||
LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
|
||||
if (handle_description->allocated) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
|
||||
return NvResult::InsufficientMemory;
|
||||
}
|
||||
|
||||
object->flags = params.flags;
|
||||
object->align = params.align;
|
||||
object->kind = params.kind;
|
||||
object->addr = params.addr;
|
||||
object->status = Object::Status::Allocated;
|
||||
|
||||
const auto result =
|
||||
handle_description->Alloc(params.flags, params.align, params.kind, params.address);
|
||||
if (result != NvResult::Success) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle);
|
||||
return result;
|
||||
}
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::Success;
|
||||
return result;
|
||||
}
|
||||
|
||||
NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
|
@ -147,19 +146,20 @@ NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output)
|
|||
|
||||
LOG_WARNING(Service_NVDRV, "called");
|
||||
|
||||
// See the comment in FromId for extra info on this function
|
||||
if (!params.handle) {
|
||||
LOG_ERROR(Service_NVDRV, "Handle is zero");
|
||||
LOG_CRITICAL(Service_NVDRV, "Error!");
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
auto object = GetObject(params.handle);
|
||||
if (!object) {
|
||||
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
|
||||
return NvResult::BadValue;
|
||||
auto handle_description{file.GetHandle(params.handle)};
|
||||
if (!handle_description) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Error!");
|
||||
return NvResult::AccessDenied; // This will always return EPERM irrespective of if the
|
||||
// handle exists or not
|
||||
}
|
||||
|
||||
params.id = object->id;
|
||||
|
||||
params.id = handle_description->id;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
@ -168,26 +168,29 @@ NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output)
|
|||
IocFromIdParams params;
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
|
||||
LOG_WARNING(Service_NVDRV, "called, id:{}");
|
||||
|
||||
auto itr = std::find_if(handles.begin(), handles.end(),
|
||||
[&](const auto& entry) { return entry.second->id == params.id; });
|
||||
if (itr == handles.end()) {
|
||||
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.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 (!params.id) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Error!");
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
auto& object = itr->second;
|
||||
if (object->status != Object::Status::Allocated) {
|
||||
LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
|
||||
auto handle_description{file.GetHandle(params.id)};
|
||||
if (!handle_description) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Error!");
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
itr->second->refcount++;
|
||||
|
||||
// Return the existing handle instead of creating a new one.
|
||||
params.handle = itr->first;
|
||||
|
||||
auto result = handle_description->Duplicate(false);
|
||||
if (result != NvResult::Success) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Error!");
|
||||
return result;
|
||||
}
|
||||
params.handle = handle_description->id;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
@ -198,35 +201,43 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output)
|
|||
IocParamParams params;
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
|
||||
LOG_DEBUG(Service_NVDRV, "(STUBBED) called type={}", params.param);
|
||||
LOG_WARNING(Service_NVDRV, "called type={}", params.param);
|
||||
|
||||
auto object = GetObject(params.handle);
|
||||
if (!object) {
|
||||
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
|
||||
if (!params.handle) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Error!");
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
if (object->status != Object::Status::Allocated) {
|
||||
LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
|
||||
auto handle_description{file.GetHandle(params.handle)};
|
||||
if (!handle_description) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Error!");
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
switch (static_cast<ParamTypes>(params.param)) {
|
||||
case ParamTypes::Size:
|
||||
params.result = object->size;
|
||||
switch (params.param) {
|
||||
case HandleParameterType::Size:
|
||||
params.result = static_cast<u32_le>(handle_description->orig_size);
|
||||
break;
|
||||
case ParamTypes::Alignment:
|
||||
params.result = object->align;
|
||||
case HandleParameterType::Alignment:
|
||||
params.result = static_cast<u32_le>(handle_description->align);
|
||||
break;
|
||||
case ParamTypes::Heap:
|
||||
// TODO(Subv): Seems to be a hardcoded value?
|
||||
case HandleParameterType::Base:
|
||||
params.result = static_cast<u32_le>(-22); // posix EINVAL
|
||||
break;
|
||||
case HandleParameterType::Heap:
|
||||
if (handle_description->allocated)
|
||||
params.result = 0x40000000;
|
||||
else
|
||||
params.result = 0x40000000;
|
||||
break;
|
||||
case ParamTypes::Kind:
|
||||
params.result = object->kind;
|
||||
case HandleParameterType::Kind:
|
||||
params.result = handle_description->kind;
|
||||
break;
|
||||
case HandleParameterType::IsSharedMemMapped:
|
||||
params.result = handle_description->is_shared_mem_mapped;
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
|
@ -234,46 +245,25 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output)
|
|||
}
|
||||
|
||||
NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
// TODO(Subv): These flags are unconfirmed.
|
||||
enum FreeFlags {
|
||||
Freed = 0,
|
||||
NotFreedYet = 1,
|
||||
};
|
||||
|
||||
IocFreeParams params;
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
|
||||
LOG_DEBUG(Service_NVDRV, "(STUBBED) called");
|
||||
LOG_WARNING(Service_NVDRV, "called");
|
||||
|
||||
auto itr = handles.find(params.handle);
|
||||
if (itr == handles.end()) {
|
||||
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
|
||||
return NvResult::BadValue;
|
||||
}
|
||||
if (!itr->second->refcount) {
|
||||
LOG_ERROR(
|
||||
Service_NVDRV,
|
||||
"There is no references to this object. The object is already freed. handle={:08X}",
|
||||
params.handle);
|
||||
return NvResult::BadValue;
|
||||
if (!params.handle) {
|
||||
LOG_CRITICAL(Service_NVDRV, "Handle null freed?");
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
itr->second->refcount--;
|
||||
|
||||
params.size = itr->second->size;
|
||||
|
||||
if (itr->second->refcount == 0) {
|
||||
params.flags = Freed;
|
||||
// The address of the nvmap is written to the output if we're finally freeing it, otherwise
|
||||
// 0 is written.
|
||||
params.address = itr->second->addr;
|
||||
if (auto freeInfo{file.FreeHandle(params.handle, false)}) {
|
||||
params.address = freeInfo->address;
|
||||
params.size = static_cast<u32>(freeInfo->size);
|
||||
params.flags = NvCore::NvMap::Handle::Flags{.map_uncached = freeInfo->was_uncached};
|
||||
} else {
|
||||
params.flags = NotFreedYet;
|
||||
params.address = 0;
|
||||
// This is possible when there's internel dups or other duplicates.
|
||||
LOG_CRITICAL(Service_NVDRV, "Not freed");
|
||||
}
|
||||
|
||||
handles.erase(params.handle);
|
||||
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
|
|
@ -9,15 +9,23 @@
|
|||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/service/nvdrv/core/nvmap.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
|
||||
namespace Service::Nvidia::NvCore {
|
||||
class Container;
|
||||
} // namespace Service::Nvidia::NvCore
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
|
||||
class nvmap final : public nvdevice {
|
||||
public:
|
||||
explicit nvmap(Core::System& system_);
|
||||
explicit nvmap(Core::System& system_, NvCore::Container& container);
|
||||
~nvmap() override;
|
||||
|
||||
nvmap(nvmap const&) = delete;
|
||||
nvmap& operator=(nvmap const&) = delete;
|
||||
|
||||
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
std::vector<u8>& output) override;
|
||||
NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
|
||||
|
@ -31,27 +39,16 @@ public:
|
|||
/// Returns the allocated address of an nvmap object given its handle.
|
||||
VAddr GetObjectAddress(u32 handle) const;
|
||||
|
||||
/// Represents an nvmap object.
|
||||
struct Object {
|
||||
enum class Status { Created, Allocated };
|
||||
u32 id;
|
||||
u32 size;
|
||||
u32 flags;
|
||||
u32 align;
|
||||
u8 kind;
|
||||
VAddr addr;
|
||||
Status status;
|
||||
u32 refcount;
|
||||
u32 dma_map_addr;
|
||||
};
|
||||
std::shared_ptr<NvCore::NvMap::Handle> GetObject(u32 handle) const;
|
||||
|
||||
std::shared_ptr<Object> GetObject(u32 handle) const {
|
||||
auto itr = handles.find(handle);
|
||||
if (itr != handles.end()) {
|
||||
return itr->second;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
enum class HandleParameterType : u32_le {
|
||||
Size = 1,
|
||||
Alignment = 2,
|
||||
Base = 3,
|
||||
Heap = 4,
|
||||
Kind = 5,
|
||||
IsSharedMemMapped = 6
|
||||
};
|
||||
|
||||
private:
|
||||
/// Id to use for the next handle that is created.
|
||||
|
@ -60,9 +57,6 @@ private:
|
|||
/// Id to use for the next object that is created.
|
||||
u32 next_id = 0;
|
||||
|
||||
/// Mapping of currently allocated handles to the objects they represent.
|
||||
std::unordered_map<u32, std::shared_ptr<Object>> handles;
|
||||
|
||||
struct IocCreateParams {
|
||||
// Input
|
||||
u32_le size{};
|
||||
|
@ -83,11 +77,11 @@ private:
|
|||
// Input
|
||||
u32_le handle{};
|
||||
u32_le heap_mask{};
|
||||
u32_le flags{};
|
||||
NvCore::NvMap::Handle::Flags flags{};
|
||||
u32_le align{};
|
||||
u8 kind{};
|
||||
INSERT_PADDING_BYTES(7);
|
||||
u64_le addr{};
|
||||
u64_le address{};
|
||||
};
|
||||
static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size");
|
||||
|
||||
|
@ -96,14 +90,14 @@ private:
|
|||
INSERT_PADDING_BYTES(4);
|
||||
u64_le address{};
|
||||
u32_le size{};
|
||||
u32_le flags{};
|
||||
NvCore::NvMap::Handle::Flags flags{};
|
||||
};
|
||||
static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size");
|
||||
|
||||
struct IocParamParams {
|
||||
// Input
|
||||
u32_le handle{};
|
||||
u32_le param{};
|
||||
HandleParameterType param{};
|
||||
// Output
|
||||
u32_le result{};
|
||||
};
|
||||
|
@ -117,14 +111,15 @@ private:
|
|||
};
|
||||
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
|
||||
|
||||
u32 CreateObject(u32 size);
|
||||
|
||||
NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
NvCore::Container& container;
|
||||
NvCore::NvMap& file;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
|
|
@ -138,21 +138,18 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
|
|||
|
||||
Module::Module(Core::System& system)
|
||||
: service_context{system, "nvdrv"}, events_interface{*this}, container{system.GPU()} {
|
||||
auto nvmap_dev = std::make_shared<Devices::nvmap>(system);
|
||||
devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev);
|
||||
devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, container);
|
||||
devices["/dev/nvhost-gpu"] =
|
||||
std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev, events_interface, container);
|
||||
std::make_shared<Devices::nvhost_gpu>(system, events_interface, container);
|
||||
devices["/dev/nvhost-ctrl-gpu"] =
|
||||
std::make_shared<Devices::nvhost_ctrl_gpu>(system, events_interface);
|
||||
devices["/dev/nvmap"] = nvmap_dev;
|
||||
devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev);
|
||||
devices["/dev/nvmap"] = std::make_shared<Devices::nvmap>(system, container);
|
||||
devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, container);
|
||||
devices["/dev/nvhost-ctrl"] =
|
||||
std::make_shared<Devices::nvhost_ctrl>(system, events_interface, container);
|
||||
devices["/dev/nvhost-nvdec"] =
|
||||
std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev, container);
|
||||
devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system, container);
|
||||
devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system);
|
||||
devices["/dev/nvhost-vic"] =
|
||||
std::make_shared<Devices::nvhost_vic>(system, nvmap_dev, container);
|
||||
devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system, container);
|
||||
}
|
||||
|
||||
Module::~Module() = default;
|
||||
|
|
Loading…
Reference in New Issue