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/hash_table.h" 38 39FILE *pandecode_dump_stream; 40 41/* Memory handling */ 42 43static struct hash_table_u64 *mmap_table; 44 45static struct util_dynarray ro_mappings; 46 47static struct pandecode_mapped_memory * 48pandecode_find_mapped_gpu_mem_containing_rw(uint64_t addr) 49{ 50 return _mesa_hash_table_u64_search(mmap_table, addr & ~(4096 - 1)); 51} 52 53struct pandecode_mapped_memory * 54pandecode_find_mapped_gpu_mem_containing(uint64_t addr) 55{ 56 struct pandecode_mapped_memory *mem = pandecode_find_mapped_gpu_mem_containing_rw(addr); 57 58 if (mem && mem->addr && !mem->ro) { 59 mprotect(mem->addr, mem->length, PROT_READ); 60 mem->ro = true; 61 util_dynarray_append(&ro_mappings, struct pandecode_mapped_memory *, mem); 62 } 63 64 return mem; 65} 66 67void 68pandecode_map_read_write(void) 69{ 70 util_dynarray_foreach(&ro_mappings, struct pandecode_mapped_memory *, mem) { 71 (*mem)->ro = false; 72 mprotect((*mem)->addr, (*mem)->length, PROT_READ | PROT_WRITE); 73 } 74 util_dynarray_clear(&ro_mappings); 75} 76 77static void 78pandecode_add_name(struct pandecode_mapped_memory *mem, uint64_t gpu_va, const char *name) 79{ 80 if (!name) { 81 /* If we don't have a name, assign one */ 82 83 snprintf(mem->name, sizeof(mem->name) - 1, 84 "memory_%" PRIx64, gpu_va); 85 } else { 86 assert((strlen(name) + 1) < sizeof(mem->name)); 87 memcpy(mem->name, name, strlen(name) + 1); 88 } 89} 90 91void 92pandecode_inject_mmap(uint64_t gpu_va, void *cpu, unsigned sz, const char *name) 93{ 94 /* First, search if we already mapped this and are just updating an address */ 95 96 struct pandecode_mapped_memory *existing = 97 pandecode_find_mapped_gpu_mem_containing_rw(gpu_va); 98 99 if (existing && existing->gpu_va == gpu_va) { 100 existing->length = sz; 101 existing->addr = cpu; 102 pandecode_add_name(existing, gpu_va, name); 103 return; 104 } 105 106 /* Otherwise, add a fresh mapping */ 107 struct pandecode_mapped_memory *mapped_mem = NULL; 108 109 mapped_mem = calloc(1, sizeof(*mapped_mem)); 110 mapped_mem->gpu_va = gpu_va; 111 mapped_mem->length = sz; 112 mapped_mem->addr = cpu; 113 pandecode_add_name(mapped_mem, gpu_va, name); 114 115 /* Add it to the table */ 116 assert((gpu_va & 4095) == 0); 117 118 for (unsigned i = 0; i < sz; i += 4096) 119 _mesa_hash_table_u64_insert(mmap_table, gpu_va + i, mapped_mem); 120} 121 122void 123pandecode_inject_free(uint64_t gpu_va, unsigned sz) 124{ 125 struct pandecode_mapped_memory *mem = 126 pandecode_find_mapped_gpu_mem_containing_rw(gpu_va); 127 128 if (!mem) 129 return; 130 131 assert(mem->gpu_va == gpu_va); 132 assert(mem->length == sz); 133 134 free(mem); 135 136 for (unsigned i = 0; i < sz; i += 4096) 137 _mesa_hash_table_u64_remove(mmap_table, gpu_va + i); 138} 139 140char * 141pointer_as_memory_reference(uint64_t ptr) 142{ 143 struct pandecode_mapped_memory *mapped; 144 char *out = malloc(128); 145 146 /* Try to find the corresponding mapped zone */ 147 148 mapped = pandecode_find_mapped_gpu_mem_containing_rw(ptr); 149 150 if (mapped) { 151 snprintf(out, 128, "%s + %d", mapped->name, (int) (ptr - mapped->gpu_va)); 152 return out; 153 } 154 155 /* Just use the raw address if other options are exhausted */ 156 157 snprintf(out, 128, "0x%" PRIx64, ptr); 158 return out; 159 160} 161 162static int pandecode_dump_frame_count = 0; 163 164static bool force_stderr = false; 165 166void 167pandecode_dump_file_open(void) 168{ 169 if (pandecode_dump_stream) 170 return; 171 172 /* This does a getenv every frame, so it is possible to use 173 * setenv to change the base at runtime. 174 */ 175 const char *dump_file_base = debug_get_option("PANDECODE_DUMP_FILE", "pandecode.dump"); 176 if (force_stderr || !strcmp(dump_file_base, "stderr")) 177 pandecode_dump_stream = stderr; 178 else { 179 char buffer[1024]; 180 snprintf(buffer, sizeof(buffer), "%s.%04d", dump_file_base, pandecode_dump_frame_count); 181 printf("pandecode: dump command stream to file %s\n", buffer); 182 pandecode_dump_stream = fopen(buffer, "w"); 183 if (!pandecode_dump_stream) 184 fprintf(stderr, 185 "pandecode: failed to open command stream log file %s\n", 186 buffer); 187 } 188} 189 190static void 191pandecode_dump_file_close(void) 192{ 193 if (pandecode_dump_stream && pandecode_dump_stream != stderr) { 194 if (fclose(pandecode_dump_stream)) 195 perror("pandecode: dump file"); 196 197 pandecode_dump_stream = NULL; 198 } 199} 200 201void 202pandecode_initialize(bool to_stderr) 203{ 204 force_stderr = to_stderr; 205 mmap_table = _mesa_hash_table_u64_create(NULL); 206 util_dynarray_init(&ro_mappings, NULL); 207} 208 209void 210pandecode_next_frame(void) 211{ 212 pandecode_dump_file_close(); 213 pandecode_dump_frame_count++; 214} 215 216void 217pandecode_close(void) 218{ 219 _mesa_hash_table_u64_destroy(mmap_table); 220 util_dynarray_fini(&ro_mappings); 221 pandecode_dump_file_close(); 222} 223 224void pandecode_abort_on_fault_v4(mali_ptr jc_gpu_va); 225void pandecode_abort_on_fault_v5(mali_ptr jc_gpu_va); 226void pandecode_abort_on_fault_v6(mali_ptr jc_gpu_va); 227void pandecode_abort_on_fault_v7(mali_ptr jc_gpu_va); 228 229void 230pandecode_abort_on_fault(mali_ptr jc_gpu_va, unsigned gpu_id) 231{ 232 switch (pan_arch(gpu_id)) { 233 case 4: pandecode_abort_on_fault_v4(jc_gpu_va); return; 234 case 5: pandecode_abort_on_fault_v5(jc_gpu_va); return; 235 case 6: pandecode_abort_on_fault_v6(jc_gpu_va); return; 236 case 7: pandecode_abort_on_fault_v7(jc_gpu_va); return; 237 default: unreachable("Unsupported architecture"); 238 } 239} 240 241void pandecode_jc_v4(mali_ptr jc_gpu_va, unsigned gpu_id); 242void pandecode_jc_v5(mali_ptr jc_gpu_va, unsigned gpu_id); 243void pandecode_jc_v6(mali_ptr jc_gpu_va, unsigned gpu_id); 244void pandecode_jc_v7(mali_ptr jc_gpu_va, unsigned gpu_id); 245 246void 247pandecode_jc(mali_ptr jc_gpu_va, unsigned gpu_id) 248{ 249 switch (pan_arch(gpu_id)) { 250 case 4: pandecode_jc_v4(jc_gpu_va, gpu_id); return; 251 case 5: pandecode_jc_v5(jc_gpu_va, gpu_id); return; 252 case 6: pandecode_jc_v6(jc_gpu_va, gpu_id); return; 253 case 7: pandecode_jc_v7(jc_gpu_va, gpu_id); return; 254 default: unreachable("Unsupported architecture"); 255 } 256} 257