gpu: pvr: debugfs: add hwrec context dump
authorLuc Verhaegen <libv@codethink.co.uk>
Tue, 22 Mar 2011 11:38:36 +0000 (12:38 +0100)
committerGrazvydas Ignotas <notasas@gmail.com>
Sun, 20 May 2012 18:43:03 +0000 (21:43 +0300)
A full context is now provided through hwrec_mem. This includes
page directory, page tables and all mapped pages.

This code is only built when debugfs is enabled, and when build type
is "Debug".

Signed-off-by: Luc Verhaegen <libv@codethink.co.uk>
Signed-off-by: Imre Deak <imre.deak@nokia.com>
pvr/mmu.c
pvr/mmu.h
pvr/pvr_debugfs.c
pvr/pvr_debugfs.h

index 7b9f0af..2f54255 100644 (file)
--- a/pvr/mmu.c
+++ b/pvr/mmu.c
 #include "sgxinfokm.h"
 #include "mmu.h"
 
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_PVR_DEBUG)
+#include "pvr_debugfs.h"
+#include <linux/io.h>
+#endif
+
 #define UINT32_MAX_VALUE       0xFFFFFFFFUL
 
 struct MMU_PT_INFO {
@@ -1437,3 +1442,103 @@ u32 mmu_get_page_dir(struct MMU_CONTEXT *psMMUContext)
 {
        return psMMUContext->sPDDevPAddr.uiAddr;
 }
+
+
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_PVR_DEBUG)
+
+static int
+hwrec_mem_dump_page(u32 dev_p_addr)
+{
+       void __iomem *page;
+
+       page = ioremap_nocache(dev_p_addr, SGX_MMU_PAGE_SIZE);
+       if (!page)
+               return -EFAULT;
+
+       /* Loop through all the pages and dump them */
+       hwrec_mem_print("<PAGE PA:0x%08X>\n", dev_p_addr);
+       hwrec_mem_write((void __force *) page, PAGE_SIZE);
+       hwrec_mem_print("</PAGE>\n");
+
+       iounmap(page);
+
+       return 0;
+}
+
+static int
+hwrec_mem_dump_table(u32 dev_p_addr)
+{
+       void __iomem *pt;
+       u32 i;
+
+       pt = ioremap_nocache(dev_p_addr, SGX_MMU_PAGE_SIZE);
+       if (!pt)
+               return -EFAULT;
+
+       /* Loop through all the page tables and dump them */
+       hwrec_mem_print("<TABLE PA:0x%08X>\n", dev_p_addr);
+       for (i = 0 ; i < 1024 ; i++)
+               hwrec_mem_print("0x%08X\n", readl(pt + 4 * i));
+       hwrec_mem_print("</TABLE>\n");
+
+       for (i = 0; i < 1024; i++) {
+               u32 addr = readl(pt + 4 * i);
+
+               if (addr & SGX_MMU_PDE_VALID)
+                       hwrec_mem_dump_page(addr & SGX_MMU_PDE_ADDR_MASK);
+       }
+
+       iounmap(pt);
+
+       return 0;
+}
+
+static int
+hwrec_mem_dump_dir(struct MMU_CONTEXT *context)
+{
+       void __iomem *pd = (void __force __iomem *) context->pvPDCpuVAddr;
+
+       int i;
+
+       hwrec_mem_print("<DIR PA:0x%08X>\n", context->sPDDevPAddr);
+
+       for (i = 0; i < 1024; i++)
+               hwrec_mem_print("0x%08X\n", readl(pd + 4 * i));
+
+       hwrec_mem_print("</DIR>\n");
+
+       for (i = 0; i < 1024; i++) {
+               u32 addr = readl(pd + 4 * i);
+
+               if (addr & SGX_MMU_PDE_VALID)
+                       hwrec_mem_dump_table(addr & SGX_MMU_PDE_ADDR_MASK);
+       }
+
+       return 0;
+}
+
+int
+mmu_hwrec_mem_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo)
+{
+       struct MMU_CONTEXT *context = psDevInfo->pvMMUContextList;
+       u32 page_dir;
+
+       page_dir = readl(psDevInfo->pvRegsBaseKM + EUR_CR_BIF_DIR_LIST_BASE0);
+
+       while (context) {
+               if (context->sPDDevPAddr.uiAddr == page_dir)
+                       break;
+
+               context = context->psNext;
+       }
+
+       if (!context) {
+               pr_err("Unable to find matching context for page directory"
+                      " 0x%08X\n", page_dir);
+               return -EFAULT;
+       }
+
+       return hwrec_mem_dump_dir(context);
+}
+
+#endif /* CONFIG_DEBUG_FS && CONFIG_PVR_DEBUG */
index 003f0c5..229b49f 100644 (file)
--- a/pvr/mmu.h
+++ b/pvr/mmu.h
@@ -83,4 +83,9 @@ enum PVRSRV_ERROR MMU_BIFResetPDAlloc(struct PVRSRV_SGXDEV_INFO *psDevInfo);
 void MMU_BIFResetPDFree(struct PVRSRV_SGXDEV_INFO *psDevInfo);
 
 u32 mmu_get_page_dir(struct MMU_CONTEXT *pMMUContext);
+
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_PVR_DEBUG)
+int mmu_hwrec_mem_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo);
 #endif
+
+#endif /* _MMU_H_ */
index 54f899e..e5fb7a0 100644 (file)
@@ -37,6 +37,7 @@
 #include "pvr_bridge_km.h"
 #include "sgxutils.h"
 #include "pvr_debugfs.h"
+#include "mmu.h"
 
 static struct dentry *debugfs_dir;
 static u32 pvr_reset;
@@ -184,6 +185,12 @@ static int hwrec_event_file_lock;
  */
 static u32 *hwrec_registers;
 
