summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service/nvdrv/core/heap_mapper.cpp
blob: 59d993bc61aeb8ba5d6f17b719fb2547ae37b723 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include <mutex>

#include <boost/container/small_vector.hpp>
#define BOOST_NO_MT
#include <boost/pool/detail/mutex.hpp>
#undef BOOST_NO_MT
#include <boost/icl/interval.hpp>
#include <boost/icl/interval_base_set.hpp>
#include <boost/icl/interval_set.hpp>
#include <boost/icl/split_interval_map.hpp>
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <boost/pool/poolfwd.hpp>

#include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "video_core/host1x/host1x.h"

namespace boost {
template <typename T>
class fast_pool_allocator<T, default_user_allocator_new_delete, details::pool::null_mutex, 4096, 0>;
}

namespace Service::Nvidia::NvCore {

using IntervalCompare = std::less<DAddr>;
using IntervalInstance = boost::icl::interval_type_default<DAddr, std::less>;
using IntervalAllocator = boost::fast_pool_allocator<DAddr>;
using IntervalSet = boost::icl::interval_set<DAddr>;
using IntervalType = typename IntervalSet::interval_type;

template <typename Type>
struct counter_add_functor : public boost::icl::identity_based_inplace_combine<Type> {
    // types
    typedef counter_add_functor<Type> type;
    typedef boost::icl::identity_based_inplace_combine<Type> 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<int>;
using OverlapSection = boost::icl::inter_section<int>;
using OverlapCounter = boost::icl::split_interval_map<DAddr, int>;

struct HeapMapper::HeapMapperInternal {
    HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : device_memory{host1x.MemoryManager()} {}
    ~HeapMapperInternal() = default;

    template <typename Func>
    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<HeapMapperInternal>(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