intel/error2aub: write GGTT buffers into the aub file
authorLionel Landwerlin <lionel.g.landwerlin@intel.com>
Tue, 4 Sep 2018 13:45:37 +0000 (14:45 +0100)
committerLionel Landwerlin <lionel.g.landwerlin@intel.com>
Fri, 8 Mar 2019 11:01:14 +0000 (11:01 +0000)
Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Reviewed-by: Rafael Antognolli <rafael.antognolli@intel.com>
src/intel/tools/aub_write.c
src/intel/tools/aub_write.h
src/intel/tools/error2aub.c

index e2bd0f4cede2e4f0ae620b91501c8d52f342b840..0447bbb39c20dea7ff371e2b1e746f84192944ec 100644 (file)
@@ -543,6 +543,71 @@ aub_write_default_setup(struct aub_file *aub)
       write_execlists_default_setup(aub);
    else
       write_legacy_default_setup(aub);
+
+   aub->has_default_setup = true;
+}
+
+void
+aub_write_ggtt(struct aub_file *aub, uint64_t virt_addr, uint64_t size, const void *data)
+{
+   if (aub->verbose_log_file) {
+      fprintf(aub->verbose_log_file,
+              " Writting GGTT address: 0x%" PRIx64 ", size: %" PRIu64"\n",
+              virt_addr, size);
+   }
+
+   /* Default setup assumes a 1 to 1 mapping between physical and virtual GGTT
+    * addresses. This is somewhat incompatible with the aub_write_ggtt()
+    * function. In practice it doesn't matter as the GGTT writes are used to
+    * replace the default setup and we've taken care to setup the PML4 as the
+    * top of the GGTT.
+    */
+   assert(!aub->has_default_setup);
+
+   /* Makes the code below a bit simpler. In practice all of the write we
+    * receive from error2aub are page aligned.
+    */
+   assert(virt_addr % 4096 == 0);
+   assert((aub->phys_addrs_allocator + size) < (1UL << 32));
+
+   /* GGTT PT */
+   uint32_t ggtt_ptes = DIV_ROUND_UP(size, 4096);
+   uint64_t phys_addr = aub->phys_addrs_allocator << 12;
+   aub->phys_addrs_allocator += ggtt_ptes;
+
+   if (aub->verbose_log_file) {
+      fprintf(aub->verbose_log_file,
+              " Writting GGTT address: 0x%" PRIx64 ", size: %" PRIu64" phys_addr=0x%lx entries=%u\n",
+              virt_addr, size, phys_addr, ggtt_ptes);
+   }
+
+   mem_trace_memory_write_header_out(aub,
+                                     (virt_addr >> 12) * GEN8_PTE_SIZE,
+                                     ggtt_ptes * GEN8_PTE_SIZE,
+                                     AUB_MEM_TRACE_MEMORY_ADDRESS_SPACE_GGTT_ENTRY,
+                                     "GGTT PT");
+   for (uint32_t i = 0; i < ggtt_ptes; i++) {
+      dword_out(aub, 1 + phys_addr + i * 4096);
+      dword_out(aub, 0);
+   }
+
+   /* We write the GGTT buffer through the GGTT aub command rather than the
+    * PHYSICAL aub command. This is because the Gen9 simulator seems to have 2
+    * different set of memory pools for GGTT and physical (probably someone
+    * didn't really understand the concept?).
+    */
+   static const char null_block[8 * 4096];
+   for (uint64_t offset = 0; offset < size; offset += 4096) {
+      uint32_t block_size = min(4096, size - offset);
+
+      mem_trace_memory_write_header_out(aub, virt_addr + offset, block_size,
+                                        AUB_MEM_TRACE_MEMORY_ADDRESS_SPACE_GGTT,
+                                        "GGTT buffer");
+      data_out(aub, (char *) data + offset, block_size);
+
+      /* Pad to a multiple of 4 bytes. */
+      data_out(aub, null_block, -block_size & 3);
+   }
 }
 
 /**
@@ -697,3 +762,12 @@ aub_write_exec(struct aub_file *aub, uint64_t batch_addr,
    }
    fflush(aub->file);
 }
+
+void
+aub_write_context_execlists(struct aub_file *aub, uint64_t context_addr,
+                            enum drm_i915_gem_engine_class engine_class)
+{
+   const struct engine *cs = engine_from_engine_class(engine_class);
+   uint64_t descriptor = ((uint64_t)1 << 62 | context_addr  | CONTEXT_FLAGS);
+   aub_dump_execlist(aub, cs, descriptor);
+}
index 604dfbc8cc5a9024bad8d19155f4f73c420b3761..bdd1045e6504e52f3946ba2113c6aea6a42616cb 100644 (file)
@@ -45,6 +45,8 @@ struct aub_ppgtt_table {
 struct aub_file {
    FILE *file;
 
+   bool has_default_setup;
+
    /* Set if you want extra logging */
    FILE *verbose_log_file;
 
@@ -85,11 +87,14 @@ aub_write_reloc(const struct gen_device_info *devinfo, void *p, uint64_t v)
 
 void aub_write_default_setup(struct aub_file *aub);
 void aub_map_ppgtt(struct aub_file *aub, uint64_t start, uint64_t size);
+void aub_write_ggtt(struct aub_file *aub, uint64_t virt_addr, uint64_t size, const void *data);
 void aub_write_trace_block(struct aub_file *aub,
                            uint32_t type, void *virtual,
                            uint32_t size, uint64_t gtt_offset);
 void aub_write_exec(struct aub_file *aub, uint64_t batch_addr,
                     uint64_t offset, enum drm_i915_gem_engine_class engine_class);
