video:uvesafb: Fix oops that uvesafb try to execute NX-protected page
[linux-flexiantxendom0.git] / drivers / video / uvesafb.c
index 74ae758..bbfa6c9 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/fb.h>
 #include <linux/io.h>
 #include <linux/mutex.h>
+#include <linux/slab.h>
 #include <video/edid.h>
 #include <video/uvesafb.h>
 #ifdef CONFIG_X86
@@ -45,7 +46,7 @@ static struct fb_fix_screeninfo uvesafb_fix __devinitdata = {
 static int mtrr                __devinitdata = 3; /* enable mtrr by default */
 static int blank       = 1;               /* enable blanking by default */
 static int ypan                = 1;             /* 0: scroll, 1: ypan, 2: ywrap */
-static int pmi_setpal  __devinitdata = 1; /* use PMI for palette changes */
+static bool pmi_setpal __devinitdata = true; /* use PMI for palette changes */
 static int nocrtc      __devinitdata; /* ignore CRTC settings */
 static int noedid      __devinitdata; /* don't try DDC transfers */
 static int vram_remap  __devinitdata; /* set amt. of memory to be used */
@@ -55,6 +56,7 @@ static u16 maxvf      __devinitdata; /* maximum vertical frequency */
 static u16 maxhf       __devinitdata; /* maximum horizontal frequency */
 static u16 vbemode     __devinitdata; /* force use of a specific VBE mode */
 static char *mode_option __devinitdata;
+static u8  dac_width   = 6;
 
 static struct uvesafb_ktask *uvfb_tasks[UVESAFB_TASKS_MAX];
 static DEFINE_MUTEX(uvfb_lock);
@@ -66,12 +68,14 @@ static DEFINE_MUTEX(uvfb_lock);
  * find the kernel part of the task struct, copy the registers and
  * the buffer contents and then complete the task.
  */
-static void uvesafb_cn_callback(void *data)
+static void uvesafb_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
 {
-       struct cn_msg *msg = data;
        struct uvesafb_task *utask;
        struct uvesafb_ktask *task;
 
+       if (!cap_raised(current_cap(), CAP_SYS_ADMIN))
+               return;
+
        if (msg->seq >= UVESAFB_TASKS_MAX)
                return;
 
@@ -117,7 +121,7 @@ static int uvesafb_helper_start(void)
                NULL,
        };
 
-       return call_usermodehelper(v86d_path, argv, envp, 1);
+       return call_usermodehelper(v86d_path, argv, envp, UMH_WAIT_PROC);
 }
 
 /*
@@ -189,7 +193,7 @@ static int uvesafb_exec(struct uvesafb_ktask *task)
        uvfb_tasks[seq] = task;
        mutex_unlock(&uvfb_lock);
 
-       err = cn_netlink_send(m, 0, gfp_any());
+       err = cn_netlink_send(m, 0, GFP_KERNEL);
        if (err == -ESRCH) {
                /*
                 * Try to start the userspace helper if sending
@@ -303,22 +307,10 @@ static void uvesafb_setup_var(struct fb_var_screeninfo *var,
                var->blue.offset   = 0;
                var->transp.offset = 0;
 
-               /*
-                * We're assuming that we can switch the DAC to 8 bits. If
-                * this proves to be incorrect, we'll update the fields
-                * later in set_par().
-                */
-               if (par->vbe_ib.capabilities & VBE_CAP_CAN_SWITCH_DAC) {
-                       var->red.length    = 8;
-                       var->green.length  = 8;
-                       var->blue.length   = 8;
-                       var->transp.length = 0;
-               } else {
-                       var->red.length    = 6;
-                       var->green.length  = 6;
-                       var->blue.length   = 6;
-                       var->transp.length = 0;
-               }
+               var->red.length    = 8;
+               var->green.length  = 8;
+               var->blue.length   = 8;
+               var->transp.length = 0;
        }
 }
 
@@ -823,8 +815,15 @@ static int __devinit uvesafb_vbe_init(struct fb_info *info)
        par->pmi_setpal = pmi_setpal;
        par->ypan = ypan;
 
-       if (par->pmi_setpal || par->ypan)
-               uvesafb_vbe_getpmi(task, par);
+       if (par->pmi_setpal || par->ypan) {
+               if (__supported_pte_mask & _PAGE_NX) {
+                       par->pmi_setpal = par->ypan = 0;
+                       printk(KERN_WARNING "uvesafb: NX protection is actively."
+                               "We have better not to use the PMI.\n");
+               } else {
+                       uvesafb_vbe_getpmi(task, par);
+               }
+       }
 #else
        /* The protected mode interface is not available on non-x86. */
        par->pmi_setpal = par->ypan = 0;
