xref: /linux/drivers/gpu/drm/panfrost/panfrost_dump.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1730c2bf4SAdrián Larumbe // SPDX-License-Identifier: GPL-2.0
2730c2bf4SAdrián Larumbe /* Copyright 2021 Collabora ltd. */
3730c2bf4SAdrián Larumbe 
4730c2bf4SAdrián Larumbe #include <linux/err.h>
5730c2bf4SAdrián Larumbe #include <linux/device.h>
6730c2bf4SAdrián Larumbe #include <linux/devcoredump.h>
7730c2bf4SAdrián Larumbe #include <linux/moduleparam.h>
8730c2bf4SAdrián Larumbe #include <linux/iosys-map.h>
9730c2bf4SAdrián Larumbe #include <drm/panfrost_drm.h>
10730c2bf4SAdrián Larumbe #include <drm/drm_device.h>
11730c2bf4SAdrián Larumbe 
12730c2bf4SAdrián Larumbe #include "panfrost_job.h"
13730c2bf4SAdrián Larumbe #include "panfrost_gem.h"
14730c2bf4SAdrián Larumbe #include "panfrost_regs.h"
15730c2bf4SAdrián Larumbe #include "panfrost_dump.h"
16730c2bf4SAdrián Larumbe #include "panfrost_device.h"
17730c2bf4SAdrián Larumbe 
18730c2bf4SAdrián Larumbe static bool panfrost_dump_core = true;
19730c2bf4SAdrián Larumbe module_param_named(dump_core, panfrost_dump_core, bool, 0600);
20730c2bf4SAdrián Larumbe 
21730c2bf4SAdrián Larumbe struct panfrost_dump_iterator {
22730c2bf4SAdrián Larumbe 	void *start;
23730c2bf4SAdrián Larumbe 	struct panfrost_dump_object_header *hdr;
24730c2bf4SAdrián Larumbe 	void *data;
25730c2bf4SAdrián Larumbe };
26730c2bf4SAdrián Larumbe 
27730c2bf4SAdrián Larumbe static const unsigned short panfrost_dump_registers[] = {
28730c2bf4SAdrián Larumbe 	SHADER_READY_LO,
29730c2bf4SAdrián Larumbe 	SHADER_READY_HI,
30730c2bf4SAdrián Larumbe 	TILER_READY_LO,
31730c2bf4SAdrián Larumbe 	TILER_READY_HI,
32730c2bf4SAdrián Larumbe 	L2_READY_LO,
33730c2bf4SAdrián Larumbe 	L2_READY_HI,
34730c2bf4SAdrián Larumbe 	JOB_INT_MASK,
35730c2bf4SAdrián Larumbe 	JOB_INT_STAT,
36730c2bf4SAdrián Larumbe 	JS_HEAD_LO(0),
37730c2bf4SAdrián Larumbe 	JS_HEAD_HI(0),
38730c2bf4SAdrián Larumbe 	JS_TAIL_LO(0),
39730c2bf4SAdrián Larumbe 	JS_TAIL_HI(0),
40730c2bf4SAdrián Larumbe 	JS_AFFINITY_LO(0),
41730c2bf4SAdrián Larumbe 	JS_AFFINITY_HI(0),
42730c2bf4SAdrián Larumbe 	JS_CONFIG(0),
43730c2bf4SAdrián Larumbe 	JS_STATUS(0),
44730c2bf4SAdrián Larumbe 	JS_HEAD_NEXT_LO(0),
45730c2bf4SAdrián Larumbe 	JS_HEAD_NEXT_HI(0),
46730c2bf4SAdrián Larumbe 	JS_AFFINITY_NEXT_LO(0),
47730c2bf4SAdrián Larumbe 	JS_AFFINITY_NEXT_HI(0),
48730c2bf4SAdrián Larumbe 	JS_CONFIG_NEXT(0),
49730c2bf4SAdrián Larumbe 	MMU_INT_MASK,
50730c2bf4SAdrián Larumbe 	MMU_INT_STAT,
51730c2bf4SAdrián Larumbe 	AS_TRANSTAB_LO(0),
52730c2bf4SAdrián Larumbe 	AS_TRANSTAB_HI(0),
53730c2bf4SAdrián Larumbe 	AS_MEMATTR_LO(0),
54730c2bf4SAdrián Larumbe 	AS_MEMATTR_HI(0),
55730c2bf4SAdrián Larumbe 	AS_FAULTSTATUS(0),
56730c2bf4SAdrián Larumbe 	AS_FAULTADDRESS_LO(0),
57730c2bf4SAdrián Larumbe 	AS_FAULTADDRESS_HI(0),
58730c2bf4SAdrián Larumbe 	AS_STATUS(0),
59730c2bf4SAdrián Larumbe };
60730c2bf4SAdrián Larumbe 
panfrost_core_dump_header(struct panfrost_dump_iterator * iter,u32 type,void * data_end)61730c2bf4SAdrián Larumbe static void panfrost_core_dump_header(struct panfrost_dump_iterator *iter,
62730c2bf4SAdrián Larumbe 				      u32 type, void *data_end)
63730c2bf4SAdrián Larumbe {
64730c2bf4SAdrián Larumbe 	struct panfrost_dump_object_header *hdr = iter->hdr;
65730c2bf4SAdrián Larumbe 
6672655fb9SSteven Price 	hdr->magic = PANFROSTDUMP_MAGIC;
6772655fb9SSteven Price 	hdr->type = type;
6872655fb9SSteven Price 	hdr->file_offset = iter->data - iter->start;
6972655fb9SSteven Price 	hdr->file_size = data_end - iter->data;
70730c2bf4SAdrián Larumbe 
71730c2bf4SAdrián Larumbe 	iter->hdr++;
7272655fb9SSteven Price 	iter->data += hdr->file_size;
73730c2bf4SAdrián Larumbe }
74730c2bf4SAdrián Larumbe 
75730c2bf4SAdrián Larumbe static void
panfrost_core_dump_registers(struct panfrost_dump_iterator * iter,struct panfrost_device * pfdev,u32 as_nr,int slot)76730c2bf4SAdrián Larumbe panfrost_core_dump_registers(struct panfrost_dump_iterator *iter,
77730c2bf4SAdrián Larumbe 			     struct panfrost_device *pfdev,
78730c2bf4SAdrián Larumbe 			     u32 as_nr, int slot)
79730c2bf4SAdrián Larumbe {
80730c2bf4SAdrián Larumbe 	struct panfrost_dump_registers *dumpreg = iter->data;
81730c2bf4SAdrián Larumbe 	unsigned int i;
82730c2bf4SAdrián Larumbe 
83730c2bf4SAdrián Larumbe 	for (i = 0; i < ARRAY_SIZE(panfrost_dump_registers); i++, dumpreg++) {
84730c2bf4SAdrián Larumbe 		unsigned int js_as_offset = 0;
85730c2bf4SAdrián Larumbe 		unsigned int reg;
86730c2bf4SAdrián Larumbe 
87730c2bf4SAdrián Larumbe 		if (panfrost_dump_registers[i] >= JS_BASE &&
88730c2bf4SAdrián Larumbe 		    panfrost_dump_registers[i] <= JS_BASE + JS_SLOT_STRIDE)
89730c2bf4SAdrián Larumbe 			js_as_offset = slot * JS_SLOT_STRIDE;
90730c2bf4SAdrián Larumbe 		else if (panfrost_dump_registers[i] >= MMU_BASE &&
91730c2bf4SAdrián Larumbe 			 panfrost_dump_registers[i] <= MMU_BASE + MMU_AS_STRIDE)
92730c2bf4SAdrián Larumbe 			js_as_offset = (as_nr << MMU_AS_SHIFT);
93730c2bf4SAdrián Larumbe 
94730c2bf4SAdrián Larumbe 		reg = panfrost_dump_registers[i] + js_as_offset;
95730c2bf4SAdrián Larumbe 
9672655fb9SSteven Price 		dumpreg->reg = reg;
9772655fb9SSteven Price 		dumpreg->value = gpu_read(pfdev, reg);
98730c2bf4SAdrián Larumbe 	}
99730c2bf4SAdrián Larumbe 
100730c2bf4SAdrián Larumbe 	panfrost_core_dump_header(iter, PANFROSTDUMP_BUF_REG, dumpreg);
101730c2bf4SAdrián Larumbe }
102730c2bf4SAdrián Larumbe 
panfrost_core_dump(struct panfrost_job * job)103730c2bf4SAdrián Larumbe void panfrost_core_dump(struct panfrost_job *job)
104730c2bf4SAdrián Larumbe {
105730c2bf4SAdrián Larumbe 	struct panfrost_device *pfdev = job->pfdev;
106730c2bf4SAdrián Larumbe 	struct panfrost_dump_iterator iter;
107730c2bf4SAdrián Larumbe 	struct drm_gem_object *dbo;
108730c2bf4SAdrián Larumbe 	unsigned int n_obj, n_bomap_pages;
10972655fb9SSteven Price 	u64 *bomap, *bomap_start;
110730c2bf4SAdrián Larumbe 	size_t file_size;
111730c2bf4SAdrián Larumbe 	u32 as_nr;
112730c2bf4SAdrián Larumbe 	int slot;
113730c2bf4SAdrián Larumbe 	int ret, i;
114730c2bf4SAdrián Larumbe 
115730c2bf4SAdrián Larumbe 	as_nr = job->mmu->as;
116730c2bf4SAdrián Larumbe 	slot = panfrost_job_get_slot(job);
117730c2bf4SAdrián Larumbe 
118730c2bf4SAdrián Larumbe 	/* Only catch the first event, or when manually re-armed */
119730c2bf4SAdrián Larumbe 	if (!panfrost_dump_core)
120730c2bf4SAdrián Larumbe 		return;
121730c2bf4SAdrián Larumbe 	panfrost_dump_core = false;
122730c2bf4SAdrián Larumbe 
123730c2bf4SAdrián Larumbe 	/* At least, we dump registers and end marker */
124730c2bf4SAdrián Larumbe 	n_obj = 2;
125730c2bf4SAdrián Larumbe 	n_bomap_pages = 0;
126730c2bf4SAdrián Larumbe 	file_size = ARRAY_SIZE(panfrost_dump_registers) *
127730c2bf4SAdrián Larumbe 			sizeof(struct panfrost_dump_registers);
128730c2bf4SAdrián Larumbe 
129730c2bf4SAdrián Larumbe 	/* Add in the active buffer objects */
130730c2bf4SAdrián Larumbe 	for (i = 0; i < job->bo_count; i++) {
131730c2bf4SAdrián Larumbe 		/*
132730c2bf4SAdrián Larumbe 		 * Even though the CPU could be configured to use 16K or 64K pages, this
133730c2bf4SAdrián Larumbe 		 * is a very unusual situation for most kernel setups on SoCs that have
134730c2bf4SAdrián Larumbe 		 * a Panfrost device. Also many places across the driver make the somewhat
135730c2bf4SAdrián Larumbe 		 * arbitrary assumption that Panfrost's MMU page size is the same as the CPU's,
136730c2bf4SAdrián Larumbe 		 * so let's have a sanity check to ensure that's always the case
137730c2bf4SAdrián Larumbe 		 */
138730c2bf4SAdrián Larumbe 		dbo = job->bos[i];
139730c2bf4SAdrián Larumbe 		WARN_ON(!IS_ALIGNED(dbo->size, PAGE_SIZE));
140730c2bf4SAdrián Larumbe 
141730c2bf4SAdrián Larumbe 		file_size += dbo->size;
142730c2bf4SAdrián Larumbe 		n_bomap_pages += dbo->size >> PAGE_SHIFT;
143730c2bf4SAdrián Larumbe 		n_obj++;
144730c2bf4SAdrián Larumbe 	}
145730c2bf4SAdrián Larumbe 
146730c2bf4SAdrián Larumbe 	/* If we have any buffer objects, add a bomap object */
147730c2bf4SAdrián Larumbe 	if (n_bomap_pages) {
148730c2bf4SAdrián Larumbe 		file_size += n_bomap_pages * sizeof(*bomap);
149730c2bf4SAdrián Larumbe 		n_obj++;
150730c2bf4SAdrián Larumbe 	}
151730c2bf4SAdrián Larumbe 
152730c2bf4SAdrián Larumbe 	/* Add the size of the headers */
153730c2bf4SAdrián Larumbe 	file_size += sizeof(*iter.hdr) * n_obj;
154730c2bf4SAdrián Larumbe 
155730c2bf4SAdrián Larumbe 	/*
156730c2bf4SAdrián Larumbe 	 * Allocate the file in vmalloc memory, it's likely to be big.
157730c2bf4SAdrián Larumbe 	 * The reason behind these GFP flags is that we don't want to trigger the
158730c2bf4SAdrián Larumbe 	 * OOM killer in the event that not enough memory could be found for our
159730c2bf4SAdrián Larumbe 	 * dump file. We also don't want the allocator to do any error reporting,
160730c2bf4SAdrián Larumbe 	 * as the right behaviour is failing gracefully if a big enough buffer
161730c2bf4SAdrián Larumbe 	 * could not be allocated.
162730c2bf4SAdrián Larumbe 	 */
163730c2bf4SAdrián Larumbe 	iter.start = __vmalloc(file_size, GFP_KERNEL | __GFP_NOWARN |
164730c2bf4SAdrián Larumbe 			__GFP_NORETRY);
165730c2bf4SAdrián Larumbe 	if (!iter.start) {
166730c2bf4SAdrián Larumbe 		dev_warn(pfdev->dev, "failed to allocate devcoredump file\n");
167730c2bf4SAdrián Larumbe 		return;
168730c2bf4SAdrián Larumbe 	}
169730c2bf4SAdrián Larumbe 
170730c2bf4SAdrián Larumbe 	/* Point the data member after the headers */
171730c2bf4SAdrián Larumbe 	iter.hdr = iter.start;
172730c2bf4SAdrián Larumbe 	iter.data = &iter.hdr[n_obj];
173730c2bf4SAdrián Larumbe 
174730c2bf4SAdrián Larumbe 	memset(iter.hdr, 0, iter.data - iter.start);
175730c2bf4SAdrián Larumbe 
176730c2bf4SAdrián Larumbe 	/*
177730c2bf4SAdrián Larumbe 	 * For now, we write the job identifier in the register dump header,
178730c2bf4SAdrián Larumbe 	 * so that we can decode the entire dump later with pandecode
179730c2bf4SAdrián Larumbe 	 */
18072655fb9SSteven Price 	iter.hdr->reghdr.jc = job->jc;
18172655fb9SSteven Price 	iter.hdr->reghdr.major = PANFROSTDUMP_MAJOR;
18272655fb9SSteven Price 	iter.hdr->reghdr.minor = PANFROSTDUMP_MINOR;
18372655fb9SSteven Price 	iter.hdr->reghdr.gpu_id = pfdev->features.id;
18472655fb9SSteven Price 	iter.hdr->reghdr.nbos = job->bo_count;
185730c2bf4SAdrián Larumbe 
186730c2bf4SAdrián Larumbe 	panfrost_core_dump_registers(&iter, pfdev, as_nr, slot);
187730c2bf4SAdrián Larumbe 
188730c2bf4SAdrián Larumbe 	/* Reserve space for the bomap */
189730c2bf4SAdrián Larumbe 	if (job->bo_count) {
190730c2bf4SAdrián Larumbe 		bomap_start = bomap = iter.data;
191730c2bf4SAdrián Larumbe 		memset(bomap, 0, sizeof(*bomap) * n_bomap_pages);
192730c2bf4SAdrián Larumbe 		panfrost_core_dump_header(&iter, PANFROSTDUMP_BUF_BOMAP,
193730c2bf4SAdrián Larumbe 					  bomap + n_bomap_pages);
194730c2bf4SAdrián Larumbe 	}
195730c2bf4SAdrián Larumbe 
196730c2bf4SAdrián Larumbe 	for (i = 0; i < job->bo_count; i++) {
197730c2bf4SAdrián Larumbe 		struct iosys_map map;
198730c2bf4SAdrián Larumbe 		struct panfrost_gem_mapping *mapping;
199730c2bf4SAdrián Larumbe 		struct panfrost_gem_object *bo;
200730c2bf4SAdrián Larumbe 		struct sg_page_iter page_iter;
201730c2bf4SAdrián Larumbe 		void *vaddr;
202730c2bf4SAdrián Larumbe 
203730c2bf4SAdrián Larumbe 		bo = to_panfrost_bo(job->bos[i]);
204730c2bf4SAdrián Larumbe 		mapping = job->mappings[i];
205730c2bf4SAdrián Larumbe 
206730c2bf4SAdrián Larumbe 		if (!bo->base.sgt) {
207730c2bf4SAdrián Larumbe 			dev_err(pfdev->dev, "Panfrost Dump: BO has no sgt, cannot dump\n");
208730c2bf4SAdrián Larumbe 			iter.hdr->bomap.valid = 0;
209730c2bf4SAdrián Larumbe 			goto dump_header;
210730c2bf4SAdrián Larumbe 		}
211730c2bf4SAdrián Larumbe 
21279e2cf2eSDmitry Osipenko 		ret = drm_gem_vmap_unlocked(&bo->base.base, &map);
213730c2bf4SAdrián Larumbe 		if (ret) {
214730c2bf4SAdrián Larumbe 			dev_err(pfdev->dev, "Panfrost Dump: couldn't map Buffer Object\n");
215730c2bf4SAdrián Larumbe 			iter.hdr->bomap.valid = 0;
216730c2bf4SAdrián Larumbe 			goto dump_header;
217730c2bf4SAdrián Larumbe 		}
218730c2bf4SAdrián Larumbe 
219730c2bf4SAdrián Larumbe 		WARN_ON(!mapping->active);
220730c2bf4SAdrián Larumbe 
22172655fb9SSteven Price 		iter.hdr->bomap.data[0] = bomap - bomap_start;
222730c2bf4SAdrián Larumbe 
223*b2139fb5SSteven Price 		for_each_sgtable_page(bo->base.sgt, &page_iter, 0)
224*b2139fb5SSteven Price 			*bomap++ = page_to_phys(sg_page_iter_page(&page_iter));
225730c2bf4SAdrián Larumbe 
22672655fb9SSteven Price 		iter.hdr->bomap.iova = mapping->mmnode.start << PAGE_SHIFT;
227730c2bf4SAdrián Larumbe 
228730c2bf4SAdrián Larumbe 		vaddr = map.vaddr;
229730c2bf4SAdrián Larumbe 		memcpy(iter.data, vaddr, bo->base.base.size);
230730c2bf4SAdrián Larumbe 
23179e2cf2eSDmitry Osipenko 		drm_gem_vunmap_unlocked(&bo->base.base, &map);
232730c2bf4SAdrián Larumbe 
23372655fb9SSteven Price 		iter.hdr->bomap.valid = 1;
234730c2bf4SAdrián Larumbe 
235730c2bf4SAdrián Larumbe dump_header:	panfrost_core_dump_header(&iter, PANFROSTDUMP_BUF_BO, iter.data +
236730c2bf4SAdrián Larumbe 					  bo->base.base.size);
237730c2bf4SAdrián Larumbe 	}
238730c2bf4SAdrián Larumbe 	panfrost_core_dump_header(&iter, PANFROSTDUMP_BUF_TRAILER, iter.data);
239730c2bf4SAdrián Larumbe 
240730c2bf4SAdrián Larumbe 	dev_coredumpv(pfdev->dev, iter.start, iter.data - iter.start, GFP_KERNEL);
241730c2bf4SAdrián Larumbe }
242