+void aub_write_context_execlists(struct aub_file *aub, uint64_t context_addr,
+                                 enum drm_i915_gem_engine_class engine_class);
 
 #ifdef __cplusplus
 }
index 1b1f8d9c5f97855692038b683e8ab551da7ad711..e2dded29c2c8b5fa9c6d955193c8210ba5c13ab1 100644 (file)
@@ -308,8 +308,6 @@ main(int argc, char *argv[])
                        NULL, pci_id, "error_state");
          fail_if(!aub_use_execlists(&aub),
                  "%s currently only works on gen8+\n", argv[0]);
-
-         aub_write_default_setup(&aub);
          continue;
       }
 
@@ -425,7 +423,18 @@ main(int argc, char *argv[])
       }
    }
 
+   /* Find the batch that trigger the hang */
+   struct bo *batch_bo = NULL;
+   list_for_each_entry(struct bo, bo_entry, &bo_list, link) {
+      if (bo_entry->type == BO_TYPE_BATCH) {
+         batch_bo = bo_entry;
+         break;
+      }
+   }
+   fail_if(!batch_bo, "Failed to find batch buffer.\n");
+
    /* Add all the BOs to the aub file */
+   struct bo *hwsp_bo = NULL;
    list_for_each_entry(struct bo, bo_entry, &bo_list, link) {
       switch (bo_entry->type) {
       case BO_TYPE_BATCH:
@@ -437,21 +446,70 @@ main(int argc, char *argv[])
          aub_map_ppgtt(&aub, bo_entry->addr, bo_entry->size);
          aub_write_trace_block(&aub, AUB_TRACE_TYPE_NOTYPE,
                                bo_entry->data, bo_entry->size, bo_entry->addr);
-      default:
          break;
-      }
-   }
+      case BO_TYPE_CONTEXT:
+         if (bo_entry->engine_class == batch_bo->engine_class &&
+             bo_entry->engine_instance == batch_bo->engine_instance) {
+            hwsp_bo = bo_entry;
+
+            uint32_t *context = (uint32_t *) (bo_entry->data + 4096 /* GuC */ + 4096 /* HWSP */);
+
+            if (context[1] == 0) {
+               fprintf(stderr,
+                       "Invalid context image data.\n"
+                       "This is likely a kernel issue : https://bugs.freedesktop.org/show_bug.cgi?id=107691\n");
+            }
+
+            /* Update the ring buffer at the last known location. */
+            context[5] = engines[bo_entry->engine_class].instances[bo_entry->engine_instance].ring_buffer_head;
+            context[7] = engines[bo_entry->engine_class].instances[bo_entry->engine_instance].ring_buffer_tail;
+            fprintf(stdout, "engine start=0x%x head/tail=0x%x/0x%x\n",
+                    context[9], context[5], context[7]);
+
+            /* The error state doesn't provide a dump of the page tables, so
+             * we have to provide our own, that's easy enough.
+             */
+            context[49] = aub.pml4.phys_addr >> 32;
+            context[51] = aub.pml4.phys_addr & 0xffffffff;
+
+            fprintf(stdout, "context dump:\n");
+            for (int i = 0; i < 60; i++) {
+               if (i % 4 == 0)
+                  fprintf(stdout, "\n 0x%08lx: ", bo_entry->addr + 8192 + i * 4);
+               fprintf(stdout, "0x%08x ", context[i]);
+            }
+            fprintf(stdout, "\n");
 
-   /* Finally exec the batch BO */
-   bool batch_found = false;
-   list_for_each_entry(struct bo, bo_entry, &bo_list, link) {
-      if (bo_entry->type == BO_TYPE_BATCH) {
-         aub_write_exec(&aub, bo_entry->addr, aub_gtt_size(&aub), bo_entry->engine_class);
-         batch_found = true;
+         }
+         aub_write_ggtt(&aub, bo_entry->addr, bo_entry->size, bo_entry->data);
+         break;
+      case BO_TYPE_RINGBUFFER:
+      case BO_TYPE_STATUS:
+      case BO_TYPE_CONTEXT_WA:
+         aub_write_ggtt(&aub, bo_entry->addr, bo_entry->size, bo_entry->data);
+         break;
+      case BO_TYPE_UNKNOWN:
+         if (bo_entry->gtt == PPGTT) {
+            aub_map_ppgtt(&aub, bo_entry->addr, bo_entry->size);
+            if (bo_entry->data) {
+               aub_write_trace_block(&aub, AUB_TRACE_TYPE_NOTYPE,
+                                     bo_entry->data, bo_entry->size, bo_entry->addr);
+            }
+         } else {
+            if (bo_entry->size > 0) {
+               void *zero_data = calloc(1, bo_entry->size);
+               aub_write_ggtt(&aub, bo_entry->addr, bo_entry->size, zero_data);
+               free(zero_data);
+            }
+         }
+         break;
+      default:
          break;
       }
    }
-   fail_if(!batch_found, "Failed to find batch buffer.\n");
+
+   fail_if(!hwsp_bo, "Failed to find Context buffer.\n");
+   aub_write_context_execlists(&aub, hwsp_bo->addr + 4096 /* skip GuC page */, hwsp_bo->engine_class);
 
    /* Cleanup */
    list_for_each_entry_safe(struct bo, bo_entry, &bo_list, link) {