mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-27 15:35:29 +03:00
Extend GPU VMM with unmapping/remapping support + code cleanup
This commit is contained in:
parent
cf468c20e2
commit
ed3ff862f6
@ -42,11 +42,11 @@ namespace skyline::gpu::vmm {
|
||||
}
|
||||
|
||||
if (extension)
|
||||
chunkList.insert(std::next(chunk), ChunkDescriptor(newChunk.address + newChunk.size, extension, (oldChunk.state == ChunkState::Mapped) ? (oldChunk.cpuAddress + oldChunk.size + newChunk.size) : 0, oldChunk.state));
|
||||
chunkList.insert(std::next(chunk), ChunkDescriptor(newChunk.address + newChunk.size, extension, (oldChunk.state == ChunkState::Mapped) ? (oldChunk.cpuAddress + newSize + newChunk.size) : 0, oldChunk.state));
|
||||
|
||||
return newChunk.address;
|
||||
} else if (chunk->address + chunk->size > newChunk.address) {
|
||||
chunk->size = (newChunk.address - chunk->address);
|
||||
chunk->size = newChunk.address - chunk->address;
|
||||
|
||||
// Deletes all chunks that are within the chunk being inserted and split the final one
|
||||
auto tailChunk = std::next(chunk);
|
||||
@ -68,6 +68,14 @@ namespace skyline::gpu::vmm {
|
||||
if (tailChunk->state == ChunkState::Mapped)
|
||||
tailChunk->cpuAddress += chunkSliceOffset;
|
||||
|
||||
|
||||
// If the size of the head chunk is zero then we can directly replace it with our new one rather than inserting it
|
||||
auto headChunk = std::prev(tailChunk);
|
||||
if (headChunk->size == 0)
|
||||
*headChunk = newChunk;
|
||||
else
|
||||
chunkList.insert(std::next(headChunk), newChunk);
|
||||
|
||||
return newChunk.address;
|
||||
}
|
||||
}
|
||||
@ -78,10 +86,10 @@ namespace skyline::gpu::vmm {
|
||||
u64 MemoryManager::AllocateSpace(u64 size) {
|
||||
size = util::AlignUp(size, constant::GpuPageSize);
|
||||
auto newChunk = FindChunk(size, ChunkState::Unmapped);
|
||||
if (!newChunk.has_value())
|
||||
if (!newChunk)
|
||||
return 0;
|
||||
|
||||
auto chunk = newChunk.value();
|
||||
auto chunk = *newChunk;
|
||||
chunk.size = size;
|
||||
chunk.state = ChunkState::Allocated;
|
||||
|
||||
@ -100,10 +108,10 @@ namespace skyline::gpu::vmm {
|
||||
u64 MemoryManager::MapAllocated(u64 address, u64 size) {
|
||||
size = util::AlignUp(size, constant::GpuPageSize);
|
||||
auto mappedChunk = FindChunk(size, ChunkState::Allocated);
|
||||
if (!mappedChunk.has_value())
|
||||
if (!mappedChunk)
|
||||
return 0;
|
||||
|
||||
auto chunk = mappedChunk.value();
|
||||
auto chunk = *mappedChunk;
|
||||
chunk.cpuAddress = address;
|
||||
chunk.size = size;
|
||||
chunk.state = ChunkState::Mapped;
|
||||
@ -120,13 +128,30 @@ namespace skyline::gpu::vmm {
|
||||
return InsertChunk(ChunkDescriptor(address, size, cpuAddress, ChunkState::Mapped));
|
||||
}
|
||||
|
||||
bool MemoryManager::Unmap(u64 address) {
|
||||
if ((address & (constant::GpuPageSize - 1)) != 0)
|
||||
return false;
|
||||
|
||||
auto chunk = std::find_if(chunkList.begin(), chunkList.end(), [address](const ChunkDescriptor &chunk) -> bool {
|
||||
return chunk.address == address;
|
||||
});
|
||||
|
||||
if (chunk == chunkList.end())
|
||||
return false;
|
||||
|
||||
chunk->state = ChunkState::Allocated;
|
||||
chunk->cpuAddress = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MemoryManager::Read(u8 *destination, u64 address, u64 size) const {
|
||||
auto chunk = --std::upper_bound(chunkList.begin(), chunkList.end(), address, [](const u64 address, const ChunkDescriptor &chunk) -> bool {
|
||||
return address < chunk.address;
|
||||
});
|
||||
|
||||
if (chunk == chunkList.end() || chunk->state != ChunkState::Mapped)
|
||||
throw exception("Failed to read region in GPU address space - address: {#:X} size: {#:X}", address, size);
|
||||
throw exception("Failed to read region in GPU address space - address: 0x{:X} size: 0x{:X}", address, size);
|
||||
|
||||
u64 chunkOffset = address - chunk->address;
|
||||
u64 destinationOffset{};
|
||||
@ -146,4 +171,31 @@ namespace skyline::gpu::vmm {
|
||||
chunk++;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::Write(u8 *source, u64 address, u64 size) const {
|
||||
auto chunk = --std::upper_bound(chunkList.begin(), chunkList.end(), address, [](const u64 address, const ChunkDescriptor &chunk) -> bool {
|
||||
return address < chunk.address;
|
||||
});
|
||||
|
||||
if (chunk == chunkList.end() || chunk->state != ChunkState::Mapped)
|
||||
throw exception("Failed to write to region in GPU address space - address: {#:X} size: {#:X}", address, size);
|
||||
|
||||
u64 chunkOffset = address - chunk->address;
|
||||
u64 sourceOffset{};
|
||||
|
||||
// A continuous region in the GPU address space may be made up of several discontinuous regions in physical memory so we have to iterate over all chunks
|
||||
while (size != 0) {
|
||||
if (chunk == chunkList.end() || chunk->state != ChunkState::Mapped)
|
||||
throw exception("Failed to write to region in GPU address space - address: {#:X} size: {#:X}", address, size);
|
||||
|
||||
u64 writeSize = std::min(chunk->size - chunkOffset, size);
|
||||
state.process->WriteMemory(source + sourceOffset, chunk->cpuAddress + chunkOffset, writeSize);
|
||||
|
||||
// After the first read all further reads will start from the base of the chunk
|
||||
chunkOffset = 0;
|
||||
size -= writeSize;
|
||||
sourceOffset += writeSize;
|
||||
chunk++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <common.h>
|
||||
|
||||
namespace skyline {
|
||||
@ -99,9 +100,51 @@ namespace skyline {
|
||||
u64 MapFixed(u64 address, u64 cpuAddress, u64 size);
|
||||
|
||||
/**
|
||||
* @brief Reads in a buffer from a region of the GPU virtual address space
|
||||
* @brief This unmaps the chunk that starts at 'offset' from the GPU address space
|
||||
* @return Whether the operation succeeded
|
||||
*/
|
||||
bool Unmap(u64 address);
|
||||
|
||||
void Read(u8 *destination, u64 address, u64 size) const;
|
||||
|
||||
/**
|
||||
* @brief Reads in a span from a region of the GPU virtual address space
|
||||
* @tparam T The type of span to read into
|
||||
*/
|
||||
template<typename T>
|
||||
void Read(std::span<T> destination, u64 address) const {
|
||||
Read(reinterpret_cast<u8*>(destination.data()), address, destination.size_bytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads in an object from a region of the GPU virtual address space
|
||||
* @tparam T The type of object to return
|
||||
*/
|
||||
template<typename T>
|
||||
T Read(u64 address) const {
|
||||
T obj;
|
||||
Read(reinterpret_cast<u8*>(&obj), address, sizeof(T));
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Write(u8 *source, u64 address, u64 size) const;
|
||||
|
||||
/**
|
||||
* @brief Writes out a span to a region of the GPU virtual address space
|
||||
*/
|
||||
template<typename T>
|
||||
void Write(std::span<T> source, u64 address) const {
|
||||
Write(reinterpret_cast<u8*>(source.data()), address, source.size_bytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads in an object from a region of the GPU virtual address space
|
||||
* @tparam T The type of object to return
|
||||
*/
|
||||
template<typename T>
|
||||
void Write(T source, u64 address) const {
|
||||
Write(reinterpret_cast<u8*>(&source), address, sizeof(T));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -10,42 +10,15 @@
|
||||
|
||||
namespace skyline::service::nvdrv::device {
|
||||
NvHostAsGpu::NvHostAsGpu(const DeviceState &state) : NvDevice(state, NvDeviceType::nvhost_as_gpu, {
|
||||
{0x4109, NFUNC(NvHostAsGpu::InitializeEx)},
|
||||
{0x4108, NFUNC(NvHostAsGpu::GetVaRegions)},
|
||||
{0x4102, NFUNC(NvHostAsGpu::AllocSpace)},
|
||||
{0x4106, NFUNC(NvHostAsGpu::Modify)},
|
||||
{0x4101, NFUNC(NvHostAsGpu::BindChannel)},
|
||||
{0x4114, 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::InitializeEx(IoctlData &buffer) {
|
||||
struct Data {
|
||||
u32 bigPageSize;
|
||||
i32 asFd;
|
||||
u32 flags;
|
||||
u32 reserved;
|
||||
u64 vaRangeStart;
|
||||
u64 vaRangeEnd;
|
||||
u64 vaRangeSplit;
|
||||
} addressSpace = state.process->GetObject<Data>(buffer.input.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];
|
||||
} regionInfo = state.process->GetReference<Data>(buffer.input.at(0).address);
|
||||
state.process->WriteMemory(regionInfo, buffer.output.at(0).address);
|
||||
}
|
||||
|
||||
void NvHostAsGpu::BindChannel(IoctlData &buffer) {
|
||||
struct Data {
|
||||
u32 fd;
|
||||
@ -76,6 +49,13 @@ namespace skyline::service::nvdrv::device {
|
||||
state.process->WriteMemory(region, buffer.output.at(0).address);
|
||||
}
|
||||
|
||||
void NvHostAsGpu::UnmapBuffer(IoctlData &buffer) {
|
||||
auto offset = state.process->GetObject<u64>(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;
|
||||
@ -107,4 +87,61 @@ namespace skyline::service::nvdrv::device {
|
||||
|
||||
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];
|
||||
} regionInfo = state.process->GetReference<Data>(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<Data>(buffer.input.at(0).address);
|
||||
}
|
||||
|
||||
void NvHostAsGpu::Remap(IoctlData &buffer) {
|
||||
struct Entry {
|
||||
u16 flags;
|
||||
u16 kind;
|
||||
u32 nvmapHandle;
|
||||
u32 mapOffset;
|
||||
u32 gpuOffset;
|
||||
u32 pages;
|
||||
};
|
||||
|
||||
size_t entryCount{buffer.input.at(0).size / sizeof(Entry)};
|
||||
std::span entries(state.process->GetPointer<Entry>(buffer.input.at(0).address), entryCount);
|
||||
|
||||
for (auto entry : entries) {
|
||||
try {
|
||||
auto nvmap = state.os->serviceManager.GetService<nvdrv::INvDrvServices>(Service::nvdrv_INvDrvServices)->GetDevice<nvdrv::device::NvMap>(nvdrv::device::NvDeviceType::nvmap)->handleTable.at(entry.nvmapHandle);
|
||||
|
||||
u64 mapAddress = static_cast<u64>(entry.gpuOffset) << 0x10;
|
||||
u64 mapPhysicalAddress = nvmap->address + (static_cast<u64>(entry.mapOffset) << 0x10);
|
||||
u64 mapSize = static_cast<u64>(entry.pages) << 0x10;
|
||||
|
||||
state.gpu->memoryManager.MapFixed(mapAddress, mapPhysicalAddress, mapSize);
|
||||
} catch (const std::exception &e) {
|
||||
buffer.status = NvStatus::BadValue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,28 +14,38 @@ namespace skyline::service::nvdrv::device {
|
||||
NvHostAsGpu(const DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief This initializes the application's GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_INITIALIZE_EX)
|
||||
* @brief This binds a channel to the address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_BIND_CHANNEL)
|
||||
*/
|
||||
void InitializeEx(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This returns the application's GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_GET_VA_REGIONS)
|
||||
*/
|
||||
void GetVaRegions(IoctlData &buffer);
|
||||
void BindChannel(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This reserves a region in the GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_ALLOC_SPACE)
|
||||
*/
|
||||
void AllocSpace(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This unmaps a region in the GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_UNMAP_BUFFER)
|
||||
*/
|
||||
void UnmapBuffer(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This maps a region in the GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_MODIFY)
|
||||
*/
|
||||
void Modify(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This binds a channel to the address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_BIND_CHANNEL)
|
||||
* @brief This returns the application's GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_GET_VA_REGIONS)
|
||||
*/
|
||||
void BindChannel(IoctlData &buffer);
|
||||
void GetVaRegions(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This initializes the application's GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_INITIALIZE_EX)
|
||||
*/
|
||||
void InitializeEx(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief Remaps a region of the GPU address space (https://switchbrew.org/wiki/NV_services#NVGPU_AS_IOCTL_REMAP)
|
||||
*/
|
||||
void Remap(IoctlData &buffer);
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user