aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>2007-10-16 04:29:48 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-16 12:43:21 -0400
commit2ce32e15a16312d2c29c8bb188bf95bc821fdab6 (patch)
tree8dc477b46bf8bf7d0a93253a67160fc6ef9799b6
parent15e4d001ef5b14f56fa51665952cbffc0001762f (diff)
ps3fb: fix possible overlap of GPU command buffer and frame buffer
ps3fb: In the case of non-fullscreen video modes, there was a partial overlap of the GPU command buffer and the frame buffer. Fix and cleanup various issues with overlap and alignment: - Move the GPU command buffer from the beginning to the end of video memory - Exclude the GPU command buffer from the actual frame buffer memory - Align the start of the virtual frame buffer to PAGE_SIZE instead of to 64 KiB, and don't waste memory if it's already aligned (for fullscreen modes) - Take into account the alignment when checking memory requirements and maximum number of frames - Make sure fb_fix_screeninfo.smem_start always points to the virtual frame buffer start, so we don't have to compensate for that in ps3fb_mmap() Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> Cc: "Antonino A. Daplas" <adaplas@pol.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/video/ps3fb.c66
1 files changed, 39 insertions, 27 deletions
diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c
index 4885978dd5be..8bbe479f73f7 100644
--- a/drivers/video/ps3fb.c
+++ b/drivers/video/ps3fb.c
@@ -52,7 +52,7 @@
52#define L1GPU_DISPLAY_SYNC_VSYNC 2 52#define L1GPU_DISPLAY_SYNC_VSYNC 2
53 53
54#define DDR_SIZE (0) /* used no ddr */ 54#define DDR_SIZE (0) /* used no ddr */
55#define GPU_OFFSET (64 * 1024) 55#define GPU_CMD_BUF_SIZE (64 * 1024)
56#define GPU_IOIF (0x0d000000UL) 56#define GPU_IOIF (0x0d000000UL)
57 57
58#define PS3FB_FULL_MODE_BIT 0x80 58#define PS3FB_FULL_MODE_BIT 0x80
@@ -117,6 +117,7 @@ struct ps3fb_priv {
117 117
118 u64 context_handle, memory_handle; 118 u64 context_handle, memory_handle;
119 void *xdr_ea; 119 void *xdr_ea;
120 size_t xdr_size;
120 struct gpu_driver_info *dinfo; 121 struct gpu_driver_info *dinfo;
121 u32 res_index; 122 u32 res_index;
122 123
@@ -280,9 +281,20 @@ static const struct fb_videomode ps3fb_modedb[] = {
280#define Y_OFF(i) (ps3fb_res[i].yoff) /* top/bottom margin (pixel) */ 281#define Y_OFF(i) (ps3fb_res[i].yoff) /* top/bottom margin (pixel) */
281#define WIDTH(i) (ps3fb_res[i].xres) /* width of FB */ 282#define WIDTH(i) (ps3fb_res[i].xres) /* width of FB */
282#define HEIGHT(i) (ps3fb_res[i].yres) /* height of FB */ 283#define HEIGHT(i) (ps3fb_res[i].yres) /* height of FB */
283#define BPP 4 /* number of bytes per pixel */ 284#define BPP 4 /* number of bytes per pixel */
284#define VP_OFF(i) (WIDTH(i) * Y_OFF(i) * BPP + X_OFF(i) * BPP) 285
285#define FB_OFF(i) (GPU_OFFSET - VP_OFF(i) % GPU_OFFSET) 286/* Start of the virtual frame buffer (relative to fullscreen ) */
287#define VP_OFF(i) ((WIDTH(i) * Y_OFF(i) + X_OFF(i)) * BPP)
288
289/*
290 * Start of the virtual frame buffer (relative to start of video memory)
291 * This is PAGE_SIZE aligned for easier mmap()
292 */
293#define VFB_OFF(i) PAGE_ALIGN(VP_OFF(i))
294
295/* Start of the fullscreen frame buffer (relative to start of video memory) */
296#define FB_OFF(i) (-VP_OFF(i) & ~PAGE_MASK)
297
286 298
287static int ps3fb_mode; 299static int ps3fb_mode;
288module_param(ps3fb_mode, int, 0); 300module_param(ps3fb_mode, int, 0);
@@ -517,7 +529,8 @@ static int ps3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
517 529
518 /* Memory limit */ 530 /* Memory limit */
519 i = ps3fb_get_res_table(var->xres, var->yres, mode); 531 i = ps3fb_get_res_table(var->xres, var->yres, mode);
520 if (ps3fb_res[i].xres*ps3fb_res[i].yres*BPP > ps3fb_videomemory.size) { 532 if (ps3fb_res[i].xres*ps3fb_res[i].yres*BPP >
533 ps3fb.xdr_size - VFB_OFF(i)) {
521 dev_dbg(info->device, "Not enough memory\n"); 534 dev_dbg(info->device, "Not enough memory\n");
522 return -ENOMEM; 535 return -ENOMEM;
523 } 536 }
@@ -549,12 +562,13 @@ static int ps3fb_set_par(struct fb_info *info)
549 i = ps3fb_get_res_table(info->var.xres, info->var.yres, mode); 562 i = ps3fb_get_res_table(info->var.xres, info->var.yres, mode);
550 ps3fb.res_index = i; 563 ps3fb.res_index = i;
551 564
552 offset = FB_OFF(i) + VP_OFF(i); 565 offset = VFB_OFF(i);
553 info->fix.smem_len = ps3fb_videomemory.size - offset; 566 info->fix.smem_start = virt_to_abs(ps3fb.xdr_ea) + offset;
567 info->fix.smem_len = ps3fb.xdr_size - offset;
554 info->screen_base = (char __iomem *)ps3fb.xdr_ea + offset; 568 info->screen_base = (char __iomem *)ps3fb.xdr_ea + offset;
555 memset(ps3fb.xdr_ea, 0, ps3fb_videomemory.size); 569 memset(ps3fb.xdr_ea, 0, ps3fb.xdr_size);
556 570
557 ps3fb.num_frames = ps3fb_videomemory.size/ 571 ps3fb.num_frames = info->fix.smem_len/
558 (ps3fb_res[i].xres*ps3fb_res[i].yres*BPP); 572 (ps3fb_res[i].xres*ps3fb_res[i].yres*BPP);
559 573
560 /* Keep the special bits we cannot set using fb_var_screeninfo */ 574 /* Keep the special bits we cannot set using fb_var_screeninfo */
@@ -596,18 +610,13 @@ static int ps3fb_setcolreg(unsigned int regno, unsigned int red,
596static int ps3fb_mmap(struct fb_info *info, struct vm_area_struct *vma) 610static int ps3fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
597{ 611{
598 unsigned long size, offset; 612 unsigned long size, offset;
599 int i;
600
601 i = ps3fb_get_res_table(info->var.xres, info->var.yres, ps3fb_mode);
602 if (i == -1)
603 return -EINVAL;
604 613
605 size = vma->vm_end - vma->vm_start; 614 size = vma->vm_end - vma->vm_start;
606 offset = vma->vm_pgoff << PAGE_SHIFT; 615 offset = vma->vm_pgoff << PAGE_SHIFT;
607 if (offset + size > info->fix.smem_len) 616 if (offset + size > info->fix.smem_len)
608 return -EINVAL; 617 return -EINVAL;
609 618
610 offset += info->fix.smem_start + FB_OFF(i) + VP_OFF(i); 619 offset += info->fix.smem_start;
611 if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, 620 if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT,
612 size, vma->vm_page_prot)) 621 size, vma->vm_page_prot))
613 return -EAGAIN; 622 return -EAGAIN;
@@ -899,8 +908,9 @@ static int ps3fb_xdr_settings(u64 xdr_lpar, struct device *dev)
899 908
900 status = lv1_gpu_context_attribute(ps3fb.context_handle, 909 status = lv1_gpu_context_attribute(ps3fb.context_handle,
901 L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP, 910 L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP,
902 xdr_lpar, ps3fb_videomemory.size, 911 xdr_lpar + ps3fb.xdr_size,
903 GPU_IOIF, 0); 912 GPU_CMD_BUF_SIZE,
913 GPU_IOIF + ps3fb.xdr_size, 0);
904 if (status) { 914 if (status) {
905 dev_err(dev, 915 dev_err(dev,
906 "%s: lv1_gpu_context_attribute FB_SETUP failed: %d\n", 916 "%s: lv1_gpu_context_attribute FB_SETUP failed: %d\n",
@@ -1038,29 +1048,31 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev)
1038 if (retval) 1048 if (retval)
1039 goto err_iounmap_dinfo; 1049 goto err_iounmap_dinfo;
1040 1050
1041 /* xdr frame buffer */ 1051 /* XDR frame buffer */
1042 ps3fb.xdr_ea = ps3fb_videomemory.address; 1052 ps3fb.xdr_ea = ps3fb_videomemory.address;
1043 xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb.xdr_ea)); 1053 xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb.xdr_ea));
1054
1055 /* Clear memory to prevent kernel info leakage into userspace */
1056 memset(ps3fb.xdr_ea, 0, ps3fb_videomemory.size);
1057
1058 /* The GPU command buffer is at the end of video memory */
1059 ps3fb.xdr_size = ps3fb_videomemory.size - GPU_CMD_BUF_SIZE;
1060
1044 retval = ps3fb_xdr_settings(xdr_lpar, &dev->core); 1061 retval = ps3fb_xdr_settings(xdr_lpar, &dev->core);
1045 if (retval) 1062 if (retval)
1046 goto err_free_irq; 1063 goto err_free_irq;
1047 1064
1048 /*
1049 * ps3fb must clear memory to prevent kernel info
1050 * leakage into userspace
1051 */
1052 memset(ps3fb.xdr_ea, 0, ps3fb_videomemory.size);
1053 info = framebuffer_alloc(sizeof(u32) * 16, &dev->core); 1065 info = framebuffer_alloc(sizeof(u32) * 16, &dev->core);
1054 if (!info) 1066 if (!info)
1055 goto err_free_irq; 1067 goto err_free_irq;
1056 1068
1057 offset = FB_OFF(ps3fb.res_index) + VP_OFF(ps3fb.res_index); 1069 offset = VFB_OFF(ps3fb.res_index);
1058 info->screen_base = (char __iomem *)ps3fb.xdr_ea + offset; 1070 info->screen_base = (char __iomem *)ps3fb.xdr_ea + offset;
1059 info->fbops = &ps3fb_ops; 1071 info->fbops = &ps3fb_ops;
1060 1072
1061 info->fix = ps3fb_fix; 1073 info->fix = ps3fb_fix;
1062 info->fix.smem_start = virt_to_abs(ps3fb.xdr_ea); 1074 info->fix.smem_start = virt_to_abs(ps3fb.xdr_ea) + offset;
1063 info->fix.smem_len = ps3fb_videomemory.size - offset; 1075 info->fix.smem_len = ps3fb.xdr_size - offset;
1064 info->pseudo_palette = info->par; 1076 info->pseudo_palette = info->par;
1065 info->par = NULL; 1077 info->par = NULL;
1066 info->flags = FBINFO_DEFAULT | FBINFO_READS_FAST; 1078 info->flags = FBINFO_DEFAULT | FBINFO_READS_FAST;
@@ -1086,7 +1098,7 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev)
1086 1098
1087 dev_info(info->device, "%s %s, using %lu KiB of video memory\n", 1099 dev_info(info->device, "%s %s, using %lu KiB of video memory\n",
1088 dev_driver_string(info->dev), info->dev->bus_id, 1100 dev_driver_string(info->dev), info->dev->bus_id,
1089 ps3fb_videomemory.size >> 10); 1101 ps3fb.xdr_size >> 10);
1090 1102
1091 task = kthread_run(ps3fbd, info, DEVICE_NAME); 1103 task = kthread_run(ps3fbd, info, DEVICE_NAME);
1092 if (IS_ERR(task)) { 1104 if (IS_ERR(task)) {