// SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #include #include #include #include #include "nvmap.h" #include "nvhost_as_gpu.h" namespace skyline::service::nvdrv::device { NvHostAsGpu::NvHostAsGpu(const DeviceState &state) : NvDevice(state, { {0x4101, NFUNC(NvHostAsGpu::BindChannel)}, {0x4102, NFUNC(NvHostAsGpu::AllocSpace)}, {0x4105, NFUNC(NvHostAsGpu::UnmapBuffer)}, {0x4106, NFUNC(NvHostAsGpu::Modify)}, {0x4108, NFUNC(NvHostAsGpu::GetVaRegions)}, {0x4109, NFUNC(NvHostAsGpu::InitializeEx)}, {0x4114, NFUNC(NvHostAsGpu::Remap)}, }) {} void NvHostAsGpu::BindChannel(IoctlData &buffer) { struct Data { u32 fd; } &channelInfo = state.process->GetReference(buffer.input.at(0).address); } void NvHostAsGpu::AllocSpace(IoctlData &buffer) { struct Data { u32 pages; u32 pageSize; u32 flags; u32 _pad_; u64 offset; } region = state.process->GetObject(buffer.input.at(0).address); u64 size = static_cast(region.pages) * static_cast(region.pageSize); if (region.flags & 1) region.offset = state.gpu->memoryManager.ReserveFixed(region.offset, size); else region.offset = state.gpu->memoryManager.ReserveSpace(size); if (region.offset == 0) { state.logger->Warn("Failed to allocate GPU address space region!"); buffer.status = NvStatus::BadParameter; } state.process->WriteMemory(region, buffer.output.at(0).address); } void NvHostAsGpu::UnmapBuffer(IoctlData &buffer) { auto offset = state.process->GetObject(buffer.input.at(0).address); if (!state.gpu->memoryManager.Unmap(offset)) state.logger->Warn("Failed to unmap chunk at 0x{:X}", offset); } void NvHostAsGpu::Modify(IoctlData &buffer) { struct Data { u32 flags; u32 kind; u32 nvmapHandle; u32 pageSize; u64 bufferOffset; u64 mappingSize; u64 offset; } region = state.process->GetObject(buffer.input.at(0).address); if (!region.nvmapHandle) return; auto driver = nvdrv::driver.lock(); auto nvmap = driver->nvMap.lock(); auto mapping = nvmap->handleTable.at(region.nvmapHandle); u64 mapPhysicalAddress = region.bufferOffset + mapping->address; u64 mapSize = region.mappingSize ? region.mappingSize : mapping->size; if (region.flags & 1) region.offset = state.gpu->memoryManager.MapFixed(region.offset, mapPhysicalAddress, mapSize); else region.offset = state.gpu->memoryManager.MapAllocate(mapPhysicalAddress, mapSize); if (region.offset == 0) { state.logger->Warn("Failed to map GPU address space region!"); buffer.status = NvStatus::BadParameter; } state.process->WriteMemory(region, buffer.output.at(0).address); } void NvHostAsGpu::GetVaRegions(IoctlData &buffer) { struct Data { u64 _pad0_; u32 bufferSize; u32 _pad1_; struct { u64 offset; u32 page_size; u32 pad; u64 pages; } regions[2]; } ®ionInfo = state.process->GetReference(buffer.input.at(0).address); state.process->WriteMemory(regionInfo, buffer.output.at(0).address); } void NvHostAsGpu::InitializeEx(IoctlData &buffer) { struct Data { u32 bigPageSize; i32 asFd; u32 flags; u32 reserved; u64 vaRangeStart; u64 vaRangeEnd; u64 vaRangeSplit; } addressSpace = state.process->GetObject(buffer.input.at(0).address); } void NvHostAsGpu::Remap(IoctlData &buffer) { struct Entry { u16 flags; u16 kind; u32 nvmapHandle; u32 mapOffset; u32 gpuOffset; u32 pages; }; constexpr u32 MinAlignmentShift{0x10}; // This shift is applied to all addresses passed to Remap size_t entryCount{buffer.input.at(0).size / sizeof(Entry)}; std::span entries(state.process->GetPointer(buffer.input.at(0).address), entryCount); for (auto entry : entries) { try { auto driver = nvdrv::driver.lock(); auto nvmap = driver->nvMap.lock(); auto mapping = nvmap->handleTable.at(entry.nvmapHandle); u64 mapAddress = static_cast(entry.gpuOffset) << MinAlignmentShift; u64 mapPhysicalAddress = mapping->address + (static_cast(entry.mapOffset) << MinAlignmentShift); u64 mapSize = static_cast(entry.pages) << MinAlignmentShift; state.gpu->memoryManager.MapFixed(mapAddress, mapPhysicalAddress, mapSize); } catch (const std::exception &e) { buffer.status = NvStatus::BadValue; return; } } } }