#include <linux/io.h>
#include <linux/slab.h>
#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
#include "sgxdefs.h"
#include "sgxmmu.h"
#include "pvr_bridge_km.h"
#include "sgx_bridge_km.h"
#include "resman.h"
+#include "bridged_support.h"
#include "pdump_km.h"
#include "ra.h"
#include "mmu.h"
+#include "mm.h"
#include "handle.h"
#include "perproc.h"
OSMemCopy(&psDevInfo->asSGXDevData, &psInitInfo->asInitDevData,
sizeof(psDevInfo->asSGXDevData));
+ psDevInfo->state_buf_ofs = psInitInfo->state_buf_ofs;
+
return PVRSRV_OK;
failed_allockernelccb:
if (dst) \
p += snprintf(dst + p, dst_len - p, fmt, ## __VA_ARGS__); \
else \
- pr_err(fmt, ## __VA_ARGS__); \
+ printk(KERN_DEBUG fmt, ## __VA_ARGS__); \
} while (0)
if (!sdev->psKernelEDMStatusBufferMemInfo)
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)
{
dump_process_info(psDeviceNode);
dump_sgx_registers(psDevInfo);
dump_edm(psDevInfo);
+ dump_sgx_state_bufs(psDeviceNode);
PDUMPSUSPEND();