mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-06-02 02:38:43 +02:00
199 lines
7.0 KiB
C++
199 lines
7.0 KiB
C++
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
|
|
|
#include <gpu.h>
|
|
#include <services/nvdrv/driver.h>
|
|
#include "nvmap.h"
|
|
#include "nvhost_as_gpu.h"
|
|
|
|
namespace skyline::service::nvdrv::device {
|
|
struct MappingFlags {
|
|
bool fixed : 1;
|
|
u8 _pad0_ : 7;
|
|
bool remap : 1;
|
|
u32 _pad1_ : 23;
|
|
};
|
|
static_assert(sizeof(MappingFlags) == sizeof(u32));
|
|
|
|
NvHostAsGpu::NvHostAsGpu(const DeviceState &state) : NvDevice(state) {}
|
|
|
|
NvStatus NvHostAsGpu::BindChannel(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
|
return NvStatus::Success;
|
|
}
|
|
|
|
NvStatus NvHostAsGpu::AllocSpace(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
|
struct Data {
|
|
u32 pages; // In
|
|
u32 pageSize; // In
|
|
MappingFlags flags; // In
|
|
u32 _pad_;
|
|
union {
|
|
u64 offset; // InOut
|
|
u64 align; // In
|
|
};
|
|
} ®ion = buffer.as<Data>();
|
|
|
|
u64 size{static_cast<u64>(region.pages) * static_cast<u64>(region.pageSize)};
|
|
|
|
if (region.flags.fixed)
|
|
region.offset = state.gpu->memoryManager.ReserveFixed(region.offset, size);
|
|
else
|
|
region.offset = state.gpu->memoryManager.ReserveSpace(size, region.align);
|
|
|
|
if (region.offset == 0) {
|
|
state.logger->Warn("Failed to allocate GPU address space region!");
|
|
return NvStatus::BadParameter;
|
|
}
|
|
|
|
return NvStatus::Success;
|
|
}
|
|
|
|
NvStatus NvHostAsGpu::UnmapBuffer(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
|
u64 offset{buffer.as<u64>()};
|
|
|
|
try {
|
|
auto region{regionMap.at(offset)};
|
|
|
|
// Non-fixed regions are unmapped so that they can be used by future non-fixed mappings
|
|
if (!region.fixed)
|
|
if (!state.gpu->memoryManager.Unmap(offset, region.size))
|
|
state.logger->Warn("Failed to unmap region at 0x{:X}", offset);
|
|
|
|
regionMap.erase(offset);
|
|
} catch (const std::out_of_range &e) {
|
|
state.logger->Warn("Couldn't find region to unmap at 0x{:X}", offset);
|
|
}
|
|
|
|
return NvStatus::Success;
|
|
}
|
|
|
|
NvStatus NvHostAsGpu::Modify(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
|
struct Data {
|
|
MappingFlags flags; // In
|
|
u32 kind; // In
|
|
u32 nvmapHandle; // In
|
|
u32 pageSize; // InOut
|
|
u64 bufferOffset; // In
|
|
u64 mappingSize; // In
|
|
u64 offset; // InOut
|
|
} &data = buffer.as<Data>();
|
|
|
|
try {
|
|
auto driver{nvdrv::driver.lock()};
|
|
auto nvmap{driver->nvMap.lock()};
|
|
auto mapping{nvmap->GetObject(data.nvmapHandle)};
|
|
|
|
if (data.flags.remap) {
|
|
auto region{regionMap.upper_bound(data.offset)};
|
|
if ((region == regionMap.begin()) || (region == regionMap.end())) {
|
|
state.logger->Warn("Cannot remap an unmapped GPU address space region: 0x{:X}", data.offset);
|
|
return NvStatus::BadParameter;
|
|
}
|
|
|
|
region--; // Upper bound gives us the region after the one we want
|
|
|
|
if (region->second.size < data.mappingSize) {
|
|
state.logger->Warn("Cannot remap an partially mapped GPU address space region: 0x{:X}", data.offset);
|
|
return NvStatus::BadParameter;
|
|
}
|
|
|
|
u64 gpuAddress{data.offset + data.bufferOffset};
|
|
u8 *cpuPtr{region->second.cpuPtr + data.bufferOffset};
|
|
|
|
if (state.gpu->memoryManager.MapFixed(gpuAddress, cpuPtr, data.mappingSize)) {
|
|
state.logger->Warn("Failed to remap GPU address space region: 0x{:X}", gpuAddress);
|
|
return NvStatus::BadParameter;
|
|
}
|
|
|
|
return NvStatus::Success;
|
|
}
|
|
|
|
u8 *mapPointer{data.bufferOffset + mapping->pointer};
|
|
u64 mapSize{data.mappingSize ? data.mappingSize : mapping->size};
|
|
|
|
if (data.flags.fixed)
|
|
data.offset = state.gpu->memoryManager.MapFixed(data.offset, mapPointer, mapSize);
|
|
else
|
|
data.offset = state.gpu->memoryManager.MapAllocate(mapPointer, mapSize);
|
|
|
|
if (data.offset == 0) {
|
|
state.logger->Warn("Failed to map GPU address space region!");
|
|
return NvStatus::BadParameter;
|
|
}
|
|
|
|
regionMap[data.offset] = {mapPointer, mapSize, data.flags.fixed};
|
|
|
|
return NvStatus::Success;
|
|
} catch (const std::out_of_range &) {
|
|
state.logger->Warn("Invalid NvMap handle: 0x{:X}", data.nvmapHandle);
|
|
return NvStatus::BadParameter;
|
|
}
|
|
}
|
|
|
|
NvStatus NvHostAsGpu::GetVaRegions(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
|
/*
|
|
struct Data {
|
|
u64 _pad0_;
|
|
u32 bufferSize; // InOut
|
|
u32 _pad1_;
|
|
|
|
struct {
|
|
u64 offset;
|
|
u32 page_size;
|
|
u32 pad;
|
|
u64 pages;
|
|
} regions[2]; // Out
|
|
} ®ionInfo = buffer.as<Data>();
|
|
*/
|
|
return NvStatus::Success;
|
|
}
|
|
|
|
NvStatus NvHostAsGpu::AllocAsEx(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
|
/*
|
|
struct Data {
|
|
u32 bigPageSize; // In
|
|
i32 asFd; // In
|
|
u32 flags; // In
|
|
u32 reserved; // In
|
|
u64 vaRangeStart; // In
|
|
u64 vaRangeEnd; // In
|
|
u64 vaRangeSplit; // In
|
|
} addressSpace = buffer.as<Data>();
|
|
*/
|
|
return NvStatus::Success;
|
|
}
|
|
|
|
NvStatus NvHostAsGpu::Remap(IoctlType type, span<u8> buffer, span<u8> inlineBuffer) {
|
|
struct Entry {
|
|
u16 flags; // In
|
|
u16 kind; // In
|
|
u32 nvmapHandle; // In
|
|
u32 mapOffset; // In
|
|
u32 gpuOffset; // In
|
|
u32 pages; // In
|
|
};
|
|
|
|
constexpr u32 MinAlignmentShift{0x10}; // This shift is applied to all addresses passed to Remap
|
|
|
|
auto entries{buffer.cast<Entry>()};
|
|
for (auto entry : entries) {
|
|
try {
|
|
auto driver{nvdrv::driver.lock()};
|
|
auto nvmap{driver->nvMap.lock()};
|
|
auto mapping{nvmap->GetObject(entry.nvmapHandle)};
|
|
|
|
u64 mapAddress{static_cast<u64>(entry.gpuOffset) << MinAlignmentShift};
|
|
u8 *mapPointer{mapping->pointer + (static_cast<u64>(entry.mapOffset) << MinAlignmentShift)};
|
|
u64 mapSize{static_cast<u64>(entry.pages) << MinAlignmentShift};
|
|
|
|
state.gpu->memoryManager.MapFixed(mapAddress, mapPointer, mapSize);
|
|
} catch (const std::out_of_range &) {
|
|
state.logger->Warn("Invalid NvMap handle: 0x{:X}", entry.nvmapHandle);
|
|
return NvStatus::BadParameter;
|
|
}
|
|
}
|
|
|
|
return NvStatus::Success;
|
|
}
|
|
}
|