|
|
|
@ -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); |
|
|
|
|
|
|
|
if (!params.size) { |
|
|
|
LOG_ERROR(Service_NVDRV, "Size is 0"); |
|
|
|
return NvResult::BadValue; |
|
|
|
LOG_WARNING(Service_NVDRV, "called, size=0x{:08X}", params.size); |
|
|
|
|
|
|
|
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 HandleParameterType::Alignment: |
|
|
|
params.result = static_cast<u32_le>(handle_description->align); |
|
|
|
break; |
|
|
|
case ParamTypes::Alignment: |
|
|
|
params.result = object->align; |
|
|
|
case HandleParameterType::Base: |
|
|
|
params.result = static_cast<u32_le>(-22); // posix EINVAL
|
|
|
|
break; |
|
|
|
case ParamTypes::Heap: |
|
|
|
// TODO(Subv): Seems to be a hardcoded value?
|
|
|
|
params.result = 0x40000000; |
|
|
|
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; |
|
|
|
} |
|
|
|
|