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 39 FILE *pandecode_dump_stream; 40 41 /* Memory handling */ 42 43 static struct hash_table_u64 *mmap_table; 44 45 static struct util_dynarray ro_mappings; 46 47 static struct pandecode_mapped_memory * 48 pandecode_find_mapped_gpu_mem_containing_rw(uint64_t addr) 49 { 50 return _mesa_hash_table_u64_search(mmap_table, addr & ~(4096 - 1)); 51 } 52 53 struct pandecode_mapped_memory * 54 pandecode_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 67 void 68 pandecode_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 77 static void 78 pandecode_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 91 void 92 pandecode_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 122 void 123 pandecode_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 140 char * 141 pointer_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 162 static int pandecode_dump_frame_count = 0; 163 164 static bool force_stderr = false; 165 166 void 167 pandecode_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 190 static void 191 pandecode_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 201 void 202 pandecode_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 209 void 210 pandecode_next_frame(void) 211 { 212 pandecode_dump_file_close(); 213 pandecode_dump_frame_count++; 214 } 215 216 void 217 pandecode_close(void) 218 { 219 _mesa_hash_table_u64_destroy(mmap_table); 220 util_dynarray_fini(&ro_mappings); 221 pandecode_dump_file_close(); 222 } 223 224 void pandecode_abort_on_fault_v4(mali_ptr jc_gpu_va); 225 void pandecode_abort_on_fault_v5(mali_ptr jc_gpu_va); 226 void pandecode_abort_on_fault_v6(mali_ptr jc_gpu_va); 227 void pandecode_abort_on_fault_v7(mali_ptr jc_gpu_va); 228 229 void 230 pandecode_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 241 void pandecode_jc_v4(mali_ptr jc_gpu_va, unsigned gpu_id); 242 void pandecode_jc_v5(mali_ptr jc_gpu_va, unsigned gpu_id); 243 void pandecode_jc_v6(mali_ptr jc_gpu_va, unsigned gpu_id); 244 void pandecode_jc_v7(mali_ptr jc_gpu_va, unsigned gpu_id); 245 246 void 247 pandecode_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