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