@@ -850,14 +849,16 @@ static int __devinit uvesafb_vbe_init_mode(struct fb_info *info)
        if (vbemode) {
                for (i = 0; i < par->vbe_modes_cnt; i++) {
                        if (par->vbe_modes[i].mode_id == vbemode) {
+                               modeid = i;
+                               uvesafb_setup_var(&info->var, info,
+                                               &par->vbe_modes[modeid]);
                                fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60,
-                                                       &info->var, info);
+                                               &info->var, info);
                                /*
                                 * With pixclock set to 0, the default BIOS
                                 * timings will be used in set_par().
                                 */
                                info->var.pixclock = 0;
-                               modeid = i;
                                goto gotmode;
                        }
                }
@@ -904,8 +905,11 @@ static int __devinit uvesafb_vbe_init_mode(struct fb_info *info)
                        fb_videomode_to_var(&info->var, mode);
                } else {
                        modeid = par->vbe_modes[0].mode_id;
+                       uvesafb_setup_var(&info->var, info,
+                                       &par->vbe_modes[modeid]);
                        fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60,
-                                   &info->var, info);
+                                       &info->var, info);
+
                        goto gotmode;
                }
        }
@@ -917,9 +921,9 @@ static int __devinit uvesafb_vbe_init_mode(struct fb_info *info)
        if (modeid == -1)
                return -EINVAL;
 
-gotmode:
        uvesafb_setup_var(&info->var, info, &par->vbe_modes[modeid]);
 
+gotmode:
        /*
         * If we are not VBE3.0+ compliant, we're done -- the BIOS will
         * ignore our timings anyway.
@@ -1001,7 +1005,7 @@ static int uvesafb_setcolreg(unsigned regno, unsigned red, unsigned green,
                struct fb_info *info)
 {
        struct uvesafb_pal_entry entry;
-       int shift = 16 - info->var.green.length;
+       int shift = 16 - dac_width;
        int err = 0;
 
        if (regno >= info->cmap.len)
@@ -1050,7 +1054,7 @@ static int uvesafb_setcolreg(unsigned regno, unsigned red, unsigned green,
 static int uvesafb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
 {
        struct uvesafb_pal_entry *entries;
-       int shift = 16 - info->var.green.length;
+       int shift = 16 - dac_width;
        int i, err = 0;
 
        if (info->var.bits_per_pixel == 8) {
@@ -1312,13 +1316,9 @@ setmode:
                err = uvesafb_exec(task);
                if (err || (task->t.regs.eax & 0xffff) != 0x004f ||
                    ((task->t.regs.ebx & 0xff00) >> 8) != 8) {
-                       /*
-                        * We've failed to set the DAC palette format -
-                        * time to correct var.
-                        */
-                       info->var.red.length    = 6;
-                       info->var.green.length  = 6;
-                       info->var.blue.length   = 6;
+                       dac_width = 6;
+               } else {
+                       dac_width = 8;
                }
        }
 
@@ -1419,23 +1419,6 @@ static int uvesafb_check_var(struct fb_var_screeninfo *var,
        return 0;
 }
 
