1053 lines
27 KiB
C
1053 lines
27 KiB
C
|
/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 and
|
||
|
* only version 2 as published by the Free Software Foundation.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*/
|
||
|
|
||
|
#include "kgsl.h"
|
||
|
#include "kgsl_sharedmem.h"
|
||
|
#include "kgsl_snapshot.h"
|
||
|
|
||
|
#include "adreno.h"
|
||
|
#include "adreno_pm4types.h"
|
||
|
#include "a2xx_reg.h"
|
||
|
#include "a3xx_reg.h"
|
||
|
|
||
|
/* Number of dwords of ringbuffer history to record */
|
||
|
#define NUM_DWORDS_OF_RINGBUFFER_HISTORY 100
|
||
|
|
||
|
/* Maintain a list of the objects we see during parsing */
|
||
|
|
||
|
#define SNAPSHOT_OBJ_BUFSIZE 64
|
||
|
|
||
|
#define SNAPSHOT_OBJ_TYPE_IB 0
|
||
|
|
||
|
/* Keep track of how many bytes are frozen after a snapshot and tell the user */
|
||
|
static int snapshot_frozen_objsize;
|
||
|
|
||
|
static struct kgsl_snapshot_obj {
|
||
|
int type;
|
||
|
uint32_t gpuaddr;
|
||
|
phys_addr_t ptbase;
|
||
|
void *ptr;
|
||
|
int dwords;
|
||
|
} objbuf[SNAPSHOT_OBJ_BUFSIZE];
|
||
|
|
||
|
/* Pointer to the next open entry in the object list */
|
||
|
static int objbufptr;
|
||
|
|
||
|
/* Push a new buffer object onto the list */
|
||
|
static void push_object(struct kgsl_device *device, int type,
|
||
|
phys_addr_t ptbase,
|
||
|
uint32_t gpuaddr, int dwords)
|
||
|
{
|
||
|
int index;
|
||
|
void *ptr;
|
||
|
|
||
|
/*
|
||
|
* Sometimes IBs can be reused in the same dump. Because we parse from
|
||
|
* oldest to newest, if we come across an IB that has already been used,
|
||
|
* assume that it has been reused and update the list with the newest
|
||
|
* size.
|
||
|
*/
|
||
|
|
||
|
for (index = 0; index < objbufptr; index++) {
|
||
|
if (objbuf[index].gpuaddr == gpuaddr &&
|
||
|
objbuf[index].ptbase == ptbase) {
|
||
|
objbuf[index].dwords = dwords;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (objbufptr == SNAPSHOT_OBJ_BUFSIZE) {
|
||
|
KGSL_DRV_ERR(device, "snapshot: too many snapshot objects\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* adreno_convertaddr verifies that the IB size is valid - at least in
|
||
|
* the context of it being smaller then the allocated memory space
|
||
|
*/
|
||
|
ptr = adreno_convertaddr(device, ptbase, gpuaddr, dwords << 2);
|
||
|
|
||
|
if (ptr == NULL) {
|
||
|
KGSL_DRV_ERR(device,
|
||
|
"snapshot: Can't find GPU address for %x\n", gpuaddr);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Put it on the list of things to parse */
|
||
|
objbuf[objbufptr].type = type;
|
||
|
objbuf[objbufptr].gpuaddr = gpuaddr;
|
||
|
objbuf[objbufptr].ptbase = ptbase;
|
||
|
objbuf[objbufptr].dwords = dwords;
|
||
|
objbuf[objbufptr++].ptr = ptr;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Return a 1 if the specified object is already on the list of buffers
|
||
|
* to be dumped
|
||
|
*/
|
||
|
|
||
|
static int find_object(int type, unsigned int gpuaddr, phys_addr_t ptbase)
|
||
|
{
|
||
|
int index;
|
||
|
|
||
|
for (index = 0; index < objbufptr; index++) {
|
||
|
if (objbuf[index].gpuaddr == gpuaddr &&
|
||
|
objbuf[index].ptbase == ptbase &&
|
||
|
objbuf[index].type == type)
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This structure keeps track of type0 writes to VSC_PIPE_DATA_ADDRESS_x and
|
||
|
* VSC_PIPE_DATA_LENGTH_x. When a draw initator is called these registers
|
||
|
* point to buffers that we need to freeze for a snapshot
|
||
|
*/
|
||
|
|
||
|
static struct {
|
||
|
unsigned int base;
|
||
|
unsigned int size;
|
||
|
} vsc_pipe[8];
|
||
|
|
||
|
/*
|
||
|
* This is the cached value of type0 writes to the VSC_SIZE_ADDRESS which
|
||
|
* contains the buffer address of the visiblity stream size buffer during a
|
||
|
* binning pass
|
||
|
*/
|
||
|
|
||
|
static unsigned int vsc_size_address;
|
||
|
|
||
|
/*
|
||
|
* This struct keeps track of type0 writes to VFD_FETCH_INSTR_0_X and
|
||
|
* VFD_FETCH_INSTR_1_X registers. When a draw initator is called the addresses
|
||
|
* and sizes in these registers point to VBOs that we need to freeze for a
|
||
|
* snapshot
|
||
|
*/
|
||
|
|
||
|
static struct {
|
||
|
unsigned int base;
|
||
|
unsigned int stride;
|
||
|
} vbo[16];
|
||
|
|
||
|
/*
|
||
|
* This is the cached value of type0 writes to VFD_INDEX_MAX. This will be used
|
||
|
* to calculate the size of the VBOs when the draw initator is called
|
||
|
*/
|
||
|
|
||
|
static unsigned int vfd_index_max;
|
||
|
|
||
|
/*
|
||
|
* This is the cached value of type0 writes to VFD_CONTROL_0 which tells us how
|
||
|
* many VBOs are active when the draw initator is called
|
||
|
*/
|
||
|
|
||
|
static unsigned int vfd_control_0;
|
||
|
|
||
|
/*
|
||
|
* Cached value of type0 writes to SP_VS_PVT_MEM_ADDR and SP_FS_PVT_MEM_ADDR.
|
||
|
* This is a buffer that contains private stack information for the shader
|
||
|
*/
|
||
|
|
||
|
static unsigned int sp_vs_pvt_mem_addr;
|
||
|
static unsigned int sp_fs_pvt_mem_addr;
|
||
|
|
||
|
/*
|
||
|
* Cached value of SP_VS_OBJ_START_REG and SP_FS_OBJ_START_REG.
|
||
|
*/
|
||
|
static unsigned int sp_vs_obj_start_reg;
|
||
|
static unsigned int sp_fs_obj_start_reg;
|
||
|
|
||
|
/*
|
||
|
* Each load state block has two possible types. Each type has a different
|
||
|
* number of dwords per unit. Use this handy lookup table to make sure
|
||
|
* we dump the right amount of data from the indirect buffer
|
||
|
*/
|
||
|
|
||
|
static int load_state_unit_sizes[7][2] = {
|
||
|
{ 2, 4 },
|
||
|
{ 0, 1 },
|
||
|
{ 2, 4 },
|
||
|
{ 0, 1 },
|
||
|
{ 8, 2 },
|
||
|
{ 8, 2 },
|
||
|
{ 8, 2 },
|
||
|
};
|
||
|
|
||
|
static int ib_parse_load_state(struct kgsl_device *device, unsigned int *pkt,
|
||
|
phys_addr_t ptbase)
|
||
|
{
|
||
|
unsigned int block, source, type;
|
||
|
int ret = 0;
|
||
|
|
||
|
/*
|
||
|
* The object here is to find indirect shaders i.e - shaders loaded from
|
||
|
* GPU memory instead of directly in the command. These should be added
|
||
|
* to the list of memory objects to dump. So look at the load state
|
||
|
* if the block is indirect (source = 4). If so then add the memory
|
||
|
* address to the list. The size of the object differs depending on the
|
||
|
* type per the load_state_unit_sizes array above.
|
||
|
*/
|
||
|
|
||
|
if (type3_pkt_size(pkt[0]) < 2)
|
||
|
return 0;
|
||
|
|
||
|
/*
|
||
|
* pkt[1] 18:16 - source
|
||
|
* pkt[1] 21:19 - state block
|
||
|
* pkt[1] 31:22 - size in units
|
||
|
* pkt[2] 0:1 - type
|
||
|
* pkt[2] 31:2 - GPU memory address
|
||
|
*/
|
||
|
|
||
|
block = (pkt[1] >> 19) & 0x07;
|
||
|
source = (pkt[1] >> 16) & 0x07;
|
||
|
type = pkt[2] & 0x03;
|
||
|
|
||
|
if (source == 4) {
|
||
|
int unitsize, ret;
|
||
|
|
||
|
if (type == 0)
|
||
|
unitsize = load_state_unit_sizes[block][0];
|
||
|
else
|
||
|
unitsize = load_state_unit_sizes[block][1];
|
||
|
|
||
|
/* Freeze the GPU buffer containing the shader */
|
||
|
|
||
|
ret = kgsl_snapshot_get_object(device, ptbase,
|
||
|
pkt[2] & 0xFFFFFFFC,
|
||
|
(((pkt[1] >> 22) & 0x03FF) * unitsize) << 2,
|
||
|
SNAPSHOT_GPU_OBJECT_SHADER);
|
||
|
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
snapshot_frozen_objsize += ret;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This opcode sets the base addresses for the visibilty stream buffer and the
|
||
|
* visiblity stream size buffer.
|
||
|
*/
|
||
|
|
||
|
static int ib_parse_set_bin_data(struct kgsl_device *device, unsigned int *pkt,
|
||
|
phys_addr_t ptbase)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
if (type3_pkt_size(pkt[0]) < 2)
|
||
|
return 0;
|
||
|
|
||
|
/* Visiblity stream buffer */
|
||
|
ret = kgsl_snapshot_get_object(device, ptbase, pkt[1], 0,
|
||
|
SNAPSHOT_GPU_OBJECT_GENERIC);
|
||
|
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
snapshot_frozen_objsize += ret;
|
||
|
|
||
|
/* visiblity stream size buffer (fixed size 8 dwords) */
|
||
|
ret = kgsl_snapshot_get_object(device, ptbase, pkt[2], 32,
|
||
|
SNAPSHOT_GPU_OBJECT_GENERIC);
|
||
|
|
||
|
if (ret >= 0)
|
||
|
snapshot_frozen_objsize += ret;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This opcode writes to GPU memory - if the buffer is written to, there is a
|
||
|
* good chance that it would be valuable to capture in the snapshot, so mark all
|
||
|
* buffers that are written to as frozen
|
||
|
*/
|
||
|
|
||
|
static int ib_parse_mem_write(struct kgsl_device *device, unsigned int *pkt,
|
||
|
phys_addr_t ptbase)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
if (type3_pkt_size(pkt[0]) < 1)
|
||
|
return 0;
|
||
|
|
||
|
/*
|
||
|
* The address is where the data in the rest of this packet is written
|
||
|
* to, but since that might be an offset into the larger buffer we need
|
||
|
* to get the whole thing. Pass a size of 0 kgsl_snapshot_get_object to
|
||
|
* capture the entire buffer.
|
||
|
*/
|
||
|
|
||
|
ret = kgsl_snapshot_get_object(device, ptbase, pkt[1] & 0xFFFFFFFC, 0,
|
||
|
SNAPSHOT_GPU_OBJECT_GENERIC);
|
||
|
|
||
|
if (ret >= 0)
|
||
|
snapshot_frozen_objsize += ret;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* The DRAW_INDX opcode sends a draw initator which starts a draw operation in
|
||
|
* the GPU, so this is the point where all the registers and buffers become
|
||
|
* "valid". The DRAW_INDX may also have an index buffer pointer that should be
|
||
|
* frozen with the others
|
||
|
*/
|
||
|
|
||
|
static int ib_parse_draw_indx(struct kgsl_device *device, unsigned int *pkt,
|
||
|
phys_addr_t ptbase)
|
||
|
{
|
||
|
int ret = 0, i;
|
||
|
|
||
|
if (type3_pkt_size(pkt[0]) < 3)
|
||
|
return 0;
|
||
|
|
||
|
/* DRAW_IDX may have a index buffer pointer */
|
||
|
|
||
|
if (type3_pkt_size(pkt[0]) > 3) {
|
||
|
ret = kgsl_snapshot_get_object(device, ptbase, pkt[4], pkt[5],
|
||
|
SNAPSHOT_GPU_OBJECT_GENERIC);
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
snapshot_frozen_objsize += ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* All of the type0 writes are valid at a draw initiator, so freeze
|
||
|
* the various buffers that we are tracking
|
||
|
*/
|
||
|
|
||
|
/* First up the visiblity stream buffer */
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(vsc_pipe); i++) {
|
||
|
if (vsc_pipe[i].base != 0 && vsc_pipe[i].size != 0) {
|
||
|
ret = kgsl_snapshot_get_object(device, ptbase,
|
||
|
vsc_pipe[i].base, vsc_pipe[i].size,
|
||
|
SNAPSHOT_GPU_OBJECT_GENERIC);
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
snapshot_frozen_objsize += ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Next the visibility stream size buffer */
|
||
|
|
||
|
if (vsc_size_address) {
|
||
|
ret = kgsl_snapshot_get_object(device, ptbase,
|
||
|
vsc_size_address, 32,
|
||
|
SNAPSHOT_GPU_OBJECT_GENERIC);
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
snapshot_frozen_objsize += ret;
|
||
|
}
|
||
|
|
||
|
/* Next private shader buffer memory */
|
||
|
if (sp_vs_pvt_mem_addr) {
|
||
|
ret = kgsl_snapshot_get_object(device, ptbase,
|
||
|
sp_vs_pvt_mem_addr, 8192,
|
||
|
SNAPSHOT_GPU_OBJECT_GENERIC);
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
snapshot_frozen_objsize += ret;
|
||
|
sp_vs_pvt_mem_addr = 0;
|
||
|
}
|
||
|
|
||
|
if (sp_fs_pvt_mem_addr) {
|
||
|
ret = kgsl_snapshot_get_object(device, ptbase,
|
||
|
sp_fs_pvt_mem_addr, 8192,
|
||
|
SNAPSHOT_GPU_OBJECT_GENERIC);
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
snapshot_frozen_objsize += ret;
|
||
|
sp_fs_pvt_mem_addr = 0;
|
||
|
}
|
||
|
|
||
|
if (sp_vs_obj_start_reg) {
|
||
|
ret = kgsl_snapshot_get_object(device, ptbase,
|
||
|
sp_vs_obj_start_reg & 0xFFFFFFE0, 0,
|
||
|
SNAPSHOT_GPU_OBJECT_GENERIC);
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
snapshot_frozen_objsize += ret;
|
||
|
sp_vs_obj_start_reg = 0;
|
||
|
}
|
||
|
|
||
|
if (sp_fs_obj_start_reg) {
|
||
|
ret = kgsl_snapshot_get_object(device, ptbase,
|
||
|
sp_fs_obj_start_reg & 0xFFFFFFE0, 0,
|
||
|
SNAPSHOT_GPU_OBJECT_GENERIC);
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
snapshot_frozen_objsize += ret;
|
||
|
sp_fs_obj_start_reg = 0;
|
||
|
}
|
||
|
|
||
|
/* Finally: VBOs */
|
||
|
|
||
|
/* The number of active VBOs is stored in VFD_CONTROL_O[31:27] */
|
||
|
for (i = 0; i < (vfd_control_0) >> 27; i++) {
|
||
|
int size;
|
||
|
|
||
|
/*
|
||
|
* The size of the VBO is the stride stored in
|
||
|
* VFD_FETCH_INSTR_0_X.BUFSTRIDE * VFD_INDEX_MAX. The base
|
||
|
* is stored in VFD_FETCH_INSTR_1_X
|
||
|
*/
|
||
|
|
||
|
if (vbo[i].base != 0) {
|
||
|
size = vbo[i].stride * vfd_index_max;
|
||
|
|
||
|
ret = kgsl_snapshot_get_object(device, ptbase,
|
||
|
vbo[i].base,
|
||
|
0, SNAPSHOT_GPU_OBJECT_GENERIC);
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
snapshot_frozen_objsize += ret;
|
||
|
}
|
||
|
|
||
|
vbo[i].base = 0;
|
||
|
vbo[i].stride = 0;
|
||
|
}
|
||
|
|
||
|
vfd_control_0 = 0;
|
||
|
vfd_index_max = 0;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Parse all the type3 opcode packets that may contain important information,
|
||
|
* such as additional GPU buffers to grab or a draw initator
|
||
|
*/
|
||
|
|
||
|
static int ib_parse_type3(struct kgsl_device *device, unsigned int *ptr,
|
||
|
phys_addr_t ptbase)
|
||
|
{
|
||
|
int opcode = cp_type3_opcode(*ptr);
|
||
|
|
||
|
if (opcode == CP_LOAD_STATE)
|
||
|
return ib_parse_load_state(device, ptr, ptbase);
|
||
|
else if (opcode == CP_SET_BIN_DATA)
|
||
|
return ib_parse_set_bin_data(device, ptr, ptbase);
|
||
|
else if (opcode == CP_MEM_WRITE)
|
||
|
return ib_parse_mem_write(device, ptr, ptbase);
|
||
|
else if (opcode == CP_DRAW_INDX)
|
||
|
return ib_parse_draw_indx(device, ptr, ptbase);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Parse type0 packets found in the stream. Some of the registers that are
|
||
|
* written are clues for GPU buffers that we need to freeze. Register writes
|
||
|
* are considred valid when a draw initator is called, so just cache the values
|
||
|
* here and freeze them when a CP_DRAW_INDX is seen. This protects against
|
||
|
* needlessly caching buffers that won't be used during a draw call
|
||
|
*/
|
||
|
|
||
|
static void ib_parse_type0(struct kgsl_device *device, unsigned int *ptr,
|
||
|
phys_addr_t ptbase)
|
||
|
{
|
||
|
int size = type0_pkt_size(*ptr);
|
||
|
int offset = type0_pkt_offset(*ptr);
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < size - 1; i++, offset++) {
|
||
|
|
||
|
/* Visiblity stream buffer */
|
||
|
|
||
|
if (offset >= A3XX_VSC_PIPE_DATA_ADDRESS_0 &&
|
||
|
offset <= A3XX_VSC_PIPE_DATA_LENGTH_7) {
|
||
|
int index = offset - A3XX_VSC_PIPE_DATA_ADDRESS_0;
|
||
|
|
||
|
/* Each bank of address and length registers are
|
||
|
* interleaved with an empty register:
|
||
|
*
|
||
|
* address 0
|
||
|
* length 0
|
||
|
* empty
|
||
|
* address 1
|
||
|
* length 1
|
||
|
* empty
|
||
|
* ...
|
||
|
*/
|
||
|
|
||
|
if ((index % 3) == 0)
|
||
|
vsc_pipe[index / 3].base = ptr[i + 1];
|
||
|
else if ((index % 3) == 1)
|
||
|
vsc_pipe[index / 3].size = ptr[i + 1];
|
||
|
} else if ((offset >= A3XX_VFD_FETCH_INSTR_0_0) &&
|
||
|
(offset <= A3XX_VFD_FETCH_INSTR_1_F)) {
|
||
|
int index = offset - A3XX_VFD_FETCH_INSTR_0_0;
|
||
|
|
||
|
/*
|
||
|
* FETCH_INSTR_0_X and FETCH_INSTR_1_X banks are
|
||
|
* interleaved as above but without the empty register
|
||
|
* in between
|
||
|
*/
|
||
|
|
||
|
if ((index % 2) == 0)
|
||
|
vbo[index >> 1].stride =
|
||
|
(ptr[i + 1] >> 7) & 0x1FF;
|
||
|
else
|
||
|
vbo[index >> 1].base = ptr[i + 1];
|
||
|
} else {
|
||
|
/*
|
||
|
* Cache various support registers for calculating
|
||
|
* buffer sizes
|
||
|
*/
|
||
|
|
||
|
switch (offset) {
|
||
|
case A3XX_VFD_CONTROL_0:
|
||
|
vfd_control_0 = ptr[i + 1];
|
||
|
break;
|
||
|
case A3XX_VFD_INDEX_MAX:
|
||
|
vfd_index_max = ptr[i + 1];
|
||
|
break;
|
||
|
case A3XX_VSC_SIZE_ADDRESS:
|
||
|
vsc_size_address = ptr[i + 1];
|
||
|
break;
|
||
|
case A3XX_SP_VS_PVT_MEM_ADDR_REG:
|
||
|
sp_vs_pvt_mem_addr = ptr[i + 1];
|
||
|
break;
|
||
|
case A3XX_SP_FS_PVT_MEM_ADDR_REG:
|
||
|
sp_fs_pvt_mem_addr = ptr[i + 1];
|
||
|
break;
|
||
|
case A3XX_SP_VS_OBJ_START_REG:
|
||
|
sp_vs_obj_start_reg = ptr[i + 1];
|
||
|
break;
|
||
|
case A3XX_SP_FS_OBJ_START_REG:
|
||
|
sp_fs_obj_start_reg = ptr[i + 1];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static inline int parse_ib(struct kgsl_device *device, phys_addr_t ptbase,
|
||
|
unsigned int gpuaddr, unsigned int dwords);
|
||
|
|
||
|
/* Add an IB as a GPU object, but first, parse it to find more goodies within */
|
||
|
|
||
|
static int ib_add_gpu_object(struct kgsl_device *device, phys_addr_t ptbase,
|
||
|
unsigned int gpuaddr, unsigned int dwords)
|
||
|
{
|
||
|
int i, ret, rem = dwords;
|
||
|
unsigned int *src;
|
||
|
|
||
|
/*
|
||
|
* If the object is already in the list, we don't need to parse it again
|
||
|
*/
|
||
|
|
||
|
if (kgsl_snapshot_have_object(device, ptbase, gpuaddr, dwords << 2))
|
||
|
return 0;
|
||
|
|
||
|
src = (unsigned int *) adreno_convertaddr(device, ptbase, gpuaddr,
|
||
|
dwords << 2);
|
||
|
|
||
|
if (src == NULL)
|
||
|
return -EINVAL;
|
||
|
|
||
|
for (i = 0; rem > 0; rem--, i++) {
|
||
|
int pktsize;
|
||
|
|
||
|
/* If the packet isn't a type 1 or a type 3, then don't bother
|
||
|
* parsing it - it is likely corrupted */
|
||
|
|
||
|
if (!pkt_is_type0(src[i]) && !pkt_is_type3(src[i]))
|
||
|
break;
|
||
|
|
||
|
pktsize = type3_pkt_size(src[i]);
|
||
|
|
||
|
if (!pktsize || (pktsize + 1) > rem)
|
||
|
break;
|
||
|
|
||
|
if (pkt_is_type3(src[i])) {
|
||
|
if (adreno_cmd_is_ib(src[i])) {
|
||
|
unsigned int gpuaddr = src[i + 1];
|
||
|
unsigned int size = src[i + 2];
|
||
|
|
||
|
ret = parse_ib(device, ptbase, gpuaddr, size);
|
||
|
|
||
|
/* If adding the IB failed then stop parsing */
|
||
|
if (ret < 0)
|
||
|
goto done;
|
||
|
} else {
|
||
|
ret = ib_parse_type3(device, &src[i], ptbase);
|
||
|
/*
|
||
|
* If the parse function failed (probably
|
||
|
* because of a bad decode) then bail out and
|
||
|
* just capture the binary IB data
|
||
|
*/
|
||
|
|
||
|
if (ret < 0)
|
||
|
goto done;
|
||
|
}
|
||
|
} else if (pkt_is_type0(src[i])) {
|
||
|
ib_parse_type0(device, &src[i], ptbase);
|
||
|
}
|
||
|
|
||
|
i += pktsize;
|
||
|
rem -= pktsize;
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
ret = kgsl_snapshot_get_object(device, ptbase, gpuaddr, dwords << 2,
|
||
|
SNAPSHOT_GPU_OBJECT_IB);
|
||
|
|
||
|
if (ret >= 0)
|
||
|
snapshot_frozen_objsize += ret;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* We want to store the last executed IB1 and IB2 in the static region to ensure
|
||
|
* that we get at least some information out of the snapshot even if we can't
|
||
|
* access the dynamic data from the sysfs file. Push all other IBs on the
|
||
|
* dynamic list
|
||
|
*/
|
||
|
static inline int parse_ib(struct kgsl_device *device, phys_addr_t ptbase,
|
||
|
unsigned int gpuaddr, unsigned int dwords)
|
||
|
{
|
||
|
unsigned int ib1base, ib2base;
|
||
|
int ret = 0;
|
||
|
|
||
|
/*
|
||
|
* Check the IB address - if it is either the last executed IB1 or the
|
||
|
* last executed IB2 then push it into the static blob otherwise put
|
||
|
* it in the dynamic list
|
||
|
*/
|
||
|
|
||
|
kgsl_regread(device, REG_CP_IB1_BASE, &ib1base);
|
||
|
kgsl_regread(device, REG_CP_IB2_BASE, &ib2base);
|
||
|
|
||
|
if (gpuaddr == ib1base || gpuaddr == ib2base)
|
||
|
push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
|
||
|
gpuaddr, dwords);
|
||
|
else
|
||
|
ret = ib_add_gpu_object(device, ptbase, gpuaddr, dwords);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* Snapshot the ringbuffer memory */
|
||
|
static int snapshot_rb(struct kgsl_device *device, void *snapshot,
|
||
|
int remain, void *priv)
|
||
|
{
|
||
|
struct kgsl_snapshot_rb *header = snapshot;
|
||
|
unsigned int *data = snapshot + sizeof(*header);
|
||
|
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
|
||
|
struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
|
||
|
unsigned int rptr, *rbptr, ibbase;
|
||
|
phys_addr_t ptbase;
|
||
|
int index, size, i;
|
||
|
int parse_ibs = 0, ib_parse_start;
|
||
|
|
||
|
/* Get the physical address of the MMU pagetable */
|
||
|
ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
|
||
|
|
||
|
/* Get the current read pointers for the RB */
|
||
|
kgsl_regread(device, REG_CP_RB_RPTR, &rptr);
|
||
|
|
||
|
/* Address of the last processed IB */
|
||
|
kgsl_regread(device, REG_CP_IB1_BASE, &ibbase);
|
||
|
|
||
|
/*
|
||
|
* Figure out the window of ringbuffer data to dump. First we need to
|
||
|
* find where the last processed IB ws submitted. Start walking back
|
||
|
* from the rptr
|
||
|
*/
|
||
|
|
||
|
index = rptr;
|
||
|
rbptr = rb->buffer_desc.hostptr;
|
||
|
|
||
|
do {
|
||
|
index--;
|
||
|
|
||
|
if (index < 0) {
|
||
|
index = rb->sizedwords - 3;
|
||
|
|
||
|
/* We wrapped without finding what we wanted */
|
||
|
if (index < rb->wptr) {
|
||
|
index = rb->wptr;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (adreno_cmd_is_ib(rbptr[index]) &&
|
||
|
rbptr[index + 1] == ibbase)
|
||
|
break;
|
||
|
} while (index != rb->wptr);
|
||
|
|
||
|
/*
|
||
|
* index points at the last submitted IB. We can only trust that the
|
||
|
* memory between the context switch and the hanging IB is valid, so
|
||
|
* the next step is to find the context switch before the submission
|
||
|
*/
|
||
|
|
||
|
while (index != rb->wptr) {
|
||
|
index--;
|
||
|
|
||
|
if (index < 0) {
|
||
|
index = rb->sizedwords - 2;
|
||
|
|
||
|
/*
|
||
|
* Wrapped without finding the context switch. This is
|
||
|
* harmless - we should still have enough data to dump a
|
||
|
* valid state
|
||
|
*/
|
||
|
|
||
|
if (index < rb->wptr) {
|
||
|
index = rb->wptr;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Break if the current packet is a context switch identifier */
|
||
|
if ((rbptr[index] == cp_nop_packet(1)) &&
|
||
|
(rbptr[index + 1] == KGSL_CONTEXT_TO_MEM_IDENTIFIER))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Index represents the start of the window of interest. We will try
|
||
|
* to dump all buffers between here and the rptr
|
||
|
*/
|
||
|
|
||
|
ib_parse_start = index;
|
||
|
|
||
|
/*
|
||
|
* Dump the entire ringbuffer - the parser can choose how much of it to
|
||
|
* process
|
||
|
*/
|
||
|
|
||
|
size = (rb->sizedwords << 2);
|
||
|
|
||
|
if (remain < size + sizeof(*header)) {
|
||
|
KGSL_DRV_ERR(device,
|
||
|
"snapshot: Not enough memory for the rb section");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Write the sub-header for the section */
|
||
|
header->start = rb->wptr;
|
||
|
header->end = rb->wptr;
|
||
|
header->wptr = rb->wptr;
|
||
|
header->rbsize = rb->sizedwords;
|
||
|
header->count = rb->sizedwords;
|
||
|
|
||
|
/*
|
||
|
* Loop through the RB, copying the data and looking for indirect
|
||
|
* buffers and MMU pagetable changes
|
||
|
*/
|
||
|
|
||
|
index = rb->wptr;
|
||
|
for (i = 0; i < rb->sizedwords; i++) {
|
||
|
*data = rbptr[index];
|
||
|
|
||
|
/*
|
||
|
* Only parse IBs between the start and the rptr or the next
|
||
|
* context switch, whichever comes first
|
||
|
*/
|
||
|
|
||
|
if (parse_ibs == 0 && index == ib_parse_start)
|
||
|
parse_ibs = 1;
|
||
|
else if (index == rptr || adreno_rb_ctxtswitch(&rbptr[index]))
|
||
|
parse_ibs = 0;
|
||
|
|
||
|
if (parse_ibs && adreno_cmd_is_ib(rbptr[index])) {
|
||
|
unsigned int ibaddr = rbptr[index + 1];
|
||
|
unsigned int ibsize = rbptr[index + 2];
|
||
|
|
||
|
/*
|
||
|
* This will return non NULL if the IB happens to be
|
||
|
* part of the context memory (i.e - context switch
|
||
|
* command buffers)
|
||
|
*/
|
||
|
|
||
|
struct kgsl_memdesc *memdesc =
|
||
|
adreno_find_ctxtmem(device, ptbase, ibaddr,
|
||
|
ibsize << 2);
|
||
|
|
||
|
/* IOMMU uses a NOP IB placed in setsate memory */
|
||
|
if (NULL == memdesc)
|
||
|
if (kgsl_gpuaddr_in_memdesc(
|
||
|
&device->mmu.setstate_memory,
|
||
|
ibaddr, ibsize << 2))
|
||
|
memdesc = &device->mmu.setstate_memory;
|
||
|
/*
|
||
|
* The IB from CP_IB1_BASE and the IBs for legacy
|
||
|
* context switch go into the snapshot all
|
||
|
* others get marked at GPU objects
|
||
|
*/
|
||
|
|
||
|
if (memdesc != NULL)
|
||
|
push_object(device, SNAPSHOT_OBJ_TYPE_IB,
|
||
|
ptbase, ibaddr, ibsize);
|
||
|
else
|
||
|
parse_ib(device, ptbase, ibaddr, ibsize);
|
||
|
}
|
||
|
|
||
|
index = index + 1;
|
||
|
|
||
|
if (index == rb->sizedwords)
|
||
|
index = 0;
|
||
|
|
||
|
data++;
|
||
|
}
|
||
|
|
||
|
/* Return the size of the section */
|
||
|
return size + sizeof(*header);
|
||
|
}
|
||
|
|
||
|
static int snapshot_capture_mem_list(struct kgsl_device *device, void *snapshot,
|
||
|
int remain, void *priv)
|
||
|
{
|
||
|
struct kgsl_snapshot_replay_mem_list *header = snapshot;
|
||
|
struct kgsl_process_private *private = NULL;
|
||
|
struct kgsl_process_private *tmp_private;
|
||
|
phys_addr_t ptbase;
|
||
|
struct rb_node *node;
|
||
|
struct kgsl_mem_entry *entry = NULL;
|
||
|
int num_mem;
|
||
|
unsigned int *data = snapshot + sizeof(*header);
|
||
|
|
||
|
ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
|
||
|
mutex_lock(&kgsl_driver.process_mutex);
|
||
|
list_for_each_entry(tmp_private, &kgsl_driver.process_list, list) {
|
||
|
if (kgsl_mmu_pt_equal(&device->mmu, tmp_private->pagetable,
|
||
|
ptbase)) {
|
||
|
private = tmp_private;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
mutex_unlock(&kgsl_driver.process_mutex);
|
||
|
if (!private) {
|
||
|
KGSL_DRV_ERR(device,
|
||
|
"Failed to get pointer to process private structure\n");
|
||
|
return 0;
|
||
|
}
|
||
|
/* We need to know the number of memory objects that the process has */
|
||
|
spin_lock(&private->mem_lock);
|
||
|
for (node = rb_first(&private->mem_rb), num_mem = 0; node; ) {
|
||
|
entry = rb_entry(node, struct kgsl_mem_entry, node);
|
||
|
node = rb_next(&entry->node);
|
||
|
num_mem++;
|
||
|
}
|
||
|
|
||
|
if (remain < ((num_mem * 3 * sizeof(unsigned int)) +
|
||
|
sizeof(*header))) {
|
||
|
KGSL_DRV_ERR(device,
|
||
|
"snapshot: Not enough memory for the mem list section");
|
||
|
spin_unlock(&private->mem_lock);
|
||
|
return 0;
|
||
|
}
|
||
|
header->num_entries = num_mem;
|
||
|
header->ptbase = (__u32)ptbase;
|
||
|
/*
|
||
|
* Walk throught the memory list and store the
|
||
|
* tuples(gpuaddr, size, memtype) in snapshot
|
||
|
*/
|
||
|
for (node = rb_first(&private->mem_rb); node; ) {
|
||
|
entry = rb_entry(node, struct kgsl_mem_entry, node);
|
||
|
node = rb_next(&entry->node);
|
||
|
|
||
|
*data++ = entry->memdesc.gpuaddr;
|
||
|
*data++ = entry->memdesc.size;
|
||
|
*data++ = (entry->memdesc.priv & KGSL_MEMTYPE_MASK) >>
|
||
|
KGSL_MEMTYPE_SHIFT;
|
||
|
}
|
||
|
spin_unlock(&private->mem_lock);
|
||
|
return sizeof(*header) + (num_mem * 3 * sizeof(unsigned int));
|
||
|
}
|
||
|
|
||
|
/* Snapshot the memory for an indirect buffer */
|
||
|
static int snapshot_ib(struct kgsl_device *device, void *snapshot,
|
||
|
int remain, void *priv)
|
||
|
{
|
||
|
struct kgsl_snapshot_ib *header = snapshot;
|
||
|
struct kgsl_snapshot_obj *obj = priv;
|
||
|
unsigned int *src = obj->ptr;
|
||
|
unsigned int *dst = snapshot + sizeof(*header);
|
||
|
int i, ret;
|
||
|
|
||
|
if (remain < (obj->dwords << 2) + sizeof(*header)) {
|
||
|
KGSL_DRV_ERR(device,
|
||
|
"snapshot: Not enough memory for the ib section");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Write the sub-header for the section */
|
||
|
header->gpuaddr = obj->gpuaddr;
|
||
|
header->ptbase = (__u32)obj->ptbase;
|
||
|
header->size = obj->dwords;
|
||
|
|
||
|
/* Write the contents of the ib */
|
||
|
for (i = 0; i < obj->dwords; i++, src++, dst++) {
|
||
|
*dst = *src;
|
||
|
|
||
|
if (pkt_is_type3(*src)) {
|
||
|
if ((obj->dwords - i) < type3_pkt_size(*src) + 1)
|
||
|
continue;
|
||
|
|
||
|
if (adreno_cmd_is_ib(*src))
|
||
|
ret = parse_ib(device, obj->ptbase, src[1],
|
||
|
src[2]);
|
||
|
else
|
||
|
ret = ib_parse_type3(device, src, obj->ptbase);
|
||
|
|
||
|
/* Stop parsing if the type3 decode fails */
|
||
|
if (ret < 0)
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (obj->dwords << 2) + sizeof(*header);
|
||
|
}
|
||
|
|
||
|
/* Dump another item on the current pending list */
|
||
|
static void *dump_object(struct kgsl_device *device, int obj, void *snapshot,
|
||
|
int *remain)
|
||
|
{
|
||
|
switch (objbuf[obj].type) {
|
||
|
case SNAPSHOT_OBJ_TYPE_IB:
|
||
|
snapshot = kgsl_snapshot_add_section(device,
|
||
|
KGSL_SNAPSHOT_SECTION_IB, snapshot, remain,
|
||
|
snapshot_ib, &objbuf[obj]);
|
||
|
break;
|
||
|
default:
|
||
|
KGSL_DRV_ERR(device,
|
||
|
"snapshot: Invalid snapshot object type: %d\n",
|
||
|
objbuf[obj].type);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return snapshot;
|
||
|
}
|
||
|
|
||
|
/* adreno_snapshot - Snapshot the Adreno GPU state
|
||
|
* @device - KGSL device to snapshot
|
||
|
* @snapshot - Pointer to the start of memory to write into
|
||
|
* @remain - A pointer to how many bytes of memory are remaining in the snapshot
|
||
|
* @hang - set if this snapshot was automatically triggered by a GPU hang
|
||
|
* This is a hook function called by kgsl_snapshot to snapshot the
|
||
|
* Adreno specific information for the GPU snapshot. In turn, this function
|
||
|
* calls the GPU specific snapshot function to get core specific information.
|
||
|
*/
|
||
|
|
||
|
void *adreno_snapshot(struct kgsl_device *device, void *snapshot, int *remain,
|
||
|
int hang)
|
||
|
{
|
||
|
int i;
|
||
|
uint32_t ibbase, ibsize;
|
||
|
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
|
||
|
phys_addr_t ptbase;
|
||
|
|
||
|
/* Reset the list of objects */
|
||
|
objbufptr = 0;
|
||
|
|
||
|
snapshot_frozen_objsize = 0;
|
||
|
|
||
|
/* Clear the caches for the visibilty stream and VBO parsing */
|
||
|
|
||
|
vfd_control_0 = 0;
|
||
|
vfd_index_max = 0;
|
||
|
vsc_size_address = 0;
|
||
|
|
||
|
memset(vsc_pipe, 0, sizeof(vsc_pipe));
|
||
|
memset(vbo, 0, sizeof(vbo));
|
||
|
|
||
|
/* Get the physical address of the MMU pagetable */
|
||
|
ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
|
||
|
|
||
|
/* Dump the ringbuffer */
|
||
|
snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_RB,
|
||
|
snapshot, remain, snapshot_rb, NULL);
|
||
|
|
||
|
/*
|
||
|
* Add a section that lists (gpuaddr, size, memtype) tuples of the
|
||
|
* hanging process
|
||
|
*/
|
||
|
snapshot = kgsl_snapshot_add_section(device,
|
||
|
KGSL_SNAPSHOT_SECTION_MEMLIST, snapshot, remain,
|
||
|
snapshot_capture_mem_list, NULL);
|
||
|
/*
|
||
|
* Make sure that the last IB1 that was being executed is dumped.
|
||
|
* Since this was the last IB1 that was processed, we should have
|
||
|
* already added it to the list during the ringbuffer parse but we
|
||
|
* want to be double plus sure.
|
||
|
*/
|
||
|
|
||
|
kgsl_regread(device, REG_CP_IB1_BASE, &ibbase);
|
||
|
kgsl_regread(device, REG_CP_IB1_BUFSZ, &ibsize);
|
||
|
|
||
|
/*
|
||
|
* The problem is that IB size from the register is the unprocessed size
|
||
|
* of the buffer not the original size, so if we didn't catch this
|
||
|
* buffer being directly used in the RB, then we might not be able to
|
||
|
* dump the whle thing. Print a warning message so we can try to
|
||
|
* figure how often this really happens.
|
||
|
*/
|
||
|
|
||
|
if (!find_object(SNAPSHOT_OBJ_TYPE_IB, ibbase, ptbase) && ibsize) {
|
||
|
push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
|
||
|
ibbase, ibsize);
|
||
|
KGSL_DRV_ERR(device, "CP_IB1_BASE not found in the ringbuffer. "
|
||
|
"Dumping %x dwords of the buffer.\n", ibsize);
|
||
|
}
|
||
|
|
||
|
kgsl_regread(device, REG_CP_IB2_BASE, &ibbase);
|
||
|
kgsl_regread(device, REG_CP_IB2_BUFSZ, &ibsize);
|
||
|
|
||
|
/*
|
||
|
* Add the last parsed IB2 to the list. The IB2 should be found as we
|
||
|
* parse the objects below, but we try to add it to the list first, so
|
||
|
* it too can be parsed. Don't print an error message in this case - if
|
||
|
* the IB2 is found during parsing, the list will be updated with the
|
||
|
* correct size.
|
||
|
*/
|
||
|
|
||
|
if (!find_object(SNAPSHOT_OBJ_TYPE_IB, ibbase, ptbase) && ibsize) {
|
||
|
push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase,
|
||
|
ibbase, ibsize);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Go through the list of found objects and dump each one. As the IBs
|
||
|
* are parsed, more objects might be found, and objbufptr will increase
|
||
|
*/
|
||
|
for (i = 0; i < objbufptr; i++)
|
||
|
snapshot = dump_object(device, i, snapshot, remain);
|
||
|
|
||
|
/* Add GPU specific sections - registers mainly, but other stuff too */
|
||
|
if (adreno_dev->gpudev->snapshot)
|
||
|
snapshot = adreno_dev->gpudev->snapshot(adreno_dev, snapshot,
|
||
|
remain, hang);
|
||
|
|
||
|
if (snapshot_frozen_objsize)
|
||
|
KGSL_DRV_ERR(device, "GPU snapshot froze %dKb of GPU buffers\n",
|
||
|
snapshot_frozen_objsize / 1024);
|
||
|
|
||
|
return snapshot;
|
||
|
}
|