msm: kgsl: rework ib checking

Separate ib parse checking from cffdump as it is useful
in other situations. This is controlled by a new debugfs
file, ib_check. All ib checking is off (0) by default,
because parsing and mem_entry lookup can have a performance
impact on some benchmarks. Level 1 checking verifies the
IB1's. Level 2 checking also verifies the IB2.
This commit is contained in:
securecrt 2012-07-26 16:14:35 +08:00
parent d842173fc6
commit c6e8ee54ff
8 changed files with 218 additions and 264 deletions

View File

@ -122,8 +122,10 @@ static struct adreno_device device_3d0 = {
.pfp_fw = NULL,
.pm4_fw = NULL,
.wait_timeout = 10000, /* in milliseconds */
.ib_check_level = 0,
};
/*
* This is the master list of all GPU cores that are supported by this
* driver.

View File

@ -75,6 +75,7 @@ struct adreno_device {
unsigned int istore_size;
unsigned int pix_shader_start;
unsigned int instruction_size;
unsigned int ib_check_level;
};
struct adreno_gpudev {

View File

@ -444,6 +444,8 @@ void adreno_debugfs_init(struct kgsl_device *device)
&kgsl_cff_dump_enable_fops);
debugfs_create_u32("wait_timeout", 0644, device->d_debugfs,
&adreno_dev->wait_timeout);
debugfs_create_u32("ib_check", 0644, device->d_debugfs,
&adreno_dev->ib_check_level);
/* Create post mortem control files */

View File

@ -29,11 +29,6 @@
/* skip N 32-bit words to get to the next packet */
#define CP_NOP 0x10
/* indirect buffer dispatch. prefetch parser uses this packet type to determine
* whether to pre-fetch the IB
*/
#define CP_INDIRECT_BUFFER 0x3f
/* indirect buffer dispatch. same as IB, but init is pipelined */
#define CP_INDIRECT_BUFFER_PFD 0x37
@ -117,6 +112,9 @@
/* load constants from a location in memory */
#define CP_LOAD_CONSTANT_CONTEXT 0x2e
/* (A2x) sets binning configuration registers */
#define CP_SET_BIN_DATA 0x2f
/* selective invalidation of state pointers */
#define CP_INVALIDATE_STATE 0x3b
@ -197,11 +195,20 @@
#define cp_nop_packet(cnt) \
(CP_TYPE3_PKT | (((cnt)-1) << 16) | (CP_NOP << 8))
#define pkt_is_type0(pkt) (((pkt) & 0XC0000000) == CP_TYPE0_PKT)
#define type0_pkt_size(pkt) ((((pkt) >> 16) & 0x3FFF) + 1)
#define type0_pkt_offset(pkt) ((pkt) & 0x7FFF)
#define pkt_is_type3(pkt) (((pkt) & 0xC0000000) == CP_TYPE3_PKT)
#define cp_type3_opcode(pkt) (((pkt) >> 8) & 0xFF)
#define type3_pkt_size(pkt) ((((pkt) >> 16) & 0x3FFF) + 1)
/* packet headers */
#define CP_HDR_ME_INIT cp_type3_packet(CP_ME_INIT, 18)
#define CP_HDR_INDIRECT_BUFFER_PFD cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2)
#define CP_HDR_INDIRECT_BUFFER cp_type3_packet(CP_INDIRECT_BUFFER, 2)
#define CP_HDR_INDIRECT_BUFFER_PFE cp_type3_packet(CP_INDIRECT_BUFFER_PFE, 2)
/* dword base address of the GFX decode space */
#define SUBBLOCK_OFFSET(reg) ((unsigned int)((reg) - (0x2000)))

View File

