|
|
@ -10,6 +10,7 @@ |
|
|
|
|
|
|
|
|
#include "common/alignment.h"
|
|
|
#include "common/alignment.h"
|
|
|
#include "common/assert.h"
|
|
|
#include "common/assert.h"
|
|
|
|
|
|
#include "common/common_types.h"
|
|
|
#include "common/logging.h"
|
|
|
#include "common/logging.h"
|
|
|
#include "core/core.h"
|
|
|
#include "core/core.h"
|
|
|
#include "core/hle/service/nvdrv/core/container.h"
|
|
|
#include "core/hle/service/nvdrv/core/container.h"
|
|
|
@ -130,12 +131,12 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) { |
|
|
|
|
|
|
|
|
const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)}; |
|
|
const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)}; |
|
|
const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)}; |
|
|
const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)}; |
|
|
vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages); |
|
|
|
|
|
|
|
|
vm.small_page_allocator.emplace(start_pages, end_pages); |
|
|
|
|
|
|
|
|
const auto start_big_pages{static_cast<u32>(vm.va_range_split >> vm.big_page_size_bits)}; |
|
|
const auto start_big_pages{static_cast<u32>(vm.va_range_split >> vm.big_page_size_bits)}; |
|
|
const auto end_big_pages{ |
|
|
const auto end_big_pages{ |
|
|
static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; |
|
|
static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; |
|
|
vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages); |
|
|
|
|
|
|
|
|
vm.big_page_allocator.emplace(start_big_pages, end_big_pages); |
|
|
|
|
|
|
|
|
gmmu = std::make_shared<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split, |
|
|
gmmu = std::make_shared<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split, |
|
|
vm.big_page_size_bits, VM::PAGE_SIZE_BITS); |
|
|
vm.big_page_size_bits, VM::PAGE_SIZE_BITS); |
|
|
@ -188,8 +189,8 @@ NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
allocation_map[params.offset] = { |
|
|
allocation_map[params.offset] = { |
|
|
.size = size, |
|
|
|
|
|
.mappings{}, |
|
|
.mappings{}, |
|
|
|
|
|
.size = size, |
|
|
.page_size = params.page_size, |
|
|
.page_size = params.page_size, |
|
|
.sparse = (params.flags & MappingFlags::Sparse) != MappingFlags::None, |
|
|
.sparse = (params.flags & MappingFlags::Sparse) != MappingFlags::None, |
|
|
.big_pages = params.page_size != VM::YUZU_PAGESIZE, |
|
|
.big_pages = params.page_size != VM::YUZU_PAGESIZE, |
|
|
@ -199,71 +200,52 @@ NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void nvhost_as_gpu::FreeMappingLocked(u64 offset) { |
|
|
void nvhost_as_gpu::FreeMappingLocked(u64 offset) { |
|
|
auto mapping{mapping_map.at(offset)}; |
|
|
|
|
|
|
|
|
|
|
|
if (!mapping->fixed) { |
|
|
|
|
|
auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; |
|
|
|
|
|
u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; |
|
|
|
|
|
u32 page_size{mapping->big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; |
|
|
|
|
|
u64 aligned_size{Common::AlignUp(mapping->size, page_size)}; |
|
|
|
|
|
|
|
|
|
|
|
allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits), |
|
|
|
|
|
static_cast<u32>(aligned_size >> page_size_bits)); |
|
|
|
|
|
|
|
|
auto const it = mapping_map.find(offset); |
|
|
|
|
|
auto const mapping = it->second; |
|
|
|
|
|
if (!mapping.fixed) { |
|
|
|
|
|
auto& allocator{mapping.big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; |
|
|
|
|
|
u32 page_size_bits{mapping.big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; |
|
|
|
|
|
u32 page_size{mapping.big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; |
|
|
|
|
|
u64 aligned_size{Common::AlignUp(mapping.size, page_size)}; |
|
|
|
|
|
allocator.Free(u32(mapping.offset >> page_size_bits), u32(aligned_size >> page_size_bits)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
nvmap.UnpinHandle(mapping->handle); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nvmap.UnpinHandle(mapping.handle); |
|
|
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
|
|
|
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
|
|
|
// Only FreeSpace can unmap them fully
|
|
|
// Only FreeSpace can unmap them fully
|
|
|
if (mapping->sparse_alloc) { |
|
|
|
|
|
gmmu->MapSparse(offset, mapping->size, mapping->big_page); |
|
|
|
|
|
|
|
|
if (mapping.sparse_alloc) { |
|
|
|
|
|
gmmu->MapSparse(offset, mapping.size, mapping.big_page); |
|
|
} else { |
|
|
} else { |
|
|
gmmu->Unmap(offset, mapping->size); |
|
|
|
|
|
|
|
|
gmmu->Unmap(offset, mapping.size); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
mapping_map.erase(offset); |
|
|
|
|
|
|
|
|
mapping_map.erase(it); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) { |
|
|
NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) { |
|
|
LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, |
|
|
|
|
|
params.pages, params.page_size); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, params.pages, params.page_size); |
|
|
std::scoped_lock lock(mutex); |
|
|
std::scoped_lock lock(mutex); |
|
|
|
|
|
|
|
|
if (!vm.initialised) { |
|
|
if (!vm.initialised) { |
|
|
return NvResult::BadValue; |
|
|
return NvResult::BadValue; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
auto allocation{allocation_map[params.offset]}; |
|
|
|
|
|
|
|
|
|
|
|
if (allocation.page_size != params.page_size || |
|
|
|
|
|
allocation.size != (static_cast<u64>(params.pages) * params.page_size)) { |
|
|
|
|
|
|
|
|
if (auto const it = allocation_map.find(params.offset); it != allocation_map.end()) { |
|
|
|
|
|
auto const allocation = it->second; |
|
|
|
|
|
if (allocation.page_size != params.page_size || allocation.size != (u64(params.pages) * params.page_size)) |
|
|
return NvResult::BadValue; |
|
|
return NvResult::BadValue; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (const auto& mapping : allocation.mappings) { |
|
|
|
|
|
FreeMappingLocked(mapping->offset); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
for (const auto mapping_offset : allocation.mappings) |
|
|
|
|
|
FreeMappingLocked(mapping_offset); |
|
|
|
|
|
|
|
|
// Unset sparse flag if required
|
|
|
// Unset sparse flag if required
|
|
|
if (allocation.sparse) { |
|
|
|
|
|
|
|
|
if (allocation.sparse) |
|
|
gmmu->Unmap(params.offset, allocation.size); |
|
|
gmmu->Unmap(params.offset, allocation.size); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator |
|
|
|
|
|
: *vm.big_page_allocator}; |
|
|
|
|
|
u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS |
|
|
|
|
|
: vm.big_page_size_bits}; |
|
|
|
|
|
|
|
|
auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator : *vm.big_page_allocator}; |
|
|
|
|
|
u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS : vm.big_page_size_bits}; |
|
|
|
|
|
|
|
|
allocator.Free(static_cast<u32>(params.offset >> page_size_bits), |
|
|
|
|
|
static_cast<u32>(allocation.size >> page_size_bits)); |
|
|
|
|
|
|
|
|
allocator.Free(u32(params.offset >> page_size_bits), u32(allocation.size >> page_size_bits)); |
|
|
allocation_map.erase(params.offset); |
|
|
allocation_map.erase(params.offset); |
|
|
} catch (const std::out_of_range&) { |
|
|
|
|
|
return NvResult::BadValue; |
|
|
|
|
|
|
|
|
return NvResult::Success; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return NvResult::Success; |
|
|
|
|
|
|
|
|
return NvResult::BadValue; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) { |
|
|
NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) { |
|
|
@ -327,26 +309,18 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { |
|
|
|
|
|
|
|
|
// Remaps a subregion of an existing mapping to a different PA
|
|
|
// Remaps a subregion of an existing mapping to a different PA
|
|
|
if ((params.flags & MappingFlags::Remap) != MappingFlags::None) { |
|
|
if ((params.flags & MappingFlags::Remap) != MappingFlags::None) { |
|
|
try { |
|
|
|
|
|
auto mapping{mapping_map.at(params.offset)}; |
|
|
|
|
|
|
|
|
|
|
|
if (mapping->size < params.mapping_size) { |
|
|
|
|
|
LOG_WARNING(Service_NVDRV, |
|
|
|
|
|
"Cannot remap a partially mapped GPU address space region: {:#X}", |
|
|
|
|
|
params.offset); |
|
|
|
|
|
|
|
|
if (auto const it = mapping_map.find(params.offset); it != mapping_map.end()) { |
|
|
|
|
|
auto const mapping = it->second; |
|
|
|
|
|
if (mapping.size < params.mapping_size) { |
|
|
|
|
|
LOG_WARNING(Service_NVDRV, "Cannot remap a partially mapped GPU address space region: {:#X}", params.offset); |
|
|
return NvResult::BadValue; |
|
|
return NvResult::BadValue; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
u64 gpu_address{static_cast<u64>(params.offset + params.buffer_offset)}; |
|
|
|
|
|
VAddr device_address{mapping->ptr + params.buffer_offset}; |
|
|
|
|
|
|
|
|
|
|
|
gmmu->Map(gpu_address, device_address, params.mapping_size, |
|
|
|
|
|
static_cast<Tegra::PTEKind>(params.kind), mapping->big_page); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
u64 gpu_address = u64(params.offset + params.buffer_offset); |
|
|
|
|
|
VAddr device_address{mapping.ptr + params.buffer_offset}; |
|
|
|
|
|
gmmu->Map(gpu_address, device_address, params.mapping_size, Tegra::PTEKind(params.kind), mapping.big_page); |
|
|
return NvResult::Success; |
|
|
return NvResult::Success; |
|
|
} catch (const std::out_of_range&) { |
|
|
|
|
|
LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: {:#X}", |
|
|
|
|
|
params.offset); |
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: {:#X}", params.offset); |
|
|
return NvResult::BadValue; |
|
|
return NvResult::BadValue; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -356,8 +330,7 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { |
|
|
return NvResult::BadValue; |
|
|
return NvResult::BadValue; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
DAddr device_address{ |
|
|
|
|
|
static_cast<DAddr>(nvmap.PinHandle(params.handle, false) + params.buffer_offset)}; |
|
|
|
|
|
|
|
|
DAddr device_address = DAddr(nvmap.PinHandle(params.handle, false) + params.buffer_offset); |
|
|
u64 size{params.mapping_size ? params.mapping_size : handle->orig_size}; |
|
|
u64 size{params.mapping_size ? params.mapping_size : handle->orig_size}; |
|
|
|
|
|
|
|
|
bool big_page{[&]() { |
|
|
bool big_page{[&]() { |
|
|
@ -381,32 +354,22 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const bool use_big_pages = alloc->second.big_pages && big_page; |
|
|
const bool use_big_pages = alloc->second.big_pages && big_page; |
|
|
gmmu->Map(params.offset, device_address, size, static_cast<Tegra::PTEKind>(params.kind), |
|
|
|
|
|
use_big_pages); |
|
|
|
|
|
|
|
|
gmmu->Map(params.offset, device_address, size, static_cast<Tegra::PTEKind>(params.kind), use_big_pages); |
|
|
|
|
|
|
|
|
auto mapping{std::make_shared<Mapping>(params.handle, device_address, params.offset, size, |
|
|
|
|
|
true, use_big_pages, alloc->second.sparse)}; |
|
|
|
|
|
alloc->second.mappings.push_back(mapping); |
|
|
|
|
|
mapping_map[params.offset] = mapping; |
|
|
|
|
|
|
|
|
alloc->second.mappings.push_back(params.offset); |
|
|
|
|
|
mapping_map.insert_or_assign(params.offset, Mapping(params.handle, device_address, params.offset, size, true, use_big_pages, alloc->second.sparse)); |
|
|
} else { |
|
|
} else { |
|
|
auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; |
|
|
auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; |
|
|
u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; |
|
|
u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; |
|
|
u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; |
|
|
u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; |
|
|
|
|
|
|
|
|
params.offset = static_cast<u64>(allocator.Allocate( |
|
|
|
|
|
static_cast<u32>(Common::AlignUp(size, page_size) >> page_size_bits))) |
|
|
|
|
|
<< page_size_bits; |
|
|
|
|
|
|
|
|
params.offset = u64(allocator.Allocate(u32(Common::AlignUp(size, page_size) >> page_size_bits))) << page_size_bits; |
|
|
if (!params.offset) { |
|
|
if (!params.offset) { |
|
|
ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!"); |
|
|
ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!"); |
|
|
return NvResult::InsufficientMemory; |
|
|
return NvResult::InsufficientMemory; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
gmmu->Map(params.offset, device_address, Common::AlignUp(size, page_size), |
|
|
|
|
|
static_cast<Tegra::PTEKind>(params.kind), big_page); |
|
|
|
|
|
|
|
|
|
|
|
auto mapping{std::make_shared<Mapping>(params.handle, device_address, params.offset, size, |
|
|
|
|
|
false, big_page, false)}; |
|
|
|
|
|
mapping_map[params.offset] = mapping; |
|
|
|
|
|
|
|
|
gmmu->Map(params.offset, device_address, Common::AlignUp(size, page_size), Tegra::PTEKind(params.kind), big_page); |
|
|
|
|
|
mapping_map.insert_or_assign(params.offset, Mapping(params.handle, device_address, params.offset, size, false, big_page, false)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
map_buffer_offsets.insert(params.offset); |
|
|
map_buffer_offsets.insert(params.offset); |
|
|
@ -415,37 +378,32 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) { |
|
|
NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) { |
|
|
if (map_buffer_offsets.find(params.offset) != map_buffer_offsets.end()) { |
|
|
|
|
|
|
|
|
std::scoped_lock lock(mutex); |
|
|
|
|
|
if (auto const offset_it = map_buffer_offsets.find(params.offset); offset_it != map_buffer_offsets.end()) { |
|
|
LOG_DEBUG(Service_NVDRV, "called, offset={:#X}", params.offset); |
|
|
LOG_DEBUG(Service_NVDRV, "called, offset={:#X}", params.offset); |
|
|
|
|
|
|
|
|
std::scoped_lock lock(mutex); |
|
|
|
|
|
|
|
|
|
|
|
if (!vm.initialised) { |
|
|
if (!vm.initialised) { |
|
|
return NvResult::BadValue; |
|
|
return NvResult::BadValue; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
auto mapping{mapping_map.at(params.offset)}; |
|
|
|
|
|
|
|
|
|
|
|
if (!mapping->fixed) { |
|
|
|
|
|
auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; |
|
|
|
|
|
u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; |
|
|
|
|
|
|
|
|
|
|
|
allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits), |
|
|
|
|
|
static_cast<u32>(mapping->size >> page_size_bits)); |
|
|
|
|
|
|
|
|
auto const it = mapping_map.find(params.offset); |
|
|
|
|
|
auto const mapping = it->second; |
|
|
|
|
|
if (!mapping.fixed) { |
|
|
|
|
|
auto& allocator{mapping.big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; |
|
|
|
|
|
u32 page_size_bits{mapping.big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; |
|
|
|
|
|
allocator.Free(u32(mapping.offset >> page_size_bits), u32(mapping.size >> page_size_bits)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
|
|
|
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
|
|
|
// Only FreeSpace can unmap them fully
|
|
|
// Only FreeSpace can unmap them fully
|
|
|
if (mapping->sparse_alloc) { |
|
|
|
|
|
gmmu->MapSparse(params.offset, mapping->size, mapping->big_page); |
|
|
|
|
|
|
|
|
if (mapping.sparse_alloc) { |
|
|
|
|
|
gmmu->MapSparse(params.offset, mapping.size, mapping.big_page); |
|
|
} else { |
|
|
} else { |
|
|
gmmu->Unmap(params.offset, mapping->size); |
|
|
|
|
|
|
|
|
gmmu->Unmap(params.offset, mapping.size); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
nvmap.UnpinHandle(mapping->handle); |
|
|
|
|
|
|
|
|
|
|
|
mapping_map.erase(params.offset); |
|
|
|
|
|
map_buffer_offsets.erase(params.offset); |
|
|
|
|
|
|
|
|
nvmap.UnpinHandle(mapping.handle); |
|
|
|
|
|
mapping_map.erase(it); |
|
|
|
|
|
map_buffer_offsets.erase(offset_it); |
|
|
} |
|
|
} |
|
|
return NvResult::Success; |
|
|
return NvResult::Success; |
|
|
} |
|
|
} |
|
|
@ -478,8 +436,7 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
NvResult nvhost_as_gpu::GetVARegions1(IoctlGetVaRegions& params) { |
|
|
NvResult nvhost_as_gpu::GetVARegions1(IoctlGetVaRegions& params) { |
|
|
LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, |
|
|
|
|
|
params.buf_size); |
|
|
|
|
|
|
|
|
LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr, params.buf_size); |
|
|
|
|
|
|
|
|
std::scoped_lock lock(mutex); |
|
|
std::scoped_lock lock(mutex); |
|
|
|
|
|
|
|
|
|