101e04c3fSmrg/*
201e04c3fSmrg * Copyright © 2015 Intel Corporation
301e04c3fSmrg *
401e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a
501e04c3fSmrg * copy of this software and associated documentation files (the "Software"),
601e04c3fSmrg * to deal in the Software without restriction, including without limitation
701e04c3fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
801e04c3fSmrg * and/or sell copies of the Software, and to permit persons to whom the
901e04c3fSmrg * Software is furnished to do so, subject to the following conditions:
1001e04c3fSmrg *
1101e04c3fSmrg * The above copyright notice and this permission notice (including the next
1201e04c3fSmrg * paragraph) shall be included in all copies or substantial portions of the
1301e04c3fSmrg * Software.
1401e04c3fSmrg *
1501e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1601e04c3fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1701e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1801e04c3fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1901e04c3fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2001e04c3fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2101e04c3fSmrg * IN THE SOFTWARE.
2201e04c3fSmrg */
2301e04c3fSmrg
2401e04c3fSmrg#include <stdlib.h>
2501e04c3fSmrg#include <stdio.h>
2601e04c3fSmrg#include <string.h>
2701e04c3fSmrg#include <stdint.h>
2801e04c3fSmrg#include <stdbool.h>
2901e04c3fSmrg#include <signal.h>
3001e04c3fSmrg#include <stdarg.h>
3101e04c3fSmrg#include <fcntl.h>
3201e04c3fSmrg#include <sys/types.h>
3301e04c3fSmrg#include <sys/sysmacros.h>
3401e04c3fSmrg#include <sys/stat.h>
3501e04c3fSmrg#include <sys/ioctl.h>
3601e04c3fSmrg#include <unistd.h>
3701e04c3fSmrg#include <errno.h>
3801e04c3fSmrg#include <sys/mman.h>
3901e04c3fSmrg#include <dlfcn.h>
409f464c52Smaya#include "drm-uapi/i915_drm.h"
4101e04c3fSmrg#include <inttypes.h>
4201e04c3fSmrg
4301e04c3fSmrg#include "intel_aub.h"
4401e04c3fSmrg#include "aub_write.h"
4501e04c3fSmrg
467ec681f3Smrg#include "dev/intel_debug.h"
477ec681f3Smrg#include "dev/intel_device_info.h"
4801e04c3fSmrg#include "util/macros.h"
4901e04c3fSmrg
5001e04c3fSmrgstatic int close_init_helper(int fd);
5101e04c3fSmrgstatic int ioctl_init_helper(int fd, unsigned long request, ...);
527ec681f3Smrgstatic int munmap_init_helper(void *addr, size_t length);
5301e04c3fSmrg
5401e04c3fSmrgstatic int (*libc_close)(int fd) = close_init_helper;
5501e04c3fSmrgstatic int (*libc_ioctl)(int fd, unsigned long request, ...) = ioctl_init_helper;
567ec681f3Smrgstatic int (*libc_munmap)(void *addr, size_t length) = munmap_init_helper;
5701e04c3fSmrg
5801e04c3fSmrgstatic int drm_fd = -1;
5901e04c3fSmrgstatic char *output_filename = NULL;
6001e04c3fSmrgstatic FILE *output_file = NULL;
6101e04c3fSmrgstatic int verbose = 0;
627ec681f3Smrgstatic bool device_override = false;
637ec681f3Smrgstatic bool capture_only = false;
647ec681f3Smrgstatic int64_t frame_id = -1;
657ec681f3Smrgstatic bool capture_finished = false;
6601e04c3fSmrg
679f464c52Smaya#define MAX_FD_COUNT 64
6801e04c3fSmrg#define MAX_BO_COUNT 64 * 1024
6901e04c3fSmrg
7001e04c3fSmrgstruct bo {
7101e04c3fSmrg   uint32_t size;
7201e04c3fSmrg   uint64_t offset;
7301e04c3fSmrg   void *map;
747ec681f3Smrg   /* Whether the buffer has been positionned in the GTT already. */
757ec681f3Smrg   bool gtt_mapped : 1;
767ec681f3Smrg   /* Tracks userspace mmapping of the buffer */
777ec681f3Smrg   bool user_mapped : 1;
787ec681f3Smrg   /* Using the i915-gem mmapping ioctl & execbuffer ioctl, track whether a
797ec681f3Smrg    * buffer has been updated.
807ec681f3Smrg    */
817ec681f3Smrg   bool dirty : 1;
8201e04c3fSmrg};
8301e04c3fSmrg
8401e04c3fSmrgstatic struct bo *bos;
8501e04c3fSmrg
8601e04c3fSmrg#define DRM_MAJOR 226
8701e04c3fSmrg
8801e04c3fSmrg/* We set bit 0 in the map pointer for userptr BOs so we know not to
8901e04c3fSmrg * munmap them on DRM_IOCTL_GEM_CLOSE.
9001e04c3fSmrg */
9101e04c3fSmrg#define USERPTR_FLAG 1
9201e04c3fSmrg#define IS_USERPTR(p) ((uintptr_t) (p) & USERPTR_FLAG)
9301e04c3fSmrg#define GET_PTR(p) ( (void *) ((uintptr_t) p & ~(uintptr_t) 1) )
9401e04c3fSmrg
957ec681f3Smrg#define fail_if(cond, ...) _fail_if(cond, "intel_dump_gpu", __VA_ARGS__)
9601e04c3fSmrg
9701e04c3fSmrgstatic struct bo *
989f464c52Smayaget_bo(unsigned fd, uint32_t handle)
9901e04c3fSmrg{
10001e04c3fSmrg   struct bo *bo;
10101e04c3fSmrg
10201e04c3fSmrg   fail_if(handle >= MAX_BO_COUNT, "bo handle too large\n");
1039f464c52Smaya   fail_if(fd >= MAX_FD_COUNT, "bo fd too large\n");
1049f464c52Smaya   bo = &bos[handle + fd * MAX_BO_COUNT];
10501e04c3fSmrg
10601e04c3fSmrg   return bo;
10701e04c3fSmrg}
10801e04c3fSmrg
10901e04c3fSmrgstatic inline uint32_t
11001e04c3fSmrgalign_u32(uint32_t v, uint32_t a)
11101e04c3fSmrg{
11201e04c3fSmrg   return (v + a - 1) & ~(a - 1);
11301e04c3fSmrg}
11401e04c3fSmrg
1157ec681f3Smrgstatic struct intel_device_info devinfo = {0};
1167ec681f3Smrgstatic int device = 0;
11701e04c3fSmrgstatic struct aub_file aub_file;
11801e04c3fSmrg
1197ec681f3Smrgstatic void
1207ec681f3Smrgensure_device_info(int fd)
1217ec681f3Smrg{
1227ec681f3Smrg   /* We can't do this at open time as we're not yet authenticated. */
1237ec681f3Smrg   if (device == 0) {
1247ec681f3Smrg      fail_if(!intel_get_device_info_from_fd(fd, &devinfo),
1257ec681f3Smrg              "failed to identify chipset.\n");
1267ec681f3Smrg      device = devinfo.chipset_id;
1277ec681f3Smrg   } else if (devinfo.ver == 0) {
1287ec681f3Smrg      fail_if(!intel_get_device_info_from_pci_id(device, &devinfo),
1297ec681f3Smrg              "failed to identify chipset.\n");
1307ec681f3Smrg   }
1317ec681f3Smrg}
1327ec681f3Smrg
13301e04c3fSmrgstatic void *
1349f464c52Smayarelocate_bo(int fd, struct bo *bo, const struct drm_i915_gem_execbuffer2 *execbuffer2,
13501e04c3fSmrg            const struct drm_i915_gem_exec_object2 *obj)
13601e04c3fSmrg{
13701e04c3fSmrg   const struct drm_i915_gem_exec_object2 *exec_objects =
13801e04c3fSmrg      (struct drm_i915_gem_exec_object2 *) (uintptr_t) execbuffer2->buffers_ptr;
13901e04c3fSmrg   const struct drm_i915_gem_relocation_entry *relocs =
14001e04c3fSmrg      (const struct drm_i915_gem_relocation_entry *) (uintptr_t) obj->relocs_ptr;
14101e04c3fSmrg   void *relocated;
14201e04c3fSmrg   int handle;
14301e04c3fSmrg
14401e04c3fSmrg   relocated = malloc(bo->size);
14501e04c3fSmrg   fail_if(relocated == NULL, "out of memory\n");
14601e04c3fSmrg   memcpy(relocated, GET_PTR(bo->map), bo->size);
14701e04c3fSmrg   for (size_t i = 0; i < obj->relocation_count; i++) {
14801e04c3fSmrg      fail_if(relocs[i].offset >= bo->size, "reloc outside bo\n");
14901e04c3fSmrg
15001e04c3fSmrg      if (execbuffer2->flags & I915_EXEC_HANDLE_LUT)
15101e04c3fSmrg         handle = exec_objects[relocs[i].target_handle].handle;
15201e04c3fSmrg      else
15301e04c3fSmrg         handle = relocs[i].target_handle;
15401e04c3fSmrg
15501e04c3fSmrg      aub_write_reloc(&devinfo, ((char *)relocated) + relocs[i].offset,
1569f464c52Smaya                      get_bo(fd, handle)->offset + relocs[i].delta);
15701e04c3fSmrg   }
15801e04c3fSmrg
15901e04c3fSmrg   return relocated;
16001e04c3fSmrg}
16101e04c3fSmrg
16201e04c3fSmrgstatic int
16301e04c3fSmrggem_ioctl(int fd, unsigned long request, void *argp)
16401e04c3fSmrg{
16501e04c3fSmrg   int ret;
16601e04c3fSmrg
16701e04c3fSmrg   do {
16801e04c3fSmrg      ret = libc_ioctl(fd, request, argp);
16901e04c3fSmrg   } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
17001e04c3fSmrg
17101e04c3fSmrg   return ret;
17201e04c3fSmrg}
17301e04c3fSmrg
17401e04c3fSmrgstatic void *
17501e04c3fSmrggem_mmap(int fd, uint32_t handle, uint64_t offset, uint64_t size)
17601e04c3fSmrg{
17701e04c3fSmrg   struct drm_i915_gem_mmap mmap = {
17801e04c3fSmrg      .handle = handle,
17901e04c3fSmrg      .offset = offset,
18001e04c3fSmrg      .size = size
18101e04c3fSmrg   };
18201e04c3fSmrg
18301e04c3fSmrg   if (gem_ioctl(fd, DRM_IOCTL_I915_GEM_MMAP, &mmap) == -1)
18401e04c3fSmrg      return MAP_FAILED;
18501e04c3fSmrg
18601e04c3fSmrg   return (void *)(uintptr_t) mmap.addr_ptr;
18701e04c3fSmrg}
18801e04c3fSmrg
1899f464c52Smayastatic enum drm_i915_gem_engine_class
1909f464c52Smayaengine_class_from_ring_flag(uint32_t ring_flag)
1919f464c52Smaya{
1929f464c52Smaya   switch (ring_flag) {
1939f464c52Smaya   case I915_EXEC_DEFAULT:
1949f464c52Smaya   case I915_EXEC_RENDER:
1959f464c52Smaya      return I915_ENGINE_CLASS_RENDER;
1969f464c52Smaya   case I915_EXEC_BSD:
1979f464c52Smaya      return I915_ENGINE_CLASS_VIDEO;
1989f464c52Smaya   case I915_EXEC_BLT:
1999f464c52Smaya      return I915_ENGINE_CLASS_COPY;
2009f464c52Smaya   case I915_EXEC_VEBOX:
2019f464c52Smaya      return I915_ENGINE_CLASS_VIDEO_ENHANCE;
2029f464c52Smaya   default:
2039f464c52Smaya      return I915_ENGINE_CLASS_INVALID;
2049f464c52Smaya   }
2059f464c52Smaya}
2069f464c52Smaya
20701e04c3fSmrgstatic void
20801e04c3fSmrgdump_execbuffer2(int fd, struct drm_i915_gem_execbuffer2 *execbuffer2)
20901e04c3fSmrg{
21001e04c3fSmrg   struct drm_i915_gem_exec_object2 *exec_objects =
21101e04c3fSmrg      (struct drm_i915_gem_exec_object2 *) (uintptr_t) execbuffer2->buffers_ptr;
21201e04c3fSmrg   uint32_t ring_flag = execbuffer2->flags & I915_EXEC_RING_MASK;
21301e04c3fSmrg   uint32_t offset;
21401e04c3fSmrg   struct drm_i915_gem_exec_object2 *obj;
21501e04c3fSmrg   struct bo *bo, *batch_bo;
21601e04c3fSmrg   int batch_index;
21701e04c3fSmrg   void *data;
21801e04c3fSmrg
2197ec681f3Smrg   ensure_device_info(fd);
2207ec681f3Smrg
2217ec681f3Smrg   if (capture_finished)
2227ec681f3Smrg      return;
22301e04c3fSmrg
2247ec681f3Smrg   if (!aub_file.file) {
2259f464c52Smaya      aub_file_init(&aub_file, output_file,
2269f464c52Smaya                    verbose == 2 ? stdout : NULL,
2279f464c52Smaya                    device, program_invocation_short_name);
2289f464c52Smaya      aub_write_default_setup(&aub_file);
22901e04c3fSmrg
23001e04c3fSmrg      if (verbose)
23101e04c3fSmrg         printf("[running, output file %s, chipset id 0x%04x, gen %d]\n",
2327ec681f3Smrg                output_filename, device, devinfo.ver);
23301e04c3fSmrg   }
23401e04c3fSmrg
23501e04c3fSmrg   if (aub_use_execlists(&aub_file))
23601e04c3fSmrg      offset = 0x1000;
23701e04c3fSmrg   else
23801e04c3fSmrg      offset = aub_gtt_size(&aub_file);
23901e04c3fSmrg
24001e04c3fSmrg   for (uint32_t i = 0; i < execbuffer2->buffer_count; i++) {
24101e04c3fSmrg      obj = &exec_objects[i];
2429f464c52Smaya      bo = get_bo(fd, obj->handle);
24301e04c3fSmrg
24401e04c3fSmrg      /* If bo->size == 0, this means they passed us an invalid
24501e04c3fSmrg       * buffer.  The kernel will reject it and so should we.
24601e04c3fSmrg       */
24701e04c3fSmrg      if (bo->size == 0) {
24801e04c3fSmrg         if (verbose)
24901e04c3fSmrg            printf("BO #%d is invalid!\n", obj->handle);
25001e04c3fSmrg         return;
25101e04c3fSmrg      }
25201e04c3fSmrg
25301e04c3fSmrg      if (obj->flags & EXEC_OBJECT_PINNED) {
2547ec681f3Smrg         if (bo->offset != obj->offset)
2557ec681f3Smrg            bo->gtt_mapped = false;
25601e04c3fSmrg         bo->offset = obj->offset;
25701e04c3fSmrg      } else {
25801e04c3fSmrg         if (obj->alignment != 0)
25901e04c3fSmrg            offset = align_u32(offset, obj->alignment);
26001e04c3fSmrg         bo->offset = offset;
26101e04c3fSmrg         offset = align_u32(offset + bo->size + 4095, 4096);
26201e04c3fSmrg      }
26301e04c3fSmrg
26401e04c3fSmrg      if (bo->map == NULL && bo->size > 0)
26501e04c3fSmrg         bo->map = gem_mmap(fd, obj->handle, 0, bo->size);
26601e04c3fSmrg      fail_if(bo->map == MAP_FAILED, "bo mmap failed\n");
2677ec681f3Smrg   }
2687ec681f3Smrg
2697ec681f3Smrg   uint64_t current_frame_id = 0;
2707ec681f3Smrg   if (frame_id >= 0) {
2717ec681f3Smrg      for (uint32_t i = 0; i < execbuffer2->buffer_count; i++) {
2727ec681f3Smrg         obj = &exec_objects[i];
2737ec681f3Smrg         bo = get_bo(fd, obj->handle);
2747ec681f3Smrg
2757ec681f3Smrg         /* Check against frame_id requirements. */
2767ec681f3Smrg         if (memcmp(bo->map, intel_debug_identifier(),
2777ec681f3Smrg                    intel_debug_identifier_size()) == 0) {
2787ec681f3Smrg            const struct intel_debug_block_frame *frame_desc =
2797ec681f3Smrg               intel_debug_get_identifier_block(bo->map, bo->size,
2807ec681f3Smrg                                                INTEL_DEBUG_BLOCK_TYPE_FRAME);
2817ec681f3Smrg
2827ec681f3Smrg            current_frame_id = frame_desc ? frame_desc->frame_id : 0;
2837ec681f3Smrg            break;
2847ec681f3Smrg         }
2857ec681f3Smrg      }
2867ec681f3Smrg   }
2877ec681f3Smrg
2887ec681f3Smrg   if (verbose)
2897ec681f3Smrg      printf("Dumping execbuffer2 (frame_id=%"PRIu64", buffers=%u):\n",
2907ec681f3Smrg             current_frame_id, execbuffer2->buffer_count);
29101e04c3fSmrg
2927ec681f3Smrg   /* Check whether we can stop right now. */
2937ec681f3Smrg   if (frame_id >= 0) {
2947ec681f3Smrg      if (current_frame_id < frame_id)
2957ec681f3Smrg         return;
2967ec681f3Smrg
2977ec681f3Smrg      if (current_frame_id > frame_id) {
2987ec681f3Smrg         aub_file_finish(&aub_file);
2997ec681f3Smrg         capture_finished = true;
3007ec681f3Smrg         return;
3017ec681f3Smrg      }
3027ec681f3Smrg   }
3037ec681f3Smrg
3047ec681f3Smrg
3057ec681f3Smrg   /* Map buffers into the PPGTT. */
3067ec681f3Smrg   for (uint32_t i = 0; i < execbuffer2->buffer_count; i++) {
3077ec681f3Smrg      obj = &exec_objects[i];
3087ec681f3Smrg      bo = get_bo(fd, obj->handle);
3097ec681f3Smrg
3107ec681f3Smrg      if (verbose) {
3117ec681f3Smrg         printf("BO #%d (%dB) @ 0x%" PRIx64 "\n",
3127ec681f3Smrg                obj->handle, bo->size, bo->offset);
3137ec681f3Smrg      }
3147ec681f3Smrg
3157ec681f3Smrg      if (aub_use_execlists(&aub_file) && !bo->gtt_mapped) {
31601e04c3fSmrg         aub_map_ppgtt(&aub_file, bo->offset, bo->size);
3177ec681f3Smrg         bo->gtt_mapped = true;
3187ec681f3Smrg      }
31901e04c3fSmrg   }
32001e04c3fSmrg
3217ec681f3Smrg   /* Write the buffer content into the Aub. */
32201e04c3fSmrg   batch_index = (execbuffer2->flags & I915_EXEC_BATCH_FIRST) ? 0 :
32301e04c3fSmrg      execbuffer2->buffer_count - 1;
3249f464c52Smaya   batch_bo = get_bo(fd, exec_objects[batch_index].handle);
32501e04c3fSmrg   for (uint32_t i = 0; i < execbuffer2->buffer_count; i++) {
32601e04c3fSmrg      obj = &exec_objects[i];
3279f464c52Smaya      bo = get_bo(fd, obj->handle);
32801e04c3fSmrg
32901e04c3fSmrg      if (obj->relocation_count > 0)
3309f464c52Smaya         data = relocate_bo(fd, bo, execbuffer2, obj);
33101e04c3fSmrg      else
33201e04c3fSmrg         data = bo->map;
33301e04c3fSmrg
3347ec681f3Smrg      bool write = !capture_only || (obj->flags & EXEC_OBJECT_CAPTURE);
3357ec681f3Smrg
3367ec681f3Smrg      if (write && bo->dirty) {
3377ec681f3Smrg         if (bo == batch_bo) {
3387ec681f3Smrg            aub_write_trace_block(&aub_file, AUB_TRACE_TYPE_BATCH,
3397ec681f3Smrg                                  GET_PTR(data), bo->size, bo->offset);
3407ec681f3Smrg         } else {
3417ec681f3Smrg            aub_write_trace_block(&aub_file, AUB_TRACE_TYPE_NOTYPE,
3427ec681f3Smrg                                  GET_PTR(data), bo->size, bo->offset);
3437ec681f3Smrg         }
3447ec681f3Smrg
3457ec681f3Smrg         if (!bo->user_mapped)
3467ec681f3Smrg            bo->dirty = false;
34701e04c3fSmrg      }
34801e04c3fSmrg
34901e04c3fSmrg      if (data != bo->map)
35001e04c3fSmrg         free(data);
35101e04c3fSmrg   }
35201e04c3fSmrg
3537ec681f3Smrg   uint32_t ctx_id = execbuffer2->rsvd1;
3547ec681f3Smrg
3557ec681f3Smrg   aub_write_exec(&aub_file, ctx_id,
35601e04c3fSmrg                  batch_bo->offset + execbuffer2->batch_start_offset,
3579f464c52Smaya                  offset, engine_class_from_ring_flag(ring_flag));
35801e04c3fSmrg
35901e04c3fSmrg   if (device_override &&
36001e04c3fSmrg       (execbuffer2->flags & I915_EXEC_FENCE_ARRAY) != 0) {
36101e04c3fSmrg      struct drm_i915_gem_exec_fence *fences =
36201e04c3fSmrg         (void*)(uintptr_t)execbuffer2->cliprects_ptr;
36301e04c3fSmrg      for (uint32_t i = 0; i < execbuffer2->num_cliprects; i++) {
36401e04c3fSmrg         if ((fences[i].flags & I915_EXEC_FENCE_SIGNAL) != 0) {
36501e04c3fSmrg            struct drm_syncobj_array arg = {
36601e04c3fSmrg               .handles = (uintptr_t)&fences[i].handle,
36701e04c3fSmrg               .count_handles = 1,
36801e04c3fSmrg               .pad = 0,
36901e04c3fSmrg            };
37001e04c3fSmrg            libc_ioctl(fd, DRM_IOCTL_SYNCOBJ_SIGNAL, &arg);
37101e04c3fSmrg         }
37201e04c3fSmrg      }
37301e04c3fSmrg   }
37401e04c3fSmrg}
37501e04c3fSmrg
37601e04c3fSmrgstatic void
3779f464c52Smayaadd_new_bo(unsigned fd, int handle, uint64_t size, void *map)
37801e04c3fSmrg{
3799f464c52Smaya   struct bo *bo = &bos[handle + fd * MAX_BO_COUNT];
38001e04c3fSmrg
38101e04c3fSmrg   fail_if(handle >= MAX_BO_COUNT, "bo handle out of range\n");
3829f464c52Smaya   fail_if(fd >= MAX_FD_COUNT, "bo fd out of range\n");
38301e04c3fSmrg   fail_if(size == 0, "bo size is invalid\n");
38401e04c3fSmrg
38501e04c3fSmrg   bo->size = size;
38601e04c3fSmrg   bo->map = map;
3877ec681f3Smrg   bo->user_mapped = false;
3887ec681f3Smrg   bo->gtt_mapped = false;
38901e04c3fSmrg}
39001e04c3fSmrg
39101e04c3fSmrgstatic void
3929f464c52Smayaremove_bo(int fd, int handle)
39301e04c3fSmrg{
3949f464c52Smaya   struct bo *bo = get_bo(fd, handle);
39501e04c3fSmrg
39601e04c3fSmrg   if (bo->map && !IS_USERPTR(bo->map))
39701e04c3fSmrg      munmap(bo->map, bo->size);
3987ec681f3Smrg   memset(bo, 0, sizeof(*bo));
39901e04c3fSmrg}
40001e04c3fSmrg
40101e04c3fSmrg__attribute__ ((visibility ("default"))) int
40201e04c3fSmrgclose(int fd)
40301e04c3fSmrg{
40401e04c3fSmrg   if (fd == drm_fd)
40501e04c3fSmrg      drm_fd = -1;
40601e04c3fSmrg
40701e04c3fSmrg   return libc_close(fd);
40801e04c3fSmrg}
40901e04c3fSmrg
4107ec681f3Smrgstatic int
4117ec681f3Smrgget_pci_id(int fd, int *pci_id)
4127ec681f3Smrg{
4137ec681f3Smrg   struct drm_i915_getparam gparam;
4147ec681f3Smrg
4157ec681f3Smrg   if (device_override) {
4167ec681f3Smrg      *pci_id = device;
4177ec681f3Smrg      return 0;
4187ec681f3Smrg   }
4197ec681f3Smrg
4207ec681f3Smrg   gparam.param = I915_PARAM_CHIPSET_ID;
4217ec681f3Smrg   gparam.value = pci_id;
4227ec681f3Smrg   return libc_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gparam);
4237ec681f3Smrg}
4247ec681f3Smrg
42501e04c3fSmrgstatic void
4267ec681f3Smrgmaybe_init(int fd)
42701e04c3fSmrg{
42801e04c3fSmrg   static bool initialized = false;
42901e04c3fSmrg   FILE *config;
43001e04c3fSmrg   char *key, *value;
43101e04c3fSmrg
43201e04c3fSmrg   if (initialized)
43301e04c3fSmrg      return;
43401e04c3fSmrg
43501e04c3fSmrg   initialized = true;
43601e04c3fSmrg
4377ec681f3Smrg   const char *config_path = getenv("INTEL_DUMP_GPU_CONFIG");
4387ec681f3Smrg   fail_if(config_path == NULL, "INTEL_DUMP_GPU_CONFIG is not set\n");
4397ec681f3Smrg
4407ec681f3Smrg   config = fopen(config_path, "r");
4417ec681f3Smrg   fail_if(config == NULL, "failed to open file %s\n", config_path);
4427ec681f3Smrg
44301e04c3fSmrg   while (fscanf(config, "%m[^=]=%m[^\n]\n", &key, &value) != EOF) {
44401e04c3fSmrg      if (!strcmp(key, "verbose")) {
44501e04c3fSmrg         if (!strcmp(value, "1")) {
44601e04c3fSmrg            verbose = 1;
44701e04c3fSmrg         } else if (!strcmp(value, "2")) {
44801e04c3fSmrg            verbose = 2;
44901e04c3fSmrg         }
45001e04c3fSmrg      } else if (!strcmp(key, "device")) {
4517ec681f3Smrg         fail_if(device != 0, "Device/Platform override specified multiple times.\n");
45201e04c3fSmrg         fail_if(sscanf(value, "%i", &device) != 1,
4537ec681f3Smrg                 "failed to parse device id '%s'\n",
45401e04c3fSmrg                 value);
45501e04c3fSmrg         device_override = true;
4569f464c52Smaya      } else if (!strcmp(key, "platform")) {
4577ec681f3Smrg         fail_if(device != 0, "Device/Platform override specified multiple times.\n");
4587ec681f3Smrg         device = intel_device_name_to_pci_device_id(value);
4597ec681f3Smrg         fail_if(device == -1, "Unknown platform '%s'\n", value);
4609f464c52Smaya         device_override = true;
46101e04c3fSmrg      } else if (!strcmp(key, "file")) {
4627ec681f3Smrg         free(output_filename);
4637ec681f3Smrg         if (output_file)
4647ec681f3Smrg            fclose(output_file);
46501e04c3fSmrg         output_filename = strdup(value);
46601e04c3fSmrg         output_file = fopen(output_filename, "w+");
46701e04c3fSmrg         fail_if(output_file == NULL,
46801e04c3fSmrg                 "failed to open file '%s'\n",
46901e04c3fSmrg                 output_filename);
4707ec681f3Smrg      } else if (!strcmp(key, "capture_only")) {
4717ec681f3Smrg         capture_only = atoi(value);
4727ec681f3Smrg      } else if (!strcmp(key, "frame")) {
4737ec681f3Smrg         frame_id = atol(value);
47401e04c3fSmrg      } else {
47501e04c3fSmrg         fprintf(stderr, "unknown option '%s'\n", key);
47601e04c3fSmrg      }
47701e04c3fSmrg
47801e04c3fSmrg      free(key);
47901e04c3fSmrg      free(value);
48001e04c3fSmrg   }
48101e04c3fSmrg   fclose(config);
48201e04c3fSmrg
4839f464c52Smaya   bos = calloc(MAX_FD_COUNT * MAX_BO_COUNT, sizeof(bos[0]));
48401e04c3fSmrg   fail_if(bos == NULL, "out of memory\n");
4857ec681f3Smrg
4867ec681f3Smrg   ASSERTED int ret = get_pci_id(fd, &device);
4877ec681f3Smrg   assert(ret == 0);
4887ec681f3Smrg
4897ec681f3Smrg   aub_file_init(&aub_file, output_file,
4907ec681f3Smrg                 verbose == 2 ? stdout : NULL,
4917ec681f3Smrg                 device, program_invocation_short_name);
4927ec681f3Smrg   aub_write_default_setup(&aub_file);
4937ec681f3Smrg
4947ec681f3Smrg   if (verbose)
4957ec681f3Smrg      printf("[running, output file %s, chipset id 0x%04x, gen %d]\n",
4967ec681f3Smrg             output_filename, device, devinfo.ver);
49701e04c3fSmrg}
49801e04c3fSmrg
49901e04c3fSmrg__attribute__ ((visibility ("default"))) int
50001e04c3fSmrgioctl(int fd, unsigned long request, ...)
50101e04c3fSmrg{
50201e04c3fSmrg   va_list args;
50301e04c3fSmrg   void *argp;
50401e04c3fSmrg   int ret;
50501e04c3fSmrg   struct stat buf;
50601e04c3fSmrg
50701e04c3fSmrg   va_start(args, request);
50801e04c3fSmrg   argp = va_arg(args, void *);
50901e04c3fSmrg   va_end(args);
51001e04c3fSmrg
51101e04c3fSmrg   if (_IOC_TYPE(request) == DRM_IOCTL_BASE &&
51201e04c3fSmrg       drm_fd != fd && fstat(fd, &buf) == 0 &&
51301e04c3fSmrg       (buf.st_mode & S_IFMT) == S_IFCHR && major(buf.st_rdev) == DRM_MAJOR) {
51401e04c3fSmrg      drm_fd = fd;
51501e04c3fSmrg      if (verbose)
51601e04c3fSmrg         printf("[intercept drm ioctl on fd %d]\n", fd);
51701e04c3fSmrg   }
51801e04c3fSmrg
51901e04c3fSmrg   if (fd == drm_fd) {
5207ec681f3Smrg      maybe_init(fd);
52101e04c3fSmrg
52201e04c3fSmrg      switch (request) {
5237ec681f3Smrg      case DRM_IOCTL_SYNCOBJ_WAIT:
5247ec681f3Smrg      case DRM_IOCTL_I915_GEM_WAIT: {
5257ec681f3Smrg         if (device_override)
5267ec681f3Smrg            return 0;
5277ec681f3Smrg         return libc_ioctl(fd, request, argp);
5287ec681f3Smrg      }
5297ec681f3Smrg
5307ec681f3Smrg      case DRM_IOCTL_I915_GET_RESET_STATS: {
5317ec681f3Smrg         if (device_override) {
5327ec681f3Smrg            struct drm_i915_reset_stats *stats = argp;
5337ec681f3Smrg
5347ec681f3Smrg            stats->reset_count = 0;
5357ec681f3Smrg            stats->batch_active = 0;
5367ec681f3Smrg            stats->batch_pending = 0;
5377ec681f3Smrg            return 0;
5387ec681f3Smrg         }
5397ec681f3Smrg         return libc_ioctl(fd, request, argp);
5407ec681f3Smrg      }
5417ec681f3Smrg
54201e04c3fSmrg      case DRM_IOCTL_I915_GETPARAM: {
54301e04c3fSmrg         struct drm_i915_getparam *getparam = argp;
54401e04c3fSmrg
5457ec681f3Smrg         ensure_device_info(fd);
5467ec681f3Smrg
5477ec681f3Smrg         if (getparam->param == I915_PARAM_CHIPSET_ID)
5487ec681f3Smrg            return get_pci_id(fd, getparam->value);
5497ec681f3Smrg
5507ec681f3Smrg         if (device_override) {
5517ec681f3Smrg            switch (getparam->param) {
5527ec681f3Smrg            case I915_PARAM_CS_TIMESTAMP_FREQUENCY:
5537ec681f3Smrg               *getparam->value = devinfo.timestamp_frequency;
5547ec681f3Smrg               return 0;
5557ec681f3Smrg
5567ec681f3Smrg            case I915_PARAM_HAS_WAIT_TIMEOUT:
5577ec681f3Smrg            case I915_PARAM_HAS_EXECBUF2:
5587ec681f3Smrg            case I915_PARAM_MMAP_VERSION:
5597ec681f3Smrg            case I915_PARAM_HAS_EXEC_ASYNC:
5607ec681f3Smrg            case I915_PARAM_HAS_EXEC_FENCE:
5617ec681f3Smrg            case I915_PARAM_HAS_EXEC_FENCE_ARRAY:
5627ec681f3Smrg               *getparam->value = 1;
5637ec681f3Smrg               return 0;
5647ec681f3Smrg
5657ec681f3Smrg            case I915_PARAM_HAS_EXEC_SOFTPIN:
5667ec681f3Smrg               *getparam->value = devinfo.ver >= 8 && !devinfo.is_cherryview;
5677ec681f3Smrg               return 0;
5687ec681f3Smrg
5697ec681f3Smrg            default:
5707ec681f3Smrg               return -1;
5717ec681f3Smrg            }
57201e04c3fSmrg         }
57301e04c3fSmrg
5747ec681f3Smrg         return libc_ioctl(fd, request, argp);
5757ec681f3Smrg      }
57601e04c3fSmrg
5777ec681f3Smrg      case DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM: {
5787ec681f3Smrg         struct drm_i915_gem_context_param *getparam = argp;
5797ec681f3Smrg
5807ec681f3Smrg         ensure_device_info(fd);
5817ec681f3Smrg
5827ec681f3Smrg         if (device_override) {
5837ec681f3Smrg            switch (getparam->param) {
5847ec681f3Smrg            case I915_CONTEXT_PARAM_GTT_SIZE:
5857ec681f3Smrg               if (devinfo.is_elkhartlake)
5867ec681f3Smrg                  getparam->value = 1ull << 36;
5877ec681f3Smrg               else if (devinfo.ver >= 8 && !devinfo.is_cherryview)
5887ec681f3Smrg                  getparam->value = 1ull << 48;
5897ec681f3Smrg               else
5907ec681f3Smrg                  getparam->value = 1ull << 31;
5917ec681f3Smrg               return 0;
5927ec681f3Smrg
5937ec681f3Smrg            default:
5947ec681f3Smrg               return -1;
5957ec681f3Smrg            }
5967ec681f3Smrg         }
59701e04c3fSmrg
5987ec681f3Smrg         return libc_ioctl(fd, request, argp);
59901e04c3fSmrg      }
60001e04c3fSmrg
60101e04c3fSmrg      case DRM_IOCTL_I915_GEM_EXECBUFFER: {
60201e04c3fSmrg         static bool once;
60301e04c3fSmrg         if (!once) {
60401e04c3fSmrg            fprintf(stderr,
60501e04c3fSmrg                    "application uses DRM_IOCTL_I915_GEM_EXECBUFFER, not handled\n");
60601e04c3fSmrg            once = true;
60701e04c3fSmrg         }
60801e04c3fSmrg         return libc_ioctl(fd, request, argp);
60901e04c3fSmrg      }
61001e04c3fSmrg
61101e04c3fSmrg      case DRM_IOCTL_I915_GEM_EXECBUFFER2:
61201e04c3fSmrg      case DRM_IOCTL_I915_GEM_EXECBUFFER2_WR: {
61301e04c3fSmrg         dump_execbuffer2(fd, argp);
61401e04c3fSmrg         if (device_override)
61501e04c3fSmrg            return 0;
61601e04c3fSmrg
61701e04c3fSmrg         return libc_ioctl(fd, request, argp);
61801e04c3fSmrg      }
61901e04c3fSmrg
6207ec681f3Smrg      case DRM_IOCTL_I915_GEM_CONTEXT_CREATE: {
6217ec681f3Smrg         uint32_t *ctx_id = NULL;
6227ec681f3Smrg         struct drm_i915_gem_context_create *create = argp;
6237ec681f3Smrg         ret = 0;
6247ec681f3Smrg         if (!device_override) {
6257ec681f3Smrg            ret = libc_ioctl(fd, request, argp);
6267ec681f3Smrg            ctx_id = &create->ctx_id;
6277ec681f3Smrg         }
6287ec681f3Smrg
6297ec681f3Smrg         if (ret == 0)
6307ec681f3Smrg            create->ctx_id = aub_write_context_create(&aub_file, ctx_id);
6317ec681f3Smrg
6327ec681f3Smrg         return ret;
6337ec681f3Smrg      }
6347ec681f3Smrg
6357ec681f3Smrg      case DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT: {
6367ec681f3Smrg         uint32_t *ctx_id = NULL;
6377ec681f3Smrg         struct drm_i915_gem_context_create_ext *create = argp;
6387ec681f3Smrg         ret = 0;
6397ec681f3Smrg         if (!device_override) {
6407ec681f3Smrg            ret = libc_ioctl(fd, request, argp);
6417ec681f3Smrg            ctx_id = &create->ctx_id;
6427ec681f3Smrg         }
6437ec681f3Smrg
6447ec681f3Smrg         if (ret == 0)
6457ec681f3Smrg            create->ctx_id = aub_write_context_create(&aub_file, ctx_id);
6467ec681f3Smrg
6477ec681f3Smrg         return ret;
6487ec681f3Smrg      }
6497ec681f3Smrg
65001e04c3fSmrg      case DRM_IOCTL_I915_GEM_CREATE: {
65101e04c3fSmrg         struct drm_i915_gem_create *create = argp;
65201e04c3fSmrg
65301e04c3fSmrg         ret = libc_ioctl(fd, request, argp);
65401e04c3fSmrg         if (ret == 0)
6559f464c52Smaya            add_new_bo(fd, create->handle, create->size, NULL);
65601e04c3fSmrg
65701e04c3fSmrg         return ret;
65801e04c3fSmrg      }
65901e04c3fSmrg
66001e04c3fSmrg      case DRM_IOCTL_I915_GEM_USERPTR: {
66101e04c3fSmrg         struct drm_i915_gem_userptr *userptr = argp;
66201e04c3fSmrg
66301e04c3fSmrg         ret = libc_ioctl(fd, request, argp);
66401e04c3fSmrg         if (ret == 0)
6659f464c52Smaya            add_new_bo(fd, userptr->handle, userptr->user_size,
66601e04c3fSmrg                       (void *) (uintptr_t) (userptr->user_ptr | USERPTR_FLAG));
6679f464c52Smaya
66801e04c3fSmrg         return ret;
66901e04c3fSmrg      }
67001e04c3fSmrg
67101e04c3fSmrg      case DRM_IOCTL_GEM_CLOSE: {
67201e04c3fSmrg         struct drm_gem_close *close = argp;
67301e04c3fSmrg
6749f464c52Smaya         remove_bo(fd, close->handle);
67501e04c3fSmrg
67601e04c3fSmrg         return libc_ioctl(fd, request, argp);
67701e04c3fSmrg      }
67801e04c3fSmrg
67901e04c3fSmrg      case DRM_IOCTL_GEM_OPEN: {
68001e04c3fSmrg         struct drm_gem_open *open = argp;
68101e04c3fSmrg
68201e04c3fSmrg         ret = libc_ioctl(fd, request, argp);
68301e04c3fSmrg         if (ret == 0)
6849f464c52Smaya            add_new_bo(fd, open->handle, open->size, NULL);
68501e04c3fSmrg
68601e04c3fSmrg         return ret;
68701e04c3fSmrg      }
68801e04c3fSmrg
68901e04c3fSmrg      case DRM_IOCTL_PRIME_FD_TO_HANDLE: {
69001e04c3fSmrg         struct drm_prime_handle *prime = argp;
69101e04c3fSmrg
69201e04c3fSmrg         ret = libc_ioctl(fd, request, argp);
69301e04c3fSmrg         if (ret == 0) {
69401e04c3fSmrg            off_t size;
69501e04c3fSmrg
69601e04c3fSmrg            size = lseek(prime->fd, 0, SEEK_END);
69701e04c3fSmrg            fail_if(size == -1, "failed to get prime bo size\n");
6989f464c52Smaya            add_new_bo(fd, prime->handle, size, NULL);
6999f464c52Smaya
70001e04c3fSmrg         }
70101e04c3fSmrg
70201e04c3fSmrg         return ret;
70301e04c3fSmrg      }
70401e04c3fSmrg
7057ec681f3Smrg      case DRM_IOCTL_I915_GEM_MMAP: {
7067ec681f3Smrg         ret = libc_ioctl(fd, request, argp);
7077ec681f3Smrg         if (ret == 0) {
7087ec681f3Smrg            struct drm_i915_gem_mmap *mmap = argp;
7097ec681f3Smrg            struct bo *bo = get_bo(fd, mmap->handle);
7107ec681f3Smrg            bo->user_mapped = true;
7117ec681f3Smrg            bo->dirty = true;
7127ec681f3Smrg         }
7137ec681f3Smrg         return ret;
7147ec681f3Smrg      }
7157ec681f3Smrg
7167ec681f3Smrg      case DRM_IOCTL_I915_GEM_MMAP_OFFSET: {
7177ec681f3Smrg         ret = libc_ioctl(fd, request, argp);
7187ec681f3Smrg         if (ret == 0) {
7197ec681f3Smrg            struct drm_i915_gem_mmap_offset *mmap = argp;
7207ec681f3Smrg            struct bo *bo = get_bo(fd, mmap->handle);
7217ec681f3Smrg            bo->user_mapped = true;
7227ec681f3Smrg            bo->dirty = true;
7237ec681f3Smrg         }
7247ec681f3Smrg         return ret;
7257ec681f3Smrg      }
7267ec681f3Smrg
72701e04c3fSmrg      default:
72801e04c3fSmrg         return libc_ioctl(fd, request, argp);
72901e04c3fSmrg      }
73001e04c3fSmrg   } else {
73101e04c3fSmrg      return libc_ioctl(fd, request, argp);
73201e04c3fSmrg   }
73301e04c3fSmrg}
73401e04c3fSmrg
73501e04c3fSmrgstatic void
73601e04c3fSmrginit(void)
73701e04c3fSmrg{
73801e04c3fSmrg   libc_close = dlsym(RTLD_NEXT, "close");
73901e04c3fSmrg   libc_ioctl = dlsym(RTLD_NEXT, "ioctl");
7407ec681f3Smrg   libc_munmap = dlsym(RTLD_NEXT, "munmap");
74101e04c3fSmrg   fail_if(libc_close == NULL || libc_ioctl == NULL,
74201e04c3fSmrg           "failed to get libc ioctl or close\n");
74301e04c3fSmrg}
74401e04c3fSmrg
74501e04c3fSmrgstatic int
74601e04c3fSmrgclose_init_helper(int fd)
74701e04c3fSmrg{
74801e04c3fSmrg   init();
74901e04c3fSmrg   return libc_close(fd);
75001e04c3fSmrg}
75101e04c3fSmrg
75201e04c3fSmrgstatic int
75301e04c3fSmrgioctl_init_helper(int fd, unsigned long request, ...)
75401e04c3fSmrg{
75501e04c3fSmrg   va_list args;
75601e04c3fSmrg   void *argp;
75701e04c3fSmrg
75801e04c3fSmrg   va_start(args, request);
75901e04c3fSmrg   argp = va_arg(args, void *);
76001e04c3fSmrg   va_end(args);
76101e04c3fSmrg
76201e04c3fSmrg   init();
76301e04c3fSmrg   return libc_ioctl(fd, request, argp);
76401e04c3fSmrg}
76501e04c3fSmrg
7667ec681f3Smrgstatic int
7677ec681f3Smrgmunmap_init_helper(void *addr, size_t length)
7687ec681f3Smrg{
7697ec681f3Smrg   init();
7707ec681f3Smrg   for (uint32_t i = 0; i < MAX_FD_COUNT * MAX_BO_COUNT; i++) {
7717ec681f3Smrg      struct bo *bo = &bos[i];
7727ec681f3Smrg      if (bo->map == addr) {
7737ec681f3Smrg         bo->user_mapped = false;
7747ec681f3Smrg         break;
7757ec681f3Smrg      }
7767ec681f3Smrg   }
7777ec681f3Smrg   return libc_munmap(addr, length);
7787ec681f3Smrg}
7797ec681f3Smrg
78001e04c3fSmrgstatic void __attribute__ ((destructor))
78101e04c3fSmrgfini(void)
78201e04c3fSmrg{
7837ec681f3Smrg   if (devinfo.ver != 0) {
7849f464c52Smaya      free(output_filename);
7857ec681f3Smrg      if (!capture_finished)
7867ec681f3Smrg         aub_file_finish(&aub_file);
7879f464c52Smaya      free(bos);
7889f464c52Smaya   }
78901e04c3fSmrg}
790