Implement AsGpu::FreeSpace and clean up locking and debug prints

This commit is contained in:
Billy Laws 2021-09-18 13:16:41 +01:00
parent 4a7700bd7c
commit b57710f260
5 changed files with 116 additions and 11 deletions

View File

@ -22,7 +22,13 @@ namespace skyline::service::nvdrv::device::nvhost {
}
PosixResult AsGpu::AllocSpace(In<u32> pages, In<u32> pageSize, In<MappingFlags> flags, InOut<u64> offset) {
state.logger->Debug("pages: 0x{:X}, pageSize: 0x{:X}, flags: ( fixed: {}, sparse: {} ), offset: 0x{:X}", pages, pageSize, flags.fixed, flags.sparse, offset);
state.logger->Debug("pages: 0x{:X}, pageSize: 0x{:X}, flags: ( fixed: {}, sparse: {} ), offset: 0x{:X}",
pages, pageSize, flags.fixed, flags.sparse, offset);
std::scoped_lock lock(mutex);
if (!vm.initialised)
return PosixResult::InvalidArgument;
if (pageSize != VM::PageSize && pageSize != vm.bigPageSize)
return PosixResult::InvalidArgument;
@ -44,7 +50,7 @@ namespace skyline::service::nvdrv::device::nvhost {
else
offset = static_cast<u64>(allocator->Allocate(pages)) << pageSizeBits;
u64 size{static_cast<u64>(pages) * static_cast<u64>(pageSize)};
u64 size{static_cast<u64>(pages) * pageSize};
if (flags.sparse)
state.soc->gm20b.gmmu.Map(offset, GMMU::SparsePlaceholderAddress(), size, true);
@ -58,14 +64,67 @@ namespace skyline::service::nvdrv::device::nvhost {
return PosixResult::Success;
}
void AsGpu::FreeMappingLocked(u64 offset) {
auto mapping{mappingMap.at(offset)};
if (!mapping->fixed) {
auto &allocator{mapping->bigPage ? vm.bigPageAllocator : vm.smallPageAllocator};
u32 pageSizeBits{mapping->bigPage ? vm.bigPageSizeBits : VM::PageSizeBits};
allocator->Free(mapping->offset >> pageSizeBits, mapping->size >> pageSizeBits);
}
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
// Only FreeSpace can unmap them fully
if (mapping->sparseAlloc)
state.soc->gm20b.gmmu.Map(offset, GMMU::SparsePlaceholderAddress(), mapping->size, true);
else
state.soc->gm20b.gmmu.Unmap(offset, mapping->size);
mappingMap.erase(offset);
}
PosixResult AsGpu::FreeSpace(In<u64> offset, In<u32> pages, In<u32> pageSize) {
// TODO: implement after UNMAP
state.logger->Debug("offset: 0x{:X}, pages: 0x{:X}, pageSize: 0x{:X}", offset, pages, pageSize);
std::scoped_lock lock(mutex);
if (!vm.initialised)
return PosixResult::InvalidArgument;
try {
auto allocation{allocationMap[offset]};
if (allocation.pageSize != pageSize || allocation.size != (static_cast<u64>(pages) * pageSize))
return PosixResult::InvalidArgument;
for (const auto &mapping : allocation.mappings)
FreeMapping(mapping->offset);
// Unset sparse flag if required
if (allocation.sparse)
state.soc->gm20b.gmmu.Unmap(offset, allocation.size);
auto &allocator{pageSize == VM::PageSize ? vm.smallPageAllocator : vm.bigPageAllocator};
u32 pageSizeBits{pageSize == VM::PageSize ? VM::PageSizeBits : vm.bigPageSizeBits};
allocator->Free(offset >> pageSizeBits, allocation.size >> pageSizeBits);
allocationMap.erase(offset);
} catch (const std::out_of_range &e) {
return PosixResult::InvalidArgument;
}
return PosixResult::Success;
}
PosixResult AsGpu::UnmapBuffer(In<u64> offset) {
state.logger->Debug("offset: 0x{:X}", offset);
std::scoped_lock lock(mutex);
if (!vm.initialised)
return PosixResult::InvalidArgument;
try {
auto mapping{mappingMap.at(offset)};
@ -92,11 +151,13 @@ namespace skyline::service::nvdrv::device::nvhost {
}
PosixResult AsGpu::MapBufferEx(In<MappingFlags> flags, In<u32> kind, In<core::NvMap::Handle::Id> handle, In<u64> bufferOffset, In<u64> mappingSize, InOut<u64> offset) {
state.logger->Debug("flags: ( fixed: {}, remap: {} ), kind: {}, handle: {}, bufferOffset: 0x{:X}, mappingSize: 0x{:X}, offset: 0x{:X}", flags.fixed, flags.remap, kind, handle, bufferOffset, mappingSize, offset);
std::scoped_lock lock(mutex);
if (!vm.initialised)
return PosixResult::InvalidArgument;
state.logger->Debug("flags: ( fixed: {}, remap: {} ), kind: {}, handle: {}, bufferOffset: 0x{:X}, mappingSize: 0x{:X}, offset: 0x{:X}", flags.fixed, flags.remap, kind, handle, bufferOffset, mappingSize, offset);
// Remaps a subregion of an existing mapping to a different PA
if (flags.remap) {
try {
@ -164,6 +225,8 @@ namespace skyline::service::nvdrv::device::nvhost {
}
PosixResult AsGpu::GetVaRegions(In<u64> bufAddr, InOut<u32> bufSize, Out<std::array<VaRegion, 2>> vaRegions) {
std::scoped_lock lock(mutex);
if (!vm.initialised)
return PosixResult::InvalidArgument;
@ -188,6 +251,8 @@ namespace skyline::service::nvdrv::device::nvhost {
}
PosixResult AsGpu::AllocAsEx(In<u32> flags, In<FileDescriptor> asFd, In<u32> bigPageSize, In<u64> vaRangeStart, In<u64> vaRangeEnd, In<u64> vaRangeSplit) {
std::scoped_lock lock(mutex);
if (vm.initialised)
throw exception("Cannot initialise an address space twice!");
@ -232,6 +297,11 @@ namespace skyline::service::nvdrv::device::nvhost {
}
PosixResult AsGpu::Remap(span<RemapEntry> entries) {
std::scoped_lock lock(mutex);
if (!vm.initialised)
return PosixResult::InvalidArgument;
for (const auto &entry : entries) {
u64 virtAddr{static_cast<u64>(entry.asOffsetBigPages) << vm.bigPageSizeBits};
u64 size{static_cast<u64>(entry.bigPages) << vm.bigPageSizeBits};

View File

@ -38,9 +38,8 @@ namespace skyline::service::nvdrv::device::nvhost {
};
std::map<u64, std::shared_ptr<Mapping>> mappingMap; //!< This maps the base addresses of mapped buffers to their total sizes and mapping type, this is needed as what was originally a single buffer may have been split into multiple GPU side buffers with the remap flag.
std::map<u64, Allocation> allocationMap;
std::map<u64, Allocation> allocationMap; //!< Holds allocations created by AllocSpace from which fixed buffers can be mapped into
std::mutex mutex; //!< Locks all AS operations
struct VM {
static constexpr u32 PageSize{0x1000};
@ -66,6 +65,7 @@ namespace skyline::service::nvdrv::device::nvhost {
bool initialised{};
} vm;
void FreeMappingLocked(u64 offset);
public:
struct MappingFlags {

View File

@ -70,6 +70,9 @@ namespace skyline::service::nvdrv::device::nvhost {
}
PosixResult Ctrl::SyncpointWaitEventImpl(In<Fence> fence, In<i32> timeout, InOut<SyncpointEventValue> value, bool allocate) {
state.logger->Debug("fence: ( id: {}, threshold: {} ), timeout: {}, value: {}, allocate: {}",
fence.id, fence.threshold, timeout, value.val, allocate);
if (fence.id >= soc::host1x::SyncpointCount)
return PosixResult::InvalidArgument;
@ -147,6 +150,8 @@ namespace skyline::service::nvdrv::device::nvhost {
}
PosixResult Ctrl::SyncpointClearEventWait(In<SyncpointEventValue> value) {
state.logger->Debug("slot: {}", value.slot);
u16 slot{value.slot};
if (slot >= SyncpointEventCount)
return PosixResult::InvalidArgument;
@ -178,7 +183,7 @@ namespace skyline::service::nvdrv::device::nvhost {
}
PosixResult Ctrl::SyncpointAllocateEvent(In<u32> slot) {
state.logger->Debug("Registering syncpoint event: {}", slot);
state.logger->Debug("slot: {}", slot);
if (slot >= SyncpointEventCount)
return PosixResult::InvalidArgument;
@ -196,11 +201,15 @@ namespace skyline::service::nvdrv::device::nvhost {
}
PosixResult Ctrl::SyncpointFreeEvent(In<u32> slot) {
state.logger->Debug("slot: {}", slot);
std::lock_guard lock(syncpointEventMutex);
return SyncpointFreeEventLocked(slot);
}
PosixResult Ctrl::SyncpointFreeEventBatch(In<u64> bitmask) {
state.logger->Debug("bitmask: 0x{:X}", bitmask);
auto err{PosixResult::Success};
// Avoid repeated locks/unlocks by just locking now

View File

@ -15,14 +15,23 @@ namespace skyline::service::nvdrv::device::nvhost {
}
PosixResult GpuChannel::SetNvmapFd(In<core::NvMap::Handle::Id> id) {
state.logger->Debug("id: {}", id);
return PosixResult::Success;
}
PosixResult GpuChannel::SetTimeout(In<u32> timeout) {
state.logger->Debug("timeout: {}", timeout);
return PosixResult::Success;
}
PosixResult GpuChannel::SubmitGpfifo(In<u64> userAddress, In<u32> numEntries, InOut<SubmitGpfifoFlags> flags, InOut<Fence> fence, span<soc::gm20b::GpEntry> gpEntries) {
state.logger->Debug("userAddress: 0x{:X}, numEntries: {},"
"flags ( fenceWait: {}, fenceIncrement: {}, hwFormat: {}, suppressWfi: {}, incrementWithValue: {}),"
"fence ( id: {}, threshold: {} )",
userAddress, numEntries,
+flags.fenceWait, +flags.fenceIncrement, +flags.hwFormat, +flags.suppressWfi, +flags.incrementWithValue,
fence.id, fence.threshold);
if (numEntries > gpEntries.size())
throw exception("GpEntry size mismatch!");
@ -54,22 +63,27 @@ namespace skyline::service::nvdrv::device::nvhost {
}
PosixResult GpuChannel::AllocObjCtx(In<u32> classId, In<u32> flags, Out<u64> objId) {
state.logger->Debug("classId: 0x{:X}, flags: 0x{:X}", classId, flags);
return PosixResult::Success;
}
PosixResult GpuChannel::ZcullBind(In<u64> gpuVa, In<u32> mode) {
state.logger->Debug("gpuVa: 0x{:X}, mode: {}", gpuVa, mode);
return PosixResult::Success;
}
PosixResult GpuChannel::SetErrorNotifier(In<u64> offset, In<u64> size, In<u32> mem) {
state.logger->Debug("offset: 0x{:X}, size: 0x{:X}, mem: 0x{:X}", offset, size, mem);
return PosixResult::Success;
}
PosixResult GpuChannel::SetPriority(In<u32> priority) {
state.logger->Debug("priority: {}", priority);
return PosixResult::Success;
}
PosixResult GpuChannel::AllocGpfifoEx2(In<u32> numEntries, In<u32> numJobs, In<u32> flags, Out<Fence> fence) {
state.logger->Debug("numEntries: {}, numJobs: {}, flags: 0x{:X}", numEntries, numJobs, flags);
state.soc->gm20b.gpfifo.Initialize(numEntries);
fence = core.syncpointManager.GetSyncpointFence(channelSyncpoint);
@ -78,10 +92,12 @@ namespace skyline::service::nvdrv::device::nvhost {
}
PosixResult GpuChannel::SetTimeslice(In<u32> timeslice) {
state.logger->Debug("timeslice: {}", timeslice);
return PosixResult::Success;
}
PosixResult GpuChannel::SetUserData(In<u64> userData) {
state.logger->Debug("userData: 0x{:X}", userData);
channelUserData = userData;
return PosixResult::Success;
}

View File

@ -20,6 +20,8 @@ namespace skyline::service::nvdrv::device {
}
PosixResult NvMap::FromId(In<NvMapCore::Handle::Id> id, Out<NvMapCore::Handle::Id> 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.
@ -34,6 +36,8 @@ namespace skyline::service::nvdrv::device {
}
PosixResult NvMap::Alloc(In<NvMapCore::Handle::Id> handle, In<u32> heapMask, In<NvMapCore::Handle::Flags> flags, InOut<u32> align, In<u8> kind, In<u64> 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;
@ -48,12 +52,12 @@ namespace skyline::service::nvdrv::device {
if (!h) [[unlikely]]
return PosixResult::InvalidArgument;
state.logger->Debug("handle: {}, flags: ( mapUncached: {}, keepUncachedAfterFree: {} ), align: 0x{:X}, kind: {}, address: 0x{:X}", handle, flags.mapUncached, flags.keepUncachedAfterFree, align, kind, address);
return h->Alloc(flags, align, kind, address);
}
PosixResult NvMap::Free(In<NvMapCore::Handle::Id> handle, Out<u64> address, Out<u32> size, Out<NvMapCore::Handle::Flags> flags) {
state.logger->Debug("handle: {}", handle);
if (!handle) [[unlikely]]
return PosixResult::Success;
@ -61,12 +65,16 @@ namespace skyline::service::nvdrv::device {
address = freeInfo->address;
size = static_cast<u32>(freeInfo->size);
flags = NvMapCore::Handle::Flags{ .mapUncached = freeInfo->wasUncached };
} else {
state.logger->Debug("Handle not freed");
}
return PosixResult::Success;
}
PosixResult NvMap::Param(In<NvMapCore::Handle::Id> handle, In<HandleParameterType> param, Out<u32> result) {
state.logger->Debug("handle: {}, param: {}", handle, param);
if (!handle)
return PosixResult::InvalidArgument;
@ -103,6 +111,8 @@ namespace skyline::service::nvdrv::device {
}
PosixResult NvMap::GetId(Out<NvMapCore::Handle::Id> id, In<NvMapCore::Handle::Id> handle) {
state.logger->Debug("handle: {}", handle);
// See the comment in FromId for extra info on this function
if (!handle) [[unlikely]]
return PosixResult::InvalidArgument;