@ -52,7 +52,7 @@ static const struct pm_id_name pm3_types[] = {
{CP_IM_LOAD, "IN__LOAD"},
{CP_IM_LOAD_IMMEDIATE, "IM_LOADI"},
{CP_IM_STORE, "IM_STORE"},
{CP_INDIRECT_BUFFER, "IND_BUF_"},
{CP_INDIRECT_BUFFER_PFE, "IND_BUF_"},
{CP_INDIRECT_BUFFER_PFD, "IND_BUFP"},
{CP_INTERRUPT, "PM4_INTR"},
{CP_INVALIDATE_STATE, "INV_STAT"},

View File

@ -22,6 +22,7 @@
#include "adreno.h"
#include "adreno_pm4types.h"
#include "adreno_ringbuffer.h"
#include "adreno_debugfs.h"
#include "a2xx_reg.h"
#include "a3xx_reg.h"
@ -550,6 +551,198 @@ adreno_ringbuffer_issuecmds(struct kgsl_device *device,
adreno_ringbuffer_addcmds(rb, flags, cmds, sizedwords);
}
static bool _parse_ibs(struct kgsl_device_private *dev_priv, uint gpuaddr,
int sizedwords);
static bool
_handle_type3(struct kgsl_device_private *dev_priv, uint *hostaddr)
{
unsigned int opcode = cp_type3_opcode(*hostaddr);
switch (opcode) {
case CP_INDIRECT_BUFFER_PFD:
case CP_INDIRECT_BUFFER_PFE:
case CP_COND_INDIRECT_BUFFER_PFE:
case CP_COND_INDIRECT_BUFFER_PFD:
return _parse_ibs(dev_priv, hostaddr[1], hostaddr[2]);
case CP_NOP:
case CP_WAIT_FOR_IDLE:
case CP_WAIT_REG_MEM:
case CP_WAIT_REG_EQ:
case CP_WAT_REG_GTE:
case CP_WAIT_UNTIL_READ:
case CP_WAIT_IB_PFD_COMPLETE:
case CP_REG_RMW:
case CP_REG_TO_MEM:
case CP_MEM_WRITE:
case CP_MEM_WRITE_CNTR:
case CP_COND_EXEC:
case CP_COND_WRITE:
case CP_EVENT_WRITE:
case CP_EVENT_WRITE_SHD:
case CP_EVENT_WRITE_CFL:
case CP_EVENT_WRITE_ZPD:
case CP_DRAW_INDX:
case CP_DRAW_INDX_2:
case CP_DRAW_INDX_BIN:
case CP_DRAW_INDX_2_BIN:
case CP_VIZ_QUERY:
case CP_SET_STATE:
case CP_SET_CONSTANT:
case CP_IM_LOAD:
case CP_IM_LOAD_IMMEDIATE:
case CP_LOAD_CONSTANT_CONTEXT:
case CP_INVALIDATE_STATE:
case CP_SET_SHADER_BASES:
case CP_SET_BIN_MASK:
case CP_SET_BIN_SELECT:
case CP_SET_BIN_BASE_OFFSET:
case CP_SET_BIN_DATA:
case CP_CONTEXT_UPDATE:
case CP_INTERRUPT:
case CP_IM_STORE:
case CP_LOAD_STATE:
break;
/* these shouldn't come from userspace */
case CP_ME_INIT:
case CP_SET_PROTECTED_MODE:
default:
KGSL_CMD_ERR(dev_priv->device, "bad CP opcode %0x\n", opcode);
return false;
break;
}
return true;
}
static bool
_handle_type0(struct kgsl_device_private *dev_priv, uint *hostaddr)
{
unsigned int reg = type0_pkt_offset(*hostaddr);
unsigned int cnt = type0_pkt_size(*hostaddr);
if (reg < 0x0192 || (reg + cnt) >= 0x8000) {
KGSL_CMD_ERR(dev_priv->device, "bad type0 reg: 0x%0x cnt: %d\n",
reg, cnt);
return false;
}
return true;
}
/*
* Traverse IBs and dump them to test vector. Detect swap by inspecting
* register writes, keeping note of the current state, and dump
* framebuffer config to test vector
*/
static bool _parse_ibs(struct kgsl_device_private *dev_priv,
uint gpuaddr, int sizedwords)
{
static uint level; /* recursion level */
bool ret = false;
uint *hostaddr, *hoststart;
int dwords_left = sizedwords; /* dwords left in the current command
buffer */
struct kgsl_mem_entry *entry;
spin_lock(&dev_priv->process_priv->mem_lock);
entry = kgsl_sharedmem_find_region(dev_priv->process_priv,
gpuaddr, sizedwords * sizeof(uint));
spin_unlock(&dev_priv->process_priv->mem_lock);
if (entry == NULL) {
KGSL_CMD_ERR(dev_priv->device,
"no mapping for gpuaddr: 0x%08x\n", gpuaddr);
return false;
}
hostaddr = (uint *)kgsl_gpuaddr_to_vaddr(&entry->memdesc, gpuaddr);
if (hostaddr == NULL) {
KGSL_CMD_ERR(dev_priv->device,
"no mapping for gpuaddr: 0x%08x\n", gpuaddr);
return false;
}
hoststart = hostaddr;
level++;
KGSL_CMD_INFO(dev_priv->device, "ib: gpuaddr:0x%08x, wc:%d, hptr:%p\n",
gpuaddr, sizedwords, hostaddr);
mb();
while (dwords_left > 0) {
bool cur_ret = true;
int count = 0; /* dword count including packet header */
switch (*hostaddr >> 30) {
case 0x0: /* type-0 */
count = (*hostaddr >> 16)+2;
cur_ret = _handle_type0(dev_priv, hostaddr);
break;
case 0x1: /* type-1 */
count = 2;
break;
case 0x3: /* type-3 */
count = ((*hostaddr >> 16) & 0x3fff) + 2;
cur_ret = _handle_type3(dev_priv, hostaddr);
break;
default:
KGSL_CMD_ERR(dev_priv->device, "unexpected type: "
"type:%d, word:0x%08x @ 0x%p, gpu:0x%08x\n",
*hostaddr >> 30, *hostaddr, hostaddr,
gpuaddr+4*(sizedwords-dwords_left));
cur_ret = false;
count = dwords_left;
break;
}
if (!cur_ret) {
KGSL_CMD_ERR(dev_priv->device,
"bad sub-type: #:%d/%d, v:0x%08x"
" @ 0x%p[gb:0x%08x], level:%d\n",
sizedwords-dwords_left, sizedwords, *hostaddr,
hostaddr, gpuaddr+4*(sizedwords-dwords_left),
level);
if (ADRENO_DEVICE(dev_priv->device)->ib_check_level
>= 2)
print_hex_dump(KERN_ERR,
level == 1 ? "IB1:" : "IB2:",
DUMP_PREFIX_OFFSET, 32, 4, hoststart,
sizedwords*4, 0);
goto done;
}
/* jump to next packet */
dwords_left -= count;
hostaddr += count;
if (dwords_left < 0) {
KGSL_CMD_ERR(dev_priv->device,
"bad count: c:%d, #:%d/%d, "
"v:0x%08x @ 0x%p[gb:0x%08x], level:%d\n",
count, sizedwords-(dwords_left+count),
sizedwords, *(hostaddr-count), hostaddr-count,
gpuaddr+4*(sizedwords-(dwords_left+count)),
level);
if (ADRENO_DEVICE(dev_priv->device)->ib_check_level
>= 2)
print_hex_dump(KERN_ERR,
level == 1 ? "IB1:" : "IB2:",
DUMP_PREFIX_OFFSET, 32, 4, hoststart,
sizedwords*4, 0);
goto done;
}
}
ret = true;
done:
if (!ret)
KGSL_DRV_ERR(dev_priv->device,
"parsing failed: gpuaddr:0x%08x, "
"host:0x%p, wc:%d\n", gpuaddr, hoststart, sizedwords);
level--;
return ret;
}
int
adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv,
struct kgsl_context *context,
@ -608,9 +801,12 @@ adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv,
*cmds++ = ibdesc[0].sizedwords;
}
for (i = start_index; i < numibs; i++) {
(void)kgsl_cffdump_parse_ibs(dev_priv, NULL,
ibdesc[i].gpuaddr, ibdesc[i].sizedwords, false);
if (unlikely(adreno_dev->ib_check_level >= 1 &&
!_parse_ibs(dev_priv, ibdesc[i].gpuaddr,
ibdesc[i].sizedwords))) {
kfree(link);
return -EINVAL;
}
*cmds++ = CP_HDR_INDIRECT_BUFFER_PFD;
*cmds++ = ibdesc[i].gpuaddr;
*cmds++ = ibdesc[i].sizedwords;
@ -660,7 +856,6 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb,
unsigned int val3;
unsigned int copy_rb_contents = 0;
unsigned int cur_context;
unsigned int j;
GSL_RB_GET_READPTR(rb, &rb->rptr);
@ -811,19 +1006,6 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb,
}
*rb_size = temp_idx;
KGSL_DRV_ERR(device, "Extracted rb contents, size: %x\n", *rb_size);
for (temp_idx = 0; temp_idx < *rb_size;) {
char str[80];
int idx = 0;
if ((temp_idx + 8) <= *rb_size)
j = 8;
else
j = *rb_size - temp_idx;
for (; j != 0; j--)
idx += scnprintf(str + idx, 80 - idx,
"%8.8X ", temp_rb_buffer[temp_idx++]);
printk(KERN_ALERT "%s", str);
}
return 0;
}

