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