+#ifdef CONFIG_PVR_DEBUG
+static size_t hwrec_mem_size;
+#define HWREC_MEM_PAGES (4 * PAGE_SIZE)
+static unsigned long hwrec_mem_pages[HWREC_MEM_PAGES];
+#endif /* CONFIG_PVR_DEBUG */
+
 static void
 hwrec_registers_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo)
 {
@@ -201,6 +208,90 @@ hwrec_registers_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo)
                hwrec_registers[i] = readl(psDevInfo->pvRegsBaseKM + 4 * i);
 }
 
+static void
+hwrec_pages_free(size_t *size, u32 *pages)
+{
+       int i;
+
+       if (!(*size))
+               return;
+
+       for (i = 0; (i * PAGE_SIZE) < *size; i++) {
+               free_page(pages[i]);
+               pages[i] = 0;
+       }
+
+       *size = 0;
+}
+
+static int
+hwrec_pages_write(u8 *buffer, size_t size, size_t *current_size, u32 *pages,
+                 int array_size)
+{
+       size_t ret = 0;
+
+       while (size) {
+               size_t count = size;
+               size_t offset = *current_size & ~PAGE_MASK;
+               int page = *current_size / PAGE_SIZE;
+
+               if (!offset) {
+                       if (((*current_size) / PAGE_SIZE) >= array_size) {
+                               pr_err("%s: Size overrun!\n", __func__);
+                               return -ENOMEM;
+                       }
+
+                       pages[page] = __get_free_page(GFP_KERNEL);
+                       if (!pages[page]) {
+                               pr_err("%s: failed to get free page.\n",
+                                      __func__);
+                               return -ENOMEM;
+                       }
+               }
+
+               if (count > (PAGE_SIZE - offset))
+                       count = PAGE_SIZE - offset;
+
+               memcpy(((u8 *) pages[page]) + offset, buffer, count);
+
+               buffer += count;
+               size -= count;
+               ret += count;
+               *current_size += count;
+       }
+
+       return ret;
+}
+
+#ifdef CONFIG_PVR_DEBUG
+static void
+hwrec_mem_free(void)
+{
+       hwrec_pages_free(&hwrec_mem_size, hwrec_mem_pages);
+}
+
+int
+hwrec_mem_write(u8 *buffer, size_t size)
+{
+       return hwrec_pages_write(buffer, size, &hwrec_mem_size,
+                                hwrec_mem_pages, ARRAY_SIZE(hwrec_mem_pages));
+}
+
+int
+hwrec_mem_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_mem_write(tmp, size);
+}
+#endif /* CONFIG_PVR_DEBUG */
+
 void
 pvr_hwrec_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo)
 {
@@ -218,6 +309,11 @@ pvr_hwrec_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo)
 
        hwrec_registers_dump(psDevInfo);
 
+#ifdef CONFIG_PVR_DEBUG
+       hwrec_mem_free();
+       mmu_hwrec_mem_dump(psDevInfo);
+#endif /* CONFIG_PVR_DEBUG */
+
        hwrec_event = 1;
 
        mutex_unlock(hwrec_mutex);
@@ -446,6 +542,69 @@ static const struct file_operations hwrec_regs_fops = {
        .release = hwrec_file_release,
 };
 
+#ifdef CONFIG_PVR_DEBUG
+/*
+ * Provides a full context dump: page directory, page tables, and all mapped
+ * pages.
+ */
+static loff_t
+hwrec_mem_llseek(struct file *filp, loff_t offset, int whence)
+{
+       loff_t f_pos;
+
+       mutex_lock(hwrec_mutex);
+
+       if (hwrec_mem_size)
+               f_pos = hwrec_llseek_helper(filp, offset, whence,
+                                           hwrec_mem_size);
+       else
+               f_pos = 0;
+
+       mutex_unlock(hwrec_mutex);
+
+       return f_pos;
+}
+
+static ssize_t
+hwrec_mem_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_mem_size)) {
+               int page, offset;
+
+               size = min(size, (size_t) hwrec_mem_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_mem_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_mem_fops = {
+       .owner = THIS_MODULE,
+       .llseek = hwrec_mem_llseek,
+       .read = hwrec_mem_read,
+       .open = hwrec_file_open,
+       .release = hwrec_file_release,
+};
+#endif /* CONFIG_PVR_DEBUG */
+
 /*
  *
  */
@@ -487,6 +646,14 @@ int pvr_debugfs_init(void)
                return -ENODEV;
        }
 
+#ifdef CONFIG_PVR_DEBUG
+       if (!debugfs_create_file("hwrec_mem", S_IRUSR, debugfs_dir, NULL,
+                                &hwrec_mem_fops)) {
+               debugfs_remove_recursive(debugfs_dir);
+               return -ENODEV;
+       }
+#endif /* CONFIG_PVR_DEBUG */
+
        return 0;
 }
 
@@ -496,4 +663,9 @@ void pvr_debugfs_cleanup(void)
 
        if (hwrec_registers)
                free_page((u32) hwrec_registers);
+
+#ifdef CONFIG_PVR_DEBUG
+       hwrec_mem_free();
+#endif /* CONFIG_PVR_DEBUG */
+
 }
index 327ef0e..b9d7ebb 100644 (file)
@@ -29,4 +29,10 @@ void pvr_debugfs_cleanup(void);
 
 void pvr_hwrec_dump(struct PVRSRV_SGXDEV_INFO *psDevInfo);
 
+#ifdef CONFIG_PVR_DEBUG
+/* to be used for memory dumping from mmu.c */
+int hwrec_mem_write(u8 *buffer, size_t size);
+int hwrec_mem_print(char *format, ...);
+#endif
+
 #endif /* _PVR_DEBUGFS_H_ */