View File

@ -909,40 +909,6 @@ static long kgsl_ioctl_device_waittimestamp(struct kgsl_device_private
return result;
}
static bool check_ibdesc(struct kgsl_device_private *dev_priv,
struct kgsl_ibdesc *ibdesc, unsigned int numibs,
bool parse)
{
bool result = true;
unsigned int i;
for (i = 0; i < numibs; i++) {
struct kgsl_mem_entry *entry;
spin_lock(&dev_priv->process_priv->mem_lock);
entry = kgsl_sharedmem_find_region(dev_priv->process_priv,
ibdesc[i].gpuaddr, ibdesc[i].sizedwords * sizeof(uint));
spin_unlock(&dev_priv->process_priv->mem_lock);
if (entry == NULL) {
KGSL_DRV_ERR(dev_priv->device,
"invalid cmd buffer gpuaddr %08x " \
"sizedwords %d\n", ibdesc[i].gpuaddr,
ibdesc[i].sizedwords);
result = false;
break;
}
if (parse && !kgsl_cffdump_parse_ibs(dev_priv, &entry->memdesc,
ibdesc[i].gpuaddr, ibdesc[i].sizedwords, true)) {
KGSL_DRV_ERR(dev_priv->device,
"invalid cmd buffer gpuaddr %08x " \
"sizedwords %d numibs %d/%d\n",
ibdesc[i].gpuaddr,
ibdesc[i].sizedwords, i+1, numibs);
result = false;
break;
}
}
return result;
}
static long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
@ -1012,12 +978,6 @@ static long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv,
param->numibs = 1;
}
if (!check_ibdesc(dev_priv, ibdesc, param->numibs, true)) {
KGSL_DRV_ERR(dev_priv->device, "bad ibdesc");
result = -EINVAL;
goto free_ibdesc;
}
result = dev_priv->device->ftbl->issueibcmds(dev_priv,
context,
ibdesc,
@ -1025,17 +985,6 @@ static long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv,
&param->timestamp,
param->flags);
if (result != 0)
goto free_ibdesc;
/* this is a check to try to detect if a command buffer was freed
* during issueibcmds().
*/
if (!check_ibdesc(dev_priv, ibdesc, param->numibs, false)) {
KGSL_DRV_ERR(dev_priv->device, "bad ibdesc AFTER issue");
result = -EINVAL;
goto free_ibdesc;
}
free_ibdesc:
kfree(ibdesc);

