panfrost_dump.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* Copyright 2021 Collabora ltd. */
  3. #include <linux/err.h>
  4. #include <linux/device.h>
  5. #include <linux/devcoredump.h>
  6. #include <linux/moduleparam.h>
  7. #include <linux/iosys-map.h>
  8. #include <drm/panfrost_drm.h>
  9. #include <drm/drm_device.h>
  10. #include "panfrost_job.h"
  11. #include "panfrost_gem.h"
  12. #include "panfrost_regs.h"
  13. #include "panfrost_dump.h"
  14. #include "panfrost_device.h"
  15. static bool panfrost_dump_core = true;
  16. module_param_named(dump_core, panfrost_dump_core, bool, 0600);
  17. struct panfrost_dump_iterator {
  18. void *start;
  19. struct panfrost_dump_object_header *hdr;
  20. void *data;
  21. };
  22. static const unsigned short panfrost_dump_registers[] = {
  23. SHADER_READY_LO,
  24. SHADER_READY_HI,
  25. TILER_READY_LO,
  26. TILER_READY_HI,
  27. L2_READY_LO,
  28. L2_READY_HI,
  29. JOB_INT_MASK,
  30. JOB_INT_STAT,
  31. JS_HEAD_LO(0),
  32. JS_HEAD_HI(0),
  33. JS_TAIL_LO(0),
  34. JS_TAIL_HI(0),
  35. JS_AFFINITY_LO(0),
  36. JS_AFFINITY_HI(0),
  37. JS_CONFIG(0),
  38. JS_STATUS(0),
  39. JS_HEAD_NEXT_LO(0),
  40. JS_HEAD_NEXT_HI(0),
  41. JS_AFFINITY_NEXT_LO(0),
  42. JS_AFFINITY_NEXT_HI(0),
  43. JS_CONFIG_NEXT(0),
  44. MMU_INT_MASK,
  45. MMU_INT_STAT,
  46. AS_TRANSTAB_LO(0),
  47. AS_TRANSTAB_HI(0),
  48. AS_MEMATTR_LO(0),
  49. AS_MEMATTR_HI(0),
  50. AS_FAULTSTATUS(0),
  51. AS_FAULTADDRESS_LO(0),
  52. AS_FAULTADDRESS_HI(0),
  53. AS_STATUS(0),
  54. };
  55. static void panfrost_core_dump_header(struct panfrost_dump_iterator *iter,
  56. u32 type, void *data_end)
  57. {
  58. struct panfrost_dump_object_header *hdr = iter->hdr;
  59. hdr->magic = PANFROSTDUMP_MAGIC;
  60. hdr->type = type;
  61. hdr->file_offset = iter->data - iter->start;
  62. hdr->file_size = data_end - iter->data;
  63. iter->hdr++;
  64. iter->data += hdr->file_size;
  65. }
  66. static void
  67. panfrost_core_dump_registers(struct panfrost_dump_iterator *iter,
  68. struct panfrost_device *pfdev,
  69. u32 as_nr, int slot)
  70. {
  71. struct panfrost_dump_registers *dumpreg = iter->data;
  72. unsigned int i;
  73. for (i = 0; i < ARRAY_SIZE(panfrost_dump_registers); i++, dumpreg++) {
  74. unsigned int js_as_offset = 0;
  75. unsigned int reg;
  76. if (panfrost_dump_registers[i] >= JS_BASE &&
  77. panfrost_dump_registers[i] <= JS_BASE + JS_SLOT_STRIDE)
  78. js_as_offset = slot * JS_SLOT_STRIDE;
  79. else if (panfrost_dump_registers[i] >= MMU_BASE &&
  80. panfrost_dump_registers[i] <= MMU_BASE + MMU_AS_STRIDE)
  81. js_as_offset = (as_nr << MMU_AS_SHIFT);
  82. reg = panfrost_dump_registers[i] + js_as_offset;
  83. dumpreg->reg = reg;
  84. dumpreg->value = gpu_read(pfdev, reg);
  85. }
  86. panfrost_core_dump_header(iter, PANFROSTDUMP_BUF_REG, dumpreg);
  87. }
  88. void panfrost_core_dump(struct panfrost_job *job)
  89. {
  90. struct panfrost_device *pfdev = job->pfdev;
  91. struct panfrost_dump_iterator iter;
  92. struct drm_gem_object *dbo;
  93. unsigned int n_obj, n_bomap_pages;
  94. u64 *bomap, *bomap_start;
  95. size_t file_size;
  96. u32 as_nr;
  97. int slot;
  98. int ret, i;
  99. as_nr = job->mmu->as;
  100. slot = panfrost_job_get_slot(job);
  101. /* Only catch the first event, or when manually re-armed */
  102. if (!panfrost_dump_core)
  103. return;
  104. panfrost_dump_core = false;
  105. /* At least, we dump registers and end marker */
  106. n_obj = 2;
  107. n_bomap_pages = 0;
  108. file_size = ARRAY_SIZE(panfrost_dump_registers) *
  109. sizeof(struct panfrost_dump_registers);
  110. /* Add in the active buffer objects */
  111. for (i = 0; i < job->bo_count; i++) {
  112. /*
  113. * Even though the CPU could be configured to use 16K or 64K pages, this
  114. * is a very unusual situation for most kernel setups on SoCs that have
  115. * a Panfrost device. Also many places across the driver make the somewhat
  116. * arbitrary assumption that Panfrost's MMU page size is the same as the CPU's,
  117. * so let's have a sanity check to ensure that's always the case
  118. */
  119. dbo = job->bos[i];
  120. WARN_ON(!IS_ALIGNED(dbo->size, PAGE_SIZE));
  121. file_size += dbo->size;
  122. n_bomap_pages += dbo->size >> PAGE_SHIFT;
  123. n_obj++;
  124. }
  125. /* If we have any buffer objects, add a bomap object */
  126. if (n_bomap_pages) {
  127. file_size += n_bomap_pages * sizeof(*bomap);
  128. n_obj++;
  129. }
  130. /* Add the size of the headers */
  131. file_size += sizeof(*iter.hdr) * n_obj;
  132. /*
  133. * Allocate the file in vmalloc memory, it's likely to be big.
  134. * The reason behind these GFP flags is that we don't want to trigger the
  135. * OOM killer in the event that not enough memory could be found for our
  136. * dump file. We also don't want the allocator to do any error reporting,
  137. * as the right behaviour is failing gracefully if a big enough buffer
  138. * could not be allocated.
  139. */
  140. iter.start = __vmalloc(file_size, GFP_KERNEL | __GFP_NOWARN |
  141. __GFP_NORETRY);
  142. if (!iter.start) {
  143. dev_warn(pfdev->dev, "failed to allocate devcoredump file\n");
  144. return;
  145. }
  146. /* Point the data member after the headers */
  147. iter.hdr = iter.start;
  148. iter.data = &iter.hdr[n_obj];
  149. memset(iter.hdr, 0, iter.data - iter.start);
  150. /*
  151. * For now, we write the job identifier in the register dump header,
  152. * so that we can decode the entire dump later with pandecode
  153. */
  154. iter.hdr->reghdr.jc = job->jc;
  155. iter.hdr->reghdr.major = PANFROSTDUMP_MAJOR;
  156. iter.hdr->reghdr.minor = PANFROSTDUMP_MINOR;
  157. iter.hdr->reghdr.gpu_id = pfdev->features.id;
  158. iter.hdr->reghdr.nbos = job->bo_count;
  159. panfrost_core_dump_registers(&iter, pfdev, as_nr, slot);
  160. /* Reserve space for the bomap */
  161. if (job->bo_count) {
  162. bomap_start = bomap = iter.data;
  163. memset(bomap, 0, sizeof(*bomap) * n_bomap_pages);
  164. panfrost_core_dump_header(&iter, PANFROSTDUMP_BUF_BOMAP,
  165. bomap + n_bomap_pages);
  166. }
  167. for (i = 0; i < job->bo_count; i++) {
  168. struct iosys_map map;
  169. struct panfrost_gem_mapping *mapping;
  170. struct panfrost_gem_object *bo;
  171. struct sg_page_iter page_iter;
  172. void *vaddr;
  173. bo = to_panfrost_bo(job->bos[i]);
  174. mapping = job->mappings[i];
  175. if (!bo->base.sgt) {
  176. dev_err(pfdev->dev, "Panfrost Dump: BO has no sgt, cannot dump\n");
  177. iter.hdr->bomap.valid = 0;
  178. goto dump_header;
  179. }
  180. ret = drm_gem_shmem_vmap(&bo->base, &map);
  181. if (ret) {
  182. dev_err(pfdev->dev, "Panfrost Dump: couldn't map Buffer Object\n");
  183. iter.hdr->bomap.valid = 0;
  184. goto dump_header;
  185. }
  186. WARN_ON(!mapping->active);
  187. iter.hdr->bomap.data[0] = bomap - bomap_start;
  188. for_each_sgtable_page(bo->base.sgt, &page_iter, 0) {
  189. struct page *page = sg_page_iter_page(&page_iter);
  190. if (!IS_ERR(page)) {
  191. *bomap++ = page_to_phys(page);
  192. } else {
  193. dev_err(pfdev->dev, "Panfrost Dump: wrong page\n");
  194. *bomap++ = 0;
  195. }
  196. }
  197. iter.hdr->bomap.iova = mapping->mmnode.start << PAGE_SHIFT;
  198. vaddr = map.vaddr;
  199. memcpy(iter.data, vaddr, bo->base.base.size);
  200. drm_gem_shmem_vunmap(&bo->base, &map);
  201. iter.hdr->bomap.valid = 1;
  202. dump_header: panfrost_core_dump_header(&iter, PANFROSTDUMP_BUF_BO, iter.data +
  203. bo->base.base.size);
  204. }
  205. panfrost_core_dump_header(&iter, PANFROSTDUMP_BUF_TRAILER, iter.data);
  206. dev_coredumpv(pfdev->dev, iter.start, iter.data - iter.start, GFP_KERNEL);
  207. }