/* * Copyright © 2011 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. * * Authors: * Ben Widawsky * * Notes: * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "drm.h" #include "i915_drm.h" #include "drmtest.h" #include "intel_chipset.h" #include "intel_bufmgr.h" #include "intel_io.h" #include "intel_batchbuffer.h" #include "intel_debug.h" #include "debug.h" #define EU_ATT 0x7810 #define EU_ATT_CLR 0x7830 #define RSVD_EU -1 #define RSVD_THREAD -1 #define RSVD_ID EUID(-1, -1, -1) enum { EBAD_SHMEM, EBAD_PROTOCOL, EBAD_MAGIC, EBAD_WRITE }; struct debuggee { int euid; int tid; int fd; int clr; uint32_t reg; }; struct debugger { struct debuggee *debuggees; int num_threads; int real_num_threads; int threads_per_eu; } *eu_info; drm_intel_bufmgr *bufmgr; struct intel_batchbuffer *batch; drm_intel_bo *scratch_bo; int handle; int drm_fd; int debug_fd = 0; const char *debug_file = "dump_debug.bin"; int debug; int clear_waits; int shutting_down = 0; struct intel_debug_handshake dh; int force_clear = 0; uint32_t old_td_ctl; /* * The docs are wrong about the attention clear bits. The clear bits are * provided as part of the structure in case they change in future generations. */ #define EUID(eu, td, clear) \ { .euid = eu, .tid = td, .reg = EU_ATT, .fd = -1, .clr = clear } #define EUID2(eu, td, clear) \ { .euid = eu, .tid = td, .reg = EU_ATT + 4, .fd = -1, .clr = clear } struct debuggee gt1_debug_ids[] = { RSVD_ID, RSVD_ID, RSVD_ID, EUID(6, 3, 28), EUID(6, 2, 27), EUID(6, 1, 26), EUID(6, 0, 25), RSVD_ID, EUID(5, 3, 23), EUID(5, 2, 22), EUID(5, 1, 21), EUID(5, 0, 20), RSVD_ID, EUID(4, 3, 18), EUID(4, 2, 17), EUID(4, 1, 16), EUID(4, 0, 15), RSVD_ID, EUID(2, 3, 13), EUID(2, 2, 12), EUID(2, 1, 11), EUID(2, 0, 10), RSVD_ID, EUID(1, 3, 8), EUID(1, 2, 7), EUID(1, 1, 6), EUID(1, 0, 5), RSVD_ID, EUID(0, 3, 3), EUID(0, 2, 2), EUID(0, 1, 1), EUID(0, 0, 0) }; struct debuggee gt2_debug_ids[] = { EUID(8, 1, 31), EUID(8, 0, 30), EUID(6, 4, 29), EUID(6, 3, 28), EUID(6, 2, 27), EUID(6, 1, 26), EUID(6, 0, 25), EUID(5, 4, 24), EUID(5, 3, 23), EUID(5, 2, 22), EUID(5, 1, 21), EUID(5, 0, 20), EUID(4, 4, 19), EUID(4, 3, 18), EUID(4, 2, 17), EUID(4, 1, 16), EUID(4, 0, 15), EUID(2, 4, 14), EUID(2, 3, 13), EUID(2, 2, 12), EUID(2, 1, 11), EUID(2, 0, 10), EUID(1, 4, 9), EUID(1, 3, 8), EUID(1, 2, 7), EUID(1, 1, 6), EUID(1, 0, 5), EUID(0, 4, 4), EUID(0, 3, 3), EUID(0, 2, 2), EUID(0, 1, 1), EUID(0, 0, 0), RSVD_ID, RSVD_ID, RSVD_ID, RSVD_ID, EUID2(14, 4, 27), EUID2(14, 3, 26), EUID2(14, 2, 25), EUID2(14, 1, 24), EUID2(14, 0, 23), EUID2(13, 4, 22), EUID2(13, 3, 21), EUID2(13, 2, 20), EUID2(13, 1, 19), EUID2(13, 0, 18), EUID2(12, 4, 17), EUID2(12, 3, 16), EUID2(12, 2, 15), EUID2(12, 1, 14), EUID2(12, 0, 13), EUID2(10, 4, 12), EUID2(10, 3, 11), EUID2(10, 2, 10), EUID2(10, 1, 9), EUID2(10, 0, 8), EUID2(9, 4, 7), EUID2(9, 3, 6), EUID2(9, 2, 5), EUID2(9, 1, 4), EUID2(9, 0, 3), EUID2(8, 4, 2), EUID2(8, 3, 1), EUID2(8, 2, 0) }; struct debugger gt1 = { .debuggees = gt1_debug_ids, .num_threads = 32, .real_num_threads = 24, .threads_per_eu = 4 }; struct debugger gt2 = { .debuggees = gt2_debug_ids, .num_threads = 64, .real_num_threads = 60, .threads_per_eu = 5 }; static void dump_debug(void *buf, size_t count) { if (!debug_fd) debug_fd = open(debug_file, O_CREAT | O_WRONLY | O_TRUNC, S_IRWXO); if (write(debug_fd, buf, count) == -1) fprintf(stderr, "Error writing to debug file: %s\n", strerror(errno)); } static volatile void * map_debug_buffer(void) { int ret; ret = drm_intel_bo_map(scratch_bo, 0); assert(ret == 0); return scratch_bo->virtual; } static void unmap_debug_buffer(void) { drm_intel_bo_unmap(scratch_bo); } static int wait_for_attn(int timeout, int *out_bits) { int step = 1; int eus_waiting = 0; int i,j; if (timeout <= 0) { timeout = 1; step = 0; } for (i = 0; i < timeout; i += step) { for (j = 0; j < 8; j += 4) { uint32_t attn = intel_register_read(EU_ATT + j); if (attn) { int bit = 0; while( (bit = ffs(attn)) != 0) { bit--; // ffs is 1 based assert(bit >= 0); out_bits[eus_waiting] = bit + (j * 8); attn &= ~(1 << bit); eus_waiting++; } } } if (intel_register_read(EU_ATT + 8) || intel_register_read(EU_ATT + 0xc)) { fprintf(stderr, "Unknown attention bits\n"); } if (eus_waiting || shutting_down) break; } return eus_waiting; } #define eu_fd(bit) eu_info->debuggees[bit].fd #define eu_id(bit) eu_info->debuggees[bit].euid #define eu_tid(bit) eu_info->debuggees[bit].tid static struct eu_state * find_eu_shmem(int bit, volatile uint8_t *buf) { struct eu_state *eu; int mem_tid, mem_euid, i; for(i = 0; i < eu_info->num_threads; i++) { eu = (struct eu_state *)(buf + i * dh.per_thread_scratch); mem_tid = eu->sr0 & 0x7; mem_euid = (eu->sr0 >> 8) & 0xf; if (mem_tid == eu_tid(bit) && mem_euid == eu_id(bit)) break; eu = NULL; } return eu; } #define GRF_CMP(a, b) memcmp(a, b, sizeof(grf)) #define GRF_CPY(a, b) memcpy(a, b, sizeof(grf)) static int verify(struct eu_state *eu) { if (GRF_CMP(eu->version, protocol_version)) { if (debug) { printf("Bad EU protocol version %x %x\n", ((uint32_t *)&eu->version)[0], DEBUG_PROTOCOL_VERSION); dump_debug((void *)eu, sizeof(*eu)); } return -EBAD_PROTOCOL; } if (GRF_CMP(eu->state_magic, eu_msg)) { if (debug) { printf("Bad EU state magic %x %x\n", ((uint32_t *)&eu->state_magic)[0], ((uint32_t *)&eu->state_magic)[1]); dump_debug((void *)eu, sizeof(*eu)); } return -EBAD_MAGIC; } else { GRF_CPY(eu->state_magic, cpu_ack); } eu->sr0 = RSVD_EU << 8 | RSVD_THREAD; return 0; } static int collect_data(int bit, volatile uint8_t *buf) { struct eu_state *eu; ssize_t num; int ret; assert(eu_id(bit) != RSVD_EU); if (eu_fd(bit) == -1) { char name[128]; sprintf(name, "dump_eu_%02d_%d.bin", eu_id(bit), eu_tid(bit)); eu_fd(bit) = open(name, O_CREAT | O_WRONLY | O_TRUNC, S_IRWXO); if (eu_fd(bit) == -1) return -1; } eu = find_eu_shmem(bit, buf); if (eu == NULL) { if (debug) printf("Bad offset %d %d\n", eu_id(bit), eu_tid(bit)); return -EBAD_SHMEM; } ret = verify(eu); if (ret) return ret; num = write(eu_fd(bit), (void *)eu, sizeof(*eu)); if (num != sizeof(*eu)) { perror("unhandled write failure"); return EBAD_WRITE; } return 0; } static void clear_attn(int bit) { #if 0 /* * This works but doesn't allow for easily changed clearing bits */ static void clear_attn_old(int bit) { int bit_to_clear = bit % 32; bit_to_clear = 31 - bit_to_clear; intel_register_write(0x7830 + (bit/32) * 4, 0); intel_register_write(0x7830 + (bit/32) * 4, 1 << bit_to_clear); } #else if (!force_clear) { int bit_to_clear; bit_to_clear = eu_info->debuggees[bit].clr; intel_register_write(EU_ATT_CLR + (bit/32) * 4, 0); intel_register_write(EU_ATT_CLR + (bit/32) * 4, 1 << bit_to_clear); } else { intel_register_write(EU_ATT_CLR + 0, 0); intel_register_write(EU_ATT_CLR + 4, 0); intel_register_write(EU_ATT_CLR + 0, 0xffffffff); intel_register_write(EU_ATT_CLR + 4, 0xffffffff); } #endif } static void db_shutdown(int sig) { shutting_down = 1; printf("Shutting down...\n"); } static void __attribute__((noreturn)) die(int reason) { int i = 0; intel_register_write(EU_ATT_CLR, 0); intel_register_write(EU_ATT_CLR + 4, 0); if (debug_fd) close(debug_fd); for (i = 0; i < eu_info->num_threads; i++) { if (eu_info->debuggees[i].fd != -1) close(eu_info->debuggees[i].fd); } unmap_debug_buffer(); if (old_td_ctl) intel_register_write(TD_CTL, old_td_ctl); intel_register_access_fini(); exit(reason); } static int identify_device(int devid) { if (!IS_SANDYBRIDGE(devid)) return -ENODEV; switch (intel_gt(devid)) { case 0: eu_info = >1; break; case 1: eu_info = >2; break; default: return 1; } return 0; } static void parse_data(const char *file_name) { struct eu_state *eu_state = NULL; struct stat st; int fd = -1; int ret, i, elements; fd = open(file_name, O_RDONLY); if (fd == -1) { perror("open"); goto out; } ret = fstat(fd, &st); if (ret == -1) { perror("fstat"); goto out; } elements = st.st_size / sizeof(struct eu_state); if (elements == 0) { fprintf(stderr, "File not big enough for 1 entry\n"); goto out; } eu_state = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (eu_state == MAP_FAILED) { perror("mmap"); goto out; } for(i = 0; i < elements; i++) { printf("AIP: "); printf("%x\n", ((uint32_t *)eu_state[i].cr0)[2]); } out: if (eu_state) munmap(eu_state, st.st_size); if (fd != -1) close(fd); } static int wait_for_scratch_bo(void) { struct sockaddr_un addr; uint32_t version; int fd, ret, dh_handle = -1; assert(sizeof(version) == sizeof(dh.version)); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) return -1; /* Clean up previous runs */ remove(SHADER_DEBUG_SOCKET); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, SHADER_DEBUG_SOCKET, sizeof(addr.sun_path) - 1); ret = bind(fd, (const struct sockaddr *)&addr, sizeof(addr)); if (ret == -1) { perror("listen"); return -1; } ret = listen(fd, 1); if (ret == -1) { perror("listen"); goto done; } while(1) { int client_fd; size_t count; char ack[] = DEBUG_HANDSHAKE_ACK; client_fd = accept(fd, NULL, NULL); if (client_fd == -1) { perror("accept"); goto done; } count = read(client_fd, &version, sizeof(version)); if (count != sizeof(version)) { perror("read version"); goto loop_out; } if (version != DEBUG_HANDSHAKE_VERSION) { fprintf(stderr, "Bad debug handshake\n"); goto loop_out; } count = read(client_fd, ((char *)&dh) + 1, sizeof(dh) - 1); if (count != sizeof(dh) - 1) { perror("read handshake"); goto loop_out; } count = write(client_fd, ack, sizeof(ack)); if (count != sizeof(ack)) { perror("write ack"); goto loop_out; } dh_handle = dh.flink_handle; if (debug > 0) { printf("Handshake completed successfully\n" "\tprotocol version = %d\n" "\tflink handle = %d\n" "\tper thread scratch = %x\n", version, dh.flink_handle, dh.per_thread_scratch); } loop_out: close(client_fd); break; } done: close(fd); return dh_handle; } static void setup_hw_bits(void) { intel_register_write(INST_PM, GEN6_GLOBAL_DEBUG_ENABLE | GEN6_GLOBAL_DEBUG_ENABLE << 16); old_td_ctl = intel_register_read(GEN6_TD_CTL); intel_register_write(GEN6_TD_CTL, GEN6_TD_CTL_FORCE_TD_BKPT); } int main(int argc, char* argv[]) { struct pci_device *pci_dev; volatile uint8_t *scratch = NULL; int bits[64]; int devid = -1, opt; while ((opt = getopt(argc, argv, "cdr:pf?h")) != -1) { switch (opt) { case 'c': clear_waits = 1; break; case 'd': debug = 1; break; case 'r': parse_data(optarg); exit(0); break; case 'p': devid = atoi(optarg); break; case 'f': force_clear = 1; break; case '?': case 'h': default: exit(0); } } pci_dev = intel_get_pci_device(); if (devid == -1) devid = pci_dev->device_id; if (identify_device(devid)) { abort(); } assert(intel_register_access_init(pci_dev, 1) == 0); memset(bits, -1, sizeof(bits)); /* * These events have to occur before the SR runs, or we need * non-blocking versions of the functions. */ if (!clear_waits) { int dh_handle; drm_fd = drm_open_driver(DRIVER_INTEL); bufmgr = drm_intel_bufmgr_gem_init(drm_fd, 4096); setup_hw_bits(); /* We are probably root, make files world friendly */ umask(0); dh_handle = wait_for_scratch_bo(); if (dh_handle == -1) { printf("No handle from mesa, please enter manually: "); if (fscanf(stdin, "%1d", &dh_handle) == 0) exit(1); } scratch_bo = intel_bo_gem_create_from_name(bufmgr, "scratch", dh_handle); if (scratch_bo == NULL) { fprintf(stderr, "Couldn't flink buffer\n"); abort(); } signal(SIGINT, db_shutdown); printf("Press Ctrl-C to stop\n"); } else { int time = force_clear ? 0 : 20000; while (wait_for_attn(time, bits)) { clear_attn(bits[0]); memset(bits, -1, sizeof(bits)); } die(0); } scratch = map_debug_buffer(); while (shutting_down == 0) { int num_events, i; memset(bits, -1, sizeof(bits)); num_events = wait_for_attn(-1, bits); if (num_events == 0) break; for (i = 0; i < num_events; i++) { assert(bits[i] < 64 && bits[i] >= 0); if (collect_data(bits[i], scratch)) { bits[i] = -1; continue; } clear_attn(bits[i]); } } die(0); return 0; }