18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2015-2018 Etnaviv Project
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/devcoredump.h>
78c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "etnaviv_cmdbuf.h"
108c2ecf20Sopenharmony_ci#include "etnaviv_dump.h"
118c2ecf20Sopenharmony_ci#include "etnaviv_gem.h"
128c2ecf20Sopenharmony_ci#include "etnaviv_gpu.h"
138c2ecf20Sopenharmony_ci#include "etnaviv_mmu.h"
148c2ecf20Sopenharmony_ci#include "etnaviv_sched.h"
158c2ecf20Sopenharmony_ci#include "state.xml.h"
168c2ecf20Sopenharmony_ci#include "state_hi.xml.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic bool etnaviv_dump_core = true;
198c2ecf20Sopenharmony_cimodule_param_named(dump_core, etnaviv_dump_core, bool, 0600);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistruct core_dump_iterator {
228c2ecf20Sopenharmony_ci	void *start;
238c2ecf20Sopenharmony_ci	struct etnaviv_dump_object_header *hdr;
248c2ecf20Sopenharmony_ci	void *data;
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic const unsigned short etnaviv_dump_registers[] = {
288c2ecf20Sopenharmony_ci	VIVS_HI_AXI_STATUS,
298c2ecf20Sopenharmony_ci	VIVS_HI_CLOCK_CONTROL,
308c2ecf20Sopenharmony_ci	VIVS_HI_IDLE_STATE,
318c2ecf20Sopenharmony_ci	VIVS_HI_AXI_CONFIG,
328c2ecf20Sopenharmony_ci	VIVS_HI_INTR_ENBL,
338c2ecf20Sopenharmony_ci	VIVS_HI_CHIP_IDENTITY,
348c2ecf20Sopenharmony_ci	VIVS_HI_CHIP_FEATURE,
358c2ecf20Sopenharmony_ci	VIVS_HI_CHIP_MODEL,
368c2ecf20Sopenharmony_ci	VIVS_HI_CHIP_REV,
378c2ecf20Sopenharmony_ci	VIVS_HI_CHIP_DATE,
388c2ecf20Sopenharmony_ci	VIVS_HI_CHIP_TIME,
398c2ecf20Sopenharmony_ci	VIVS_HI_CHIP_MINOR_FEATURE_0,
408c2ecf20Sopenharmony_ci	VIVS_HI_CACHE_CONTROL,
418c2ecf20Sopenharmony_ci	VIVS_HI_AXI_CONTROL,
428c2ecf20Sopenharmony_ci	VIVS_PM_POWER_CONTROLS,
438c2ecf20Sopenharmony_ci	VIVS_PM_MODULE_CONTROLS,
448c2ecf20Sopenharmony_ci	VIVS_PM_MODULE_STATUS,
458c2ecf20Sopenharmony_ci	VIVS_PM_PULSE_EATER,
468c2ecf20Sopenharmony_ci	VIVS_MC_MMU_FE_PAGE_TABLE,
478c2ecf20Sopenharmony_ci	VIVS_MC_MMU_TX_PAGE_TABLE,
488c2ecf20Sopenharmony_ci	VIVS_MC_MMU_PE_PAGE_TABLE,
498c2ecf20Sopenharmony_ci	VIVS_MC_MMU_PEZ_PAGE_TABLE,
508c2ecf20Sopenharmony_ci	VIVS_MC_MMU_RA_PAGE_TABLE,
518c2ecf20Sopenharmony_ci	VIVS_MC_DEBUG_MEMORY,
528c2ecf20Sopenharmony_ci	VIVS_MC_MEMORY_BASE_ADDR_RA,
538c2ecf20Sopenharmony_ci	VIVS_MC_MEMORY_BASE_ADDR_FE,
548c2ecf20Sopenharmony_ci	VIVS_MC_MEMORY_BASE_ADDR_TX,
558c2ecf20Sopenharmony_ci	VIVS_MC_MEMORY_BASE_ADDR_PEZ,
568c2ecf20Sopenharmony_ci	VIVS_MC_MEMORY_BASE_ADDR_PE,
578c2ecf20Sopenharmony_ci	VIVS_MC_MEMORY_TIMING_CONTROL,
588c2ecf20Sopenharmony_ci	VIVS_MC_BUS_CONFIG,
598c2ecf20Sopenharmony_ci	VIVS_FE_DMA_STATUS,
608c2ecf20Sopenharmony_ci	VIVS_FE_DMA_DEBUG_STATE,
618c2ecf20Sopenharmony_ci	VIVS_FE_DMA_ADDRESS,
628c2ecf20Sopenharmony_ci	VIVS_FE_DMA_LOW,
638c2ecf20Sopenharmony_ci	VIVS_FE_DMA_HIGH,
648c2ecf20Sopenharmony_ci	VIVS_FE_AUTO_FLUSH,
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic void etnaviv_core_dump_header(struct core_dump_iterator *iter,
688c2ecf20Sopenharmony_ci	u32 type, void *data_end)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct etnaviv_dump_object_header *hdr = iter->hdr;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	hdr->magic = cpu_to_le32(ETDUMP_MAGIC);
738c2ecf20Sopenharmony_ci	hdr->type = cpu_to_le32(type);
748c2ecf20Sopenharmony_ci	hdr->file_offset = cpu_to_le32(iter->data - iter->start);
758c2ecf20Sopenharmony_ci	hdr->file_size = cpu_to_le32(data_end - iter->data);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	iter->hdr++;
788c2ecf20Sopenharmony_ci	iter->data += hdr->file_size;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic void etnaviv_core_dump_registers(struct core_dump_iterator *iter,
828c2ecf20Sopenharmony_ci	struct etnaviv_gpu *gpu)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct etnaviv_dump_registers *reg = iter->data;
858c2ecf20Sopenharmony_ci	unsigned int i;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(etnaviv_dump_registers); i++, reg++) {
888c2ecf20Sopenharmony_ci		reg->reg = etnaviv_dump_registers[i];
898c2ecf20Sopenharmony_ci		reg->value = gpu_read(gpu, etnaviv_dump_registers[i]);
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	etnaviv_core_dump_header(iter, ETDUMP_BUF_REG, reg);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void etnaviv_core_dump_mmu(struct core_dump_iterator *iter,
968c2ecf20Sopenharmony_ci	struct etnaviv_iommu_context *mmu, size_t mmu_size)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	etnaviv_iommu_dump(mmu, iter->data);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	etnaviv_core_dump_header(iter, ETDUMP_BUF_MMU, iter->data + mmu_size);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void etnaviv_core_dump_mem(struct core_dump_iterator *iter, u32 type,
1048c2ecf20Sopenharmony_ci	void *ptr, size_t size, u64 iova)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	memcpy(iter->data, ptr, size);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	iter->hdr->iova = cpu_to_le64(iova);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	etnaviv_core_dump_header(iter, type, iter->data + size);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_civoid etnaviv_core_dump(struct etnaviv_gem_submit *submit)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct etnaviv_gpu *gpu = submit->gpu;
1168c2ecf20Sopenharmony_ci	struct core_dump_iterator iter;
1178c2ecf20Sopenharmony_ci	struct etnaviv_gem_object *obj;
1188c2ecf20Sopenharmony_ci	unsigned int n_obj, n_bomap_pages;
1198c2ecf20Sopenharmony_ci	size_t file_size, mmu_size;
1208c2ecf20Sopenharmony_ci	__le64 *bomap, *bomap_start;
1218c2ecf20Sopenharmony_ci	int i;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	/* Only catch the first event, or when manually re-armed */
1248c2ecf20Sopenharmony_ci	if (!etnaviv_dump_core)
1258c2ecf20Sopenharmony_ci		return;
1268c2ecf20Sopenharmony_ci	etnaviv_dump_core = false;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	mutex_lock(&submit->mmu_context->lock);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	mmu_size = etnaviv_iommu_dump_size(submit->mmu_context);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* We always dump registers, mmu, ring, hanging cmdbuf and end marker */
1338c2ecf20Sopenharmony_ci	n_obj = 5;
1348c2ecf20Sopenharmony_ci	n_bomap_pages = 0;
1358c2ecf20Sopenharmony_ci	file_size = ARRAY_SIZE(etnaviv_dump_registers) *
1368c2ecf20Sopenharmony_ci			sizeof(struct etnaviv_dump_registers) +
1378c2ecf20Sopenharmony_ci		    mmu_size + gpu->buffer.size + submit->cmdbuf.size;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* Add in the active buffer objects */
1408c2ecf20Sopenharmony_ci	for (i = 0; i < submit->nr_bos; i++) {
1418c2ecf20Sopenharmony_ci		obj = submit->bos[i].obj;
1428c2ecf20Sopenharmony_ci		file_size += obj->base.size;
1438c2ecf20Sopenharmony_ci		n_bomap_pages += obj->base.size >> PAGE_SHIFT;
1448c2ecf20Sopenharmony_ci		n_obj++;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* If we have any buffer objects, add a bomap object */
1488c2ecf20Sopenharmony_ci	if (n_bomap_pages) {
1498c2ecf20Sopenharmony_ci		file_size += n_bomap_pages * sizeof(__le64);
1508c2ecf20Sopenharmony_ci		n_obj++;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* Add the size of the headers */
1548c2ecf20Sopenharmony_ci	file_size += sizeof(*iter.hdr) * n_obj;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	/* Allocate the file in vmalloc memory, it's likely to be big */
1578c2ecf20Sopenharmony_ci	iter.start = __vmalloc(file_size, GFP_KERNEL | __GFP_NOWARN |
1588c2ecf20Sopenharmony_ci			__GFP_NORETRY);
1598c2ecf20Sopenharmony_ci	if (!iter.start) {
1608c2ecf20Sopenharmony_ci		mutex_unlock(&submit->mmu_context->lock);
1618c2ecf20Sopenharmony_ci		dev_warn(gpu->dev, "failed to allocate devcoredump file\n");
1628c2ecf20Sopenharmony_ci		return;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/* Point the data member after the headers */
1668c2ecf20Sopenharmony_ci	iter.hdr = iter.start;
1678c2ecf20Sopenharmony_ci	iter.data = &iter.hdr[n_obj];
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	memset(iter.hdr, 0, iter.data - iter.start);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	etnaviv_core_dump_registers(&iter, gpu);
1728c2ecf20Sopenharmony_ci	etnaviv_core_dump_mmu(&iter, submit->mmu_context, mmu_size);
1738c2ecf20Sopenharmony_ci	etnaviv_core_dump_mem(&iter, ETDUMP_BUF_RING, gpu->buffer.vaddr,
1748c2ecf20Sopenharmony_ci			      gpu->buffer.size,
1758c2ecf20Sopenharmony_ci			      etnaviv_cmdbuf_get_va(&gpu->buffer,
1768c2ecf20Sopenharmony_ci					&submit->mmu_context->cmdbuf_mapping));
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	etnaviv_core_dump_mem(&iter, ETDUMP_BUF_CMD,
1798c2ecf20Sopenharmony_ci			      submit->cmdbuf.vaddr, submit->cmdbuf.size,
1808c2ecf20Sopenharmony_ci			      etnaviv_cmdbuf_get_va(&submit->cmdbuf,
1818c2ecf20Sopenharmony_ci					&submit->mmu_context->cmdbuf_mapping));
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	mutex_unlock(&submit->mmu_context->lock);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/* Reserve space for the bomap */
1868c2ecf20Sopenharmony_ci	if (n_bomap_pages) {
1878c2ecf20Sopenharmony_ci		bomap_start = bomap = iter.data;
1888c2ecf20Sopenharmony_ci		memset(bomap, 0, sizeof(*bomap) * n_bomap_pages);
1898c2ecf20Sopenharmony_ci		etnaviv_core_dump_header(&iter, ETDUMP_BUF_BOMAP,
1908c2ecf20Sopenharmony_ci					 bomap + n_bomap_pages);
1918c2ecf20Sopenharmony_ci	} else {
1928c2ecf20Sopenharmony_ci		/* Silence warning */
1938c2ecf20Sopenharmony_ci		bomap_start = bomap = NULL;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	for (i = 0; i < submit->nr_bos; i++) {
1978c2ecf20Sopenharmony_ci		struct etnaviv_vram_mapping *vram;
1988c2ecf20Sopenharmony_ci		struct page **pages;
1998c2ecf20Sopenharmony_ci		void *vaddr;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		obj = submit->bos[i].obj;
2028c2ecf20Sopenharmony_ci		vram = submit->bos[i].mapping;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		mutex_lock(&obj->lock);
2058c2ecf20Sopenharmony_ci		pages = etnaviv_gem_get_pages(obj);
2068c2ecf20Sopenharmony_ci		mutex_unlock(&obj->lock);
2078c2ecf20Sopenharmony_ci		if (!IS_ERR(pages)) {
2088c2ecf20Sopenharmony_ci			int j;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci			iter.hdr->data[0] = bomap - bomap_start;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci			for (j = 0; j < obj->base.size >> PAGE_SHIFT; j++)
2138c2ecf20Sopenharmony_ci				*bomap++ = cpu_to_le64(page_to_phys(*pages++));
2148c2ecf20Sopenharmony_ci		}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		iter.hdr->iova = cpu_to_le64(vram->iova);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci		vaddr = etnaviv_gem_vmap(&obj->base);
2198c2ecf20Sopenharmony_ci		if (vaddr)
2208c2ecf20Sopenharmony_ci			memcpy(iter.data, vaddr, obj->base.size);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci		etnaviv_core_dump_header(&iter, ETDUMP_BUF_BO, iter.data +
2238c2ecf20Sopenharmony_ci					 obj->base.size);
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	etnaviv_core_dump_header(&iter, ETDUMP_BUF_END, iter.data);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	dev_coredumpv(gpu->dev, iter.start, iter.data - iter.start, GFP_KERNEL);
2298c2ecf20Sopenharmony_ci}
230