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
39 FILE *pandecode_dump_stream;
40
41 /* Memory handling */
42
43 static struct rb_tree mmap_tree;
44
45 static struct util_dynarray ro_mappings;
46
47 static 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 */
57 static int
pandecode_cmp_key(const struct rb_node * lhs,const void * key)58 pandecode_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
69 static int
pandecode_cmp(const struct rb_node * lhs,const struct rb_node * rhs)70 pandecode_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
75 static struct pandecode_mapped_memory *
pandecode_find_mapped_gpu_mem_containing_rw(uint64_t addr)76 pandecode_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
85 struct pandecode_mapped_memory *
pandecode_find_mapped_gpu_mem_containing(uint64_t addr)86 pandecode_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
101 void
pandecode_map_read_write(void)102 pandecode_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
113 static void
pandecode_add_name(struct pandecode_mapped_memory * mem,uint64_t gpu_va,const char * name)114 pandecode_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
129 void
pandecode_inject_mmap(uint64_t gpu_va,void * cpu,unsigned sz,const char * name)130 pandecode_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
160 void
pandecode_inject_free(uint64_t gpu_va,unsigned sz)161 pandecode_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
179 char *
pointer_as_memory_reference(uint64_t ptr)180 pointer_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
203 static int pandecode_dump_frame_count = 0;
204
205 static bool force_stderr = false;
206
207 void
pandecode_dump_file_open(void)208 pandecode_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
233 static void
pandecode_dump_file_close(void)234 pandecode_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
246 void
pandecode_initialize(bool to_stderr)247 pandecode_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
254 void
pandecode_next_frame(void)255 pandecode_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
265 void
pandecode_close(void)266 pandecode_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
281 void
pandecode_dump_mappings(void)282 pandecode_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
303 void pandecode_abort_on_fault_v4(mali_ptr jc_gpu_va);
304 void pandecode_abort_on_fault_v5(mali_ptr jc_gpu_va);
305 void pandecode_abort_on_fault_v6(mali_ptr jc_gpu_va);
306 void pandecode_abort_on_fault_v7(mali_ptr jc_gpu_va);
307 void pandecode_abort_on_fault_v9(mali_ptr jc_gpu_va);
308
309 void
pandecode_abort_on_fault(mali_ptr jc_gpu_va,unsigned gpu_id)310 pandecode_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
326 void pandecode_jc_v4(mali_ptr jc_gpu_va, unsigned gpu_id);
327 void pandecode_jc_v5(mali_ptr jc_gpu_va, unsigned gpu_id);
328 void pandecode_jc_v6(mali_ptr jc_gpu_va, unsigned gpu_id);
329 void pandecode_jc_v7(mali_ptr jc_gpu_va, unsigned gpu_id);
330 void pandecode_jc_v9(mali_ptr jc_gpu_va, unsigned gpu_id);
331
332 void
pandecode_jc(mali_ptr jc_gpu_va,unsigned gpu_id)333 pandecode_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