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