/* * Copyright (c) 2012, 2013, Intel Corporation. * All Rights Reserved. */ /* * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. * * 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. */ #include #include #include #include #include "i915_drv.h" #include "intel_drv.h" /* * Defines */ /* dcmd options */ #define ACTIVE_LIST 0x01 #define INACTIVE_LIST 0x02 #define BOUND_LIST 0x04 #define UNBOUND_LIST 0x08 #define RENDER_RING 0x01 #define BLT_RING 0x02 #define BSD_RING 0x04 u32 count, mappable_count, purgeable_count; size_t size, mappable_size, purgeable_size; /* * Initialize the proc_t walker by either using the given starting address, * or reading the value of the kernel's practive pointer. We also allocate * a proc_t for storage, and save this using the walk_data pointer. */ static int head_list_walk_init(mdb_walk_state_t *wsp) { if (wsp->walk_addr == NULL) { mdb_warn("head is NULL"); return (WALK_ERR); } wsp->walk_data = mdb_alloc(sizeof (struct list_head), UM_SLEEP); if (mdb_vread(wsp->walk_data, sizeof (struct list_head), wsp->walk_addr) == -1) { mdb_warn("failed to read list head at %p", wsp->walk_addr); return (WALK_DONE); } wsp->walk_arg = (void *)wsp->walk_addr; wsp->walk_addr = (uintptr_t)(((struct list_head *)wsp->walk_data)->next); return (WALK_NEXT); } /* * At each step, read a proc_t into our private storage, and then invoke * the callback function. We terminate when we reach a NULL p_next pointer. */ static int head_list_walk_step(mdb_walk_state_t *wsp) { int status; if (wsp->walk_addr == NULL) { mdb_warn("uncompletement list"); return (WALK_DONE); } if (mdb_vread(wsp->walk_data, sizeof (struct list_head), wsp->walk_addr) == -1) { mdb_warn("failed to read list at %p", wsp->walk_addr); return (WALK_DONE); } if ((void *)wsp->walk_addr == wsp->walk_arg) { return (WALK_DONE); } status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, wsp->walk_cbdata); wsp->walk_addr = (uintptr_t)(((struct list_head *)wsp->walk_data)->next); return (status); } /* * The walker's fini function is invoked at the end of each walk. Since we * dynamically allocated a proc_t in sp_walk_init, we must free it now. */ static void head_list_walk_fini(mdb_walk_state_t *wsp) { mdb_free(wsp->walk_data, sizeof (proc_t)); } static int get_drm_dev(struct drm_device *drm_dev) { uintptr_t i915_ss; void *state; i_ddi_soft_state *ss; uintptr_t array; state = mdb_alloc(sizeof (struct drm_device), UM_SLEEP); if (mdb_readsym(&i915_ss, sizeof (i915_ss), "i915_statep") == -1) { mdb_warn("failed to read i915_statep"); mdb_free(state, sizeof (struct drm_device)); return (-1); } if (mdb_vread(state, sizeof (struct drm_device), i915_ss) == -1) { mdb_warn("Failed to read state\n"); mdb_free(state, sizeof (struct drm_device)); return (-1); } ss = (i_ddi_soft_state *) state; if (mdb_vread(&array, sizeof (uintptr_t), (uintptr_t)ss->array) == -1) { mdb_warn("Failed to read array\n"); mdb_free(state, sizeof (struct drm_device)); return (-1); } if (mdb_vread(drm_dev, sizeof (struct drm_device), array) == -1) { mdb_warn("Failed to read drm_dev\n"); mdb_free(state, sizeof (struct drm_device)); return (-1); } mdb_free(state, sizeof (struct drm_device)); return (DCMD_OK); } static int get_i915_private(struct drm_i915_private *dev_priv) { struct drm_device *drm_dev; int ret; drm_dev = mdb_alloc(sizeof (struct drm_device), UM_SLEEP); ret = get_drm_dev(drm_dev); if (ret == DCMD_OK) { if (mdb_vread(dev_priv, sizeof (struct drm_i915_private), (uintptr_t)drm_dev->dev_private) == -1) { mdb_warn("Failed to read i915 private\n"); mdb_free(drm_dev, sizeof (struct drm_device)); return (-1); } } mdb_free(drm_dev, sizeof (struct drm_device)); return (ret); } void i915_pciid_help(void) { mdb_printf("Print device ID information of Intel graphics card.\n"); } /* ARGSUSED */ static int i915_pciid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { int ret; struct drm_device *drm_dev; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx\n", addr); return (DCMD_OK); } drm_dev = mdb_alloc(sizeof (struct drm_device), UM_SLEEP); ret = get_drm_dev(drm_dev); if (ret == DCMD_OK) mdb_printf(" vendor 0x%x device 0x%x\n", drm_dev->pci_vendor, drm_dev->pci_device); mdb_free(drm_dev, sizeof (struct drm_device)); return (ret); } void i915_gtt_total_help(void) { mdb_printf("Print graphics translation table (GTT) size\n"); } /* ARGSUSED */ static int i915_gtt_total(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { int ret; struct drm_i915_private *dev_priv; dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret == DCMD_OK) { mdb_printf("gtt total size 0x%x", dev_priv->gtt.total); mdb_printf("gtt mappable size 0x%x", dev_priv->gtt.mappable_end); mdb_printf("gtt stolen size 0x%x", dev_priv->gtt.stolen_size); } mdb_free(dev_priv, sizeof (struct drm_i915_private)); return (ret); } static const char *get_pin_flag(struct drm_i915_gem_object *obj) { if (obj->user_pin_count > 0) return ("P"); else if (obj->pin_count > 0) return ("p"); else return (" "); } static const char *get_tiling_flag(struct drm_i915_gem_object *obj) { switch (obj->tiling_mode) { default: case I915_TILING_NONE: return " "; case I915_TILING_X: return "X"; case I915_TILING_Y: return "Y"; } } static const char *cache_level_str(int type) { switch (type) { case I915_CACHE_NONE: return " uncached"; case I915_CACHE_LLC: return " snooped (LLC)"; case I915_CACHE_LLC_MLC: return " snooped (LLC+MLC)"; default: return ""; } } static void describe_obj(struct drm_i915_gem_object *obj) { mdb_printf("%p: %s%s %8zdKiB %02x %02x %d %d %d%s%s%s", &obj->base, get_pin_flag(obj), get_tiling_flag(obj), obj->base.size / 1024, obj->base.read_domains, obj->base.write_domain, obj->last_read_seqno, obj->last_write_seqno, obj->last_fenced_seqno, cache_level_str(obj->cache_level), obj->dirty ? " dirty" : "", obj->madv == I915_MADV_DONTNEED ? " purgeable" : ""); if (obj->base.name) mdb_printf(" (name: %d)", obj->base.name); if (obj->pin_count) mdb_printf(" (pinned x %d)", obj->pin_count); if (obj->fence_reg != I915_FENCE_REG_NONE) mdb_printf(" (fence: %d)", obj->fence_reg); if (obj->gtt_space != NULL) mdb_printf(" (gtt offset: %08x, size: %08x)", obj->gtt_offset, (unsigned int)obj->base.real_size); if (obj->pin_mappable || obj->fault_mappable) { char s[3], *t = s; if (obj->pin_mappable) *t++ = 'p'; if (obj->fault_mappable) *t++ = 'f'; *t = '\0'; mdb_printf(" (%s mappable)", s); } } static void i915_obj_info(struct drm_i915_gem_object *obj) { /* "size", "gtt_off", "kaddr", "pfn_array" */ mdb_printf(" 0x%-8x 0x%-8x 0x%lx 0x%lx\n", obj->base.real_size, obj->gtt_offset, obj->base.kaddr, obj->base.pfnarray); size += obj->base.real_size; ++count; if (obj->map_and_fenceable) { mappable_size += obj->base.real_size; ++mappable_count; } } void i915_obj_list_node_help(void) { mdb_printf("Print objects information for a given list pointer\n" "Fields printed:\n" " obj:\tpointer to drm_i915_gem_object structure\n" " size:\tobject size\n" " gtt_off:\tobject offset in GTT (graphics address)\n" " kaddr:\tobject kernel virtual address\n" " pfn_array:\tArrary which contains object's PFN\n"); } /* ARGSUSED */ static int i915_obj_list_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { int ret = DCMD_OK; struct list_head list; struct drm_i915_gem_object *obj; if (!(flags & DCMD_ADDRSPEC)) return (DCMD_USAGE); if (mdb_vread(&list, sizeof (struct list), addr) == -1) { mdb_warn("failed to read list"); return (DCMD_ERR); } if (list.contain_ptr == 0) { mdb_warn("no object!"); return (DCMD_ERR); } obj = mdb_alloc(sizeof (struct drm_i915_gem_object), UM_SLEEP); if (mdb_vread(obj, sizeof (struct drm_i915_gem_object), (uintptr_t)list.contain_ptr) == -1) { mdb_warn("failed to read object infor"); ret = DCMD_ERR; goto err; } mdb_printf("0x%lx ", list.contain_ptr); i915_obj_info(obj); err: mdb_free(obj, sizeof (struct drm_i915_gem_object)); return (ret); } static int obj_walk_list(uintptr_t addr, const char *str) { struct list_head *head; int ret = DCMD_OK; head = mdb_alloc(sizeof (struct list_head), UM_SLEEP); if (mdb_vread(head, sizeof (struct list_head), addr) == -1) { mdb_warn("failed to read active_list"); ret = DCMD_ERR; goto err; } mdb_printf("Dump %s List\n", str); mdb_printf("%s %20s %14s %9s %23s\n", "obj", "size", "gtt_off", "kaddr", "pfn_array"); if (mdb_pwalk_dcmd("head_list", "i915_obj_list_node", 0, NULL, (uintptr_t)head->prev) == -1) { mdb_warn("failed to walk head_list"); ret = DCMD_ERR; goto err; } err: mdb_free(head, sizeof (struct list_head)); return (ret); } void i915_obj_list_help(void) { mdb_printf("Print object lists information\n" "Fields printed:\n" " obj:\tpointer to drm_i915_gem_object structure\n" " size:\tobject size\n" " gtt_off:\tobject offset in GTT (graphics address)\n" " kaddr:\tobject kernel virtual address\n" " pfn_array:\tArrary which contains object's PFN\n" "Options:\n" " -a flag:\tprint only active list with flag set\n" " -i flag:\tprint only inactive list with flag set\n" " -b flag:\tprint only bound list with flag set\n" " -u flag:\tprint only unbound list with flag set\n" "\nWithout the option, print information about all objects in " "system.\n" "Please make sure mdb_track is enabled in i915 driver by " "mdb_track_enable,\n" "before dump all objects information.\n"); } /* ARGSUSED */ static int i915_obj_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { uint_t list_flag = 0; struct drm_i915_private *dev_priv; int ret = DCMD_OK; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx, just ignore\n", addr); } if (mdb_getopts(argc, argv, 'a', MDB_OPT_SETBITS, ACTIVE_LIST, &list_flag, 'i', MDB_OPT_SETBITS, INACTIVE_LIST, &list_flag, 'b', MDB_OPT_SETBITS, BOUND_LIST, &list_flag, 'u', MDB_OPT_SETBITS, UNBOUND_LIST, &list_flag, NULL) != argc) { mdb_printf("\nUsage:\n" "-a dump active_list\n" "-i dump inactive_list\n" "-b dump bound_list\n" "-u dump unbound_list\n"); return (DCMD_USAGE); } dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err; } mdb_printf("%u objects, 0x%lx bytes\n", dev_priv->mm.object_count, dev_priv->mm.object_memory); if (list_flag == 0) { int mdb_track = 0; if (mdb_readvar(&mdb_track, "mdb_track_enable") == -1) { mdb_warn("failed to read mdb_track"); mdb_track = 0; } if (mdb_track == 0) { mdb_printf("mdb_track is not enabled. Please enable " "it by set drm:mdb_track_enable=1"); goto err; } /* dump whole gem objects list */ struct drm_device *drm_dev; struct list_head *head; drm_dev = mdb_alloc(sizeof (struct drm_device), UM_SLEEP); ret = get_drm_dev(drm_dev); if (ret != DCMD_OK) goto err2; head = mdb_alloc(sizeof (struct list_head), UM_SLEEP); if (mdb_vread(head, sizeof (struct list_head), (uintptr_t)drm_dev->gem_objects_list.next) == -1) { mdb_warn("failed to read whole gem list"); ret = DCMD_ERR; goto err1; } mdb_printf("Dump %s List\n", "Whole gem objects"); mdb_printf("%s %20s %14s %9s %23s\n", "obj", "size", "gtt_off", "kaddr", "pfn_array"); if (mdb_pwalk_dcmd("head_list", "i915_obj_list_node", 0, NULL, (uintptr_t)head->prev) == -1) { mdb_warn("failed to walk head_list"); ret = DCMD_ERR; goto err; } err1: mdb_free(head, sizeof (struct list_head)); err2: mdb_free(drm_dev, sizeof (struct drm_device)); goto err; } if (list_flag & ACTIVE_LIST) { size = count = mappable_size = mappable_count = 0; ret = obj_walk_list((uintptr_t)dev_priv->mm.active_list.next, "Activate"); if (ret != DCMD_OK) goto err; mdb_printf(" %u [%u] active objects, 0x%lx [0x%lx] bytes\n", count, mappable_count, size, mappable_size); } if (list_flag & INACTIVE_LIST) { size = count = mappable_size = mappable_count = 0; ret = obj_walk_list((uintptr_t)dev_priv->mm.inactive_list.next, "Inactivate"); if (ret != DCMD_OK) goto err; mdb_printf(" %u [%u] inactive objects, 0x%lx [0x%lx] bytes\n", count, mappable_count, size, mappable_size); } if (list_flag & BOUND_LIST) { size = count = mappable_size = mappable_count = 0; ret = obj_walk_list((uintptr_t)dev_priv->mm.bound_list.next, "Bound"); if (ret != DCMD_OK) goto err; mdb_printf("%u [%u] objects, 0x%lx [0x%lx] bytes in gtt\n", count, mappable_count, size, mappable_size); } if (list_flag & UNBOUND_LIST) { size = count = purgeable_size = purgeable_count = 0; ret = obj_walk_list((uintptr_t)dev_priv->mm.unbound_list.next, "Unbound"); if (ret != DCMD_OK) goto err; mdb_printf("%u unbound objects, 0x%lx bytes\n", count, size); } err: mdb_free(dev_priv, sizeof (struct drm_i915_private)); return (ret); } void i915_ringbuffer_info_help(void) { mdb_printf("Print ring object information\n" "Fields printed:\n" " mmio_base:\tMMIO base address\n" " obj:\tpointer to ring object's drm_i915_gem_object structure\n" "Options:\n" " -l flag:\tprint only BLT ring information with flag set\n" " -r flag:\tprint only RENDER ring information with flag set\n" " -s flag:\tprint only BSD ring information with flag set\n" "Without the option given, print information about all rings.\n"); } /* ARGSUSED */ static int i915_ringbuffer_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_i915_private *dev_priv; uint_t ring_flag = 0; int ret = DCMD_OK; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx, just ignore\n", addr); } if (mdb_getopts(argc, argv, 'l', MDB_OPT_SETBITS, BLT_RING, &ring_flag, 'r', MDB_OPT_SETBITS, RENDER_RING, &ring_flag, 's', MDB_OPT_SETBITS, BSD_RING, &ring_flag, NULL) != argc) { mdb_printf("\nUsage:\n" "-l blt ring information\n" "-r render ring information\n" "-s bsd ring information\n"); return (DCMD_USAGE); } dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err; } if (ring_flag == 0) ring_flag = 0xff; if (ring_flag & RENDER_RING) { mdb_printf("Render ring mmio_base 0x%lx obj 0x%lx\n", dev_priv->ring[0].mmio_base, dev_priv->ring[0].obj); } if (ring_flag & BLT_RING) { mdb_printf("BLT ring mmio_base 0x%lx obj 0x%lx\n", dev_priv->ring[2].mmio_base, dev_priv->ring[2].obj); } if (ring_flag & BSD_RING) { mdb_printf("BSD ring mmio_base 0x%lx obj 0x%lx\n", dev_priv->ring[1].mmio_base, dev_priv->ring[1].obj); } err: mdb_free(dev_priv, sizeof (struct drm_i915_private)); return (ret); } void i915_gtt_dump_help(void) { mdb_printf("Print the address of gtt_dump\n" "\ngtt_dump is a snapshot of whole GTT table when GPU hang " "happened.\n" "Please make sure gpu_dump is enabled in i915 driver before " "using it.\n" "\nSee also: \"::help i915_error_reg_dump\"\n"); } /* ARGSUSED */ static int i915_gtt_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_device *drm_dev; int ret = DCMD_OK; drm_dev = mdb_alloc(sizeof (struct drm_device), UM_SLEEP); ret = get_drm_dev(drm_dev); if (ret != DCMD_OK) { goto err; } if (drm_dev->gtt_dump != 0) mdb_printf("gtt_dump address 0x%lx\n", drm_dev->gtt_dump); else mdb_printf("There is no gtt_dump"); err: mdb_free(drm_dev, sizeof (struct drm_device)); return (ret); } static uint32_t i915_read(struct drm_i915_private *dev_priv, uintptr_t addr, uint32_t *val) { struct drm_local_map regs; int ret = DCMD_OK; if (mdb_vread(®s, sizeof (struct drm_local_map), (intptr_t)dev_priv->regs) == -1) { mdb_warn("Failed to read dev_priv->regs\n"); ret = DCMD_ERR; return (ret); } if (mdb_pread(val, sizeof (uint32_t), (intptr_t)regs.offset + addr) == -1) { mdb_warn("Failed to read register 0x%x\n", addr); ret = DCMD_ERR; return (ret); } return (ret); } void i915_register_read_help(void) { mdb_printf("Print register value for a given register offset\n"); } /* ARGSUSED */ static int i915_register_read(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_i915_private *dev_priv; int ret = DCMD_OK; uint32_t val; if (!(flags & DCMD_ADDRSPEC)) { return (DCMD_USAGE); } dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err; } ret = i915_read(dev_priv, addr, &val); if (ret == DCMD_ERR) { goto err; } mdb_printf("Register [0x%x]: 0x%x\n", addr, val); err: mdb_free(dev_priv, sizeof (struct drm_i915_private)); return (ret); } void i915_error_reg_dump_help(void) { mdb_printf("Print debug register information\n" "Registers printed:\n" " PGTBL_ER:\tPage Table Errors Register\n" " INSTPM:\tCommand Parser Mode Register\n" " EIR:\tError Identity Register\n" " EHR:\tError Header Register\n" " INSTDONE:\tInstruction Stream Interface Done Register\n" " INSTDONE1:\tInstruction Stream Interface Done 1\n" " INSTPS:\tInstruction Parser State Register\n" " ACTHD:\tActive Head Pointer Register\n" " DMA_FADD_P:\tPrimary DMA Engine Fetch Address Register\n" "\ngtt_dump is a snapshot of whole GTT table when GPU hang happened.\n" "\nSee also: \"::help i915_gtt_dump\"\n"); } /* ARGSUSED */ static int i915_error_reg_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_device *dev; struct drm_i915_private *dev_priv; int ret = DCMD_OK; uint32_t val; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx\n", addr); return (DCMD_OK); } dev = mdb_alloc(sizeof (struct drm_device), UM_SLEEP); ret = get_drm_dev(dev); if (ret == DCMD_ERR) goto err1; dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err2; } ret = i915_read(dev_priv, (uintptr_t)PGTBL_ER, &val); if (ret == DCMD_OK) mdb_printf("PGTBL_ER: 0x%lx\n", val); ret = i915_read(dev_priv, (uintptr_t)INSTPM, &val); if (ret == DCMD_OK) mdb_printf("INSTPM: 0x%lx\n", val); ret = i915_read(dev_priv, (uintptr_t)EIR, &val); if (ret == DCMD_OK) mdb_printf("EIR: 0x%lx\n", val); ret = i915_read(dev_priv, (uintptr_t)ERROR_GEN6, &val); if (ret == DCMD_OK) mdb_printf("ERROR_GEN6: 0x%lx\n", val); mdb_printf("\nBlitter command stream:\n"); ret = i915_read(dev_priv, (uintptr_t)0x22064, &val); if (ret == DCMD_OK) mdb_printf(" BLT EIR: 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)0x22068, &val); if (ret == DCMD_OK) mdb_printf(" BLT EHR: 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)0x2206C, &val); if (ret == DCMD_OK) mdb_printf(" INSTDONE: 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)0x22074, &val); if (ret == DCMD_OK) mdb_printf(" ACTHD: 0x%08x\n", val); mdb_printf("\nRender command stream:\n"); ret = i915_read(dev_priv, (uintptr_t)IPEIR_I965, &val); if (ret == DCMD_OK) mdb_printf(" RENDER EIR: 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)IPEHR_I965, &val); if (ret == DCMD_OK) mdb_printf(" RENDER EHR: 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)INSTDONE_I965, &val); if (ret == DCMD_OK) mdb_printf(" INSTDONE: 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)INSTPS, &val); if (ret == DCMD_OK) mdb_printf(" INSTPS: 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)INSTDONE1, &val); if (ret == DCMD_OK) mdb_printf(" INSTDONE1: 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)ACTHD_I965, &val); if (ret == DCMD_OK) mdb_printf(" ACTHD: 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)0x2078, &val); if (ret == DCMD_OK) mdb_printf(" DMA_FADD_P: 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)0x04094, &val); if (ret == DCMD_OK) mdb_printf("\nGraphics Engine Fault 0x%lx\n", val); ret = i915_read(dev_priv, (uintptr_t)0x04194, &val); if (ret == DCMD_OK) mdb_printf("Media Engine Fault 0x%lx\n", val); ret = i915_read(dev_priv, (uintptr_t)0x04294, &val); if (ret == DCMD_OK) mdb_printf("Blitter Engine Fault 0x%lx\n", val); if (dev->gtt_dump != NULL) mdb_printf("gtt dump to %p\n", dev->gtt_dump); else mdb_printf("no gtt dump\n"); err2: mdb_free(dev_priv, sizeof (struct drm_i915_private)); err1: mdb_free(dev, sizeof (struct drm_device)); return (ret); } void i915_obj_history_help(void) { mdb_printf("Print object history information for a given " "drm_i915_gem_object pointer\n" "Fields printed:\n" " event:\tOperation name\n" " cur_seq:\tCurrent system SeqNO\n" " last_seq:\tLast SeqNO has been processed\n" " ring addr:\tPoint to intel_ring_buffer structure\n" "\nNOTE: Please make sure mdb_track is enabled in i915 driver " "by setting mdb_track_enable\n"); } /* ARGSUSED */ static int i915_obj_history(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_gem_object obj; struct list_head node; uintptr_t temp; struct drm_history_list history_node; int ret = DCMD_OK; int condition = 1; int mdb_track = 0; if (!(flags & DCMD_ADDRSPEC)) { return (DCMD_USAGE); } if (mdb_vread(&obj, sizeof (struct drm_gem_object), addr) == -1) { mdb_warn("failed to read gem object infor"); ret = DCMD_ERR; goto err; } if (mdb_readvar(&mdb_track, "mdb_track_enable") == -1) { mdb_warn("failed to read mdb_track"); mdb_track = 0; } if (mdb_track == 0) { mdb_printf("mdb_track is not enabled. Please enable it " "by set drm:mdb_track_enable=1"); goto err; } mdb_printf("Dump obj history\n"); mdb_printf("%s %20s %10s %10s\n", "event", "cur_seq", "last_seq", "ring addr"); temp = (uintptr_t)obj.his_list.next; while (condition) { if (mdb_vread(&node, sizeof (struct list_head), temp) == -1) { mdb_warn("failed to read his_list node"); ret = DCMD_ERR; goto err; } if (node.contain_ptr == NULL) break; if (mdb_vread(&history_node, sizeof (struct drm_history_list), (uintptr_t)node.contain_ptr) == -1) { mdb_warn("failed to read history node"); ret = DCMD_ERR; goto err; } mdb_printf("%s %-8d %-8d 0x%lx\n", history_node.info, history_node.cur_seq, history_node.last_seq, history_node.ring_ptr); temp = (uintptr_t)node.next; } err: return (ret); } void i915_batch_history_help(void) { mdb_printf("Print batchbuffer information\n" "\nAll batchbuffers' information is printed one by one " "from the latest one to the oldest one.\n" "The information includes objects number, assigned SeqNO. " "and objects list.\n" "\nNOTE: Please make sure mdb_track is enabled in i915 " "driver by setting mdb_track_enable\n"); } /* ARGSUSED */ static int i915_batch_history(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct list_head node; uintptr_t temp; struct batch_info_list batch_node; caddr_t *obj_list; int ret, i; struct drm_i915_private *dev_priv; int condition = 1; int mdb_track = 0; dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { mdb_warn("failed to read history node"); goto err; } if (mdb_readvar(&mdb_track, "mdb_track_enable") == -1) { mdb_warn("failed to read mdb_track"); mdb_track = 0; } if (mdb_track == 0) { mdb_printf("mdb_track is not enabled. Please enable it by " "set drm:mdb_track_enable=1"); goto err; } mdb_printf("Dump batchbuffer history\n"); temp = (uintptr_t)dev_priv->batch_list.next; while (condition) { if (mdb_vread(&node, sizeof (struct list_head), temp) == -1) { mdb_warn("failed to read his_list node"); ret = DCMD_ERR; goto err; } if (node.contain_ptr == NULL) break; if (mdb_vread(&batch_node, sizeof (struct batch_info_list), (uintptr_t)node.contain_ptr) == -1) { mdb_warn("failed to read batch node"); ret = DCMD_ERR; goto err; } mdb_printf("batch buffer includes %d objects, seqno 0x%x\n", batch_node.num, batch_node.seqno); obj_list = mdb_alloc(batch_node.num * sizeof (caddr_t), UM_SLEEP); if (mdb_vread(obj_list, batch_node.num * sizeof (caddr_t), (uintptr_t)batch_node.obj_list) == -1) { mdb_warn("failed to read batch object list"); ret = DCMD_ERR; goto err; } for (i = 0; i < batch_node.num; i++) { mdb_printf("obj: 0x%lx\n", obj_list[i]); } mdb_free(obj_list, batch_node.num * sizeof (caddr_t)); temp = (uintptr_t)node.next; } err: mdb_free(dev_priv, sizeof (struct drm_i915_private)); return (ret); } static const char *yesno(int v) { return (v ? "yes" : "no"); } void i915_capabilities_help(void) { mdb_printf("Print capability information for Intel graphics card\n" "Fields printed:\n" " is_mobile:\tMobile Platform\n" " is_i85x:\tIntel I85x series graphics card\n" " is_i915g:\tIntel I915g series graphics card\n" " is_i945gm:\tIntel I945gm series graphics card\n" " is_g33:\tIntel G33 series graphics card\n" " need_gfx_hws:\tNeed setup graphics hardware status page\n" " is_g4x:\tIntel G4x series graphics card\n" " is_pineview:\tIntel Pineview series graphics card\n" " is_broadwater:\tIntel Broadwater series graphics card\n" " is_crestline:\tIntel Crestline series graphics card\n" " is_ivybridge:\tIntel Ivybridge series graphics card\n" " is_valleyview:\tIntel Valleyview series graphics card\n" " has_force_wake:\tHas force awake\n" " is_haswell:\tIntel Haswell series graphics card\n" " has_fbc:\tHas Framebuffer compression\n" " has_pipe_cxsr:\tHas CxSR\n" " has_hotplug:\tHas output hotplug\n" " cursor_needs_physical:\tHas physical cursor object\n" " has_overlay:\tHas Overlay\n" " overlay_needs_physical:\tHas physical overlay object\n" " supports_tv:\tSupport TV output\n" " has_bsd_ring:\tHas BSD ring\n" " has_blt_ring:\tHas BLT ring\n" " has_llc:\tHas last level cache\n"); } /* ARGSUSED */ static int i915_capabilities(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { int ret; struct drm_device *drm_dev; struct drm_i915_private *dev_priv; struct intel_device_info info; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx\n", addr); return (DCMD_OK); } drm_dev = mdb_alloc(sizeof (struct drm_device), UM_SLEEP); ret = get_drm_dev(drm_dev); if (ret == DCMD_ERR) goto err1; dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err2; } if (mdb_vread(&info, sizeof (struct intel_device_info), (uintptr_t)dev_priv->info) == -1) { mdb_warn("failed to read i915 chip info"); ret = DCMD_ERR; goto err2; } mdb_printf("gen: %d\n", info.gen); mdb_printf("pch: %d\n", dev_priv->pch_type); /* BEGIN CSTYLED */ #define SEP_SEMICOLON ; #define DEFINE_FLAG(x) mdb_printf(#x ": %s\n", yesno(info.x)) DEV_INFO_FOR_EACH_FLAG(DEFINE_FLAG, SEP_SEMICOLON); #undef DEFINE_FLAG #undef SEP_SEMICOLON /* END CSTYLED */ err2: mdb_free(dev_priv, sizeof (struct drm_i915_private)); err1: mdb_free(drm_dev, sizeof (struct drm_device)); return (ret); } void i915_request_list_node_help(void) { mdb_printf("Print request list information for a given list pointer\n" "The information includes request, SeqNO. and timestamp.\n"); } /* ARGSUSED */ static int i915_request_list_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { int ret = DCMD_OK; struct list_head list; struct drm_i915_gem_request *request; if (!(flags & DCMD_ADDRSPEC)) return (DCMD_USAGE); if (mdb_vread(&list, sizeof (struct list), addr) == -1) { mdb_warn("failed to read list"); return (DCMD_ERR); } if (list.contain_ptr == 0) { mdb_warn("no request!"); return (DCMD_ERR); } request = mdb_alloc(sizeof (struct drm_i915_gem_request), UM_SLEEP); if (mdb_vread(request, sizeof (struct drm_i915_gem_request), (uintptr_t)list.contain_ptr) == -1) { mdb_warn("failed to read request infor"); ret = DCMD_ERR; goto err; } mdb_printf("0x%lx ", list.contain_ptr); mdb_printf(" %d @ %ld\n", request->seqno, request->emitted_jiffies); err: mdb_free(request, sizeof (struct drm_i915_gem_request)); return (ret); } static int request_walk_list(uintptr_t addr, const char *str) { struct list_head *head; int ret = DCMD_OK; mdb_printf("Dump %s ring request List %p\n", str, addr); head = mdb_alloc(sizeof (struct list_head), UM_SLEEP); if (mdb_vread(head, sizeof (struct list_head), addr) == -1) { mdb_warn("failed to render ring request list"); ret = DCMD_ERR; goto err; } if (mdb_pwalk_dcmd("head_list", "i915_request_list_node", 0, NULL, (uintptr_t)head->prev) == -1) { mdb_warn("failed to walk request head_list"); ret = DCMD_ERR; goto err; } err: mdb_free(head, sizeof (struct list_head)); return (ret); } void i915_gem_request_info_help(void) { mdb_printf("Print request list for each ring buffer\n" "\nSee also: \"::help i915_request_list_node\"\n"); } /* ARGSUSED */ static int i915_gem_request_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_i915_private *dev_priv; int ret = DCMD_OK; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx, just ignore\n", addr); } dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err; } ret = request_walk_list((uintptr_t)dev_priv->ring[0].request_list.next, "RENDER"); if (ret != DCMD_OK) { goto err; } ret = request_walk_list((uintptr_t)dev_priv->ring[1].request_list.next, "BSD"); if (ret != DCMD_OK) { goto err; } ret = request_walk_list((uintptr_t)dev_priv->ring[2].request_list.next, "BLT"); if (ret != DCMD_OK) { goto err; } err: mdb_free(dev_priv, sizeof (struct drm_i915_private)); return (ret); } static int get_hws_info(uintptr_t addr, const char *str, int index) { u32 *regs; int i, ret = DCMD_OK; regs = mdb_alloc(0x4000, UM_SLEEP); if (mdb_vread(regs, 0x4000, addr) == -1) { mdb_warn("failed to read hardware status page"); ret = DCMD_ERR; goto err; } if (index < 0) { for (i = 0; i < 4096 / sizeof (u32) / 4; i += 4) { mdb_printf("0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n", i * 4, regs[i], regs[i + 1], regs[i + 2], regs[i + 3]); } } else mdb_printf("%s ring seqno %d \n", str, regs[index]); err: mdb_free(regs, 0x4000); return (ret); } void i915_ring_seqno_info_help(void) { mdb_printf("Print current SeqNO. for each ring buffer\n"); } /* ARGSUSED */ static int i915_ring_seqno_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_i915_private *dev_priv; int ret = DCMD_OK; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx\n", addr); return (DCMD_OK); } dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err; } ret = get_hws_info((uintptr_t)dev_priv->ring[0].status_page.page_addr, "RENDER", I915_GEM_HWS_INDEX); if (ret != DCMD_OK) { goto err; } ret = get_hws_info((uintptr_t)dev_priv->ring[1].status_page.page_addr, "BSD", I915_GEM_HWS_INDEX); if (ret != DCMD_OK) { goto err; } ret = get_hws_info((uintptr_t)dev_priv->ring[2].status_page.page_addr, "BLT", I915_GEM_HWS_INDEX); if (ret != DCMD_OK) { goto err; } err: mdb_free(dev_priv, sizeof (struct drm_i915_private)); return (ret); } void i915_gem_fence_regs_info_help(void) { mdb_printf("Print current Fence registers information\n" "The information includes object address, user pin flag, tiling mode,\n" "size, read domain, write domain, read SeqNO, write SeqNO, fence " "SeqNo,\n" "catch level, dirty info, object name, pin count, fence reg number,\n" "GTT offset, pin mappable and fault mappable.\n"); } /* ARGSUSED */ static int i915_gem_fence_regs_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_i915_private *dev_priv; int i, ret = DCMD_OK; struct drm_i915_gem_object obj; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx\n", addr); return (DCMD_OK); } dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err; } mdb_printf("Reserved fences start = %d\n", dev_priv->fence_reg_start); mdb_printf("Total fences = %d\n", dev_priv->num_fence_regs); for (i = 0; i < dev_priv->num_fence_regs; i++) { mdb_printf("Fence %d, pin count = %d, object = ", i, dev_priv->fence_regs[i].pin_count); if (dev_priv->fence_regs[i].obj != NULL) { mdb_vread(&obj, sizeof (struct drm_i915_gem_object), (uintptr_t)dev_priv->fence_regs[i].obj); describe_obj(&obj); } else { mdb_printf("unused"); } mdb_printf("\n"); } err: mdb_free(dev_priv, sizeof (struct drm_i915_private)); return (ret); } void i915_interrupt_info_help(void) { mdb_printf("Print debug register information\n" "Registers printed:\n" " IER:\tInterrupt Enable Register\n" " IIR:\tInterrupt Identity Register\n" " IMR:\tInterrupt Mask Register\n" " ISR:\tInterrupt Status Register\n"); } /* ARGSUSED */ static int i915_interrupt_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_device *dev; struct drm_i915_private *dev_priv; struct intel_device_info info; int pipe, ret = DCMD_OK; uint32_t val; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx\n", addr); return (DCMD_OK); } dev = mdb_alloc(sizeof (struct drm_device), UM_SLEEP); ret = get_drm_dev(dev); if (ret == DCMD_ERR) goto err1; dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err2; } if (mdb_vread(&info, sizeof (struct intel_device_info), (uintptr_t)dev_priv->info) == -1) { mdb_warn("failed to read i915 chip info"); ret = DCMD_ERR; goto err2; } if (info.is_valleyview) { ret = i915_read(dev_priv, (uintptr_t)VLV_IER, &val); if (ret == DCMD_OK) mdb_printf("Display IER:\t%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)VLV_IIR, &val); if (ret == DCMD_OK) mdb_printf("Display IIR:\t%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)VLV_IIR_RW, &val); if (ret == DCMD_OK) mdb_printf("Display IIR_RW:\t%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)VLV_IMR, &val); if (ret == DCMD_OK) mdb_printf("Display IMR:\t%08x\n", val); for_each_pipe(pipe) { ret = i915_read(dev_priv, PIPESTAT(pipe), &val); if (ret == DCMD_OK) mdb_printf("Pipe %c stat:\t%08x\n", pipe_name(pipe), val); } ret = i915_read(dev_priv, (uintptr_t)VLV_MASTER_IER, &val); if (ret == DCMD_OK) mdb_printf("Master IER:\t%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)GTIER, &val); if (ret == DCMD_OK) mdb_printf("Render IER:\t%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)GTIIR, &val); if (ret == DCMD_OK) mdb_printf("Render IIR:\t%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)GTIMR, &val); if (ret == DCMD_OK) mdb_printf("Render IMR:\t%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)GEN6_PMIER, &val); if (ret == DCMD_OK) mdb_printf("PM IER:\t\t%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)GEN6_PMIIR, &val); if (ret == DCMD_OK) mdb_printf("PM IIR:\t\t%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)GEN6_PMIMR, &val); if (ret == DCMD_OK) mdb_printf("PM IMR:\t\t%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)PORT_HOTPLUG_EN, &val); if (ret == DCMD_OK) mdb_printf("Port hotplug:\t%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)VLV_DPFLIPSTAT, &val); if (ret == DCMD_OK) mdb_printf("DPFLIPSTAT:\t%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)DPINVGTT, &val); if (ret == DCMD_OK) mdb_printf("DPINVGTT:\t%08x\n", val); } else if (dev_priv->pch_type == PCH_NONE) { ret = i915_read(dev_priv, (uintptr_t)IER, &val); if (ret == DCMD_OK) mdb_printf("Interrupt enable: %08x\n", val); ret = i915_read(dev_priv, (uintptr_t)IIR, &val); if (ret == DCMD_OK) mdb_printf("Interrupt identity: %08x\n", val); ret = i915_read(dev_priv, (uintptr_t)IMR, &val); if (ret == DCMD_OK) mdb_printf("Interrupt mask: %08x\n", val); for_each_pipe(pipe) { ret = i915_read(dev_priv, (uintptr_t)PIPESTAT(pipe), &val); if (ret == DCMD_OK) mdb_printf("Pipe %c stat: %08x\n", pipe_name(pipe), val); } } else { ret = i915_read(dev_priv, (uintptr_t)DEIER, &val); if (ret == DCMD_OK) mdb_printf( "North Display Interrupt enable: %08x\n", val); ret = i915_read(dev_priv, (uintptr_t)DEIIR, &val); if (ret == DCMD_OK) mdb_printf( "North Display Interrupt identity: %08x\n", val); ret = i915_read(dev_priv, (uintptr_t)DEIMR, &val); if (ret == DCMD_OK) mdb_printf( "North Display Interrupt mask: %08x\n", val); ret = i915_read(dev_priv, (uintptr_t)SDEIER, &val); if (ret == DCMD_OK) mdb_printf( "South Display Interrupt enable: %08x\n", val); ret = i915_read(dev_priv, (uintptr_t)SDEIIR, &val); if (ret == DCMD_OK) mdb_printf( "South Display Interrupt identity: %08x\n", val); ret = i915_read(dev_priv, (uintptr_t)SDEIMR, &val); if (ret == DCMD_OK) mdb_printf( "South Display Interrupt mask: %08x\n", val); ret = i915_read(dev_priv, (uintptr_t)GTIER, &val); if (ret == DCMD_OK) mdb_printf( "Graphics Interrupt enable: %08x\n", val); ret = i915_read(dev_priv, (uintptr_t)GTIIR, &val); if (ret == DCMD_OK) mdb_printf( "Graphics Interrupt identity: %08x\n", val); ret = i915_read(dev_priv, (uintptr_t)GTIMR, &val); if (ret == DCMD_OK) mdb_printf( "Graphics Interrupt mask: %08x\n", val); } mdb_printf("Interrupts received: %d\n", dev_priv->irq_received); err2: mdb_free(dev_priv, sizeof (struct drm_i915_private)); err1: mdb_free(dev, sizeof (struct drm_device)); return (ret); } void i915_hws_info_help(void) { mdb_printf("Print hardware status page for each RING\n"); } /* ARGSUSED */ static int i915_hws_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_i915_private *dev_priv; int ret = DCMD_OK; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx\n", addr); return (DCMD_OK); } dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err; } mdb_printf("Hardware status page for %s\n", "RENDER"); ret = get_hws_info((uintptr_t)dev_priv->ring[0].status_page.page_addr, "RENDER", -1); if (ret != DCMD_OK) { goto err; } mdb_printf("Hardware status page for %s\n", "BSD"); ret = get_hws_info((uintptr_t)dev_priv->ring[1].status_page.page_addr, "BSD", -1); if (ret != DCMD_OK) { goto err; } mdb_printf("Hardware status page for %s\n", "BLT"); ret = get_hws_info((uintptr_t)dev_priv->ring[2].status_page.page_addr, "BLT", -1); if (ret != DCMD_OK) { goto err; } err: mdb_free(dev_priv, sizeof (struct drm_i915_private)); return (ret); } void i915_fbc_status_help(void) { mdb_printf("Print the status and reason for Framebuffer Compression " "Enabling\n"); } /* ARGSUSED */ static int i915_fbc_status(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_device *dev; struct drm_i915_private *dev_priv; struct intel_device_info info; int ret = DCMD_OK; uint32_t val; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx\n", addr); return (DCMD_OK); } dev = mdb_alloc(sizeof (struct drm_device), UM_SLEEP); ret = get_drm_dev(dev); if (ret == DCMD_ERR) goto err1; dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err2; } if (mdb_vread(&info, sizeof (struct intel_device_info), (uintptr_t)dev_priv->info) == -1) { mdb_warn("failed to read i915 chip info"); ret = DCMD_ERR; goto err2; } if (!info.has_fbc) { mdb_printf("FBC unsupported on this chipset\n"); goto err2; } if (dev_priv->pch_type != PCH_NONE) { ret = i915_read(dev_priv, (uintptr_t)ILK_DPFC_CONTROL, &val); if (ret == DCMD_OK) { val &= DPFC_CTL_EN; } else { mdb_printf("Failed to read FBC register\n"); goto err2; } } else if (IS_GM45(dev)) { ret = i915_read(dev_priv, (uintptr_t)DPFC_CONTROL, &val); if (ret == DCMD_OK) { val &= DPFC_CTL_EN; } else { mdb_printf("Failed to read FBC register\n"); goto err2; } } else if (info.is_crestline) { ret = i915_read(dev_priv, (uintptr_t)FBC_CONTROL, &val); if (ret == DCMD_OK) { val &= FBC_CTL_EN; } else { mdb_printf("Failed to read FBC register\n"); goto err2; } } if (val) { mdb_printf("FBC enabled\n"); } else { mdb_printf("FBC disabled: "); switch (dev_priv->no_fbc_reason) { case FBC_NO_OUTPUT: mdb_printf("no outputs"); break; case FBC_STOLEN_TOO_SMALL: mdb_printf("not enough stolen memory"); break; case FBC_UNSUPPORTED_MODE: mdb_printf("mode not supported"); break; case FBC_MODE_TOO_LARGE: mdb_printf("mode too large"); break; case FBC_BAD_PLANE: mdb_printf("FBC unsupported on plane"); break; case FBC_NOT_TILED: mdb_printf("scanout buffer not tiled"); break; case FBC_MULTIPLE_PIPES: mdb_printf("multiple pipes are enabled"); break; case FBC_MODULE_PARAM: mdb_printf("disabled per module param (default off)"); break; default: mdb_printf("unknown reason"); } mdb_printf("\n"); } err2: mdb_free(dev_priv, sizeof (struct drm_i915_private)); err1: mdb_free(dev, sizeof (struct drm_device)); return (ret); } /* ARGSUSED */ static int i915_sr_status(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_device *dev; struct drm_i915_private *dev_priv; struct intel_device_info info; int ret = DCMD_OK; uint32_t val = 0; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx\n", addr); return (DCMD_OK); } dev = mdb_alloc(sizeof (struct drm_device), UM_SLEEP); ret = get_drm_dev(dev); if (ret == DCMD_ERR) goto err1; dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err2; } if (mdb_vread(&info, sizeof (struct intel_device_info), (uintptr_t)dev_priv->info) == -1) { mdb_warn("failed to read i915 chip info"); ret = DCMD_ERR; goto err2; } if (dev_priv->pch_type != PCH_NONE) { ret = i915_read(dev_priv, (uintptr_t)WM1_LP_ILK, &val); if (ret == DCMD_OK) { val &= WM1_LP_SR_EN; } else { mdb_printf("Failed to read sr register\n"); goto err2; } } else if (info.is_crestline || IS_I945G(dev) || info.is_i945gm) { ret = i915_read(dev_priv, (uintptr_t)FW_BLC_SELF, &val); if (ret == DCMD_OK) { val &= FW_BLC_SELF_EN; } else { mdb_printf("Failed to read sr register\n"); goto err2; } } else if (IS_I915GM(dev)) { ret = i915_read(dev_priv, (uintptr_t)INSTPM, &val); if (ret == DCMD_OK) { val &= INSTPM_SELF_EN; } else { mdb_printf("Failed to read sr register\n"); goto err2; } } else if (info.is_pineview) { ret = i915_read(dev_priv, (uintptr_t)DSPFW3, &val); if (ret == DCMD_OK) { val &= PINEVIEW_SELF_REFRESH_EN; } else { mdb_printf("Failed to read sr register\n"); goto err2; } } mdb_printf("self-refresh: %s\n", val ? "enabled" : "disabled"); err2: mdb_free(dev_priv, sizeof (struct drm_i915_private)); err1: mdb_free(dev, sizeof (struct drm_device)); return (ret); } void i915_gem_framebuffer_info_help(void) { mdb_printf("Print console framebuffer information\n" "The information includes width, height, depth,\n" "bits_per_pixel and object structure address\n"); } /* ARGSUSED */ static int i915_gem_framebuffer_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_device *dev; struct drm_i915_private *dev_priv; struct intel_fbdev fbdev; struct drm_framebuffer fb; int ret = DCMD_OK; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx\n", addr); return (DCMD_OK); } dev = mdb_alloc(sizeof (struct drm_device), UM_SLEEP); ret = get_drm_dev(dev); if (ret == DCMD_ERR) goto err1; dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err2; } if (mdb_vread(&fbdev, sizeof (struct intel_fbdev), (uintptr_t)dev_priv->fbdev) == -1) { mdb_warn("failed to read intel_fbdev info"); ret = DCMD_ERR; goto err2; } if (mdb_vread(&fb, sizeof (struct drm_framebuffer), (uintptr_t)fbdev.helper.fb) == -1) { mdb_warn("failed to read framebuffer info"); ret = DCMD_ERR; goto err2; } mdb_printf("fbcon size: %d x %d, depth %d, %d bpp, obj %p", fb.width, fb.height, fb.depth, fb.bits_per_pixel, fb.base); mdb_printf("\n"); err2: mdb_free(dev_priv, sizeof (struct drm_i915_private)); err1: mdb_free(dev, sizeof (struct drm_device)); return (ret); } /* ARGSUSED */ static int i915_gen6_forcewake_count_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_i915_private *dev_priv; int ret = DCMD_OK; unsigned forcewake_count; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx\n", addr); return (DCMD_OK); } dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err; } forcewake_count = dev_priv->forcewake_count; mdb_printf("forcewake count = %u\n", forcewake_count); err: mdb_free(dev_priv, sizeof (struct drm_i915_private)); return (ret); } static const char *swizzle_string(unsigned swizzle) { switch (swizzle) { case I915_BIT_6_SWIZZLE_NONE: return ("none"); case I915_BIT_6_SWIZZLE_9: return ("bit9"); case I915_BIT_6_SWIZZLE_9_10: return ("bit9/bit10"); case I915_BIT_6_SWIZZLE_9_11: return ("bit9/bit11"); case I915_BIT_6_SWIZZLE_9_10_11: return ("bit9/bit10/bit11"); case I915_BIT_6_SWIZZLE_9_17: return ("bit9/bit17"); case I915_BIT_6_SWIZZLE_9_10_17: return ("bit9/bit10/bit17"); case I915_BIT_6_SWIZZLE_UNKNOWN: return ("unkown"); } return ("bug"); } void i915_swizzle_info_help(void) { mdb_printf("Print object swizzle information\n" "Registers printed:\n" " TILECTL:\tTile Control\n" " ARB_MODE:\tArbiter Mode Control Register\n" " DISP_ARB_CTL:\tDisplay Arbiter Control\n"); } /* ARGSUSED */ static int i915_swizzle_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_device *dev; struct drm_i915_private *dev_priv; struct intel_device_info info; int ret = DCMD_OK; uint32_t val = 0; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx\n", addr); return (DCMD_OK); } dev = mdb_alloc(sizeof (struct drm_device), UM_SLEEP); ret = get_drm_dev(dev); if (ret == DCMD_ERR) goto err1; dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err2; } if (mdb_vread(&info, sizeof (struct intel_device_info), (uintptr_t)dev_priv->info) == -1) { mdb_warn("failed to read i915 chip info"); ret = DCMD_ERR; goto err2; } mdb_printf("bit6 swizzle for X-tiling = %s\n", swizzle_string(dev_priv->mm.bit_6_swizzle_x)); mdb_printf("bit6 swizzle for Y-tiling = %s\n", swizzle_string(dev_priv->mm.bit_6_swizzle_y)); if ((info.gen == 3) || (info.gen == 4)) { ret = i915_read(dev_priv, (uintptr_t)DCC, &val); if (ret == DCMD_OK) mdb_printf("DDC = 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)C0DRB3, &val); if (ret == DCMD_OK) mdb_printf("C0DRB3 = 0x%04x\n", val); ret = i915_read(dev_priv, (uintptr_t)C1DRB3, &val); if (ret == DCMD_OK) mdb_printf("C1DRB3 = 0x%04x\n", val); } else if ((info.gen == 6) || (info.gen == 7)) { ret = i915_read(dev_priv, (uintptr_t)MAD_DIMM_C0, &val); if (ret == DCMD_OK) mdb_printf("MAD_DIMM_C0 = 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)MAD_DIMM_C1, &val); if (ret == DCMD_OK) mdb_printf("MAD_DIMM_C1 = 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)MAD_DIMM_C2, &val); if (ret == DCMD_OK) mdb_printf("MAD_DIMM_C2 = 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)TILECTL, &val); if (ret == DCMD_OK) mdb_printf("TILECTL = 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)ARB_MODE, &val); if (ret == DCMD_OK) mdb_printf("ARB_MODE = 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)DISP_ARB_CTL, &val); if (ret == DCMD_OK) mdb_printf("DISP_ARB_CTL = 0x%08x\n", val); } err2: mdb_free(dev_priv, sizeof (struct drm_i915_private)); err1: mdb_free(dev, sizeof (struct drm_device)); return (ret); } void i915_ppgtt_info_help(void) { mdb_printf("Print Per-Process GTT (PPGTT) information\n" "Registers printed:\n" " GFX_MODE:\tGraphics Mode Register\n" " PP_DIR_BASE:\tPage Directory Base Register\n" " ECOCHK:\tMain Graphic Arbiter Misc Register\n"); } /* ARGSUSED */ static int i915_ppgtt_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { struct drm_device *dev; struct drm_i915_private *dev_priv; struct intel_device_info info; int i, ret = DCMD_OK; uint32_t val = 0; if (flags & DCMD_ADDRSPEC) { mdb_printf("don't need to set address 0x%lx\n", addr); return (DCMD_OK); } dev = mdb_alloc(sizeof (struct drm_device), UM_SLEEP); ret = get_drm_dev(dev); if (ret == DCMD_ERR) goto err1; dev_priv = mdb_alloc(sizeof (struct drm_i915_private), UM_SLEEP); ret = get_i915_private(dev_priv); if (ret != DCMD_OK) { goto err2; } if (mdb_vread(&info, sizeof (struct intel_device_info), (uintptr_t)dev_priv->info) == -1) { mdb_warn("failed to read i915 chip info"); ret = DCMD_ERR; goto err2; } if (info.gen == 6) { ret = i915_read(dev_priv, (uintptr_t)GFX_MODE, &val); if (ret == DCMD_OK) mdb_printf("GFX_MODE: 0x%08x\n", val); } for (i = 0; i < 3; i ++) { mdb_printf("RING %d\n", i); if (info.gen == 7) { ret = i915_read(dev_priv, (uintptr_t)(dev_priv->ring[i].mmio_base + 0x29c), &val); if (ret == DCMD_OK) mdb_printf("GFX_MODE: 0x%08x\n", val); } ret = i915_read(dev_priv, (uintptr_t)(dev_priv->ring[i].mmio_base + 0x228), &val); if (ret == DCMD_OK) mdb_printf("PP_DIR_BASE: 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)(dev_priv->ring[i].mmio_base + 0x518), &val); if (ret == DCMD_OK) mdb_printf("PP_DIR_BASE_READ: 0x%08x\n", val); ret = i915_read(dev_priv, (uintptr_t)(dev_priv->ring[i].mmio_base + 0x220), &val); if (ret == DCMD_OK) mdb_printf("PP_DIR_DCLV: 0x%08x\n", val); } if (dev_priv->mm.aliasing_ppgtt) { struct i915_hw_ppgtt ppgtt; if (mdb_vread(&ppgtt, sizeof (struct i915_hw_ppgtt), (uintptr_t)dev_priv->mm.aliasing_ppgtt) == -1) { mdb_warn("failed to read aliasing_ppgtt info"); ret = DCMD_ERR; goto err2; } mdb_printf("aliasing PPGTT:\n"); mdb_printf("pd gtt offset: 0x%08x\n", ppgtt.pd_offset); } ret = i915_read(dev_priv, (uintptr_t)GAM_ECOCHK, &val); if (ret == DCMD_OK) mdb_printf("ECOCHK: 0x%08x\n", val); err2: mdb_free(dev_priv, sizeof (struct drm_i915_private)); err1: mdb_free(dev, sizeof (struct drm_device)); return (ret); } /* * MDB module linkage information: * * We declare a list of structures describing our dcmds, a list of structures * describing our walkers, and a function named _mdb_init to return a pointer * to our module information. */ static const mdb_dcmd_t dcmds[] = { { "i915_pciid", "?", "pciid information", i915_pciid, i915_pciid_help }, { "i915_gtt_total", "?", "get gtt total size", i915_gtt_total, i915_gtt_total_help }, { "i915_obj_list", "[-aibu]", "i915 objects list information", i915_obj_list, i915_obj_list_help }, { "i915_obj_list_node", ":", "i915 objects list node information", i915_obj_list_node, i915_obj_list_node_help }, { "i915_ringbuffer_info", "[-lrs]", "i915 ring buffer information", i915_ringbuffer_info, i915_ringbuffer_info_help }, { "i915_gtt_dump", "?", "dump gtt_dump_add address", i915_gtt_dump, i915_gtt_dump_help }, { "i915_register_read", ":", "read i915 register", i915_register_read, i915_register_read_help }, { "i915_error_reg_dump", "?", "i915 error registers dump", i915_error_reg_dump, i915_error_reg_dump_help }, { "i915_obj_history", ":", "i915 objects track information", i915_obj_history, i915_obj_history_help }, { "i915_batch_history", "?", "i915 objects track information", i915_batch_history, i915_batch_history_help }, { "i915_capabilities", "?", "i915 capabilities information", i915_capabilities, i915_capabilities_help }, { "i915_request_list_node", ":", "i915 request list node information", i915_request_list_node, i915_request_list_node_help }, { "i915_gem_request", "?", "i915 gem request information", i915_gem_request_info, i915_gem_request_info_help }, { "i915_ring_seqno", "?", "ring seqno information", i915_ring_seqno_info, i915_ring_seqno_info_help }, { "i915_gem_fence_regs", "?", "fence register information", i915_gem_fence_regs_info, i915_gem_fence_regs_info_help }, { "i915_interrupt", "?", "interrupt information", i915_interrupt_info, i915_interrupt_info_help }, { "i915_gem_hws", "?", "hardware status page", i915_hws_info, i915_hws_info_help }, { "i915_fbc_status", "?", "framebuffer compression information", i915_fbc_status, i915_fbc_status_help }, { "i915_sr_status", "?", "Print self-refresh status", i915_sr_status, NULL }, { "i915_gem_framebuffer", "?", "Print framebuffer information", i915_gem_framebuffer_info, i915_gem_framebuffer_info_help }, { "i915_gen6_forcewake_count", "?", "Print forcewake count", i915_gen6_forcewake_count_info, NULL }, { "i915_swizzle", "?", "Print swizzle information", i915_swizzle_info, i915_swizzle_info_help }, { "i915_ppgtt", "?", "Print ppgtt information", i915_ppgtt_info, i915_ppgtt_info_help }, { NULL } }; static const mdb_walker_t walkers[] = { { "head_list", "walk head list", head_list_walk_init, head_list_walk_step, head_list_walk_fini }, { NULL } }; static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers }; const mdb_modinfo_t * _mdb_init(void) { return (&modinfo); }