1 /*
2 * Copyright 2024 Intel Corporation
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "aubinator_error_decode_xe.h"
7
8 #include <stdint.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include "aubinator_error_decode_lib.h"
13 #include "intel/compiler/brw_isa_info.h"
14 #include "intel/dev/intel_device_info.h"
15
16 enum xe_vm_topic_type {
17 XE_VM_TOPIC_TYPE_UNKNOWN = 0,
18 XE_VM_TOPIC_TYPE_LENGTH,
19 XE_VM_TOPIC_TYPE_DATA,
20 XE_VM_TOPIC_TYPE_ERROR,
21 };
22
23 struct xe_vm_entry {
24 uint64_t address;
25 uint32_t length;
26 const uint32_t *data;
27 };
28
29 struct xe_vm {
30 /* TODO: entries could be appended sorted or a hash could be used to
31 * optimize performance
32 */
33 struct xe_vm_entry *entries;
34 uint32_t entries_len;
35 };
36
37 static const char *
read_parameter_helper(const char * line,const char * parameter)38 read_parameter_helper(const char *line, const char *parameter)
39 {
40 if (!strstr(line, parameter))
41 return NULL;
42
43 while (*line != ':')
44 line++;
45 /* skip ':' and ' ' */
46 line += 2;
47
48 return line;
49 }
50
51 /* parse lines like 'batch_addr[0]: 0x0000effeffff5000 */
52 static bool
read_u64_hexacimal_parameter(const char * line,const char * parameter,uint64_t * value)53 read_u64_hexacimal_parameter(const char *line, const char *parameter, uint64_t *value)
54 {
55 line = read_parameter_helper(line, parameter);
56 if (!line)
57 return false;
58
59 *value = (uint64_t)strtoull(line, NULL, 0);
60 return true;
61 }
62
63 /* parse lines like 'PCI ID: 0x9a49' */
64 static bool
read_hexacimal_parameter(const char * line,const char * parameter,int * value)65 read_hexacimal_parameter(const char *line, const char *parameter, int *value)
66 {
67 line = read_parameter_helper(line, parameter);
68 if (!line)
69 return false;
70
71 *value = (int)strtol(line, NULL, 0);
72 return true;
73 }
74
75 /* parse lines like 'rcs0 (physical), logical instance=0' */
76 static bool
read_xe_engine_name(const char * line,char * ring_name)77 read_xe_engine_name(const char *line, char *ring_name)
78 {
79 int i;
80
81 if (!strstr(line, " (physical), logical instance="))
82 return false;
83
84 i = 0;
85 for (i = 0; *line != ' '; i++, line++)
86 ring_name[i] = *line;
87
88 ring_name[i] = 0;
89 return true;
90 }
91
92 /* return type of VM topic lines like '[200000].data: x...' and points
93 * value_ptr to first char of data of topic type
94 */
95 static enum xe_vm_topic_type
read_xe_vm_line(const char * line,uint64_t * address,const char ** value_ptr)96 read_xe_vm_line(const char *line, uint64_t *address, const char **value_ptr)
97 {
98 enum xe_vm_topic_type type;
99 char text_addr[64];
100 int i;
101
102 if (*line != '[')
103 return XE_VM_TOPIC_TYPE_UNKNOWN;
104
105 for (i = 0, line++; *line != ']'; i++, line++)
106 text_addr[i] = *line;
107
108 text_addr[i] = 0;
109 *address = (uint64_t)strtoull(text_addr, NULL, 16);
110
111 /* at this point line points to last address digit so +3 to point to type */
112 line += 2;
113 switch (*line) {
114 case 'd':
115 type = XE_VM_TOPIC_TYPE_DATA;
116 break;
117 case 'l':
118 type = XE_VM_TOPIC_TYPE_LENGTH;
119 break;
120 case 'e':
121 type = XE_VM_TOPIC_TYPE_ERROR;
122 break;
123 default:
124 printf("type char: %c\n", *line);
125 return XE_VM_TOPIC_TYPE_UNKNOWN;
126 }
127
128 for (; *line != ':'; line++);
129
130 *value_ptr = line + 2;
131 return type;
132 }
133
xe_vm_init(struct xe_vm * xe_vm)134 static void xe_vm_init(struct xe_vm *xe_vm)
135 {
136 xe_vm->entries = NULL;
137 xe_vm->entries_len = 0;
138 }
139
xe_vm_fini(struct xe_vm * xe_vm)140 static void xe_vm_fini(struct xe_vm *xe_vm)
141 {
142 uint32_t i;
143
144 for (i = 0; i < xe_vm->entries_len; i++)
145 free((uint32_t *)xe_vm->entries[i].data);
146
147 free(xe_vm->entries);
148 }
149
150 /*
151 * xe_vm_fini() will take care to free data
152 */
153 static bool
xe_vm_append(struct xe_vm * xe_vm,const uint64_t address,const uint32_t length,const uint32_t * data)154 xe_vm_append(struct xe_vm *xe_vm, const uint64_t address, const uint32_t length, const uint32_t *data)
155 {
156 size_t len = sizeof(*xe_vm->entries) * (xe_vm->entries_len + 1);
157
158 xe_vm->entries = realloc(xe_vm->entries, len);
159 if (!xe_vm->entries)
160 return false;
161
162 xe_vm->entries[xe_vm->entries_len].address = address;
163 xe_vm->entries[xe_vm->entries_len].length = length;
164 xe_vm->entries[xe_vm->entries_len].data = data;
165 xe_vm->entries_len++;
166 return true;
167 }
168
169 static const struct xe_vm_entry *
xe_vm_entry_get(struct xe_vm * xe_vm,const uint64_t address)170 xe_vm_entry_get(struct xe_vm *xe_vm, const uint64_t address)
171 {
172 uint32_t i;
173
174 for (i = 0; i < xe_vm->entries_len; i++) {
175 struct xe_vm_entry *entry = &xe_vm->entries[i];
176
177 if (entry->address == address)
178 return entry;
179
180 if (address > entry->address &&
181 address < (entry->address + entry->length))
182 return entry;
183 }
184
185 return NULL;
186 }
187
188 static uint32_t *
xe_vm_entry_address_get_data(const struct xe_vm_entry * entry,const uint64_t address)189 xe_vm_entry_address_get_data(const struct xe_vm_entry *entry, const uint64_t address)
190 {
191 uint32_t offset = (address - entry->address) / sizeof(uint32_t);
192 return (uint32_t *)&entry->data[offset];
193 }
194
195 static uint32_t
xe_vm_entry_address_get_len(const struct xe_vm_entry * entry,const uint64_t address)196 xe_vm_entry_address_get_len(const struct xe_vm_entry *entry, const uint64_t address)
197 {
198 return entry->length - (address - entry->address);
199 }
200
201 static bool
ascii85_decode_allocated(const char * in,uint32_t * out,uint32_t vm_entry_bytes_len)202 ascii85_decode_allocated(const char *in, uint32_t *out, uint32_t vm_entry_bytes_len)
203 {
204 const uint32_t dword_len = vm_entry_bytes_len / sizeof(uint32_t);
205 uint32_t i;
206
207 for (i = 0; (*in >= '!') && (*in <= 'z') && (i < dword_len); i++)
208 in = ascii85_decode_char(in, &out[i]);
209
210 if (dword_len != i)
211 printf("mismatch dword_len=%u i=%u\n", dword_len, i);
212
213 return dword_len == i && (*in < '!' || *in > 'z');
214 }
215
216 static struct intel_batch_decode_bo
get_bo(void * user_data,bool ppgtt,uint64_t bo_addr)217 get_bo(void *user_data, bool ppgtt, uint64_t bo_addr)
218 {
219 struct intel_batch_decode_bo ret = {};
220 const struct xe_vm_entry *vm_entry;
221 struct xe_vm *xe_vm = user_data;
222
223 if (!ppgtt)
224 return ret;
225
226 vm_entry = xe_vm_entry_get(xe_vm, bo_addr);
227 if (!vm_entry)
228 return ret;
229
230 ret.addr = bo_addr;
231 ret.map = xe_vm_entry_address_get_data(vm_entry, bo_addr);
232 ret.size = xe_vm_entry_address_get_len(vm_entry, bo_addr);
233
234 return ret;
235 }
236
237 void
read_xe_data_file(FILE * file,enum intel_batch_decode_flags batch_flags,const char * spec_xml_path,bool option_dump_kernels,bool option_print_all_bb)238 read_xe_data_file(FILE *file,
239 enum intel_batch_decode_flags batch_flags,
240 const char *spec_xml_path,
241 bool option_dump_kernels,
242 bool option_print_all_bb)
243 {
244 struct intel_batch_decode_ctx batch_ctx;
245 struct intel_device_info devinfo;
246 struct intel_spec *spec = NULL;
247 struct brw_isa_info isa;
248 struct {
249 uint64_t *addrs;
250 uint8_t len;
251 } batch_buffers = { .addrs = NULL, .len = 0 };
252 enum intel_engine_class engine_class = INTEL_ENGINE_CLASS_INVALID;
253 uint32_t *vm_entry_data = NULL;
254 uint32_t vm_entry_len = 0;
255 bool ring_wraps = false;
256 uint64_t acthd = 0;
257 struct xe_vm xe_vm;
258 char *line = NULL;
259 size_t line_size;
260
261 xe_vm_init(&xe_vm);
262
263 while (getline(&line, &line_size, file) > 0) {
264 static const char *xe_topic_strings[] = {
265 "**** Xe Device Coredump ****",
266 "**** GuC CT ****",
267 "**** Job ****",
268 "**** HW Engines ****",
269 "**** VM state ****",
270 };
271 enum {
272 TOPIC_DEVICE = 0,
273 TOPIC_GUC_CT,
274 TOPIC_JOB,
275 TOPIC_HW_ENGINES,
276 TOPIC_VM,
277 TOPIC_INVALID,
278 } xe_topic = TOPIC_INVALID;
279 bool topic_changed = false;
280 bool print_line = true;
281
282 /* handle Xe dump topics */
283 for (int i = 0; i < ARRAY_SIZE(xe_topic_strings); i++) {
284 if (strncmp(xe_topic_strings[i], line, strlen(xe_topic_strings[i])) == 0) {
285 topic_changed = true;
286 xe_topic = i;
287 print_line = (xe_topic != TOPIC_VM);
288 break;
289 }
290 }
291 if (topic_changed) {
292 if (print_line)
293 fputs(line, stdout);
294 continue;
295 }
296
297 switch (xe_topic) {
298 case TOPIC_DEVICE: {
299 int int_value;
300
301 if (read_hexacimal_parameter(line, "PCI ID", &int_value)) {
302 if (intel_get_device_info_from_pci_id(int_value, &devinfo)) {
303 printf("Detected GFX ver %i\n", devinfo.verx10);
304 brw_init_isa_info(&isa, &devinfo);
305
306 if (spec_xml_path == NULL)
307 spec = intel_spec_load(&devinfo);
308 else
309 spec = intel_spec_load_from_path(&devinfo, spec_xml_path);
310 } else {
311 printf("Unable to identify devid: 0x%x\n", int_value);
312 }
313 }
314
315 break;
316 }
317 case TOPIC_HW_ENGINES: {
318 char engine_name[64];
319 uint64_t u64_reg;
320
321 if (read_xe_engine_name(line, engine_name))
322 ring_name_to_class(engine_name, &engine_class);
323
324 if (read_u64_hexacimal_parameter(line, "ACTHD", &u64_reg))
325 acthd = u64_reg;
326
327 /* TODO: parse other engine registers */
328 break;
329 }
330 case TOPIC_JOB: {
331 uint64_t u64_value;
332
333 if (read_u64_hexacimal_parameter(line, "batch_addr[", &u64_value)) {
334 batch_buffers.addrs = realloc(batch_buffers.addrs, sizeof(uint64_t) * (batch_buffers.len + 1));
335 batch_buffers.addrs[batch_buffers.len] = u64_value;
336 batch_buffers.len++;
337 }
338
339 break;
340 }
341 case TOPIC_VM: {
342 enum xe_vm_topic_type type;
343 const char *value_ptr;
344 uint64_t address;
345
346 print_line = false;
347 type = read_xe_vm_line(line, &address, &value_ptr);
348 switch (type) {
349 case XE_VM_TOPIC_TYPE_DATA: {
350 if (!ascii85_decode_allocated(value_ptr, vm_entry_data, vm_entry_len))
351 printf("Failed to parse VMA 0x%lx data\n", address);
352 break;
353 }
354 case XE_VM_TOPIC_TYPE_LENGTH: {
355 vm_entry_len = strtoul(value_ptr, NULL, 0);
356 vm_entry_data = calloc(1, vm_entry_len);
357 if (!vm_entry_data) {
358 printf("Out of memory to allocate a buffer to store content of VMA 0x%lx\n", address);
359 break;
360 }
361 if (!xe_vm_append(&xe_vm, address, vm_entry_len, vm_entry_data)) {
362 printf("xe_vm_append() failed for VMA 0x%lx\n", address);
363 break;
364 }
365 break;
366 }
367 case XE_VM_TOPIC_TYPE_ERROR:
368 printf("VMA 0x%lx not present in dump, content will be zeroed. %s\n", address, line);
369 break;
370 default:
371 printf("Not expected line in VM state: %s", line);
372 }
373 break;
374 }
375 default:
376 break;
377 }
378
379 if (print_line)
380 fputs(line, stdout);
381 }
382
383 printf("**** Batch buffers ****\n");
384 intel_batch_decode_ctx_init_brw(&batch_ctx, &isa, &devinfo, stdout,
385 batch_flags, spec_xml_path, get_bo,
386 NULL, &xe_vm);
387 batch_ctx.acthd = acthd;
388
389 if (option_dump_kernels)
390 batch_ctx.shader_binary = dump_shader_binary;
391
392 for (int i = 0; i < batch_buffers.len; i++) {
393 const uint64_t bb_addr = batch_buffers.addrs[i];
394 const struct xe_vm_entry *vm_entry = xe_vm_entry_get(&xe_vm, bb_addr);
395 const char *engine_name = intel_engines_class_to_string(engine_class);
396 const char *buffer_name = "batch buffer";
397 const uint32_t *bb_data;
398 bool is_ring_buffer;
399 uint32_t bb_len;
400
401 if (!vm_entry)
402 continue;
403
404 bb_data = xe_vm_entry_address_get_data(vm_entry, bb_addr);
405 bb_len = xe_vm_entry_address_get_len(vm_entry, bb_addr);
406
407 printf("--- %s (%s) at 0x%016"PRIx64"\n",
408 buffer_name, engine_name, batch_buffers.addrs[i]);
409
410 /* TODO: checks around buffer_name are copied from i915, if Xe KMD
411 * starts to dump HW context or ring buffer this might become
412 * useful.
413 */
414 is_ring_buffer = strcmp(buffer_name, "ring buffer") == 0;
415 if (option_print_all_bb || is_ring_buffer ||
416 strcmp(buffer_name, "batch buffer") == 0 ||
417 strcmp(buffer_name, "HW Context") == 0) {
418 if (is_ring_buffer && ring_wraps)
419 batch_ctx.flags &= ~INTEL_BATCH_DECODE_OFFSETS;
420 batch_ctx.engine = engine_class;
421 intel_print_batch(&batch_ctx, bb_data, bb_len, bb_addr, is_ring_buffer);
422 batch_ctx.flags = batch_flags;
423 }
424 }
425
426 intel_batch_decode_ctx_finish(&batch_ctx);
427 intel_spec_destroy(spec);
428 free(batch_buffers.addrs);
429 free(line);
430 xe_vm_fini(&xe_vm);
431 }
432