From: Luc Verhaegen Date: Tue, 22 Mar 2011 11:38:36 +0000 (+0100) Subject: gpu: pvr: debugfs: add hwrec context dump X-Git-Url: http://git.openpandora.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7b898a9c61c71065a2dfcc16d7059b9dbea9088c;p=sgx.git gpu: pvr: debugfs: add hwrec context dump 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 Signed-off-by: Imre Deak --- diff --git a/pvr/mmu.c b/pvr/mmu.c index 7b9f0af..2f54255 100644 --- a/pvr/mmu.c +++ b/pvr/mmu.c @@ -37,6 +37,11 @@ #include "sgxinfokm.h" #include "mmu.h" +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_PVR_DEBUG) +#include "pvr_debugfs.h" +#include +#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("\n", dev_p_addr); + hwrec_mem_write((void __force *) page, PAGE_SIZE); + hwrec_mem_print("\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("\n", dev_p_addr); + for (i = 0 ; i < 1024 ; i++) + hwrec_mem_print("0x%08X\n", readl(pt + 4 * i)); + hwrec_mem_print("
\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("\n", context->sPDDevPAddr); + + for (i = 0; i < 1024; i++) + hwrec_mem_print("0x%08X\n", readl(pd + 4 * i)); + + hwrec_mem_print("\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 */ diff --git a/pvr/mmu.h b/pvr/mmu.h index 003f0c5..229b49f 100644 --- 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_ */ diff --git a/pvr/pvr_debugfs.c b/pvr/pvr_debugfs.c index 54f899e..e5fb7a0 100644 --- a/pvr/pvr_debugfs.c +++ b/pvr/pvr_debugfs.c @@ -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 */ + } diff --git a/pvr/pvr_debugfs.h b/pvr/pvr_debugfs.h index 327ef0e..b9d7ebb 100644 --- a/pvr/pvr_debugfs.h +++ b/pvr/pvr_debugfs.h @@ -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_ */