View File

@ -497,195 +497,6 @@ int kgsl_cffdump_waitirq(void)
}
EXPORT_SYMBOL(kgsl_cffdump_waitirq);
#define ADDRESS_STACK_SIZE 256
#define GET_PM4_TYPE3_OPCODE(x) ((*(x) >> 8) & 0xFF)
static unsigned int kgsl_cffdump_addr_count;
static bool kgsl_cffdump_handle_type3(struct kgsl_device_private *dev_priv,
uint *hostaddr, bool check_only)
{
static uint addr_stack[ADDRESS_STACK_SIZE];
static uint size_stack[ADDRESS_STACK_SIZE];
switch (GET_PM4_TYPE3_OPCODE(hostaddr)) {
case CP_INDIRECT_BUFFER_PFD:
case CP_INDIRECT_BUFFER:
{
/* traverse indirect buffers */
int i;
uint ibaddr = hostaddr[1];
uint ibsize = hostaddr[2];
/* is this address already in encountered? */
for (i = 0;
i < kgsl_cffdump_addr_count && addr_stack[i] != ibaddr;
++i)
;
if (kgsl_cffdump_addr_count == i) {
addr_stack[kgsl_cffdump_addr_count] = ibaddr;
size_stack[kgsl_cffdump_addr_count++] = ibsize;
if (kgsl_cffdump_addr_count >= ADDRESS_STACK_SIZE) {
KGSL_CORE_ERR("stack overflow\n");
return false;
}
return kgsl_cffdump_parse_ibs(dev_priv, NULL,
ibaddr, ibsize, check_only);
} else if (size_stack[i] != ibsize) {
KGSL_CORE_ERR("gpuaddr: 0x%08x, "
"wc: %u, with size wc: %u already on the "
"stack\n", ibaddr, ibsize, size_stack[i]);
return false;
}
}
break;
}
return true;
}
/*
* Traverse IBs and dump them to test vector. Detect swap by inspecting
* register writes, keeping note of the current state, and dump
* framebuffer config to test vector
*/
bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv,
const struct kgsl_memdesc *memdesc, uint gpuaddr, int sizedwords,
bool check_only)
{
static uint level; /* recursion level */
bool ret = true;
uint *hostaddr, *hoststart;
int dwords_left = sizedwords; /* dwords left in the current command
buffer */
if (level == 0)
kgsl_cffdump_addr_count = 0;
if (memdesc == NULL) {
struct kgsl_mem_entry *entry;
spin_lock(&dev_priv->process_priv->mem_lock);
entry = kgsl_sharedmem_find_region(dev_priv->process_priv,
gpuaddr, sizedwords * sizeof(uint));
spin_unlock(&dev_priv->process_priv->mem_lock);
if (entry == NULL) {
KGSL_CORE_ERR("did not find mapping "
"for gpuaddr: 0x%08x\n", gpuaddr);
return true;
}
memdesc = &entry->memdesc;
}
hostaddr = (uint *)kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr);
if (hostaddr == NULL) {
KGSL_CORE_ERR("no kernel mapping for "
"gpuaddr: 0x%08x\n", gpuaddr);
return true;
}
hoststart = hostaddr;
level++;
if (!memdesc->physaddr) {
KGSL_CORE_ERR("no physaddr");
} else {
mb();
kgsl_cache_range_op((struct kgsl_memdesc *)memdesc,
KGSL_CACHE_OP_INV);
}
#ifdef DEBUG
pr_info("kgsl: cffdump: ib: gpuaddr:0x%08x, wc:%d, hptr:%p\n",
gpuaddr, sizedwords, hostaddr);
#endif
while (dwords_left > 0) {
int count = 0; /* dword count including packet header */
bool cur_ret = true;
switch (*hostaddr >> 30) {
case 0x0: /* type-0 */
count = (*hostaddr >> 16)+2;
break;
case 0x1: /* type-1 */
count = 2;
break;
case 0x3: /* type-3 */
count = ((*hostaddr >> 16) & 0x3fff) + 2;
cur_ret = kgsl_cffdump_handle_type3(dev_priv,
hostaddr, check_only);
break;
default:
pr_warn("kgsl: cffdump: parse-ib: unexpected type: "
"type:%d, word:0x%08x @ 0x%p, gpu:0x%08x\n",
*hostaddr >> 30, *hostaddr, hostaddr,
gpuaddr+4*(sizedwords-dwords_left));
cur_ret = false;
count = dwords_left;
break;
}
#ifdef DEBUG
if (!cur_ret) {
pr_info("kgsl: cffdump: bad sub-type: #:%d/%d, v:0x%08x"
" @ 0x%p[gb:0x%08x], level:%d\n",
sizedwords-dwords_left, sizedwords, *hostaddr,
hostaddr, gpuaddr+4*(sizedwords-dwords_left),
level);
print_hex_dump(KERN_ERR, level == 1 ? "IB1:" : "IB2:",
DUMP_PREFIX_OFFSET, 32, 4, hoststart,
sizedwords*4, 0);
}
#endif
ret = ret && cur_ret;
/* jump to next packet */
dwords_left -= count;
hostaddr += count;
cur_ret = dwords_left >= 0;
#ifdef DEBUG
if (!cur_ret) {
pr_info("kgsl: cffdump: bad count: c:%d, #:%d/%d, "
"v:0x%08x @ 0x%p[gb:0x%08x], level:%d\n",
count, sizedwords-(dwords_left+count),
sizedwords, *(hostaddr-count), hostaddr-count,
gpuaddr+4*(sizedwords-(dwords_left+count)),
level);
print_hex_dump(KERN_ERR, level == 1 ? "IB1:" : "IB2:",
DUMP_PREFIX_OFFSET, 32, 4, hoststart,
sizedwords*4, 0);
}
#endif
ret = ret && cur_ret;
}
if (!ret)
pr_info("kgsl: cffdump: parsing failed: gpuaddr:0x%08x, "
"host:0x%p, wc:%d\n", gpuaddr, hoststart, sizedwords);
if (!check_only) {
#ifdef DEBUG
uint offset = gpuaddr - memdesc->gpuaddr;
pr_info("kgsl: cffdump: ib-dump: hostptr:%p, gpuaddr:%08x, "
"physaddr:%08x, offset:%d, size:%d", hoststart,
gpuaddr, memdesc->physaddr + offset, offset,
sizedwords*4);
#endif
kgsl_cffdump_syncmem(dev_priv, memdesc, gpuaddr, sizedwords*4,
false);
}
level--;
return ret;
}
static int subbuf_start_handler(struct rchan_buf *buf,
void *subbuf, void *prev_subbuf, uint prev_padding)
{