1/*
2 * Copyright (C) 2019 Alyssa Rosenzweig
3 * Copyright (C) 2017-2018 Lyude Paul
4 * Copyright (C) 2019 Collabora, Ltd.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <assert.h>
29#include <stdint.h>
30#include <string.h>
31#include <sys/mman.h>
32
33#include "decode.h"
34#include "util/macros.h"
35#include "util/u_debug.h"
36#include "util/u_dynarray.h"
37#include "util/simple_mtx.h"
38
39FILE *pandecode_dump_stream;
40
41/* Memory handling */
42
43static struct rb_tree mmap_tree;
44
45static struct util_dynarray ro_mappings;
46
47static simple_mtx_t pandecode_lock = _SIMPLE_MTX_INITIALIZER_NP;
48
49#define to_mapped_memory(x) \
50	rb_node_data(struct pandecode_mapped_memory, x, node)
51
52/*
53 * Compare a GPU VA to a node, considering a GPU VA to be equal to a node if it
54 * is contained in the interval the node represents. This lets us store
55 * intervals in our tree.
56 */
57static int
58pandecode_cmp_key(const struct rb_node *lhs, const void *key)
59{
60        struct pandecode_mapped_memory *mem = to_mapped_memory(lhs);
61        uint64_t *gpu_va = (uint64_t *) key;
62
63        if (mem->gpu_va <= *gpu_va && *gpu_va < (mem->gpu_va + mem->length))
64                return 0;
65        else
66                return mem->gpu_va - *gpu_va;
67}
68
69static int
70pandecode_cmp(const struct rb_node *lhs, const struct rb_node *rhs)
71{
72        return to_mapped_memory(lhs)->gpu_va - to_mapped_memory(rhs)->gpu_va;
73}
74
75static struct pandecode_mapped_memory *
76pandecode_find_mapped_gpu_mem_containing_rw(uint64_t addr)
77{
78        simple_mtx_assert_locked(&pandecode_lock);
79
80        struct rb_node *node = rb_tree_search(&mmap_tree, &addr, pandecode_cmp_key);
81
82        return to_mapped_memory(node);
83}
84
85struct pandecode_mapped_memory *
86pandecode_find_mapped_gpu_mem_containing(uint64_t addr)
87{
88        simple_mtx_assert_locked(&pandecode_lock);
89
90        struct pandecode_mapped_memory *mem = pandecode_find_mapped_gpu_mem_containing_rw(addr);
91
92        if (mem && mem->addr && !mem->ro) {
93                mprotect(mem->addr, mem->length, PROT_READ);
94                mem->ro = true;
95                util_dynarray_append(&ro_mappings, struct pandecode_mapped_memory *, mem);
96        }
97
98        return mem;
99}
100
101void
102pandecode_map_read_write(void)
103{
104        simple_mtx_assert_locked(&pandecode_lock);
105
106        util_dynarray_foreach(&ro_mappings, struct pandecode_mapped_memory *, mem) {
107                (*mem)->ro = false;
108                mprotect((*mem)->addr, (*mem)->length, PROT_READ | PROT_WRITE);
109        }
110        util_dynarray_clear(&ro_mappings);
111}
112
113static void
114pandecode_add_name(struct pandecode_mapped_memory *mem, uint64_t gpu_va, const char *name)
115{
116        simple_mtx_assert_locked(&pandecode_lock);
117
118        if (!name) {
119                /* If we don't have a name, assign one */
120
121                snprintf(mem->name, sizeof(mem->name) - 1,
122                         "memory_%" PRIx64, gpu_va);
123        } else {
124                assert((strlen(name) + 1) < sizeof(mem->name));
125                memcpy(mem->name, name, strlen(name) + 1);
126        }
127}
128
129void
130pandecode_inject_mmap(uint64_t gpu_va, void *cpu, unsigned sz, const char *name)
131{
132        simple_mtx_lock(&pandecode_lock);
133
134        /* First, search if we already mapped this and are just updating an address */
135
136        struct pandecode_mapped_memory *existing =
137                pandecode_find_mapped_gpu_mem_containing_rw(gpu_va);
138
139        if (existing && existing->gpu_va == gpu_va) {
140                existing->length = sz;
141                existing->addr = cpu;
142                pandecode_add_name(existing, gpu_va, name);
143        } else {
144                /* Otherwise, add a fresh mapping */
145                struct pandecode_mapped_memory *mapped_mem = NULL;
146
147                mapped_mem = calloc(1, sizeof(*mapped_mem));
148                mapped_mem->gpu_va = gpu_va;
149                mapped_mem->length = sz;
150                mapped_mem->addr = cpu;
151                pandecode_add_name(mapped_mem, gpu_va, name);
152
153                /* Add it to the tree */
154                rb_tree_insert(&mmap_tree, &mapped_mem->node, pandecode_cmp);
155        }
156
157        simple_mtx_unlock(&pandecode_lock);
158}
159
160void
161pandecode_inject_free(uint64_t gpu_va, unsigned sz)
162{
163        simple_mtx_lock(&pandecode_lock);
164
165        struct pandecode_mapped_memory *mem =
166                pandecode_find_mapped_gpu_mem_containing_rw(gpu_va);
167
168        if (mem) {
169                assert(mem->gpu_va == gpu_va);
170                assert(mem->length == sz);
171
172                rb_tree_remove(&mmap_tree, &mem->node);
173                free(mem);
174        }
175
176        simple_mtx_unlock(&pandecode_lock);
177}
178
179char *
180pointer_as_memory_reference(uint64_t ptr)
181{
182        simple_mtx_assert_locked(&pandecode_lock);
183
184        struct pandecode_mapped_memory *mapped;
185        char *out = malloc(128);
186
187        /* Try to find the corresponding mapped zone */
188
189        mapped = pandecode_find_mapped_gpu_mem_containing_rw(ptr);
190
191        if (mapped) {
192                snprintf(out, 128, "%s + %d", mapped->name, (int) (ptr - mapped->gpu_va));
193                return out;
194        }
195
196        /* Just use the raw address if other options are exhausted */
197
198        snprintf(out, 128, "0x%" PRIx64, ptr);
199        return out;
200
201}
202
203static int pandecode_dump_frame_count = 0;
204
205static bool force_stderr = false;
206
207void
208pandecode_dump_file_open(void)
209{
210        simple_mtx_assert_locked(&pandecode_lock);
211
212        if (pandecode_dump_stream)
213                return;
214
215        /* This does a getenv every frame, so it is possible to use
216         * setenv to change the base at runtime.
217         */
218        const char *dump_file_base = debug_get_option("PANDECODE_DUMP_FILE", "pandecode.dump");
219        if (force_stderr || !strcmp(dump_file_base, "stderr"))
220                pandecode_dump_stream = stderr;
221        else {
222                char buffer[1024];
223                snprintf(buffer, sizeof(buffer), "%s.%04d", dump_file_base, pandecode_dump_frame_count);
224                printf("pandecode: dump command stream to file %s\n", buffer);
225                pandecode_dump_stream = fopen(buffer, "w");
226                if (!pandecode_dump_stream)
227                        fprintf(stderr,
228                                "pandecode: failed to open command stream log file %s\n",
229                                buffer);
230        }
231}
232
233static void
234pandecode_dump_file_close(void)
235{
236        simple_mtx_assert_locked(&pandecode_lock);
237
238        if (pandecode_dump_stream && pandecode_dump_stream != stderr) {
239                if (fclose(pandecode_dump_stream))
240                        perror("pandecode: dump file");
241
242                pandecode_dump_stream = NULL;
243        }
244}
245
246void
247pandecode_initialize(bool to_stderr)
248{
249        force_stderr = to_stderr;
250        rb_tree_init(&mmap_tree);
251        util_dynarray_init(&ro_mappings, NULL);
252}
253
254void
255pandecode_next_frame(void)
256{
257        simple_mtx_lock(&pandecode_lock);
258
259        pandecode_dump_file_close();
260        pandecode_dump_frame_count++;
261
262        simple_mtx_unlock(&pandecode_lock);
263}
264
265void
266pandecode_close(void)
267{
268        simple_mtx_lock(&pandecode_lock);
269
270        rb_tree_foreach_safe(struct pandecode_mapped_memory, it, &mmap_tree, node) {
271                rb_tree_remove(&mmap_tree, &it->node);
272                free(it);
273        }
274
275        util_dynarray_fini(&ro_mappings);
276        pandecode_dump_file_close();
277
278        simple_mtx_unlock(&pandecode_lock);
279}
280
281void
282pandecode_dump_mappings(void)
283{
284        simple_mtx_lock(&pandecode_lock);
285
286        pandecode_dump_file_open();
287
288        rb_tree_foreach(struct pandecode_mapped_memory, it, &mmap_tree, node) {
289                if (!it->addr || !it->length)
290                        continue;
291
292                fprintf(pandecode_dump_stream, "Buffer: %s gpu %" PRIx64 "\n\n",
293                        it->name, it->gpu_va);
294
295                pan_hexdump(pandecode_dump_stream, it->addr, it->length, false);
296                fprintf(pandecode_dump_stream, "\n");
297        }
298
299        fflush(pandecode_dump_stream);
300        simple_mtx_unlock(&pandecode_lock);
301}
302
303void pandecode_abort_on_fault_v4(mali_ptr jc_gpu_va);
304void pandecode_abort_on_fault_v5(mali_ptr jc_gpu_va);
305void pandecode_abort_on_fault_v6(mali_ptr jc_gpu_va);
306void pandecode_abort_on_fault_v7(mali_ptr jc_gpu_va);
307void pandecode_abort_on_fault_v9(mali_ptr jc_gpu_va);
308
309void
310pandecode_abort_on_fault(mali_ptr jc_gpu_va, unsigned gpu_id)
311{
312        simple_mtx_lock(&pandecode_lock);
313
314        switch (pan_arch(gpu_id)) {
315        case 4: pandecode_abort_on_fault_v4(jc_gpu_va); break;
316        case 5: pandecode_abort_on_fault_v5(jc_gpu_va); break;
317        case 6: pandecode_abort_on_fault_v6(jc_gpu_va); break;
318        case 7: pandecode_abort_on_fault_v7(jc_gpu_va); break;
319        case 9: pandecode_abort_on_fault_v9(jc_gpu_va); break;
320        default: unreachable("Unsupported architecture");
321        }
322
323        simple_mtx_unlock(&pandecode_lock);
324}
325
326void pandecode_jc_v4(mali_ptr jc_gpu_va, unsigned gpu_id);
327void pandecode_jc_v5(mali_ptr jc_gpu_va, unsigned gpu_id);
328void pandecode_jc_v6(mali_ptr jc_gpu_va, unsigned gpu_id);
329void pandecode_jc_v7(mali_ptr jc_gpu_va, unsigned gpu_id);
330void pandecode_jc_v9(mali_ptr jc_gpu_va, unsigned gpu_id);
331
332void
333pandecode_jc(mali_ptr jc_gpu_va, unsigned gpu_id)
334{
335        simple_mtx_lock(&pandecode_lock);
336
337        switch (pan_arch(gpu_id)) {
338        case 4: pandecode_jc_v4(jc_gpu_va, gpu_id); break;
339        case 5: pandecode_jc_v5(jc_gpu_va, gpu_id); break;
340        case 6: pandecode_jc_v6(jc_gpu_va, gpu_id); break;
341        case 7: pandecode_jc_v7(jc_gpu_va, gpu_id); break;
342        case 9: pandecode_jc_v9(jc_gpu_va, gpu_id); break;
343        default: unreachable("Unsupported architecture");
344        }
345
346        simple_mtx_unlock(&pandecode_lock);
347}
348