|
|
|
@ -71,10 +71,9 @@ NvResult NvMap::Handle::Duplicate(bool internal_session) { |
|
|
|
|
|
|
|
NvMap::NvMap(Container& core_, Tegra::Host1x::Host1x& host1x_) : host1x{host1x_}, core{core_} {} |
|
|
|
|
|
|
|
void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) { |
|
|
|
std::scoped_lock lock(handles_lock); |
|
|
|
|
|
|
|
handles.emplace(handle_description->id, std::move(handle_description)); |
|
|
|
void NvMap::AddHandle(Handle&& handle_description) { |
|
|
|
std::scoped_lock l(handles_lock); |
|
|
|
handles.emplace(handle_description.id, std::move(handle_description)); |
|
|
|
} |
|
|
|
|
|
|
|
void NvMap::UnmapHandle(Handle& handle_description) { |
|
|
|
@ -113,65 +112,56 @@ void NvMap::UnmapHandle(Handle& handle_description) { |
|
|
|
bool NvMap::TryRemoveHandle(const Handle& handle_description) { |
|
|
|
// No dupes left, we can remove from handle map
|
|
|
|
if (handle_description.dupes == 0 && handle_description.internal_dupes == 0) { |
|
|
|
std::scoped_lock lock(handles_lock); |
|
|
|
|
|
|
|
auto it{handles.find(handle_description.id)}; |
|
|
|
std::scoped_lock l(handles_lock); |
|
|
|
auto it = handles.find(handle_description.id); |
|
|
|
if (it != handles.end()) { |
|
|
|
handles.erase(it); |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
} else { |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
NvResult NvMap::CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out) { |
|
|
|
if (!size) [[unlikely]] { |
|
|
|
NvResult NvMap::CreateHandle(u64 size, Handle::Id& out_handle) { |
|
|
|
if (!Common::AlignUp(size, YUZU_PAGESIZE)) { |
|
|
|
return NvResult::BadValue; |
|
|
|
} |
|
|
|
|
|
|
|
u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)}; |
|
|
|
auto handle_description{std::make_shared<Handle>(size, id)}; |
|
|
|
AddHandle(handle_description); |
|
|
|
|
|
|
|
result_out = handle_description; |
|
|
|
u32 id = next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed); |
|
|
|
AddHandle(Handle(size, id)); |
|
|
|
out_handle = id; |
|
|
|
return NvResult::Success; |
|
|
|
} |
|
|
|
|
|
|
|
std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) { |
|
|
|
std::optional<std::reference_wrapper<NvMap::Handle>> NvMap::GetHandle(Handle::Id handle) { |
|
|
|
std::scoped_lock lock(handles_lock); |
|
|
|
try { |
|
|
|
return handles.at(handle); |
|
|
|
} catch (std::out_of_range&) { |
|
|
|
return nullptr; |
|
|
|
} |
|
|
|
if (auto const it = handles.find(handle); it != handles.end()) |
|
|
|
return {it->second}; |
|
|
|
return std::nullopt; |
|
|
|
} |
|
|
|
|
|
|
|
DAddr NvMap::GetHandleAddress(Handle::Id handle) { |
|
|
|
std::scoped_lock lock(handles_lock); |
|
|
|
try { |
|
|
|
return handles.at(handle)->d_address; |
|
|
|
} catch (std::out_of_range&) { |
|
|
|
if (auto const it = handles.find(handle); it != handles.end()) |
|
|
|
return it->second.d_address; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) { |
|
|
|
auto handle_description{GetHandle(handle)}; |
|
|
|
if (!handle_description) [[unlikely]] { |
|
|
|
auto o = GetHandle(handle); |
|
|
|
if (!o) [[unlikely]] { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
auto handle_description = &o->get(); |
|
|
|
|
|
|
|
std::scoped_lock lock(handle_description->mutex); |
|
|
|
const auto map_low_area = [&] { |
|
|
|
if (handle_description->pin_virt_address == 0) { |
|
|
|
auto& gmmu_allocator = host1x.Allocator(); |
|
|
|
auto& gmmu = host1x.GMMU(); |
|
|
|
u32 address = |
|
|
|
gmmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size)); |
|
|
|
gmmu.Map(static_cast<GPUVAddr>(address), handle_description->d_address, |
|
|
|
handle_description->aligned_size); |
|
|
|
u32 address = gmmu_allocator.Allocate(u32(handle_description->aligned_size)); |
|
|
|
gmmu.Map(GPUVAddr(address), handle_description->d_address, handle_description->aligned_size); |
|
|
|
handle_description->pin_virt_address = address; |
|
|
|
} |
|
|
|
}; |
|
|
|
@ -181,17 +171,15 @@ DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) { |
|
|
|
{ |
|
|
|
// Lock now to prevent our queue entry from being removed for allocation in-between the
|
|
|
|
// following check and erase
|
|
|
|
std::scoped_lock queueLock(unmap_queue_lock); |
|
|
|
std::scoped_lock ql(unmap_queue_lock); |
|
|
|
if (handle_description->unmap_queue_entry) { |
|
|
|
unmap_queue.erase(*handle_description->unmap_queue_entry); |
|
|
|
handle_description->unmap_queue_entry.reset(); |
|
|
|
|
|
|
|
if (low_area_pin) { |
|
|
|
map_low_area(); |
|
|
|
handle_description->pins++; |
|
|
|
return static_cast<DAddr>(handle_description->pin_virt_address); |
|
|
|
return DAddr(handle_description->pin_virt_address); |
|
|
|
} |
|
|
|
|
|
|
|
handle_description->pins++; |
|
|
|
return handle_description->d_address; |
|
|
|
} |
|
|
|
@ -212,12 +200,12 @@ DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) { |
|
|
|
while ((address = smmu.Allocate(aligned_up)) == 0) { |
|
|
|
// Free handles until the allocation succeeds
|
|
|
|
std::scoped_lock queueLock(unmap_queue_lock); |
|
|
|
if (auto freeHandleDesc{unmap_queue.front()}) { |
|
|
|
if (auto free_handle = handles.find(unmap_queue.front()); free_handle != handles.end()) { |
|
|
|
// Handles in the unmap queue are guaranteed not to be pinned so don't bother
|
|
|
|
// checking if they are before unmapping
|
|
|
|
std::scoped_lock freeLock(freeHandleDesc->mutex); |
|
|
|
std::scoped_lock fl(free_handle->second.mutex); |
|
|
|
if (handle_description->d_address) |
|
|
|
UnmapHandle(*freeHandleDesc); |
|
|
|
UnmapHandle(free_handle->second); |
|
|
|
} else { |
|
|
|
LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!"); |
|
|
|
} |
|
|
|
@ -235,51 +223,44 @@ DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) { |
|
|
|
|
|
|
|
handle_description->pins++; |
|
|
|
if (low_area_pin) { |
|
|
|
return static_cast<DAddr>(handle_description->pin_virt_address); |
|
|
|
return DAddr(handle_description->pin_virt_address); |
|
|
|
} |
|
|
|
return handle_description->d_address; |
|
|
|
} |
|
|
|
|
|
|
|
void NvMap::UnpinHandle(Handle::Id handle) { |
|
|
|
auto handle_description{GetHandle(handle)}; |
|
|
|
if (!handle_description) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (auto o = GetHandle(handle); o) { |
|
|
|
auto handle_description = &o->get(); |
|
|
|
std::scoped_lock lock(handle_description->mutex); |
|
|
|
if (--handle_description->pins < 0) { |
|
|
|
LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!"); |
|
|
|
} else if (!handle_description->pins) { |
|
|
|
std::scoped_lock queueLock(unmap_queue_lock); |
|
|
|
|
|
|
|
std::scoped_lock ql(unmap_queue_lock); |
|
|
|
// Add to the unmap queue allowing this handle's memory to be freed if needed
|
|
|
|
unmap_queue.push_back(handle_description); |
|
|
|
unmap_queue.push_back(handle); |
|
|
|
handle_description->unmap_queue_entry = std::prev(unmap_queue.end()); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void NvMap::DuplicateHandle(Handle::Id handle, bool internal_session) { |
|
|
|
auto handle_description{GetHandle(handle)}; |
|
|
|
if (!handle_description) { |
|
|
|
auto o = GetHandle(handle); |
|
|
|
if (!o) { |
|
|
|
LOG_CRITICAL(Service_NVDRV, "Unregistered handle!"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
auto result = handle_description->Duplicate(internal_session); |
|
|
|
auto result = o->get().Duplicate(internal_session); |
|
|
|
if (result != NvResult::Success) { |
|
|
|
LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool internal_session) { |
|
|
|
std::weak_ptr<Handle> hWeak{GetHandle(handle)}; |
|
|
|
FreeInfo freeInfo; |
|
|
|
|
|
|
|
// We use a weak ptr here so we can tell when the handle has been freed and report that back to
|
|
|
|
// guest
|
|
|
|
if (auto handle_description = hWeak.lock()) { |
|
|
|
std::scoped_lock lock(handle_description->mutex); |
|
|
|
|
|
|
|
if (auto o = GetHandle(handle); o) { |
|
|
|
auto handle_description = &o->get(); |
|
|
|
std::scoped_lock l(handle_description->mutex); |
|
|
|
if (internal_session) { |
|
|
|
if (--handle_description->internal_dupes < 0) |
|
|
|
LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!"); |
|
|
|
@ -289,25 +270,25 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna |
|
|
|
} else if (handle_description->dupes == 0) { |
|
|
|
// Force unmap the handle
|
|
|
|
if (handle_description->d_address) { |
|
|
|
std::scoped_lock queueLock(unmap_queue_lock); |
|
|
|
std::scoped_lock ql(unmap_queue_lock); |
|
|
|
UnmapHandle(*handle_description); |
|
|
|
} |
|
|
|
|
|
|
|
handle_description->pins = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Try to remove the shared ptr to the handle from the map, if nothing else is using the
|
|
|
|
// handle then it will now be freed when `handle_description` goes out of scope
|
|
|
|
if (TryRemoveHandle(*handle_description)) { |
|
|
|
LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle); |
|
|
|
} else { |
|
|
|
LOG_DEBUG(Service_NVDRV, |
|
|
|
"Tried to free nvmap handle: {} but didn't as it still has duplicates", |
|
|
|
handle); |
|
|
|
} |
|
|
|
|
|
|
|
freeInfo = { |
|
|
|
LOG_DEBUG(Service_NVDRV, "Tried to free nvmap handle: {} but didn't as it still has duplicates", handle); |
|
|
|
} |
|
|
|
// // If the handle hasn't been freed from memory, mark that
|
|
|
|
// if (!hWeak.expired()) {
|
|
|
|
// LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle);
|
|
|
|
// freeInfo.can_unlock = false;
|
|
|
|
// }
|
|
|
|
return FreeInfo{ |
|
|
|
.address = handle_description->address, |
|
|
|
.size = handle_description->size, |
|
|
|
.was_uncached = handle_description->flags.map_uncached.Value() != 0, |
|
|
|
@ -316,14 +297,6 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna |
|
|
|
} else { |
|
|
|
return std::nullopt; |
|
|
|
} |
|
|
|
|
|
|
|
// If the handle hasn't been freed from memory, mark that
|
|
|
|
if (!hWeak.expired()) { |
|
|
|
LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle); |
|
|
|
freeInfo.can_unlock = false; |
|
|
|
} |
|
|
|
|
|
|
|
return freeInfo; |
|
|
|
} |
|
|
|
|
|
|
|
void NvMap::UnmapAllHandles(NvCore::SessionId session_id) { |
|
|
|
@ -334,8 +307,8 @@ void NvMap::UnmapAllHandles(NvCore::SessionId session_id) { |
|
|
|
|
|
|
|
for (auto& [id, handle] : handles_copy) { |
|
|
|
{ |
|
|
|
std::scoped_lock lk{handle->mutex}; |
|
|
|
if (handle->session_id.id != session_id.id || handle->dupes <= 0) { |
|
|
|
std::scoped_lock lk{handle.mutex}; |
|
|
|
if (handle.session_id.id != session_id.id || handle.dupes <= 0) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
|