#include "sgxutils.h"
#include "pvr_debugfs.h"
#include "mmu.h"
+#include "bridged_support.h"
+#include "mm.h"
static struct dentry *debugfs_dir;
static u32 pvr_reset;
goto exit;
}
- HWRecoveryResetSGX(node);
+ HWRecoveryResetSGX(node, __func__);
SGXTestActivePowerEvent(node);
exit:
}
#endif /* CONFIG_PVR_DEBUG */
+/*
+ * Render status buffer dumping.
+ */
+static size_t hwrec_status_size;
+static u32 hwrec_status_pages[1024];
+
+static int
+hwrec_status_write(char *buffer, size_t size)
+{
+ return hwrec_pages_write(buffer, size, &hwrec_status_size,
+ hwrec_status_pages,
+ ARRAY_SIZE(hwrec_status_pages));
+}
+
+static void
+hwrec_status_free(void)
+{
+ hwrec_pages_free(&hwrec_status_size, hwrec_status_pages);
+}
+
+static int
+hwrec_status_print(char *format, ...)
+{
+ char tmp[25];
+ va_list ap;
+ size_t size;
+
+ va_start(ap, format);
+ size = vscnprintf(tmp, sizeof(tmp), format, ap);
+ va_end(ap);
+
+ return hwrec_status_write(tmp, size);
+}
+
+#define BUF_DESC_CORRUPT (1 << 31)
+
+static void add_uniq_items(struct render_state_buf_list *dst,
+ const struct render_state_buf_list *src)
+{
+ int i;
+
+ for (i = 0; i < src->cnt; i++) {
+ const struct render_state_buf_info *sbinf = &src->info[i];
+ int j;
+
+ for (j = 0; j < dst->cnt; j++) {
+ if (sbinf->buf_id == dst->info[j].buf_id) {
+ if (memcmp(sbinf, &dst->info[j], sizeof(sbinf)))
+ dst->info[j].type |= BUF_DESC_CORRUPT;
+ break;
+ }
+ }
+ if (j == dst->cnt) {
+ /* Bound for cnt is guaranteed by the caller */
+ dst->info[dst->cnt] = *sbinf;
+ dst->cnt++;
+ }
+ }
+}
+
+static struct render_state_buf_list *create_merged_uniq_list(
+ struct render_state_buf_list **bl_set, int set_size)
+{
+ int i;
+ struct render_state_buf_list *dbl;
+ size_t size;
+
+ /*
+ * Create a buf list big enough to contain all elements from each
+ * list in bl_set.
+ */
+ size = offsetof(struct render_state_buf_list, info[0]);
+ for (i = 0; i < set_size; i++) {
+ if (!bl_set[i])
+ continue;
+ size += bl_set[i]->cnt * sizeof(bl_set[i]->info[0]);
+ }
+ if (!size)
+ return NULL;
+ dbl = kmalloc(size, GFP_KERNEL);
+ if (!dbl)
+ return NULL;
+
+ dbl->cnt = 0;
+ for (i = 0; i < set_size; i++) {
+ if (bl_set[i])
+ add_uniq_items(dbl, bl_set[i]);
+ }
+
+ return dbl;
+}
+
+static void *vmap_buf(struct PVRSRV_PER_PROCESS_DATA *proc,
+ u32 handle, off_t offset, size_t size)
+{
+ struct PVRSRV_KERNEL_MEM_INFO *minfo;
+ struct LinuxMemArea *mem_area;
+ enum PVRSRV_ERROR err;
+ unsigned start_ofs;
+ unsigned end_ofs;
+ int pg_cnt;
+ struct page **pages;
+ void *map = NULL;
+ int i;
+
+ if (offset & PAGE_MASK)
+ return NULL;
+
+ err = PVRSRVLookupHandle(proc->psHandleBase, (void **)&minfo,
+ (void *)handle, PVRSRV_HANDLE_TYPE_MEM_INFO);
+ if (err != PVRSRV_OK)
+ return NULL;
+ if (minfo->pvLinAddrKM)
+ return minfo->pvLinAddrKM;
+
+ err = PVRSRVLookupOSMemHandle(proc->psHandleBase, (void *)&mem_area,
+ (void *)handle);
+ if (err != PVRSRV_OK)
+ return NULL;
+
+ start_ofs = offset & PAGE_MASK;
+ end_ofs = PAGE_ALIGN(offset + size);
+ pg_cnt = (end_ofs - start_ofs) >> PAGE_SHIFT;
+ pages = kmalloc(pg_cnt * sizeof(pages[0]), GFP_KERNEL);
+ if (!pages)
+ return NULL;
+ for (i = 0; i < pg_cnt; i++) {
+ unsigned pfn;
+
+ pfn = LinuxMemAreaToCpuPFN(mem_area, start_ofs);
+ if (!pfn_valid(pfn))
+ goto err;
+ pages[i] = pfn_to_page(pfn);
+ start_ofs += PAGE_SIZE;
+ }
+ map = vmap(pages, pg_cnt, VM_MAP, PAGE_KERNEL);
+ map += offset;
+err:
+ kfree(pages);
+
+ return map;
+}
+
+static void vunmap_buf(struct PVRSRV_PER_PROCESS_DATA *proc,
+ u32 handle, void *map)
+{
+ struct PVRSRV_KERNEL_MEM_INFO *minfo;
+ enum PVRSRV_ERROR err;
+
+ err = PVRSRVLookupHandle(proc->psHandleBase, (void **)&minfo,
+ (void *)handle, PVRSRV_HANDLE_TYPE_MEM_INFO);
+ if (err != PVRSRV_OK)
+ return;
+ if (minfo->pvLinAddrKM)
+ return;
+ vunmap((void *)(((unsigned long)map) & PAGE_MASK));
+}
+
+static void dump_buf(void *start, size_t size, u32 type)
+{
+ char *corr = "";
+
+ if (type & BUF_DESC_CORRUPT) {
+ type &= ~BUF_DESC_CORRUPT;
+ corr = "(corrupt)";
+ }
+ hwrec_status_print("<type %d%s size %d>\n", type, corr, size);
+ hwrec_status_write(start, size);
+}
+
+static struct render_state_buf_list *get_state_buf_list(
+ struct PVRSRV_PER_PROCESS_DATA *proc,
+ u32 handle, off_t offset)
+{
+ struct PVRSRV_KERNEL_MEM_INFO *container;
+ struct render_state_buf_list *buf;
+ enum PVRSRV_ERROR err;
+
+ err = PVRSRVLookupHandle(proc->psHandleBase, (void **)&container,
+ (void *)handle, PVRSRV_HANDLE_TYPE_MEM_INFO);
+ if (err != PVRSRV_OK)
+ return NULL;
+ if (!container->pvLinAddrKM)
+ return NULL;
+ if (offset + sizeof(*buf) > container->ui32AllocSize)
+ return NULL;
+
+ buf = container->pvLinAddrKM + offset;
+
+ if (buf->cnt > ARRAY_SIZE(buf->info))
+ return NULL;
+
+ return buf;
+}
+
+static void dump_state_buf_list(struct PVRSRV_PER_PROCESS_DATA *proc,
+ struct render_state_buf_list *bl)
+{
+ int i;
+
+ if (!bl->cnt)
+ return;
+
+ pr_info("Dumping %d render state buffers\n", bl->cnt);
+ for (i = 0; i < bl->cnt; i++) {
+ struct render_state_buf_info *binfo;
+ void *map;
+
+ binfo = &bl->info[i];
+
+ map = vmap_buf(proc, binfo->buf_id, binfo->offset, binfo->size);
+ if (!map)
+ continue;
+ dump_buf(map, binfo->size, binfo->type);
+
+ vunmap_buf(proc, binfo->buf_id, map);
+ }
+}
+
+static void dump_sgx_state_bufs(struct PVRSRV_PER_PROCESS_DATA *proc,
+ struct PVRSRV_SGXDEV_INFO *dev_info)
+{
+ struct SGXMKIF_HOST_CTL __iomem *hctl = dev_info->psSGXHostCtl;
+ struct render_state_buf_list *bl_set[2] = { NULL };
+ struct render_state_buf_list *mbl;
+ u32 handle_ta;
+ u32 handle_3d;
+
+ if (!proc)
+ return;
+
+ handle_ta = readl(&hctl->render_state_buf_ta_handle);
+ handle_3d = readl(&hctl->render_state_buf_3d_handle);
+ bl_set[0] = get_state_buf_list(proc, handle_ta,
+ dev_info->state_buf_ofs);
+ /*
+ * The two buf list can be the same if the TA and 3D phases used the
+ * same context at the time of the HWrec. In this case just ignore
+ * one of them.
+ */
+ if (handle_ta != handle_3d)
+ bl_set[1] = get_state_buf_list(proc, handle_3d,
+ dev_info->state_buf_ofs);
+ mbl = create_merged_uniq_list(bl_set, ARRAY_SIZE(bl_set));
+ if (!mbl)
+ return;
+
+ dump_state_buf_list(proc, mbl);
+ kfree(mbl);
+}
+
void
-pvr_hwrec_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo)
+pvr_hwrec_dump(struct PVRSRV_PER_PROCESS_DATA *proc_data,
+ struct PVRSRV_SGXDEV_INFO *psDevInfo)
{
mutex_lock(hwrec_mutex);
hwrec_edm_buf = pvr_edm_buffer_create(psDevInfo);
#endif
+ hwrec_status_free();
+ dump_sgx_state_bufs(proc_data, psDevInfo);
+
hwrec_event = 1;
mutex_unlock(hwrec_mutex);
};
#endif /* PVRSRV_USSE_EDM_STATUS_DEBUG */
+/*
+ * Provides a dump of the TA and 3D status buffers.
+ */
+static loff_t
+hwrec_status_llseek(struct file *filp, loff_t offset, int whence)
+{
+ loff_t f_pos;
+
+ mutex_lock(hwrec_mutex);
+
+ if (hwrec_status_size)
+ f_pos = hwrec_llseek_helper(filp, offset, whence,
+ hwrec_status_size);
+ else
+ f_pos = 0;
+
+ mutex_unlock(hwrec_mutex);
+
+ return f_pos;
+}
+
+static ssize_t
+hwrec_status_read(struct file *filp, char __user *buf, size_t size,
+ loff_t *f_pos)
+{
+ mutex_lock(hwrec_mutex);
+
+ if ((*f_pos >= 0) && (*f_pos < hwrec_status_size)) {
+ int page, offset;
+
+ size = min(size, (size_t) hwrec_status_size - (size_t) *f_pos);
+
+ page = (*f_pos) / PAGE_SIZE;
+ offset = (*f_pos) & ~PAGE_MASK;
+
+ size = min(size, (size_t) PAGE_SIZE - offset);
+
+ if (copy_to_user(buf,
+ ((u8 *) hwrec_status_pages[page]) + offset,
+ size)) {
+ mutex_unlock(hwrec_mutex);
+ return -EFAULT;
+ }
+ } else
+ size = 0;
+
+ mutex_unlock(hwrec_mutex);
+
+ *f_pos += size;
+ return size;
+}
+
+static const struct file_operations hwrec_status_fops = {
+ .owner = THIS_MODULE,
+ .llseek = hwrec_status_llseek,
+ .read = hwrec_status_read,
+ .open = hwrec_file_open,
+ .release = hwrec_file_release,
+};
+
/*
*
*/
}
#endif
+ if (!debugfs_create_file("hwrec_status", S_IRUSR, debugfs_dir, NULL,
+ &hwrec_status_fops)) {
+ debugfs_remove_recursive(debugfs_dir);
+ return -ENODEV;
+ }
+
return 0;
}
if (hwrec_edm_buf)
pvr_edm_buffer_destroy(hwrec_edm_buf);
#endif
+
+ hwrec_status_free();
}
int pvr_debugfs_init(void);
void pvr_debugfs_cleanup(void);
-void pvr_hwrec_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo);
+void pvr_hwrec_dump(struct PVRSRV_PER_PROCESS_DATA *proc_data,
+ struct PVRSRV_SGXDEV_INFO *psDevInfo);
#ifdef CONFIG_PVR_DEBUG
/* to be used for memory dumping from mmu.c */
SGXOSTimerInit(struct PVRSRV_DEVICE_NODE *psDeviceNode);
void SGXOSTimerDeInit(struct timer_work_data *data);
-void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode);
+void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode,
+ const char *caller);
void SGXReset(struct PVRSRV_SGXDEV_INFO *psDevInfo, u32 ui32PDUMPFlags);
enum PVRSRV_ERROR SGXInitialise(struct PVRSRV_SGXDEV_INFO *psDevInfo,
return proc_data;
}
-static void dump_process_info(struct PVRSRV_DEVICE_NODE *dev)
+static void pr_err_process_info(struct PVRSRV_PER_PROCESS_DATA *proc)
{
- struct PVRSRV_PER_PROCESS_DATA *proc;
-
- proc = find_cur_proc_data(dev);
+ struct task_struct *tsk;
+ int pid;
- if (proc) {
- struct task_struct *tsk;
- int pid;
+ if (!proc)
+ return;
- pid = proc->ui32PID;
- rcu_read_lock();
- tsk = pid_task(find_vpid(pid), PIDTYPE_PID);
- pr_err("PID = %d, process name = %s\n", pid, tsk->comm);
- rcu_read_unlock();
- }
+ pid = proc->ui32PID;
+ rcu_read_lock();
+ tsk = pid_task(find_vpid(pid), PIDTYPE_PID);
+ pr_err("PID = %d, process name = %s\n", pid, tsk->comm);
+ rcu_read_unlock();
}
-static void dump_sgx_registers(struct PVRSRV_SGXDEV_INFO *psDevInfo)
+static void pr_err_sgx_registers(struct PVRSRV_SGXDEV_INFO *psDevInfo)
{
pr_err("EVENT_STATUS = 0x%08X\n"
"EVENT_STATUS2 = 0x%08X\n"
readl(psDevInfo->pvRegsBaseKM + EUR_CR_CLKGATECTL));
}
-#define BUF_DESC_CORRUPT (1 << 31)
-
-static void add_uniq_items(struct render_state_buf_list *dst,
- const struct render_state_buf_list *src)
-{
- int i;
-
- for (i = 0; i < src->cnt; i++) {
- const struct render_state_buf_info *sbinf = &src->info[i];
- int j;
-
- for (j = 0; j < dst->cnt; j++) {
- if (sbinf->buf_id == dst->info[j].buf_id) {
- if (memcmp(sbinf, &dst->info[j], sizeof(sbinf)))
- dst->info[j].type |= BUF_DESC_CORRUPT;
- break;
- }
- }
- if (j == dst->cnt) {
- /* Bound for cnt is guaranteed by the caller */
- dst->info[dst->cnt] = *sbinf;
- dst->cnt++;
- }
- }
-}
-
-static struct render_state_buf_list *create_merged_uniq_list(
- struct render_state_buf_list **bl_set, int set_size)
-{
- int i;
- struct render_state_buf_list *dbl;
- size_t size;
-
- /*
- * Create a buf list big enough to contain all elements from each
- * list in bl_set.
- */
- size = offsetof(struct render_state_buf_list, info[0]);
- for (i = 0; i < set_size; i++) {
- if (!bl_set[i])
- continue;
- size += bl_set[i]->cnt * sizeof(bl_set[i]->info[0]);
- }
- if (!size)
- return NULL;
- dbl = kmalloc(size, GFP_KERNEL);
- if (!dbl)
- return NULL;
-
- dbl->cnt = 0;
- for (i = 0; i < set_size; i++) {
- if (bl_set[i])
- add_uniq_items(dbl, bl_set[i]);
- }
-
- return dbl;
-}
-
-static void *vmap_buf(struct PVRSRV_PER_PROCESS_DATA *proc,
- u32 handle, off_t offset, size_t size)
-{
- struct PVRSRV_KERNEL_MEM_INFO *minfo;
- struct LinuxMemArea *mem_area;
- enum PVRSRV_ERROR err;
- unsigned start_ofs;
- unsigned end_ofs;
- int pg_cnt;
- struct page **pages;
- void *map;
- int i;
-
- if (offset & PAGE_MASK)
- return NULL;
-
- err = PVRSRVLookupHandle(proc->psHandleBase, (void **)&minfo,
- (void *)handle, PVRSRV_HANDLE_TYPE_MEM_INFO);
- if (err != PVRSRV_OK)
- return NULL;
- if (minfo->pvLinAddrKM)
- return minfo->pvLinAddrKM;
-
- err = PVRSRVLookupOSMemHandle(proc->psHandleBase, (void *)&mem_area,
- (void *)handle);
- if (err != PVRSRV_OK)
- return NULL;
-
- start_ofs = offset & PAGE_MASK;
- end_ofs = PAGE_ALIGN(offset + size);
- pg_cnt = (end_ofs - start_ofs) >> PAGE_SHIFT;
- pages = kmalloc(pg_cnt * sizeof(pages[0]), GFP_KERNEL);
- if (!pages)
- return NULL;
- for (i = 0; i < pg_cnt; i++) {
- unsigned pfn;
-
- pfn = LinuxMemAreaToCpuPFN(mem_area, start_ofs);
- if (!pfn_valid(pfn))
- goto err;
- pages[i] = pfn_to_page(pfn);
- start_ofs += PAGE_SIZE;
- }
- map = vmap(pages, pg_cnt, VM_MAP, PAGE_KERNEL);
- map += offset;
-err:
- kfree(pages);
-
- return map;
-}
-
-static void vunmap_buf(struct PVRSRV_PER_PROCESS_DATA *proc,
- u32 handle, void *map)
-{
- struct PVRSRV_KERNEL_MEM_INFO *minfo;
- enum PVRSRV_ERROR err;
-
- err = PVRSRVLookupHandle(proc->psHandleBase, (void **)&minfo,
- (void *)handle, PVRSRV_HANDLE_TYPE_MEM_INFO);
- if (err != PVRSRV_OK)
- return;
- if (minfo->pvLinAddrKM)
- return;
- vunmap((void *)(((unsigned long)map) & PAGE_MASK));
-}
-
-static void dump_buf(void *start, size_t size, u32 type)
-{
- unsigned addr = 0;
- char *corr = "";
-
- if (type & BUF_DESC_CORRUPT) {
- type &= ~BUF_DESC_CORRUPT;
- corr = "(corrupt)";
- }
- printk(KERN_DEBUG "type %d%s size %d\n", type, corr, size);
- while (addr < size) {
- if (!(addr % 16)) {
- if (addr)
- printk("\n");
- printk(KERN_DEBUG "%08x ", addr);
- }
- printk("%08x ", *(u32 *)(start + addr));
- addr += 4;
- }
- if (addr)
- printk("\n");
-}
-
-static struct render_state_buf_list *get_state_buf_list(
- struct PVRSRV_PER_PROCESS_DATA *proc,
- u32 handle, off_t offset)
-{
- struct PVRSRV_KERNEL_MEM_INFO *container;
- struct render_state_buf_list *buf;
- enum PVRSRV_ERROR err;
-
- err = PVRSRVLookupHandle(proc->psHandleBase, (void **)&container,
- (void *)handle, PVRSRV_HANDLE_TYPE_MEM_INFO);
- if (err != PVRSRV_OK)
- return NULL;
- if (!container->pvLinAddrKM)
- return NULL;
- if (offset + sizeof(*buf) > container->ui32AllocSize)
- return NULL;
-
- buf = container->pvLinAddrKM + offset;
-
- if (buf->cnt > ARRAY_SIZE(buf->info))
- return NULL;
-
- return buf;
-}
-
-static void dump_state_buf_list(struct PVRSRV_PER_PROCESS_DATA *proc,
- struct render_state_buf_list *bl)
-{
- int i;
-
- if (!bl->cnt)
- return;
-
- printk(KERN_DEBUG "Dumping %d render state buffers\n", bl->cnt);
- for (i = 0; i < bl->cnt; i++) {
- struct render_state_buf_info *binfo;
- void *map;
-
- binfo = &bl->info[i];
-
- map = vmap_buf(proc, binfo->buf_id, binfo->offset, binfo->size);
- if (!map)
- continue;
- dump_buf(map, binfo->size, binfo->type);
-
- vunmap_buf(proc, binfo->buf_id, map);
- }
-}
-
-static void dump_sgx_state_bufs(struct PVRSRV_DEVICE_NODE *dev_node)
-{
- struct PVRSRV_SGXDEV_INFO *dev_info = dev_node->pvDevice;
- struct SGXMKIF_HOST_CTL __iomem *hctl = dev_info->psSGXHostCtl;
- struct PVRSRV_PER_PROCESS_DATA *proc;
- struct render_state_buf_list *bl_set[2] = { NULL };
- struct render_state_buf_list *mbl;
- u32 handle_ta;
- u32 handle_3d;
-
- proc = find_cur_proc_data(dev_node);
- if (!proc)
- return;
-
- handle_ta = readl(&hctl->render_state_buf_ta_handle);
- handle_3d = readl(&hctl->render_state_buf_3d_handle);
- bl_set[0] = get_state_buf_list(proc, handle_ta,
- dev_info->state_buf_ofs);
- /*
- * The two buf list can be the same if the TA and 3D phases used the
- * same context at the time of the HWrec. In this case just ignore
- * one of them.
- */
- if (handle_ta != handle_3d)
- bl_set[1] = get_state_buf_list(proc, handle_3d,
- dev_info->state_buf_ofs);
- mbl = create_merged_uniq_list(bl_set, ARRAY_SIZE(bl_set));
- if (!mbl)
- return;
-
- dump_state_buf_list(proc, mbl);
- kfree(mbl);
-}
-
/* Should be called with pvr_lock held */
-void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode)
+void
+HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode, const char *caller)
{
enum PVRSRV_ERROR eError;
struct PVRSRV_SGXDEV_INFO *psDevInfo =
(struct PVRSRV_SGXDEV_INFO *)psDeviceNode->pvDevice;
struct SGXMKIF_HOST_CTL __iomem *psSGXHostCtl =
psDevInfo->psSGXHostCtl;
+ struct PVRSRV_PER_PROCESS_DATA *proc;
u32 l;
int max_retries = 10;
l |= PVRSRV_USSE_EDM_INTERRUPT_HWR;
writel(l, &psSGXHostCtl->ui32InterruptClearFlags);
- pr_err("%s: SGX Hardware Recovery triggered\n", __func__);
+ pr_err("SGX Hardware Recovery triggered (from %s)\n", caller);
+
+ proc = find_cur_proc_data(psDeviceNode);
- dump_process_info(psDeviceNode);
- dump_sgx_registers(psDevInfo);
- dump_sgx_state_bufs(psDeviceNode);
+ pr_err_process_info(proc);
+ pr_err_sgx_registers(psDevInfo);
#ifdef CONFIG_DEBUG_FS
- pvr_hwrec_dump(psDevInfo);
+ pvr_hwrec_dump(proc, psDevInfo);
#endif
PDUMPSUSPEND();
l++;
writel(l, &psSGXHostCtl->ui32HostDetectedLockups);
- HWRecoveryResetSGX(psDeviceNode);
+ HWRecoveryResetSGX(psDeviceNode, __func__);
pvr_dev_unlock();
}
if ((l1 & PVRSRV_USSE_EDM_INTERRUPT_HWR) &&
!(l2 & PVRSRV_USSE_EDM_INTERRUPT_HWR))
- HWRecoveryResetSGX(psDeviceNode);
+ HWRecoveryResetSGX(psDeviceNode, __func__);
if (psDeviceNode->bReProcessDeviceCommandComplete)
SGXScheduleProcessQueues(psDeviceNode);