new file mode 100644
@@ -0,0 +1,665 @@
+/*
+ * Copyright © 2017 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Author:
+ * Oscar Mateo <oscar.mateo@intel.com>
+ *
+ */
+
+#include "intel_drv.h"
+#include "i915_aubmemtrace.h"
+#include "i915_aubmemtrace_format.h"
+
+/**
+ * DOC: AUB Memtrace
+ *
+ * The "AUB" memtrace file format provides a way to log GPU workloads in the
+ * same (or a very similar) form as they would be sent to the Intel Graphics
+ * Hardware. These logs are then provided to the user, who can use them for
+ * multiple purposes. For example: to easily browse the workload in order to
+ * find HW programming errors or to replay the workload using a GPU simulator or
+ * emulator.
+ *
+ * Technically, the format is the same used by intel_aubdump (a userspace tool
+ * that you can find in intel-gpu-tools) but by writing AUB files from the KMD
+ * we can log information that a userspace tool by itself cannot. E.g.: real GPU
+ * virtual addresses, pagetables, GPU contexts, workaround batchbuffers, etc...
+ *
+ * Trivia:
+ * In case the reader was wondering, AUB is a shorthand for "Auburn", the
+ * code name of the Intel740™ Graphics Accelerator (also known as the i740).
+ * We maintain the name of the file format for historical reasons.
+ *
+ */
+
+#define AUB_TOOL_VERSION_MAJOR 0
+#define AUB_TOOL_VERSION_MINOR 1
+
+#define AUB_WRITE(data, len) do { \
+ aub->write(aub->priv, data, len); \
+} while (0)
+
+#define PADDING(x) ((4 - ((x) & 3)) & 3)
+
+static inline void aub_write_padding(struct intel_aub *aub, u32 bytes)
+{
+ u32 zero = 0;
+
+ if (GEM_WARN_ON(bytes > 3))
+ return;
+
+ AUB_WRITE(&zero, bytes);
+}
+
+static inline void aub_header_fill(struct aub_cmd_hdr *header, u32 type,
+ u32 opcode, u32 sub_opcode,
+ u32 dword_count)
+{
+ header->type = type;
+ header->opcode = opcode;
+ header->sub_opcode = sub_opcode;
+ header->dword_count = dword_count;
+}
+
+struct aub_chip_revision {
+ uint rev_id;
+ uint stepping;
+ uint metal;
+};
+
+static const struct aub_chip_revision bdw_revs[] = {
+ { 0, STEP_A, 0 },
+};
+
+static const struct aub_chip_revision chv_revs[] = {
+ { 0, STEP_A, 0 },
+};
+
+static const struct aub_chip_revision skl_revs[] = {
+ { SKL_REVID_A0, STEP_A, 0 },
+ { SKL_REVID_B0, STEP_B, 0 },
+ { SKL_REVID_C0, STEP_C, 0 },
+ { SKL_REVID_D0, STEP_D, 0 },
+ { SKL_REVID_E0, STEP_E, 0 },
+ { SKL_REVID_F0, STEP_E, 0 },
+ { SKL_REVID_G0, STEP_G, 0 },
+ { SKL_REVID_H0, STEP_H, 0 },
+};
+
+static const struct aub_chip_revision bxt_revs[] = {
+ { BXT_REVID_A0, STEP_A, 0 },
+ { BXT_REVID_A1, STEP_A, 1 },
+ { BXT_REVID_B0, STEP_B, 0 },
+ { BXT_REVID_B_LAST, STEP_B, 1 },
+ { BXT_REVID_C0, STEP_C, 0 },
+};
+
+static const struct aub_chip_revision kbl_revs[] = {
+ { KBL_REVID_A0, STEP_A, 0 },
+ { KBL_REVID_B0, STEP_B, 0 },
+ { KBL_REVID_C0, STEP_C, 0 },
+ { KBL_REVID_D0, STEP_D, 0 },
+ { KBL_REVID_E0, STEP_E, 0 },
+};
+
+static const struct aub_chip_revision glk_revs[] = {
+ { GLK_REVID_A0, STEP_A, 0 },
+ { GLK_REVID_A1, STEP_A, 1 },
+};
+
+static const struct aub_chip_revision cnl_revs[] = {
+ { CNL_REVID_A0, STEP_A, 0 },
+ { CNL_REVID_B0, STEP_B, 0 },
+ { CNL_REVID_C0, STEP_C, 0 },
+};
+
+struct aub_platforms_table {
+ uint platform_id;
+ uint device;
+ const struct aub_chip_revision *table;
+ uint count;
+};
+
+static const struct aub_platforms_table platforms[] = {
+ { INTEL_BROADWELL, DEV_BDW, bdw_revs, ARRAY_SIZE(bdw_revs) },
+ { INTEL_CHERRYVIEW, DEV_CHV, chv_revs, ARRAY_SIZE(chv_revs) },
+ { INTEL_SKYLAKE, DEV_SKL, skl_revs, ARRAY_SIZE(skl_revs) },
+ { INTEL_BROXTON, DEV_BXT, bxt_revs, ARRAY_SIZE(bxt_revs) },
+ { INTEL_KABYLAKE, DEV_KBL, kbl_revs, ARRAY_SIZE(kbl_revs) },
+ { INTEL_GEMINILAKE, DEV_GLK, glk_revs, ARRAY_SIZE(glk_revs) },
+ { INTEL_CANNONLAKE, DEV_CNL, cnl_revs, ARRAY_SIZE(cnl_revs) },
+};
+
+static int aub_write_version_packet(struct intel_aub *aub,
+ enum intel_platform platform,
+ u8 revision, const char *message)
+{
+ u32 length, padding;
+ struct cmd_memtrace_version cmd;
+ char *buf = (char *)aub->scratch;
+ bool rev_warning = false;
+ int i, j;
+
+ memset(&cmd, 0, sizeof(cmd));
+ aub_header_fill(&cmd.header, CMD_TYPE_AUB, CMD_OPC_MEMTRACE,
+ CMD_SUBOPC_MEMTRACE_VERSION, sizeof(cmd) / 4 - 2);
+
+ cmd.memtrace_file_version = AUB_FILE_FORMAT_VERSION;
+ cmd.swizzling = SWIZZLING_DISABLED;
+ cmd.recording_method = METHOD_PHY;
+ cmd.pch = PCH_DEFAULT;
+ cmd.capture_tool = CAPTURE_TOOL_KMD;
+
+ for (i = 0; i < ARRAY_SIZE(platforms); i++) {
+ if (platform == platforms[i].platform_id) {
+ const struct aub_chip_revision *table =
+ platforms[i].table;
+ uint count = platforms[i].count;
+ cmd.device = platforms[i].device;
+
+ for (j = 0; j < count; j++) {
+ if (revision == table[j].rev_id) {
+ cmd.stepping = table[j].stepping;
+ cmd.metal = table[j].metal;
+ }
+ }
+
+ if (j == count) {
+ rev_warning = true;
+ cmd.stepping = table[count - 1].stepping;
+ cmd.metal = table[count - 1].metal;
+ }
+
+ break;
+ }
+
+ if (i == ARRAY_SIZE(platforms)) {
+ DRM_ERROR("Unsupported platform 0x%x\n", platform);
+ return -ENODEV;
+ }
+ }
+
+ cmd.tool_primary_version = AUB_TOOL_VERSION_MAJOR;
+ cmd.tool_secondary_version = AUB_TOOL_VERSION_MINOR;
+
+ snprintf(buf, AUB_COMMENT_MAX_LENGTH, message);
+ length = strlen(buf);
+ padding = PADDING(length);
+ cmd.header.dword_count += (length + padding) / 4;
+
+ AUB_WRITE(&cmd, sizeof(cmd) - 4);
+ AUB_WRITE(buf, length);
+ aub_write_padding(aub, padding);
+
+ if (rev_warning)
+ i915_aub_comment(aub,
+ "Unknown revid 0x%x. Using last known step/metal",
+ revision);
+
+ return 0;
+}
+
+static void aub_write_comment_packet(struct intel_aub *aub, const char *comment)
+{
+ struct cmd_memtrace_comment cmd;
+ const char preface[] = "AUB: ";
+ uint preface_len = strlen(preface);
+ uint comment_len = strlen(comment) + 1;
+ uint padding = PADDING(comment_len + preface_len);
+
+ memset(&cmd, 0, sizeof(cmd));
+ aub_header_fill(&cmd.header, CMD_TYPE_AUB, CMD_OPC_MEMTRACE,
+ CMD_SUBOPC_MEMTRACE_COMMENT, sizeof(cmd) / 4 - 2);
+ cmd.header.dword_count += (preface_len + comment_len + padding) / 4;
+
+ AUB_WRITE(&cmd, sizeof(cmd) - 4);
+ AUB_WRITE(preface, preface_len);
+ AUB_WRITE(comment, comment_len);
+ aub_write_padding(aub, padding);
+}
+
+static int aub_write_mem_packet(struct intel_aub *aub,
+ enum tiling_values tiling,
+ enum data_type_values type,
+ enum address_space_values space,
+ u64 address,
+ const void *data,
+ u32 bytes)
+{
+ struct cmd_memtrace_memwrite cmd;
+ uint max_bytes = 4 * (0xffff - (sizeof(cmd) / 4 - 2));
+ uint padding = PADDING(bytes);
+ uint num_dwords = (bytes + padding) / 4;
+
+ if (bytes > max_bytes)
+ return -E2BIG;
+
+ memset(&cmd, 0, sizeof(cmd));
+ aub_header_fill(&cmd.header, CMD_TYPE_AUB, CMD_OPC_MEMTRACE,
+ CMD_SUBOPC_MEMTRACE_MEMORY_WRITE, sizeof(cmd) / 4 - 2);
+ cmd.header.dword_count += num_dwords;
+
+ cmd.address = address;
+ cmd.tiling = tiling;
+ cmd.data_type_hint = type;
+ cmd.address_space = space;
+ cmd.data_size = bytes;
+
+ AUB_WRITE(&cmd, sizeof(cmd) - 4);
+ AUB_WRITE(data, bytes);
+ aub_write_padding(aub, padding);
+
+ return bytes;
+}
+
+static int aub_write_mem_discon_packet(struct intel_aub *aub,
+ enum tiling_values tiling,
+ enum data_type_values type,
+ enum address_space_values space,
+ const struct memwrite_element *elements,
+ const void **data,
+ uint count)
+{
+ struct aub_cmd_hdr header;
+ struct aub_cmd_memwrite_discon_opts cmd_opts;
+ uint cmd_size = sizeof(struct cmd_memtrace_memwrite_discon);
+ uint max_bytes = 4 * (0xffff - (cmd_size / 4 - 2));
+ uint total_bytes = 0;
+ uint padding;
+ uint num_dwords;
+ int i;
+
+ if (count > DISCONTIGUOUS_WRITE_MAX_ELEMENTS)
+ return -E2BIG;
+
+ for (i = 0; i < count; i++)
+ total_bytes += elements[i].data_size;
+
+ padding = PADDING(total_bytes);
+ num_dwords = (total_bytes + padding) / 4;
+
+ if (total_bytes > max_bytes)
+ return -E2BIG;
+
+ memset(&header, 0, sizeof(header));
+ aub_header_fill(&header, CMD_TYPE_AUB, CMD_OPC_MEMTRACE,
+ CMD_SUBOPC_MEMTRACE_MEMORY_WRITE_DISCONTIGUOUS,
+ cmd_size / 4 - 2);
+ header.dword_count += num_dwords;
+
+ memset(&cmd_opts, 0, sizeof(cmd_opts));
+ cmd_opts.tiling = tiling;
+ cmd_opts.data_type_hint = type;
+ cmd_opts.address_space = space;
+ cmd_opts.number_of_elements = count;
+
+ AUB_WRITE(&header, sizeof(header));
+ AUB_WRITE(&cmd_opts, sizeof(cmd_opts));
+
+ AUB_WRITE(elements, count * sizeof(*elements));
+ for (i = count; i < DISCONTIGUOUS_WRITE_MAX_ELEMENTS; i++) {
+ struct memwrite_element zero = {0, 0};
+ AUB_WRITE(&zero, sizeof(zero));
+ }
+
+ for (i = 0; i < count; i++)
+ AUB_WRITE(data[i], elements[i].data_size);
+
+ aub_write_padding(aub, padding);
+
+ return total_bytes;
+}
+
+static void aub_write_register_packet(struct intel_aub *aub, i915_reg_t reg,
+ u32 value)
+{
+ struct cmd_memtrace_register_write cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ aub_header_fill(&cmd.header, CMD_TYPE_AUB, CMD_OPC_MEMTRACE,
+ CMD_SUBOPC_MEMTRACE_REGISTER_WRITE, sizeof(cmd) / 4 - 1);
+ cmd.message_source = SOURCE_IA;
+ cmd.register_size = SIZE_DWORD;
+ cmd.register_space = SPACE_MMIO;
+ cmd.write_mask_low = 0xffffffff;
+ cmd.write_mask_high = 0x0;
+
+ cmd.register_offset = i915_mmio_reg_offset(reg);
+ cmd.data[0] = value;
+
+ AUB_WRITE(&cmd, sizeof(cmd));
+}
+
+static void aub_write_pci_register_packet(struct intel_aub *aub, u16 bus,
+ u8 device, u8 function,
+ u32 offset, u32 value)
+{
+ struct cmd_memtrace_register_write cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ aub_header_fill(&cmd.header, CMD_TYPE_AUB, CMD_OPC_MEMTRACE,
+ CMD_SUBOPC_MEMTRACE_REGISTER_WRITE, sizeof(cmd) / 4 - 1);
+ cmd.message_source = SOURCE_IA;
+ cmd.register_size = SIZE_DWORD;
+ cmd.register_space = SPACE_PCI;
+ cmd.write_mask_low = 0xffffffff;
+ cmd.write_mask_high = 0x0;
+
+ cmd.bus = bus;
+ cmd.device = device;
+ cmd.function = function;
+ cmd.offset = offset;
+ cmd.data[0] = value;
+
+ AUB_WRITE(&cmd, sizeof(cmd));
+}
+
+static void aub_write_regpoll_packet(struct intel_aub *aub, i915_reg_t reg,
+ u32 mask, u32 value)
+{
+ struct cmd_memtrace_register_poll cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ aub_header_fill(&cmd.header, CMD_TYPE_AUB, CMD_OPC_MEMTRACE,
+ CMD_SUBOPC_MEMTRACE_REGISTER_POLL, sizeof(cmd) / 4 - 1);
+ cmd.abort_on_timeout = 1;
+ cmd.poll_not_equal = 0;
+ cmd.operation_type = OPERATION_TYPE_NORMAL;
+ cmd.register_size = SIZE_DWORD;
+ cmd.register_space = SPACE_MMIO;
+
+ cmd.poll_mask_low = mask;
+ cmd.register_offset = i915_mmio_reg_offset(reg);
+ cmd.data[0] = value;
+
+ AUB_WRITE(&cmd, sizeof(cmd));
+}
+
+static inline phys_addr_t adjust_gsm_paddr(struct intel_aub *aub,
+ bool global_gtt,
+ phys_addr_t pte_paddr)
+{
+ if (global_gtt) {
+ /*
+ * We already told the other end about the base
+ * of the GGTT stolen memory, so treat it here
+ * as if it was 0x0
+ */
+ return (pte_paddr - aub->gsm_paddr);
+ } else
+ return pte_paddr;
+}
+
+static int aub_write_discon_pages(struct intel_aub *aub,
+ bool global_gtt,
+ enum tiling_values tiling,
+ enum data_type_values type,
+ enum address_space_values space,
+ const struct drm_i915_error_page *pages,
+ uint count)
+{
+ enum address_space_values pte_space;
+ uint count_left = count;
+ const struct drm_i915_error_page *pages_left = pages;
+ struct memwrite_element *elements =
+ (struct memwrite_element *)aub->scratch;
+ const void **data =
+ (const void **)(elements + DISCONTIGUOUS_WRITE_MAX_ELEMENTS);
+ int ret;
+ int i;
+
+ BUILD_BUG_ON(sizeof(aub->scratch) <
+ DISCONTIGUOUS_WRITE_MAX_ELEMENTS * sizeof(*elements) +
+ DISCONTIGUOUS_WRITE_MAX_ELEMENTS * sizeof(*data));
+
+ pte_space = global_gtt ? ADDRESS_SPACE_GTT_ENTRY :
+ ADDRESS_SPACE_PPGTT_ENTRY;
+
+ while (count_left) {
+ uint c = min(count_left, (uint)DISCONTIGUOUS_WRITE_MAX_ELEMENTS);
+
+ if (c == 1) {
+ const gen8_pte_t *pte = &pages_left[0].pte;
+ phys_addr_t pte_paddr =
+ adjust_gsm_paddr(aub, global_gtt,
+ pages_left[0].pte_paddr);
+
+ ret = aub_write_mem_packet(aub, TILING_NONE, TYPE_NOTYPE,
+ pte_space, pte_paddr, pte,
+ sizeof(u64));
+ } else {
+ for (i = 0; i < c; i++) {
+ elements[i].address =
+ adjust_gsm_paddr(aub, global_gtt,
+ pages_left[i].pte_paddr);
+ elements[i].data_size = sizeof(u64);
+ data[i] = &pages_left[i].pte;
+ }
+
+ ret = aub_write_mem_discon_packet(aub, TILING_NONE,
+ TYPE_NOTYPE, pte_space,
+ elements,
+ data, c);
+ }
+ if (ret < 0)
+ return ret;
+
+ if (c == 1) {
+ ret = aub_write_mem_packet(aub, tiling, type, space,
+ pages_left[0].paddr,
+ pages_left[0].storage,
+ PAGE_SIZE);
+ } else {
+ for (i = 0; i < c; i++) {
+ elements[i].address = pages_left[i].paddr;
+ elements[i].data_size = PAGE_SIZE;
+ data[i] = pages_left[i].storage;
+ }
+
+ ret = aub_write_mem_discon_packet(aub, tiling, type,
+ space, elements,
+ data, c);
+ }
+ if (ret < 0)
+ return ret;
+
+ count_left -= c;
+ pages_left += c;
+ }
+
+ return 0;
+}
+
+struct intel_aub *i915_aub_start(struct drm_i915_private *i915,
+ write_aub_fn write_function,
+ void *private_data,
+ const char *message,
+ bool verbose)
+{
+ struct i915_ggtt *ggtt = &i915->ggtt;
+ struct intel_aub *aub;
+ int ret;
+
+ aub = kmalloc(sizeof(*aub), GFP_KERNEL);
+ if (!aub)
+ return ERR_PTR(-ENOMEM);
+
+ aub->write = write_function;
+ aub->priv = private_data;
+ aub->platform = i915->info.platform;
+ aub->revision = INTEL_REVID(i915);
+ aub->gsm_paddr = ggtt->gsm_paddr;
+ aub->verbose = verbose;
+
+ ret = aub_write_version_packet(aub, aub->platform,
+ aub->revision, message);
+ if (ret < 0) {
+ kfree(aub);
+ return ERR_PTR(ret);
+ }
+
+ /* Tell the other end about the physical GGTT location */
+ GEM_BUG_ON(upper_32_bits(aub->gsm_paddr));
+ aub_write_pci_register_packet(aub, 0, 0, 0, 0xb4,
+ lower_32_bits(aub->gsm_paddr));
+
+ return aub;
+}
+
+void i915_aub_comment(struct intel_aub *aub, const char *format, ...)
+{
+ va_list args;
+ char *buf = (char *)aub->scratch;
+ BUILD_BUG_ON(sizeof(aub->scratch) < AUB_COMMENT_MAX_LENGTH);
+
+ if (!aub->verbose)
+ return;
+
+ va_start(args, format);
+ vsnprintf(buf, AUB_COMMENT_MAX_LENGTH, format, args);
+ va_end(args);
+
+ aub_write_comment_packet(aub, buf);
+}
+
+void i915_aub_register(struct intel_aub *aub, i915_reg_t reg, u32 value)
+{
+ aub_write_register_packet(aub, reg, value);
+}
+
+void i915_aub_gtt(struct intel_aub *aub, enum pagemap_level lvl,
+ phys_addr_t paddr, const u64 *entries, uint count)
+{
+ enum address_space_values space;
+ uint max_count = PAGE_SIZE / sizeof(*entries);
+ uint c = min(count, max_count);
+
+ switch (lvl) {
+ default:
+ MISSING_CASE(lvl);
+ case PPGTT_LEVEL4:
+ space = ADDRESS_SPACE_PPGTT_PML4_ENTRY;
+ break;
+ case PPGTT_LEVEL3:
+ space = ADDRESS_SPACE_PPGTT_PDP_ENTRY;
+ break;
+ case PPGTT_LEVEL2:
+ space = ADDRESS_SPACE_PPGTT_PD_ENTRY;
+ break;
+ case PPGTT_LEVEL1:
+ space = ADDRESS_SPACE_PPGTT_ENTRY;
+ break;
+ case GGTT_LEVEL1:
+ space = ADDRESS_SPACE_GTT_ENTRY;
+ paddr = adjust_gsm_paddr(aub, true, paddr);
+ break;
+ }
+
+ aub_write_mem_packet(aub, TILING_NONE, TYPE_NOTYPE, space, paddr,
+ entries, c * sizeof(*entries));
+}
+
+void i915_aub_context(struct intel_aub *aub, u8 class,
+ const struct drm_i915_error_page *pages, uint count)
+{
+ enum data_type_values type;
+
+ switch (class) {
+ default:
+ MISSING_CASE(class);
+ case OTHER_CLASS:
+ case RENDER_CLASS:
+ type = TYPE_LOGICAL_RING_CONTEXT_RCS;
+ break;
+ case VIDEO_DECODE_CLASS:
+ type = TYPE_LOGICAL_RING_CONTEXT_VCS;
+ break;
+ case VIDEO_ENHANCEMENT_CLASS:
+ type = TYPE_LOGICAL_RING_CONTEXT_VECS;
+ break;
+ case COPY_ENGINE_CLASS:
+ type = TYPE_LOGICAL_RING_CONTEXT_BCS;
+ break;
+ }
+
+ aub_write_discon_pages(aub, true, TILING_NONE, type,
+ ADDRESS_SPACE_PHYSICAL, pages, count);
+}
+
+void i915_aub_batchbuffer(struct intel_aub *aub, bool global_gtt,
+ const struct drm_i915_error_page *pages, uint count)
+{
+ aub_write_discon_pages(aub, global_gtt, TILING_NONE, TYPE_BATCH_BUFFER,
+ ADDRESS_SPACE_PHYSICAL, pages, count);
+}
+
+void i915_aub_buffer(struct intel_aub *aub, bool global_gtt, int tiling_mode,
+ const struct drm_i915_error_page *pages, uint count)
+{
+ enum tiling_values tiling;
+
+ switch (tiling_mode) {
+ default:
+ MISSING_CASE(tiling_mode);
+ case I915_TILING_NONE:
+ tiling = TILING_NONE;
+ break;
+ case I915_TILING_X:
+ tiling = TILING_X;
+ break;
+ case I915_TILING_Y:
+ tiling = TILING_Y;
+ break;
+ }
+
+ aub_write_discon_pages(aub, global_gtt, tiling, TYPE_NOTYPE,
+ ADDRESS_SPACE_PHYSICAL, pages, count);
+}
+
+void i915_aub_elsp_submit(struct intel_aub *aub, struct intel_engine_cs *engine,
+ u64 desc)
+{
+ i915_reg_t elsp = RING_ELSP(engine);
+ i915_reg_t elsp_status = RING_EXECLIST_STATUS_LO(engine);
+ u32 value;
+
+ aub_write_register_packet(aub, elsp, 0x0);
+ aub_write_register_packet(aub, elsp, 0x0);
+ aub_write_register_packet(aub, elsp, upper_32_bits(desc));
+ aub_write_register_packet(aub, elsp, lower_32_bits(desc));
+
+ /*
+ * Due to the nature of the AUB file (no timing information), we cannot
+ * use it to model asynchronous things like Lite Restores or Preemption.
+ * This is the reason we use this "fake" ELSP submission with just one
+ * element at a time instead of just capturing the real submission. And
+ * also the reason why here we force the other end to wait until the HW
+ * becomes idle again.
+ */
+ value = GEN8_CTX_STATUS_ACTIVE_IDLE << EL_STATUS_LAST_CTX_SWITCH_SHIFT;
+ aub_write_regpoll_packet(aub, elsp_status, value, value);
+}
+
+void i915_aub_stop(struct intel_aub *aub)
+{
+ kfree(aub);
+}
new file mode 100644
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2017 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _INTEL_AUBCAPTURE_H_
+#define _INTEL_AUBCAPTURE_H_
+
+#define AUB_COMMENT_MAX_LENGTH 512
+#define AUB_SCRATCH_SIZE 1280
+
+typedef void (*write_aub_fn)(void *priv, const void *data, size_t length);
+
+struct intel_aub {
+ struct drm_i915_private *i915;
+
+ write_aub_fn write;
+ void *priv;
+
+ enum intel_platform platform;
+ u8 revision;
+
+ phys_addr_t gsm_paddr;
+
+ bool verbose;
+
+ /* Avoid using the stack */
+ u8 scratch[AUB_SCRATCH_SIZE];
+};
+
+enum pagemap_level {
+ PPGTT_LEVEL4,
+ PPGTT_LEVEL3,
+ PPGTT_LEVEL2,
+ PPGTT_LEVEL1,
+ GGTT_LEVEL1,
+};
+
+struct intel_aub *i915_aub_start(struct drm_i915_private *i915,
+ write_aub_fn write_function,
+ void *private_data,
+ const char *message,
+ bool verbose);
+void i915_aub_comment(struct intel_aub *aub, const char *format, ...);
+void i915_aub_register(struct intel_aub *aub, i915_reg_t reg, u32 value);
+void i915_aub_gtt(struct intel_aub *aub, enum pagemap_level lvl,
+ phys_addr_t paddr, const u64 *entries, uint count);
+void i915_aub_context(struct intel_aub *aub, u8 class,
+ const struct drm_i915_error_page *pages, uint count);
+void i915_aub_batchbuffer(struct intel_aub *aub, bool global_gtt,
+ const struct drm_i915_error_page *pages, uint count);
+void i915_aub_buffer(struct intel_aub *aub, bool global_gtt, int tiling_mode,
+ const struct drm_i915_error_page *pages, uint count);
+void i915_aub_elsp_submit(struct intel_aub *aub, struct intel_engine_cs *engine,
+ u64 desc);
+void i915_aub_stop(struct intel_aub *aub);
+
+#endif
new file mode 100644
@@ -0,0 +1,359 @@
+/*
+ * Copyright © 2017 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _INTEL_AUBCAPTURE_FORMAT_H_
+#define _INTEL_AUBCAPTURE_FORMAT_H_
+
+#pragma pack(push, 4)
+
+#define AUB_FILE_FORMAT_VERSION 0
+
+#define CMD_TYPE_AUB 0x7
+
+#define CMD_OPC_MEMTRACE 0x2e
+
+#define CMD_SUBOPC_MEMTRACE_VERSION 0xe
+#define CMD_SUBOPC_MEMTRACE_COMMENT 0x8
+#define CMD_SUBOPC_MEMTRACE_REGISTER_POLL 0x2
+#define CMD_SUBOPC_MEMTRACE_REGISTER_WRITE 0x3
+#define CMD_SUBOPC_MEMTRACE_MEMORY_WRITE 0x6
+#define CMD_SUBOPC_MEMTRACE_MEMORY_WRITE_DISCONTIGUOUS 0xb
+
+/**
+ * struct aub_cmd_hdr - AUB command header
+ */
+struct aub_cmd_hdr {
+ /** @dword_count: The number of dwords in the command not including the
+ * first dword */
+ uint32_t dword_count : 16;
+ uint32_t sub_opcode : 7;
+ uint32_t opcode : 6;
+ uint32_t type : 3;
+};
+
+enum stepping_values {
+ STEP_A = 0, STEP_B, STEP_C, STEP_D, STEP_E, STEP_F, STEP_G, STEP_H,
+ STEP_I, STEP_J, STEP_K, STEP_L, STEP_M, STEP_N, STEP_O, STEP_P, STEP_Q,
+ STEP_R, STEP_S, STEP_T, STEP_U, STEP_V, STEP_W, STEP_X, STEP_Y, STEP_Z
+};
+
+enum device_values {
+ DEV_BDW = 11,
+ DEV_CHV = 13,
+ DEV_SKL = 12,
+ DEV_BXT = 14,
+ DEV_KBL = 16,
+ DEV_GLK = 17,
+ DEV_CNL = 15,
+};
+
+enum swizzling_values {
+ SWIZZLING_ENABLED = 1,
+ SWIZZLING_DISABLED = 0
+};
+
+enum recording_method_values {
+ METHOD_PHY = 1,
+ METHOD_GFX = 0
+};
+
+enum pch_values {
+ PCH_DEFAULT = 0
+};
+
+enum capture_tool_values {
+ CAPTURE_TOOL_KMD = 1
+};
+
+/**
+ * struct cmd_memtrace_version - first packet to appear on the AUB file (kind of
+ * a file header).
+ *
+ * Includes version information about the memtrace file that contains it.
+ */
+struct cmd_memtrace_version {
+ struct aub_cmd_hdr header;
+
+ /** @memtrace_file_version: memtrace file format version. */
+ uint32_t memtrace_file_version;
+
+ struct {
+ /** @metal: Which HW metal the memtrace file was generated on */
+ uint32_t metal : 3;
+ /** @stepping: Which HW stepping the memtrace file was generated
+ * on. One of enum stepping_values */
+ uint32_t stepping : 5;
+ /** @device: Which device the memtrace file was generated on.
+ * One of enum device_values */
+ uint32_t device : 8;
+ /** @swizzling: Which swizzling the data is in. One of enum
+ * swizzling_values */
+ uint32_t swizzling : 2;
+ /** @recording_method: Which recording method was used.
+ * One of enum recording_method_values */
+ uint32_t recording_method : 2;
+ /** @pch: Which PCH was used. One of enum pch_values */
+ uint32_t pch : 8;
+ /** @capture_tool: Which tool generated the memtrace file. One
+ * of enum capture_tool_values */
+ uint32_t capture_tool : 4;
+ };
+
+ /** @tool_primary_version: The primary version number for the capture
+ * tool used. */
+ uint32_t tool_primary_version;
+
+ /** @tool_secondary_version: The secondary version number for the
+ * capture tool used. */
+ uint32_t tool_secondary_version;
+
+ /**
+ * @command_line: Command line used to generate the memtrace file (N
+ * dwords). If this string is not 4 byte aligned it has to be padded
+ * with 0s at the end.
+ */
+ char command_line[4];
+};
+
+/**
+ * struct cmd_memtrace_comment - A comment in the AUB file.
+ *
+ * Free-style text, can be used for a number of reasons.
+ */
+struct cmd_memtrace_comment {
+ struct aub_cmd_hdr header;
+
+ uint32_t reserved;
+
+ /**
+ * @comment: A comment that should be printed to console (N dwords).
+ * If this string is not 4 byte aligned it has to be padded with 0s
+ * at the end.
+ */
+ char comment[4];
+};
+
+enum message_source_values {
+ SOURCE_IA = 0
+};
+
+enum register_size_values {
+ SIZE_BYTE = 0,
+ SIZE_WORD = 1,
+ SIZE_DWORD = 2,
+ SIZE_QWORD = 3,
+};
+
+enum register_space_values {
+ SPACE_MMIO = 0,
+ SPACE_PCI = 2,
+};
+
+struct cmd_memtrace_register_write {
+ struct aub_cmd_hdr header;
+
+ /** @register_offset: The offset in the selected register space. For
+ * PCI configuration registers this offset field is split into four
+ * sub-fields: [31:16] is the bus number, [15:11] is the device number,
+ * [10:8] is the function number, and [7:0] is the register offset. */
+ union {
+ uint32_t register_offset;
+ struct {
+ uint32_t bus : 16;
+ uint32_t device : 5;
+ uint32_t function : 3;
+ uint32_t offset : 8;
+ };
+ };
+
+ struct {
+ uint32_t : 4;
+ /** @message_source: Origin of the register write. One of enum
+ * message_source_values */
+ uint32_t message_source : 4;
+ uint32_t : 8;
+ /** @register_size: Size of the data. One of enum
+ * register_size_values */
+ uint32_t register_size : 4;
+ uint32_t : 8;
+ /** @register_space: Which register space to use. One of enum
+ * register_space_values */
+ uint32_t register_space : 4;
+ };
+
+ uint32_t write_mask_low;
+
+ /** @write_mask_high: ignored if register_size is not QWORD. */
+ uint32_t write_mask_high;
+
+ /** @data: The data that is expected from the register write. */
+ uint32_t data[1];
+};
+
+enum operation_type_values {
+ OPERATION_TYPE_NORMAL = 0,
+ OPERATION_TYPE_INTERLACED_CRC = 1,
+};
+
+struct cmd_memtrace_register_poll {
+ struct aub_cmd_hdr header;
+
+ /** @register_offset: The offset in the selected register space. For
+ * PCI configuration registers this offset field is split into four
+ * sub-fields: [31:16] is the bus number, [15:11] is the device number,
+ * [10:8] is the function number, and [7:0] is the register offset. */
+ union {
+ uint32_t register_offset;
+ struct {
+ uint32_t bus : 16;
+ uint32_t device : 5;
+ uint32_t function : 3;
+ uint32_t offset : 8;
+ };
+ };
+
+ struct {
+ uint32_t : 1;
+ /** @timeout_action: Abort if the timeout expires? */
+ uint32_t abort_on_timeout : 1;
+ /** @poll_not_equal: Poll until value != target */
+ uint32_t poll_not_equal : 1;
+ uint32_t : 1;
+ /** @operation_type: One of operation_type_values */
+ uint32_t operation_type : 4;
+ uint32_t : 8;
+ /** @register_size: Size of the data. One of enum
+ * register_size_values */
+ uint32_t register_size : 4;
+ uint32_t : 8;
+ /** @register_space: Which register space to use. One of enum
+ * register_space_values */
+ uint32_t register_space : 4;
+ };
+
+ uint32_t poll_mask_low;
+
+ /** @write_mask_high: ignored if register_size is not QWORD. */
+ uint32_t poll_mask_high;
+
+ /** @data: The data that is expected from the register read. */
+ uint32_t data[1];
+};
+
+enum tiling_values {
+ TILING_NONE = 0,
+ TILING_X = 1,
+ TILING_Y = 2,
+};
+
+enum data_type_values {
+ TYPE_NOTYPE = 0,
+ TYPE_BATCH_BUFFER = 1,
+ TYPE_LOGICAL_RING_CONTEXT_RCS = 48,
+ TYPE_LOGICAL_RING_CONTEXT_BCS = 49,
+ TYPE_LOGICAL_RING_CONTEXT_VCS = 50,
+ TYPE_LOGICAL_RING_CONTEXT_VECS = 51,
+};
+
+enum address_space_values {
+ ADDRESS_SPACE_PHYSICAL = 2,
+ ADDRESS_SPACE_GTT_GFX = 0,
+ ADDRESS_SPACE_GTT_ENTRY = 4,
+ ADDRESS_SPACE_PPGTT_GFX = 5,
+ ADDRESS_SPACE_PPGTT_PML4_ENTRY = 10,
+ ADDRESS_SPACE_PPGTT_PDP_ENTRY = 8,
+ ADDRESS_SPACE_PPGTT_PD_ENTRY = 9,
+ ADDRESS_SPACE_PPGTT_ENTRY = 6,
+};
+
+struct cmd_memtrace_memwrite {
+ struct aub_cmd_hdr header;
+
+ /** @address: The address of the memory to read. The address space is
+ * determined by the address_space field. */
+ uint64_t address;
+
+ struct {
+ uint32_t : 2;
+ /** @tiling: Tiling format. One of enum tiling_values */
+ uint32_t tiling : 2;
+ uint32_t : 16;
+ /** @data_type_hint: This parameter specifies the type of data
+ * block that follows. One of enum data_type_values. If it isn't
+ * known mark it as TYPE_NOTYPE */
+ uint32_t data_type_hint : 8;
+ /** @address_space: This parameter specifies the type of memory
+ * corresponding to the data block (GTT-relative, physical
+ * local, physical system, etc...). One of enum
+ * address_space_values */
+ uint32_t address_space : 4;
+ };
+
+ /** @data_size: The number of bytes that will be written. The data
+ * elements are packed into dwords in the data parameter, padded with
+ * zeroes */
+ uint32_t data_size;
+
+ /** @data: The data that will be written. */
+ uint32_t data[1];
+};
+
+#define DISCONTIGUOUS_WRITE_MAX_ELEMENTS 63
+
+struct memwrite_element {
+ /** @address: The address of the memory to read. */
+ uint64_t address;
+ /** @data_size: The number of bytes that will be written. */
+ uint32_t data_size;
+};
+
+struct cmd_memtrace_memwrite_discon {
+ struct aub_cmd_hdr header;
+
+ struct aub_cmd_memwrite_discon_opts {
+ uint32_t : 2;
+ /** @tiling: Tiling format. One of enum tiling_values */
+ uint32_t tiling : 2;
+
+ /** @tiling: Number of address and data_size pairs */
+ uint32_t number_of_elements : 16;
+ /** @data_type_hint: This parameter specifies the type of data
+ * block that follows. One of enum data_type_values. If it isn't
+ * known mark it as TYPE_NOTYPE */
+ uint32_t data_type_hint : 8;
+ /** @address_space: This parameter specifies the type of memory
+ * corresponding to the data block (GTT-relative, physical
+ * local, physical system, etc...). One of enum
+ * address_space_values */
+ uint32_t address_space : 4;
+ } opts;
+
+ struct memwrite_element elements[DISCONTIGUOUS_WRITE_MAX_ELEMENTS];
+
+ /** @data: The data that will be written. */
+ uint32_t data[1];
+};
+
+#pragma pack(pop)
+
+#endif
This is where the magic happens. For the moment, this will be used by AubCrash to output a correctly-formatted AUB file of GPU error information. In the future, it could be used by other modules to generate all kinds of AUB files. D.g. a running AUB capture of everything a given userspace application ends up sending to the hardware (like i-g-t's intel_aubdump tool, but at the kernel level). Signed-off-by: Oscar Mateo <oscar.mateo@intel.com> Cc: Chris Wilson <chris@chris-wsilon.co.uk> --- drivers/gpu/drm/i915/i915_aubmemtrace.c | 665 +++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_aubmemtrace.h | 76 +++ drivers/gpu/drm/i915/i915_aubmemtrace_format.h | 359 +++++++++++++ 3 files changed, 1100 insertions(+) create mode 100644 drivers/gpu/drm/i915/i915_aubmemtrace.c create mode 100644 drivers/gpu/drm/i915/i915_aubmemtrace.h create mode 100644 drivers/gpu/drm/i915/i915_aubmemtrace_format.h