-static void uvesafb_save_state(struct fb_info *info)
-{
-       struct uvesafb_par *par = info->par;
-
-       if (par->vbe_state_saved)
-               kfree(par->vbe_state_saved);
-
-       par->vbe_state_saved = uvesafb_vbe_state_save(par);
-}
-
-static void uvesafb_restore_state(struct fb_info *info)
-{
-       struct uvesafb_par *par = info->par;
-
-       uvesafb_vbe_state_restore(par, par->vbe_state_saved);
-}
-
 static struct fb_ops uvesafb_ops = {
        .owner          = THIS_MODULE,
        .fb_open        = uvesafb_open,
@@ -1449,8 +1432,6 @@ static struct fb_ops uvesafb_ops = {
        .fb_imageblit   = cfb_imageblit,
        .fb_check_var   = uvesafb_check_var,
        .fb_set_par     = uvesafb_set_par,
-       .fb_save_state  = uvesafb_save_state,
-       .fb_restore_state = uvesafb_restore_state,
 };
 
 static void __devinit uvesafb_init_info(struct fb_info *info,
@@ -1467,15 +1448,6 @@ static void __devinit uvesafb_init_info(struct fb_info *info,
        info->fix.ypanstep = par->ypan ? 1 : 0;
        info->fix.ywrapstep = (par->ypan > 1) ? 1 : 0;
 
-       /*
-        * If we were unable to get the state buffer size, disable
-        * functions for saving and restoring the hardware state.
-        */
-       if (par->vbe_state_size == 0) {
-               info->fbops->fb_save_state = NULL;
-               info->fbops->fb_restore_state = NULL;
-       }
-
        /* Disable blanking if the user requested so. */
        if (!blank)
                info->fbops->fb_blank = NULL;
@@ -1552,7 +1524,7 @@ static void __devinit uvesafb_init_info(struct fb_info *info,
        }
 
        info->flags = FBINFO_FLAG_DEFAULT |
-                       (par->ypan) ? FBINFO_HWACCEL_YPAN : 0;
+                       (par->ypan ? FBINFO_HWACCEL_YPAN : 0);
 
        if (!par->ypan)
                info->fbops->fb_pan_display = NULL;
@@ -1587,8 +1559,7 @@ static void __devinit uvesafb_init_mtrr(struct fb_info *info)
                        int rc;
 
                        /* Find the largest power-of-two */
-                       while (temp_size & (temp_size - 1))
-                               temp_size &= (temp_size - 1);
+                       temp_size = roundup_pow_of_two(temp_size);
 
                        /* Try and find a power of two to add */
                        do {
@@ -1601,6 +1572,28 @@ static void __devinit uvesafb_init_mtrr(struct fb_info *info)
 #endif /* CONFIG_MTRR */
 }
 
+static void __devinit uvesafb_ioremap(struct fb_info *info)
+{
+#ifdef CONFIG_X86
+       switch (mtrr) {
+       case 1: /* uncachable */
+               info->screen_base = ioremap_nocache(info->fix.smem_start, info->fix.smem_len);
+               break;
+       case 2: /* write-back */
+               info->screen_base = ioremap_cache(info->fix.smem_start, info->fix.smem_len);
+               break;
+       case 3: /* write-combining */
+               info->screen_base = ioremap_wc(info->fix.smem_start, info->fix.smem_len);
+               break;
+       case 4: /* write-through */
+       default:
+               info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
+               break;
+       }
+#else
+       info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
+#endif /* CONFIG_X86 */
+}
 
 static ssize_t uvesafb_show_vbe_ver(struct device *dev,
                struct device_attribute *attr, char *buf)
@@ -1771,15 +1764,22 @@ static int __devinit uvesafb_probe(struct platform_device *dev)
 
        uvesafb_init_info(info, mode);
 
+       if (!request_region(0x3c0, 32, "uvesafb")) {
+               printk(KERN_ERR "uvesafb: request region 0x3c0-0x3e0 failed\n");
+               err = -EIO;
+               goto out_mode;
+       }
+
        if (!request_mem_region(info->fix.smem_start, info->fix.smem_len,
                                "uvesafb")) {
                printk(KERN_ERR "uvesafb: cannot reserve video memory at "
                                "0x%lx\n", info->fix.smem_start);
                err = -EIO;
-               goto out_mode;
+               goto out_reg;
        }
 
-       info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
+       uvesafb_init_mtrr(info);
+       uvesafb_ioremap(info);
 
        if (!info->screen_base) {
                printk(KERN_ERR
@@ -1790,20 +1790,13 @@ static int __devinit uvesafb_probe(struct platform_device *dev)
                goto out_mem;
        }
 
-       if (!request_region(0x3c0, 32, "uvesafb")) {
-               printk(KERN_ERR "uvesafb: request region 0x3c0-0x3e0 failed\n");
-               err = -EIO;
-               goto out_unmap;
-       }
-
-       uvesafb_init_mtrr(info);
        platform_set_drvdata(dev, info);
 
        if (register_framebuffer(info) < 0) {
                printk(KERN_ERR
                        "uvesafb: failed to register framebuffer device\n");
                err = -EINVAL;
-               goto out_reg;
+               goto out_unmap;
        }
 
        printk(KERN_INFO "uvesafb: framebuffer at 0x%lx, mapped to 0x%p, "
@@ -1820,12 +1813,12 @@ static int __devinit uvesafb_probe(struct platform_device *dev)
 
        return 0;
 
-out_reg:
-       release_region(0x3c0, 32);
 out_unmap:
        iounmap(info->screen_base);
 out_mem:
        release_mem_region(info->fix.smem_start, info->fix.smem_len);
+out_reg:
+       release_region(0x3c0, 32);
 out_mode:
        if (!list_empty(&info->modelist))
                fb_destroy_modelist(&info->modelist);
@@ -2012,12 +2005,7 @@ static void __devexit uvesafb_exit(void)
 
 module_exit(uvesafb_exit);
 
-static int param_get_scroll(char *buffer, struct kernel_param *kp)
-{
-       return 0;
-}
-
-static int param_set_scroll(const char *val, struct kernel_param *kp)
+static int param_set_scroll(const char *val, const struct kernel_param *kp)
 {
        ypan = 0;
 
@@ -2027,10 +2015,14 @@ static int param_set_scroll(const char *val, struct kernel_param *kp)
                ypan = 1;
        else if (!strcmp(val, "ywrap"))
                ypan = 2;
+       else
+               return -EINVAL;
 
        return 0;
 }
-
+static struct kernel_param_ops param_ops_scroll = {
+       .set = param_set_scroll,
+};
 #define param_check_scroll(name, p) __param_check(name, p, void)
 
 module_param_named(scroll, ypan, scroll, 0);