// SPDX-License-Identifier: MIT OR MPL-2.0 // Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) #include #include #include "nvmap.h" namespace skyline::service::nvdrv::device { NvMap::NvMap(const DeviceState &state, Driver &driver, Core &core, const SessionContext &ctx) : NvDevice(state, driver, core, ctx) {} PosixResult NvMap::Create(In size, Out handle) { auto handleDesc{core.nvMap.CreateHandle(util::AlignUp(static_cast(size), PAGE_SIZE))}; if (handleDesc) { (*handleDesc)->origSize = size; // Orig size is the unaligned size handle = (*handleDesc)->id; state.logger->Debug("handle: {}, size: 0x{:X}", (*handleDesc)->id, size); } return handleDesc; } PosixResult NvMap::FromId(In id, Out handle) { state.logger->Debug("id: {}", id); // 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 handleDesc{core.nvMap.GetHandle(id)}; if (!handleDesc) [[unlikely]] return PosixResult::InvalidArgument; auto result{handleDesc->Duplicate(ctx.internalSession)}; if (result == PosixResult::Success) handle = id; return result; } PosixResult NvMap::Alloc(In handle, In heapMask, In flags, InOut align, In kind, In address) { state.logger->Debug("handle: {}, flags: ( mapUncached: {}, keepUncachedAfterFree: {} ), align: 0x{:X}, kind: {}, address: 0x{:X}", handle, flags.mapUncached, flags.keepUncachedAfterFree, align, kind, address); if (!handle) [[unlikely]] return PosixResult::InvalidArgument; if (!std::has_single_bit(align)) [[unlikely]] return PosixResult::InvalidArgument; // Force page size alignment at a minimum if (align < PAGE_SIZE) [[unlikely]] align = PAGE_SIZE; auto handleDesc{core.nvMap.GetHandle(handle)}; if (!handleDesc) [[unlikely]] return PosixResult::InvalidArgument; return handleDesc->Alloc(flags, align, kind, address); } PosixResult NvMap::Free(In handle, Out address, Out size, Out flags) { state.logger->Debug("handle: {}", handle); if (!handle) [[unlikely]] return PosixResult::Success; if (auto freeInfo{core.nvMap.FreeHandle(handle, ctx.internalSession)}) { address = freeInfo->address; size = static_cast(freeInfo->size); flags = NvMapCore::Handle::Flags{ .mapUncached = freeInfo->wasUncached }; } else { state.logger->Debug("Handle not freed"); } return PosixResult::Success; } PosixResult NvMap::Param(In handle, In param, Out result) { state.logger->Debug("handle: {}, param: {}", handle, param); if (!handle) return PosixResult::InvalidArgument; auto handleDesc{core.nvMap.GetHandle(handle)}; if (!handleDesc) [[unlikely]] return PosixResult::InvalidArgument; switch (param) { case HandleParameterType::Size: result = static_cast(handleDesc->origSize); return PosixResult::Success; case HandleParameterType::Alignment: result = static_cast(handleDesc->align); return PosixResult::Success; case HandleParameterType::Base: result = static_cast(-static_cast(PosixResult::InvalidArgument)); return PosixResult::Success; case HandleParameterType::Heap: if (handleDesc->allocated) result = 0x40000000; else result = 0; return PosixResult::Success; case HandleParameterType::Kind: result = handleDesc->kind; return PosixResult::Success; case HandleParameterType::IsSharedMemMapped: result = handleDesc->isSharedMemMapped; return PosixResult::Success; default: return PosixResult::InvalidArgument; } } PosixResult NvMap::GetId(Out id, In handle) { state.logger->Debug("handle: {}", handle); // See the comment in FromId for extra info on this function if (!handle) [[unlikely]] return PosixResult::InvalidArgument; auto handleDesc{core.nvMap.GetHandle(handle)}; if (!handleDesc) [[unlikely]] return PosixResult::NotPermitted; // This will always return EPERM irrespective of if the handle exists or not id = handleDesc->id; return PosixResult::Success; } #include "deserialisation/macro_def.inc" static constexpr u32 NvMapMagic{1}; IOCTL_HANDLER_FUNC(NvMap, ({ IOCTL_CASE_ARGS(INOUT, SIZE(0x8), MAGIC(NvMapMagic), FUNC(0x1), Create, ARGS(In, Out)) IOCTL_CASE_ARGS(INOUT, SIZE(0x8), MAGIC(NvMapMagic), FUNC(0x3), FromId, ARGS(In, Out)) IOCTL_CASE_ARGS(INOUT, SIZE(0x20), MAGIC(NvMapMagic), FUNC(0x4), Alloc, ARGS(In, In, In, InOut, In, Pad, In)) IOCTL_CASE_ARGS(INOUT, SIZE(0x18), MAGIC(NvMapMagic), FUNC(0x5), Free, ARGS(In, Pad, Out, Out, Out)) IOCTL_CASE_ARGS(INOUT, SIZE(0xC), MAGIC(NvMapMagic), FUNC(0x9), Param, ARGS(In, In, Out)) IOCTL_CASE_ARGS(INOUT, SIZE(0x8), MAGIC(NvMapMagic), FUNC(0xE), GetId, ARGS(Out, In)) })) #include "deserialisation/macro_undef.inc" }