242 lines · c
1// SPDX-License-Identifier: GPL-2.02/* 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 18static bool panfrost_dump_core = true;19module_param_named(dump_core, panfrost_dump_core, bool, 0600);20 21struct panfrost_dump_iterator {22 void *start;23 struct panfrost_dump_object_header *hdr;24 void *data;25};26 27static 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 61static 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 75static void76panfrost_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 103void 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, this133 * is a very unusual situation for most kernel setups on SoCs that have134 * a Panfrost device. Also many places across the driver make the somewhat135 * 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 case137 */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 the158 * OOM killer in the event that not enough memory could be found for our159 * 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 buffer161 * 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 pandecode179 */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 *bomap++ = page_to_phys(sg_page_iter_page(&page_iter));225 226 iter.hdr->bomap.iova = mapping->mmnode.start << PAGE_SHIFT;227 228 vaddr = map.vaddr;229 memcpy(iter.data, vaddr, bo->base.base.size);230 231 drm_gem_vunmap_unlocked(&bo->base.base, &map);232 233 iter.hdr->bomap.valid = 1;234 235dump_header: panfrost_core_dump_header(&iter, PANFROSTDUMP_BUF_BO, iter.data +236 bo->base.base.size);237 }238 panfrost_core_dump_header(&iter, PANFROSTDUMP_BUF_TRAILER, iter.data);239 240 dev_coredumpv(pfdev->dev, iter.start, iter.data - iter.start, GFP_KERNEL);241}242