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