brintos

brintos / linux-shallow public Read only

0
0
Text · 6.4 KiB · 4775130 Raw
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