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.ma...@intel.com>
Cc: Chris Wilson <ch...@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

diff --git a/drivers/gpu/drm/i915/i915_aubmemtrace.c 
b/drivers/gpu/drm/i915/i915_aubmemtrace.c
new file mode 100644
index 0000000..61b1392
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_aubmemtrace.c
@@ -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.ma...@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);
+}
diff --git a/drivers/gpu/drm/i915/i915_aubmemtrace.h 
b/drivers/gpu/drm/i915/i915_aubmemtrace.h
new file mode 100644
index 0000000..baab082
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_aubmemtrace.h
@@ -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
diff --git a/drivers/gpu/drm/i915/i915_aubmemtrace_format.h 
b/drivers/gpu/drm/i915/i915_aubmemtrace_format.h
new file mode 100644
index 0000000..0821cfc
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_aubmemtrace_format.h
@@ -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
-- 
1.9.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to