// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #include #include #define BOOST_NO_MT #include #undef BOOST_NO_MT #include #include #include #include #include #include #include #include "core/hle/service/nvdrv/core/heap_mapper.h" #include "video_core/host1x/host1x.h" namespace boost { template class fast_pool_allocator; } namespace Service::Nvidia::NvCore { using IntervalCompare = std::less; using IntervalInstance = boost::icl::interval_type_default; using IntervalAllocator = boost::fast_pool_allocator; using IntervalSet = boost::icl::interval_set; using IntervalType = typename IntervalSet::interval_type; template struct counter_add_functor : public boost::icl::identity_based_inplace_combine { // types typedef counter_add_functor type; typedef boost::icl::identity_based_inplace_combine base_type; // public member functions void operator()(Type& current, const Type& added) const { current += added; if (current < base_type::identity_element()) { current = base_type::identity_element(); } } // public static functions static void version(Type&){}; }; using OverlapCombine = counter_add_functor; using OverlapSection = boost::icl::inter_section; using OverlapCounter = boost::icl::split_interval_map; struct HeapMapper::HeapMapperInternal { HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {} ~HeapMapperInternal() = default; template void ForEachInOverlapCounter(OverlapCounter& current_range, VAddr cpu_addr, u64 size, Func&& func) { const DAddr start_address = cpu_addr; const DAddr end_address = start_address + size; const IntervalType search_interval{start_address, end_address}; auto it = current_range.lower_bound(search_interval); if (it == current_range.end()) { return; } auto end_it = current_range.upper_bound(search_interval); for (; it != end_it; it++) { auto& inter = it->first; DAddr inter_addr_end = inter.upper(); DAddr inter_addr = inter.lower(); if (inter_addr_end > end_address) { inter_addr_end = end_address; } if (inter_addr < start_address) { inter_addr = start_address; } func(inter_addr, inter_addr_end, it->second); } } void RemoveEachInOverlapCounter(OverlapCounter& current_range, const IntervalType search_interval, int subtract_value) { bool any_removals = false; current_range.add(std::make_pair(search_interval, subtract_value)); do { any_removals = false; auto it = current_range.lower_bound(search_interval); if (it == current_range.end()) { return; } auto end_it = current_range.upper_bound(search_interval); for (; it != end_it; it++) { if (it->second <= 0) { any_removals = true; current_range.erase(it); break; } } } while (any_removals); } IntervalSet base_set; OverlapCounter mapping_overlaps; Tegra::MaxwellDeviceMemoryManager& device_memory; std::mutex guard; }; HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, size_t smmu_id, Tegra::Host1x::Host1x& host1x) : m_vaddress{start_vaddress}, m_daddress{start_daddress}, m_size{size}, m_smmu_id{smmu_id} { m_internal = std::make_unique(host1x); } HeapMapper::~HeapMapper() { m_internal->device_memory.Unmap(m_daddress, m_size); } DAddr HeapMapper::Map(VAddr start, size_t size) { std::scoped_lock lk(m_internal->guard); m_internal->base_set.clear(); const IntervalType interval{start, start + size}; m_internal->base_set.insert(interval); m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size, [this](VAddr start_addr, VAddr end_addr, int){ const IntervalType other{start_addr, end_addr}; m_internal->base_set.subtract(other); }); if (!m_internal->base_set.empty()) { auto it = m_internal->base_set.begin(); auto end_it = m_internal->base_set.end(); for (; it != end_it; it++) { const VAddr inter_addr_end = it->upper(); const VAddr inter_addr = it->lower(); const size_t offset = inter_addr - m_vaddress; const size_t sub_size = inter_addr_end - inter_addr; m_internal->device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, m_smmu_id); } } m_internal->mapping_overlaps += std::make_pair(interval, 1); m_internal->base_set.clear(); return m_daddress + (start - m_vaddress); } void HeapMapper::Unmap(VAddr start, size_t size) { std::scoped_lock lk(m_internal->guard); m_internal->base_set.clear(); m_internal->ForEachInOverlapCounter(m_internal->mapping_overlaps, start, size, [this](VAddr start_addr, VAddr end_addr, int value) { if (value <= 1) { const IntervalType other{start_addr, end_addr}; m_internal->base_set.insert(other); } }); if (!m_internal->base_set.empty()) { auto it = m_internal->base_set.begin(); auto end_it = m_internal->base_set.end(); for (; it != end_it; it++) { const VAddr inter_addr_end = it->upper(); const VAddr inter_addr = it->lower(); const size_t offset = inter_addr - m_vaddress; const size_t sub_size = inter_addr_end - inter_addr; m_internal->device_memory.Unmap(m_daddress + offset, sub_size); } } const IntervalType to_remove{start, start + size}; m_internal->RemoveEachInOverlapCounter(m_internal->mapping_overlaps, to_remove, -1); m_internal->base_set.clear(); } } // namespace Service::Nvidia::NvCore