From patchwork Fri Oct 27 18:01:14 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: oscar.mateo@intel.com X-Patchwork-Id: 10030423 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id C203A6022E for ; Fri, 27 Oct 2017 18:01:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BD01028FB5 for ; Fri, 27 Oct 2017 18:01:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B1FB128FBC; Fri, 27 Oct 2017 18:01:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id EDD4B28FB5 for ; Fri, 27 Oct 2017 18:01:43 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 389156E9D6; Fri, 27 Oct 2017 18:01:43 +0000 (UTC) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) by gabe.freedesktop.org (Postfix) with ESMTPS id 522766E9B2 for ; Fri, 27 Oct 2017 18:01:17 +0000 (UTC) Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga101.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 27 Oct 2017 11:01:15 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos; i="5.44,304,1505804400"; d="scan'208"; a="1236250410" Received: from omateolo-linux.fm.intel.com ([10.1.27.13]) by fmsmga002.fm.intel.com with ESMTP; 27 Oct 2017 11:01:15 -0700 From: Oscar Mateo To: intel-gfx@lists.freedesktop.org Date: Fri, 27 Oct 2017 11:01:14 -0700 Message-Id: <1509127275-22121-12-git-send-email-oscar.mateo@intel.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1509127275-22121-1-git-send-email-oscar.mateo@intel.com> References: <1509127275-22121-1-git-send-email-oscar.mateo@intel.com> MIME-Version: 1.0 Cc: Chris Wilson Subject: [Intel-gfx] [RFC PATCH 11/12] drm/i915: Add an AUB file format writer X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Virus-Scanned: ClamAV using ClamSMTP 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 Cc: Chris Wilson --- 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 + * + */ + +#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