diff -Naurp linux-2.6.13-gentoo-r2/Documentation/fb/vesafb.txt linux-2.6.13-gentoo-r2-vesa/Documentation/fb/vesafb.txt --- linux-2.6.13-gentoo-r2/Documentation/fb/vesafb.txt 2005-09-23 15:46:45.000000000 +0200 +++ linux-2.6.13-gentoo-r2-vesa/Documentation/fb/vesafb.txt 2005-09-23 15:43:44.000000000 +0200 @@ -4,16 +4,16 @@ What is vesafb? Vesafb is a generic framebuffer driver for x86 and x86_64 boxes. -VESA BIOS Extensions Version 2.0 are required, because we need a linear -frame buffer. VBE 3.0 is required if you want to use modes with a higher -(than the standard 60Hz) refresh rate. +VESA BIOS Extensions Version 2.0 are required, because we need access to +a linear frame buffer. VBE 3.0 is required if you want to use modes with a +higher (than the standard 60Hz) refresh rate. -The VESA framebuffer driver comes in two flavors - the standard vesafb -and vesafb-tng. Vesafb-tng is available only on 32-bit x86 due to the +The VESA framebuffer driver comes in two flavors - the standard 'vesafb' +and 'vesafb-tng'. Vesafb-tng is available only on 32-bit x86 due to the technology it uses (vm86). Vesafb-tng has more features than vesafb -(adjusting the refresh rate on VBE3.0-compliant boards, switching the -video mode without rebooting, selecting a mode by providing its -modedb name, and more) but might be unstable on some systems. +(adjusting the refresh rate on VBE3.0-compliant boards, switching the +video mode without rebooting, selecting a mode by providing its +modedb name, and more). Advantages: @@ -31,34 +31,33 @@ Disadvantages: How to use it? ============== -If you are running your system on hardware platform where vm86 is supported -(this is 32-bit x86 only as of the time of writing this document) and you -decide to use vesafb-tng, you can either the driver into the kernel or use it -as a module. The graphic mode you want to use is in both cases specified using -the standard modedb format. - -If your system doesn't support vm86 calls yet (all 64-bit platforms), things -get a little more tricky. Since on such systems you can't do BIOS calls from -protected mode in which kernel runs, you have to decide at boot time whenever -you want to run in text or in graphics mode. Switching mode later on is -impossible. Switching modes is done using the vga=... boot parameter. Read -Documentation/svga.txt for details. Below is a more detailed description of -what to do on systems using the standard vesafb driver. - -You should compile in both vgacon (for text mode) and vesafb (for graphics mode). -Which of them takes over the console depends on whenever the specified mode is -text or graphics. +If you are running a 32-bit x86 system and you decide to use vesafb-tng, +you can either compile the driver into the kernel or use it as a module. +The graphics mode you want to use is in both cases specified using the +standard modedb format. + +If your system doesn't support vm86 calls, things get a little more tricky. +Since on such systems you can't do BIOS calls from protected mode in which +kernel runs, you have to decide at boot time whenever you want to run in text +or in graphics mode. Switching mode later on is impossible. Switching modes +is done using the vga=... boot parameter. Read Documentation/svga.txt for +details. Below is a more detailed description of what to do on systems using +the standard vesafb driver. + +You should compile in both vgacon (for text mode) and vesafb (for graphics +mode). Which of them takes over the console depends on whenever the +specified mode is text or graphics. -The graphic modes are NOT in the list which you get if you boot with vga=ask -and hit return. The mode you wish to use is derived from the VESA mode number. +The graphic modes are NOT in the list which you get if you boot with vga=ask +and hit return. The mode you wish to use is derived from the VESA mode number. Here are those VESA mode numbers: | 640x480 800x600 1024x768 1280x1024 ----+------------------------------------- -256 | 0x101 0x103 0x105 0x107 -32k | 0x110 0x113 0x116 0x119 -64k | 0x111 0x114 0x117 0x11A -16M | 0x112 0x115 0x118 0x11B +256 | 0x101 0x103 0x105 0x107 +32k | 0x110 0x113 0x116 0x119 +64k | 0x111 0x114 0x117 0x11A +16M | 0x112 0x115 0x118 0x11B The video mode number of the Linux kernel is the VESA mode number plus 0x200. @@ -68,14 +67,14 @@ So the table for the Kernel mode numbers | 640x480 800x600 1024x768 1280x1024 ----+------------------------------------- -256 | 0x301 0x303 0x305 0x307 -32k | 0x310 0x313 0x316 0x319 -64k | 0x311 0x314 0x317 0x31A -16M | 0x312 0x315 0x318 0x31B - -To enable one of those modes you have to specify "vga=ask" in the lilo.conf -file and rerun LILO. Then you can type in the desired mode at the "vga=ask" -prompt. For example if you like to use 1024x768x256 colors you have to say +256 | 0x301 0x303 0x305 0x307 +32k | 0x310 0x313 0x316 0x319 +64k | 0x311 0x314 0x317 0x31A +16M | 0x312 0x315 0x318 0x31B + +To enable one of those modes you have to specify "vga=ask" in the lilo.conf +file and rerun LILO. Then you can type in the desired mode at the "vga=ask" +prompt. For example if you like to use 1024x768x256 colors you have to say "305" at this prompt. If this does not work, this might be because your BIOS does not support @@ -84,7 +83,7 @@ Even if your board does, it might be the Extensions v2.0 are required, 1.2 is NOT sufficient. You will get a "bad mode number" message if something goes wrong. -1. Note: LILO cannot handle hex, for booting directly with +1. Note: LILO cannot handle hex, for booting directly with "vga=mode-number" you have to transform the numbers to decimal. 2. Note: Some newer versions of LILO appear to work with those hex values, if you set the 0x in front of the numbers. @@ -97,17 +96,21 @@ XF68_FBDev should work just fine, but it another (accelerated) X-Server like XF86_SVGA might or might not work. It depends on X-Server and graphics board. -The X-Server must restore the video mode correctly, else you end up +The X-Server must restore the video mode correctly, or else you end up with a broken console (and vesafb cannot do anything about this). With vesafb-tng chances are that the console will be restored properly -even if the X server messed up the video mode. +even if the X server messes up the video mode. + Refresh rates ============= -With VBE3.0 compatible BIOSes and vesafb-tng it is possible to change -the refresh rate either at boot time (by specifying the @ part of -the mode name) or later, using the fbset utility. +With VBE3.0 compatible BIOSes and vesafb-tng it is possible to change +the refresh rate either at boot time (by specifying the @ part of +the mode name) or later, using the fbset utility. + +If you want to use the default BIOS refresh rate while switching modes +on a running system, set pixclock to 0. With VBE2.0 there is no way to change the mode timings after booting Linux. If you are not happy with the 60 Hz refresh rate, you have @@ -118,71 +121,78 @@ these options: * use a native driver (matroxfb/atyfb) instead if vesafb. If none is available, write a new one! * use a BIOS editor to change the default refresh rate (such an - editor does exist at least for ATI Radeon BIOSes). - * if you're running a non-vm86 and VBE3.0-compatible system, you can - use a kernel patch to hard-code some mode timings in the kernel and - use these while setting the graphic mode at boot time. + editor does exist at least for ATI Radeon BIOSes). + * if you're running a non-vm86 and VBE3.0-compatible system, you can + use a kernel patch (vesafb-rrc) to hard-code some mode timings in + the kernel and use these while setting the graphic mode at boot time. + +Note that there are some boards (nVidia 59**, 57** and newer models) +claiming that their Video BIOS is VBE3.0 compliant, while ignoring the +CRTC values provided by software such as vesafb-tng. You'll not be able +to change the refresh rate if you're using one of these boards. Configuration ============= -The VESA BIOS provides protected mode interface for changing some parameters. -vesafb can use it for palette changes and to pan the display. It is turned -off by default because it seems not to work with some BIOS versions, but there -are options to turn it on. +The VESA BIOS provides protected mode interface for changing some parameters. +vesafb can use it for palette changes and to pan the display. It is turned +off by default because it seems not to work with some BIOS versions, but +there are options to turn it on. -You can pass options to vesafb using "video=vesafb:option" on the kernel -command line. Multiple options should be separated by comma, like this: +You can pass options to vesafb using "video=vesafb:option" on the kernel +command line. Multiple options should be separated by comma, like this: "video=vesafb:ypan,1024x768-32@85" +Note that vesafb-tng still uses the "video=vesafb:option" format of the +kernel command line video parameter. 'video=vesafb-tng:xxx" is incorrect. + Accepted options (both vesafb and vesafb-tng): - -ypan Enable display panning using the VESA protected mode - interface or vm86 calls. The visible screen is just a window of the - video memory, console scrolling is done by changing the - start of the window. - pro: * scrolling (fullscreen) is fast, because there is - no need to copy around data. - * you'll get scrollback (the Shift-PgUp thing), - the video memory can be used as scrollback buffer - con: * scrolling only parts of the screen causes some - ugly flicker effects (boot logo flickers for - example). - -ywrap Same as ypan, but assumes your gfx board can wrap-around the video - memory (i.e. starts reading from top if it reaches the end of video - memory). Faster than ypan. - -redraw Scroll by redrawing the affected part of the screen, this is the - safe (and slow) default. - -vgapal Use the standard VGA registers for palette changes. - This is the default. - -pmipal Use the protected mode interface for palette changes. - -mtrr:n setup memory type range registers for the vesafb framebuffer - where n: - 0 - disabled (equivalent to nomtrr) - 1 - uncachable - 2 - write-back - 3 - write-combining (default) - 4 - write-through - If you see the following in dmesg, choose the type that matches the - old one. In this example, use "mtrr:2". +ypan Enable display panning using the VESA protected mode interface + The visible screen is just a window of the video memory, + console scrolling is done by changing the start of the window. + pro: * scrolling (fullscreen) is fast, because there is + no need to copy around data. + * you'll get scrollback (the Shift-PgUp thing), + the video memory can be used as scrollback buffer + con: * scrolling only parts of the screen causes some + ugly flicker effects (boot logo flickers for + example). + +ywrap Same as ypan, but assumes your gfx board can wrap-around the video + memory (i.e. starts reading from top if it reaches the end of + video memory). Faster than ypan. + +redraw Scroll by redrawing the affected part of the screen, this is the + safe (and slow) default. + +vgapal Use the standard VGA registers for palette changes. + This is the default. + +pmipal Use the protected mode interface for palette changes. + +mtrr:n Setup memory type range registers for the vesafb framebuffer + where n: + 0 - disabled (equivalent to nomtrr) + 1 - uncachable + 2 - write-back + 3 - write-combining (default) + 4 - write-through + + If you see the following in dmesg, choose the type that matches + the old one. In this example, use "mtrr:2". ... mtrr: type mismatch for e0000000,8000000 old: write-back new: write-combining ... -nomtrr disable mtrr +nomtrr Do not use memory type range registers for vesafb. vremap:n remap 'n' MiB of video RAM. If 0 or not specified, remap memory - according to video mode. (2.5.66 patch/idea by Antonino Daplas - reversed to give override possibility (allocate more fb memory - than the kernel would) to 2.4 by tmb@iki.fi) + according to video mode. (2.5.66 patch/idea by Antonino Daplas + reversed to give override possibility (allocate more fb memory + than the kernel would) to 2.4 by tmb@iki.fi) vtotal:n if the video BIOS of your card incorrectly determines the total @@ -190,84 +200,48 @@ vtotal:n Options accepted only by vesafb-tng: - The mode you want to set, in the standard modedb format. Refer to - modedb.txt for detailed description. If you specify a mode that is - not supported by your board's BIOS, vesafb will attempt to set a - similar mode. The list of supported modes can be found in - /proc/fbx/modes, where x is the framebuffer number (usually 0). - When vesafb is compiled as a module, the mode string should be - provided as a value of the parameter 'mode'. + The mode you want to set, in the standard modedb format. Refer to + modedb.txt for detailed description. If you specify a mode that is + not supported by your board's BIOS, vesafb will attempt to set a + similar mode. The list of supported modes can be found in + /proc/fbx/modes, where x is the framebuffer number (usually 0). + When vesafb is compiled as a module, the mode string should be + provided as a value of the parameter 'mode'. vbemode:x - Force the use of VBE mode x. The mode will only be set if it's - found in VBE-provided list of supported modes. - NOTE: The mode number 'x' should be specified in VESA mode number - notation, not the Linux kernel one (ie. 257 instead of 769). - HINT: If you use this option because normal parameter does - not work for you and you use a X server, you'll probably want to - set the 'nocrtc' option to ensure that the video mode is properly - restored after console <-> X switches. - -nocrtc Do not use CRTC timings while setting the graphic mode. This option - makes sence only with VBE3.0 compliant systems. Use it if you have - problems with the modes set in the standard way. Note that specifying - this option means the refresh rate will be ignored and will stay at - your BIOS' default (60 Hz). - -noedid Do not try to fetch and use EDID-provided modes. - -gtf Force the use of VESA's GTF (Generalized Timing Formula). Specifying - this will cause vesafb to skip it's internal modedb and EDID-modedb - and jump straight to the GTF part of the code (normally used only is - everything else failed). This can be useful if you want to get as much - as possible from you graphics board but your BIOS doesn't support - modes with refresh rates you require. Note that you may need to - specify the maxhf, maxvf and maxclk parameters if they are not - provided by EDID. + Force the use of VBE mode x. The mode will only be set if it's + found in VBE-provided list of supported modes. + NOTE: The mode number 'x' should be specified in VESA mode number + notation, not the Linux kernel one (ie. 257 instead of 769). + HINT: If you use this option because normal parameter does + not work for you and you use a X server, you'll probably want to + set the 'nocrtc' option to ensure that the video mode is properly + restored after console <-> X switches. + +nocrtc Do not use CRTC timings while setting the graphic mode. This option + makes sence only with VBE3.0 compliant systems. Use it if you have + problems with the modes set in the standard way. Note that specifying + this option means the refresh rate will be ignored and will stay at + your BIOS default (60 Hz). + +noedid Do not try to fetch and use EDID-provided modes. + +gtf Force the use of VESA's GTF (Generalized Timing Formula). Specifying + this will cause vesafb to skip it's internal modedb and EDID-modedb + and jump straight to the GTF part of the code (normally used only if + everything else failed). This can be useful if you want to get as + much as possible from you graphics board but your BIOS doesn't + support modes with refresh rates you require. Note that you may need + to specify the maxhf, maxvf and maxclk parameters if they are not + provided by EDID. Additionally, the following parameters may be provided. They all override the -EDID-provided values and BIOS defaults. Refer to you monitor's specs to get +EDID-provided values and BIOS defaults. Refer to your monitor's specs to get the correct values for maxhf, maxvf and maxclk for your hardware. -maxhf:n Maximum horizontal frequency (in kHz). -maxvf:n Maximum vertical frequency (in Hz). -maxclk:n Maximum pixel clock (in MHz). - - -Vesafb-tng Technical details -============================ - -1. The driver architecture. - -The driver's code is stored in 3 files: - /drivers/video/vesafb-tng.c - /drivers/video/vesafb-thread.c - /include/video/vesa.h - -vesafb-tng.c contains the main code. vesafb-thread.c contains code for the -vesafb service thread. A separate thread is necessary because we need to remap -memory in order to be able to use the vm86 calls. The service thread is started -regardless of whether vesafb is compiled into the kernel or compiled as a -module. This is necessary because of the active_mm stuff, better described in -the header of vesafb-thread.c. - -2. The driver initialization - - o vesafb_vbe_init - - get basic info about the graphics BIOS - - fetch data about all modes supported by VBE - - get info about the protected mode interface - - get EDID data and attempt to create an EDID modedb - - o vesafb_probe - - get service thread's PID (started earlier from fbmem.c) - - call vesafb_vbe_init - - find a matching VBE mode - - try to find the specified mode in vesa_modes modedb - - try to find the specified mode in the EDID modedb - - try to calculate timings with the GTF - - low level setup - request_mem_region, ioremap, etc. - - setup /proc/fb/modes and /proc/fb/vbe_info +maxhf:n Maximum horizontal frequency (in kHz). +maxvf:n Maximum vertical frequency (in Hz). +maxclk:n Maximum pixel clock (in MHz). Have fun! @@ -275,9 +249,9 @@ Have fun! Original document for the vesafb driver by Gerd Knorr -Minor (mostly typo) changes by +Minor (mostly typo) changes by Nico Schmoigl Extended documentation for vm86, VBE3.0 and vesafb-tng by -Micha³ Januszewski +Michal Januszewski diff -Naurp linux-2.6.13-gentoo-r2/drivers/video/fbmem.c linux-2.6.13-gentoo-r2-vesa/drivers/video/fbmem.c --- linux-2.6.13-gentoo-r2/drivers/video/fbmem.c 2005-09-23 15:46:45.000000000 +0200 +++ linux-2.6.13-gentoo-r2-vesa/drivers/video/fbmem.c 2005-09-23 15:43:44.000000000 +0200 @@ -52,8 +52,6 @@ * Frame buffer device initialization and setup routines */ -extern int vesafb_init_thread(void); - #define FBPIXMAPSIZE (1024 * 8) static struct notifier_block *fb_notifier_list; @@ -1160,9 +1158,6 @@ fbmem_init(void) fb_class = NULL; } -#if defined(CONFIG_FB_VESA_TNG) || defined(CONFIG_FB_VESA_TNG_MODULE) - vesafb_init_thread(); -#endif return 0; } diff -Naurp linux-2.6.13-gentoo-r2/drivers/video/fbmon.c linux-2.6.13-gentoo-r2-vesa/drivers/video/fbmon.c --- linux-2.6.13-gentoo-r2/drivers/video/fbmon.c 2005-09-23 15:46:45.000000000 +0200 +++ linux-2.6.13-gentoo-r2-vesa/drivers/video/fbmon.c 2005-09-23 15:43:17.000000000 +0200 @@ -516,7 +516,7 @@ static void get_detailed_timing(unsigned * This function builds a mode database using the contents of the EDID * data */ -struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) +static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) { struct fb_videomode *mode, *m; unsigned char *block; @@ -590,7 +590,7 @@ void fb_destroy_modedb(struct fb_videomo kfree(modedb); } -int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs) +static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs) { int i, retval = 1; unsigned char *block; @@ -1178,17 +1178,9 @@ void fb_edid_to_monspecs(unsigned char * { specs = NULL; } -struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) -{ - return NULL; -} void fb_destroy_modedb(struct fb_videomode *modedb) { } -int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs) -{ - return 1; -} int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_info *info) { @@ -1265,5 +1257,3 @@ EXPORT_SYMBOL(fb_edid_to_monspecs); EXPORT_SYMBOL(fb_get_mode); EXPORT_SYMBOL(fb_validate_mode); EXPORT_SYMBOL(fb_destroy_modedb); -EXPORT_SYMBOL(fb_create_modedb); -EXPORT_SYMBOL(fb_get_monitor_limits); diff -Naurp linux-2.6.13-gentoo-r2/drivers/video/Makefile linux-2.6.13-gentoo-r2-vesa/drivers/video/Makefile --- linux-2.6.13-gentoo-r2/drivers/video/Makefile 2005-09-23 15:46:45.000000000 +0200 +++ linux-2.6.13-gentoo-r2-vesa/drivers/video/Makefile 2005-09-23 15:43:44.000000000 +0200 @@ -95,21 +95,10 @@ obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxf obj-$(CONFIG_FB_IMX) += imxfb.o # Platform or fallback drivers go here -ifeq ($(CONFIG_FB_VESA),m) - ifeq ($(CONFIG_FB_VESA_STD),y) - obj-y += vesafb.o - else - obj-m += vesafb-tng.o - obj-y += vesafb-thread.o - endif +ifeq ($(CONFIG_FB_VESA_STD),y) + obj-y += vesafb.o else - ifeq ($(CONFIG_FB_VESA),y) - ifeq ($(CONFIG_FB_VESA_STD),y) - obj-y += vesafb.o - else - obj-y += vesafb-tng.o vesafb-thread.o - endif - endif + obj-$(CONFIG_FB_VESA) += vesafb-thread.o vesafb-tng.o endif obj-$(CONFIG_FB_VGA16) += vga16fb.o vgastate.o obj-$(CONFIG_FB_OF) += offb.o diff -Naurp linux-2.6.13-gentoo-r2/drivers/video/modedb.c linux-2.6.13-gentoo-r2-vesa/drivers/video/modedb.c --- linux-2.6.13-gentoo-r2/drivers/video/modedb.c 2005-09-23 15:46:45.000000000 +0200 +++ linux-2.6.13-gentoo-r2-vesa/drivers/video/modedb.c 2005-09-23 15:43:44.000000000 +0200 @@ -605,6 +605,7 @@ void fb_var_to_videomode(struct fb_video { u32 pixclock, hfreq, htotal, vtotal; + mode->refresh = 0; mode->name = NULL; mode->xres = var->xres; mode->yres = var->yres; diff -Naurp linux-2.6.13-gentoo-r2/drivers/video/vesafb-thread.c linux-2.6.13-gentoo-r2-vesa/drivers/video/vesafb-thread.c --- linux-2.6.13-gentoo-r2/drivers/video/vesafb-thread.c 2005-09-23 15:46:45.000000000 +0200 +++ linux-2.6.13-gentoo-r2-vesa/drivers/video/vesafb-thread.c 2005-09-23 15:43:44.000000000 +0200 @@ -1,23 +1,19 @@ /* - * Framebuffer driver for VBE 2.0+ compliant graphic boards - kernel thread - * and vm86 routines. - * - * This code has to be compiled into the kernel even if vesafb is configured - * as a module. If vesafb_thread were to be started while the module is being - * initialized, it would share its active_mm with modprobe. This mm would be - * lost after modprobe finished its work, and we can't allow it, because we - * need it for as long as the vesafb thread is active. + * Framebuffer driver for VBE 2.0+ compliant graphic boards. + * Kernel thread and vm86 routines. * * (c) 2004-2005 Michal Januszewski - * + * */ +#include +#include #include +#include #include #include #include #include -#include #include #include #include @@ -29,66 +25,85 @@ #include #include #include +#include #include "edid.h" -#ifdef DEBUG -#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) -#else -#define DPRINTK(fmt, args...) +#ifdef MODULE +int errno; #endif -int vesafb_pid = 0; -struct vm86_struct vm86; - -static DECLARE_MUTEX(vesafb_sem); +static DECLARE_COMPLETION(vesafb_th_completion); +static DECLARE_MUTEX(vesafb_task_list_sem); static LIST_HEAD(vesafb_task_list); static DECLARE_WAIT_QUEUE_HEAD(vesafb_wait); +static struct vm86_struct vm86; +static int vesafb_pid = 0; _syscall3(int,ioperm,unsigned long, a, unsigned long, b, unsigned long, c); _syscall1(int,vm86old,struct vm86_struct __user*, v86); -#define DEFAULT_VM86_FLAGS (IF_MASK | IOPL_MASK) -#define VM86_PUSHW(x) vm86.regs.esp -= 2; *(unsigned short*)(real_mem+vm86.regs.esp) = x; -#define REAL_MEM_SIZE 0x4000 -#define REAL_MEM 0x2000 -#define RET_CODE_SIZE 0x02 -#define STACK_SIZE 0x500 -#define BUFFER (STACK_SIZE + RET_CODE_SIZE) +#define DEFAULT_VM86_FLAGS (IF_MASK | IOPL_MASK) +#define VM86_PUSHW(x) \ +do { \ + vm86.regs.esp -= 2; \ + *(u16*)(STACK_ADDR + vm86.regs.esp) = x; \ +} while(0); + +/* The IVT, BDA, EBDA and our stack and buffers will be put into + * one contiguous memory chunk: + * + * [ IVT | BDA | STACK | RET_CODE | BUFFER ] + */ +#define IVTBDA_SIZE 0x0600 +#define RET_CODE_SIZE 0x0010 +#define STACK_SIZE 0x0500 +#define BUFFER_SIZE 0x2500 + +/* The amount of memory that will be allocated should be a multiple + * of PAGE_SIZE. */ +#define __MEM_SIZE (IVTBDA_SIZE + RET_CODE_SIZE + \ + STACK_SIZE + BUFFER_SIZE) +#define REAL_MEM_SIZE (((__MEM_SIZE / PAGE_SIZE) + 1) * PAGE_SIZE) + +#define IVTBDA_ADDR 0x00000 +#define STACK_ADDR (IVTBDA_ADDR + IVTBDA_SIZE) +#define RET_CODE_ADDR (STACK_ADDR + STACK_SIZE) +#define BUF_ADDR (RET_CODE_ADDR + RET_CODE_SIZE) + #define FLAG_D (1 << 10) -/* segment prefix opcodes */ -enum { - P_CS = 0x2e, - P_SS = 0x36, +/* Segment prefix opcodes */ +enum { + P_CS = 0x2e, + P_SS = 0x36, P_DS = 0x3e, - P_ES = 0x26, - P_FS = 0x64, + P_ES = 0x26, + P_FS = 0x64, P_GS = 0x65 }; -void vesafb_queue_task(struct vesafb_task *task) -{ - list_add_tail(&task->node, &vesafb_task_list); - wake_up(&vesafb_wait); -} - -/* emulated vm86 ins instruction */ +/* Emulated vm86 ins instruction */ static void vm86_ins(int size) { u32 edx, edi; - edx = vm86.regs.edx & 0xffff; edi = (vm86.regs.edi & 0xffff) + (u32)(vm86.regs.es << 4); if (vm86.regs.eflags & FLAG_D) asm volatile ("std\n"); - else + else asm volatile ("cld\n"); - + switch (size) { - case 4: asm volatile ("insl\n" : "=D" (edi) : "d" (edx), "0" (edi)); break; - case 2: asm volatile ("insw\n" : "=D" (edi) : "d" (edx), "0" (edi)); break; - case 1: asm volatile ("insb\n" : "=D" (edi) : "d" (edx), "0" (edi)); + case 4: + asm volatile ("insl\n" : "=D" (edi) : "d" (edx), "0" (edi)); + break; + case 2: + asm volatile ("insw\n" : "=D" (edi) : "d" (edx), "0" (edi)); + break; + case 1: + asm volatile ("insb\n" : "=D" (edi) : "d" (edx), "0" (edi)); + break; } if (vm86.regs.eflags & FLAG_D) @@ -102,15 +117,14 @@ static void vm86_ins(int size) static void vm86_rep_ins(int size) { - u16 cx = vm86.regs.ecx; - + u16 cx = vm86.regs.ecx; while (cx--) vm86_ins(size); vm86.regs.ecx &= 0xffff0000; } -/* emulated vm86 outs instruction */ +/* Emulated vm86 outs instruction */ static void vm86_outs(int size, int segment) { u32 edx, esi, base; @@ -135,14 +149,20 @@ static void vm86_outs(int size, int segm asm volatile ("cld\n"); switch (size) { - case 4: asm volatile ("outsl\n" : "=S" (esi) : "d" (edx), "0" (esi)); break; - case 2: asm volatile ("outsw\n" : "=S" (esi) : "d" (edx), "0" (esi)); break; - case 1: asm volatile ("outsb\n" : "=S" (esi) : "d" (edx), "0" (esi)); break; + case 4: + asm volatile ("outsl\n" : "=S" (esi) : "d" (edx), "0" (esi)); + break; + case 2: + asm volatile ("outsw\n" : "=S" (esi) : "d" (edx), "0" (esi)); + break; + case 1: + asm volatile ("outsb\n" : "=S" (esi) : "d" (edx), "0" (esi)); + break; } if (vm86.regs.eflags & FLAG_D) asm volatile ("cld"); - + esi -= base << 4; vm86.regs.esi &= 0xffff0000; vm86.regs.esi |= (esi & 0xffff); @@ -151,416 +171,566 @@ static void vm86_outs(int size, int segm static void vm86_rep_outs(int size, int segment) { u16 cx = vm86.regs.ecx; - while (cx--) vm86_outs(size, segment); vm86.regs.ecx &= 0xffff0000; } +static int vm86_do_unknown(void) +{ + u8 data32 = 0, segment = P_DS, rep = 0; + u8 *instr; + int ret = 0, i = 0; + + instr = (u8*)((vm86.regs.cs << 4) + vm86.regs.eip); + + while (1) { + switch(instr[i]) { + case 0x66: /* operand size prefix */ + data32 = 1 - data32; + i++; + break; + case 0xf2: /* repnz */ + case 0xf3: /* rep */ + rep = 1; + i++; + break; + case P_CS: /* segment prefix */ + case P_SS: + case P_DS: + case P_ES: + case P_FS: + case P_GS: + segment = instr[i]; + i++; + break; + case 0xf0: /* LOCK - ignored */ + case 0x67: /* address size prefix - ignored */ + i++; + break; + case 0x6c: /* insb */ + if (rep) + vm86_rep_ins(1); + else + vm86_ins(1); + i++; + goto out; + case 0x6d: /* insw / insd */ + if (rep) { + if (data32) + vm86_rep_ins(4); + else + vm86_rep_ins(2); + } else { + if (data32) + vm86_ins(4); + else + vm86_ins(2); + } + i++; + goto out; + case 0x6e: /* outsb */ + if (rep) + vm86_rep_outs(1, segment); + else + vm86_outs(1, segment); + i++; + goto out; + case 0x6f: /* outsw / outsd */ + if (rep) { + if (data32) + vm86_rep_outs(4, segment); + else + vm86_rep_outs(2, segment); + } else { + if (data32) + vm86_outs(4, segment); + else + vm86_outs(2, segment); + } + i++; + goto out; + case 0xe4: /* inb xx */ + asm volatile ( + "inb %w1, %b0" + : "=a" (vm86.regs.eax) + : "d" (instr[i+1]), "0" (vm86.regs.eax)); + i += 2; + goto out; + case 0xe5: /* inw xx / ind xx */ + if (data32) { + asm volatile ( + "inl %w1, %0" + : "=a" (vm86.regs.eax) + : "d" (instr[i+1]), + "0" (vm86.regs.eax)); + } else { + asm volatile ( + "inw %w1, %w0" + : "=a" (vm86.regs.eax) + : "d" (instr[i+1]), + "0" (vm86.regs.eax)); + } + i += 2; + goto out; + + case 0xec: /* inb dx */ + asm volatile ( + "inb %w1, %b0" + : "=a" (vm86.regs.eax) + : "d" (vm86.regs.edx), "0" (vm86.regs.eax)); + i++; + goto out; + case 0xed: /* inw dx / ind dx */ + if (data32) { + asm volatile ( + "inl %w1, %0" + : "=a" (vm86.regs.eax) + : "d" (vm86.regs.edx)); + } else { + asm volatile ( + "inw %w1, %w0" + : "=a" (vm86.regs.eax) + : "d" (vm86.regs.edx)); + } + i++; + goto out; + case 0xe6: /* outb xx */ + asm volatile ( + "outb %b0, %w1" + : /* no return value */ + : "a" (vm86.regs.eax), "d" (instr[i+1])); + i += 2; + goto out; + case 0xe7: /* outw xx / outd xx */ + if (data32) { + asm volatile ( + "outl %0, %w1" + : /* no return value */ + : "a" (vm86.regs.eax), + "d" (instr[i+1])); + } else { + asm volatile ( + "outw %w0, %w1" + : /* no return value */ + : "a" (vm86.regs.eax), + "d" (instr[i+1])); + } + i += 2; + goto out; + case 0xee: /* outb dx */ + asm volatile ( + "outb %b0, %w1" + : /* no return value */ + : "a" (vm86.regs.eax), "d" (vm86.regs.edx)); + i++; + goto out; + case 0xef: /* outw dx / outd dx */ + if (data32) { + asm volatile ( + "outl %0, %w1" + : /* no return value */ + : "a" (vm86.regs.eax), + "d" (vm86.regs.edx)); + } else { + asm volatile ( + "outw %w0, %w1" + : /* no return value */ + : "a" (vm86.regs.eax), + "d" (vm86.regs.edx)); + } + i++; + goto out; + default: + printk(KERN_ERR "vesafb: BUG, opcode %x emulation " + "not supported\n", instr[i]); + ret = 1; + goto out; + } + } +out: vm86.regs.eip += i; + return ret; +} + void vesafb_do_vm86(struct vm86_regs *regs) { - unsigned char *real_mem = (void*)REAL_MEM; unsigned int ret; - + u8 *retcode = (void*)RET_CODE_ADDR; + memset(&vm86,0,sizeof(vm86)); memcpy(&vm86.regs, regs, sizeof(struct vm86_regs)); - /* the return code */ - real_mem[0] = 0xcd; /* int opcode */ - real_mem[1] = 0xff; /* int number (255) */ + /* The return code */ + retcode[0] = 0xcd; /* int opcode */ + retcode[1] = 0xff; /* int number (255) */ - /* we use int 255 to get back to protected mode */ + /* We use int 0xff to get back to protected mode */ memset(&vm86.int_revectored, 0, sizeof(vm86.int_revectored)); - ((unsigned char *) &vm86.int_revectored)[0xff / 8] |= (1 << (0xff % 8)); /* int 0xff */ - - /* it's up to the caller to set the rest of the registers */ + ((unsigned char *)&vm86.int_revectored)[0xff / 8] |= (1 << (0xff % 8)); + + /* + * We want to call int 0x10, so we set: + * CS = 0x42 = 0x10 * 4 + 2 + * IP = 0x40 = 0x10 * 4 + * and SS:ESP. It's up to the caller to set the rest of the registers. + */ vm86.regs.eflags = DEFAULT_VM86_FLAGS; - vm86.regs.cs = *(unsigned short *)0x42; /* 0x10 * 4 + 2 - the int map starts at 0x0 */ - vm86.regs.eip = *(unsigned short *)0x40; /* 0x10 * 4 */ - - /* stack @ 0x10500, size: 0x500-4 - should be enough for our needs */ - vm86.regs.ss = (REAL_MEM >> 4); - vm86.regs.esp = STACK_SIZE+RET_CODE_SIZE; + vm86.regs.cs = *(unsigned short *)0x42; + vm86.regs.eip = *(unsigned short *)0x40; + vm86.regs.ss = (STACK_ADDR >> 4); + vm86.regs.esp = ((STACK_ADDR & 0x0000f) + STACK_SIZE); - /* these will be fetched off the stack when we come to an iret in the int's 0x10 code */ + /* These will be fetched off the stack when we come to an iret in the + * int's 0x10 code */ VM86_PUSHW(DEFAULT_VM86_FLAGS); - VM86_PUSHW((REAL_MEM >> 4)); /* return code segment */ - VM86_PUSHW(0x0000); /* return code offset */ - + VM86_PUSHW((RET_CODE_ADDR >> 4)); /* return code segment */ + VM86_PUSHW((RET_CODE_ADDR & 0x0000f)); /* return code offset */ + while(1) { ret = vm86old(&vm86); if (VM86_TYPE(ret) == VM86_INTx) { - int vint = VM86_ARG(ret); - /* if exit from vm86 was caused by int 0xff - we're done.. */ + /* If exit from vm86 was caused by int 0xff, then + * we're done.. */ if (vint == 0xff) - goto vm86_done_call; - - /* .. otherwise, we have call the int handler manually */ + goto out; + + /* .. otherwise, we have to call the int handler + * manually */ VM86_PUSHW(vm86.regs.eflags); VM86_PUSHW(vm86.regs.cs); VM86_PUSHW(vm86.regs.eip); - vm86.regs.cs = *(unsigned short *)((vint << 2) + 2); - vm86.regs.eip = *(unsigned short *)(vint << 2); + vm86.regs.cs = *(u16 *)((vint << 2) + 2); + vm86.regs.eip = *(u16 *)(vint << 2); vm86.regs.eflags &= ~(VIF_MASK | TF_MASK); - } else if (VM86_TYPE(ret) == VM86_UNKNOWN) { - - u8 *instr; - u8 data32 = 0, segment = P_DS, rep = 0; - int i = 0; - - instr = (u8*)((vm86.regs.cs << 4) + vm86.regs.eip); - - while (1) { - - switch(instr[i]) { - - case 0x66: /* operand size prefix */ - data32 = 1 - data32; - i++; - break; - case 0xf2: /* repnz */ - case 0xf3: /* rep */ - rep = 1; - i++; - break; - case P_CS: /* segment prefix */ - case P_SS: - case P_DS: - case P_ES: - case P_FS: - case P_GS: - segment = instr[i]; - i++; - break; - case 0xf0: /* LOCK - ignored */ - case 0x67: /* address size prefix - ignored */ - i++; - break; - case 0x6c: /* insb */ - if (rep) vm86_rep_ins(1); - else vm86_ins(1); - i++; - goto vm86_done_emu; - case 0x6d: /* insw / insd */ - if (rep) - if (data32) vm86_rep_ins(4); - else vm86_rep_ins(2); - else - if (data32) vm86_ins(4); - else vm86_ins(2); - i++; - goto vm86_done_emu; - case 0x6e: /* outsb */ - if (rep) vm86_rep_outs(1, segment); - else vm86_outs(1, segment); - i++; - goto vm86_done_emu; - case 0x6f: /* outsw / outsd */ - if (rep) - if (data32) vm86_rep_outs(4, segment); - else vm86_rep_outs(2, segment); - else - if (data32) vm86_outs(4, segment); - else vm86_outs(2, segment); - i++; - goto vm86_done_emu; - case 0xe4: /* inb xx */ - asm volatile ( - "inb %w1, %b0" - : "=a" (vm86.regs.eax) - : "d" (instr[i+1]), "0" (vm86.regs.eax)); - i += 2; - goto vm86_done_emu; - case 0xe5: /* inw xx / ind xx */ - if (data32) - asm volatile ( - "inl %w1, %0" - : "=a" (vm86.regs.eax) - : "d" (instr[i+1]), "0" (vm86.regs.eax)); - else - asm volatile ( - "inw %w1, %w0" - : "=a" (vm86.regs.eax) - : "d" (instr[i+1]), "0" (vm86.regs.eax)); - i += 2; - goto vm86_done_emu; - case 0xec: /* inb dx */ - asm volatile ( - "inb %w1, %b0" - : "=a" (vm86.regs.eax) - : "d" (vm86.regs.edx), "0" (vm86.regs.eax)); - i++; - goto vm86_done_emu; - case 0xed: /* inw dx / ind dx */ - if (data32) - asm volatile ( - "inl %w1, %0" - : "=a" (vm86.regs.eax) - : "d" (vm86.regs.edx)); - else - asm volatile ( - "inw %w1, %w0" - : "=a" (vm86.regs.eax) - : "d" (vm86.regs.edx)); - i++; - goto vm86_done_emu; - case 0xe6: /* outb xx */ - asm volatile ( - "outb %b0, %w1" - : : "a" (vm86.regs.eax), "d" (instr[i+1])); - i += 2; - goto vm86_done_emu; - case 0xe7: /* outw xx / outd xx */ - if (data32) - asm volatile ( - "outl %0, %w1" - : : "a" (vm86.regs.eax), "d" (instr[i+1])); - else - asm volatile ( - "outw %w0, %w1" - : : "a" (vm86.regs.eax), "d" (instr[i+1])); - i += 2; - goto vm86_done_emu; - case 0xee: /* outb dx */ - asm volatile ( - "outb %b0, %w1" - : : "a" (vm86.regs.eax), "d" (vm86.regs.edx)); - i++; - goto vm86_done_emu; - case 0xef: /* outw dx / outd dx */ - if (data32) - asm volatile ( - "outl %0, %w1" - : : "a" (vm86.regs.eax), "d" (vm86.regs.edx)); - else - asm volatile ( - "outw %w0, %w1" - : : "a" (vm86.regs.eax), "d" (vm86.regs.edx)); - i++; - goto vm86_done_emu; - default: - printk(KERN_ERR "vesafb: BUG, opcode %x emulation not supported\n", instr[i]); - goto vm86_done_call; - } - } -vm86_done_emu: vm86.regs.eip += i; + if (vm86_do_unknown()) + goto out; } else { - printk(KERN_ERR "vesafb: BUG, returned from vm86 with %x\n", ret); - goto vm86_done_call; - } + printk(KERN_ERR "vesafb: BUG, returned from " + "vm86 with %x\n", ret); + goto out; + } } -vm86_done_call: - - /* copy the registers' state back to the caller's struct */ +out: /* copy the registers' state back to the caller's struct */ memcpy(regs, &vm86.regs, sizeof(struct vm86_regs)); } -#define vesafb_get_string(str) { \ - \ - /* the address is in the form ssssoooo, where oooo = offset, ssss = segment */ \ - addr = ((vbe_pib(task->res)->str & 0xffff0000) >> 12) + \ - (vbe_pib(task->res)->str & 0x0000ffff); \ - \ - /* the data is in ROM which is shared between processes, so we just translate the \ - real mode address into one visible from the kernel space */ \ - if (addr >= 0xa0000) { \ - vbe_pib(task->res)->str = (u32) __va(addr); \ - \ - /* the data is in the buffer, we just have to convert the address so that it would \ - point into the buffer user provided */ \ - } else if (addr > REAL_MEM+BUFFER && addr < REAL_MEM+BUFFER + \ - sizeof(struct vesafb_vbe_info_block)) { \ - addr -= BUFFER+REAL_MEM; \ - vbe_pib(task->res)->str = (u32) (task->res + addr); \ - \ - /* this should never happen: someone was insane enough to put the data somewhere in the RAM */ \ - } else { \ - vbe_pib(task->res)->str = (u32) ""; \ - } \ +static int vesafb_remap_pfn_range(unsigned long start, unsigned long end, + unsigned long pgoff, unsigned long prot) +{ + struct vm_area_struct *vma; + struct mm_struct *mm = current->mm; + int ret = 0; + + vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + if (!vma) + return -ENOMEM; + memset(vma, 0, sizeof(*vma)); + down_write(&mm->mmap_sem); + vma->vm_mm = mm; + vma->vm_start = start; + vma->vm_end = end; + vma->vm_flags = VM_READ | VM_WRITE | VM_EXEC; + vma->vm_flags |= mm->def_flags; + vma->vm_page_prot.pgprot = prot; + vma->vm_pgoff = pgoff; + + if ((ret = insert_vm_struct(mm, vma))) { + up_write(&mm->mmap_sem); + kmem_cache_free(vm_area_cachep, vma); + return ret; + } + ret = remap_pfn_range(vma, + vma->vm_start, + vma->vm_pgoff, + vma->vm_end-vma->vm_start, + vma->vm_page_prot); + up_write(&mm->mmap_sem); + return ret; +} + +static inline void* vesafb_init_mem(void) +{ + void *mem, *t; + int ret = 0; + + mem = kmalloc(REAL_MEM_SIZE, GFP_KERNEL); + if (!mem) + return NULL; + + for (t = (void*)0x0; t < (void*)REAL_MEM_SIZE; t += PAGE_SIZE) { + SetPageReserved(virt_to_page(__va(t))); + } + + /* This area is should already be marked as reserved, but we + * set the page attributes here to be completely sure about it. */ + for (t = (void*)0xa0000; t < (void*)0xd0000; t += PAGE_SIZE) { + SetPageReserved(virt_to_page(__va(t))); + } + + /* The memory chunks we're remapping here should be multiples + * of PAGE_SIZE. */ + ret += vesafb_remap_pfn_range(0x00000, REAL_MEM_SIZE, + __pa(mem) >> PAGE_SHIFT, + PROT_READ | PROT_EXEC | PROT_WRITE); + ret += vesafb_remap_pfn_range(0xa0000, 0xd0000, + 0xa0000 >> PAGE_SHIFT, + PROT_READ | PROT_EXEC | PROT_WRITE); + if (ret) { + printk(KERN_ERR "vesafb thread: memory remapping failed\n"); + return NULL; + } + + /* Copy the first REAL_MEM_SIZE bytes from low mem to our private + * memory, which is then used for the vm86 calls. */ + memcpy((void*)0x0, __va(0x0), REAL_MEM_SIZE); + return mem; +} + +static inline void vesafb_deinit_mem(void *mem) +{ + if (!mem) + return; + kfree(mem); + return; +} + +#define vesafb_get_string(str) \ +{ \ + /* The address is in the form ssssoooo, where oooo = offset, \ + * ssss = segment */ \ + addr = ((p_vbe(tsk->buf)->str & 0xffff0000) >> 12) + \ + (p_vbe(tsk->buf)->str & 0x0000ffff); \ + \ + /* The data is in ROM which is shared between processes, so we \ + * just translate the real mode address into one visible from \ + * kernel space */ \ + if (addr >= 0xa0000) { \ + p_vbe(tsk->buf)->str = (u32) __va(addr); \ + \ + /* The data is in the buffer, we just have to convert the \ + * address so that it points into the buffer user provided. */ \ + } else if (addr > BUF_ADDR && addr < BUF_ADDR + \ + sizeof(struct vesafb_vbe_ib)) { \ + addr -= BUF_ADDR; \ + p_vbe(tsk->buf)->str = (u32) (tsk->buf + addr); \ + \ + /* This should never happen: someone was insane enough to put \ + * the data somewhere in RAM.. */ \ + } else { \ + p_vbe(tsk->buf)->str = (u32) ""; \ + } \ } -void vesafb_handle_tasks(void) +void vesafb_handle_getvbeib(struct vesafb_task *tsk) { - struct vesafb_task *task; - struct list_head *node, *next; int addr, res; - - down(&vesafb_sem); - list_for_each_safe(node, next, &vesafb_task_list) { - - task = container_of(node, struct vesafb_task, node); - - switch (task->type) { - - case VESAFB_TASK_DOVM86: - vesafb_do_vm86(&task->regs); - break; - - case VESAFB_TASK_GETVBE_IB: - task->regs.es = (REAL_MEM >> 4); - task->regs.edi = BUFFER; - strncpy(vbe_pib(REAL_MEM+BUFFER)->vbe_signature,"VBE2",4); - - vesafb_do_vm86(&task->regs); - - memcpy(task->res, (void*)(REAL_MEM + BUFFER), sizeof(struct vesafb_vbe_info_block)); - - /* the OEM fields were not defined prior to VBE 2.0 */ - if (vbe_pib(task->res)->vbe_version >= 0x200) { - vesafb_get_string(oem_string_ptr); - vesafb_get_string(oem_vendor_name_ptr); - vesafb_get_string(oem_product_name_ptr); - vesafb_get_string(oem_product_rev_ptr); - } - - /* this is basically the same as vesafb_get_string */ - addr = ((vbe_pib(task->res)->mode_list_ptr & 0xffff0000) >> 12) + - (vbe_pib(task->res)->mode_list_ptr & 0x0000ffff); - - if (addr >= 0xa0000) { - vbe_pib(task->res)->mode_list_ptr = (u32) __va(addr); - } else if (addr > REAL_MEM+BUFFER && addr < REAL_MEM+BUFFER + - sizeof(struct vesafb_vbe_info_block)) - { - addr -= BUFFER+REAL_MEM; - vbe_pib(task->res)->mode_list_ptr = (u32) (task->res + addr); - } else { - res = 0; - printk(KERN_WARNING "vesafb: warning, copying modelist from somewhere in RAM!\n"); - while (*(u16*)(addr+res) != 0xffff && - res < (sizeof(vbe_pib(task->res)->reserved) - 2) ) - { - *(u16*) ((u32)&(vbe_pib(task->res)->reserved) + res) = - *(u16*)(addr+res); - res += 2; - } - - *(u16*) ((u32)&(vbe_pib(task->res)->reserved) + res) = 0xffff; - } - break; - - case VESAFB_TASK_GETVBE_MODEINFO: - task->regs.es = (REAL_MEM >> 4); - task->regs.edi = BUFFER; - vesafb_do_vm86(&task->regs); - memcpy(task->res, (void*)(REAL_MEM + BUFFER), sizeof(struct vesafb_mode_info_block)); - break; - - case VESAFB_TASK_SWITCHMODE: - if (task->res != NULL) { - task->regs.es = (REAL_MEM >> 4); - task->regs.edi = BUFFER; - memcpy((void*)(REAL_MEM + BUFFER), task->res, sizeof(struct vesafb_crtc_info_block)); - } - - vesafb_do_vm86(&task->regs); - break; - - case VESAFB_TASK_SETPAL: - task->regs.es = (REAL_MEM >> 4); - task->regs.edi = BUFFER; - memcpy((void*)(REAL_MEM + BUFFER), task->res, sizeof(struct vesafb_pal_entry)); - vesafb_do_vm86(&task->regs); - break; - - case VESAFB_TASK_GETEDID: - task->regs.es = (REAL_MEM >> 4); - task->regs.edi = BUFFER; - vesafb_do_vm86(&task->regs); - memcpy(task->res, (void*)(REAL_MEM + BUFFER), EDID_LENGTH); - - default: - break; + + tsk->regs.es = (BUF_ADDR >> 4); + tsk->regs.edi = (BUF_ADDR & 0x000f); + strncpy(p_vbe(BUF_ADDR)->vbe_signature, "VBE2", 4); + + vesafb_do_vm86(&tsk->regs); + memcpy(tsk->buf, (void*)(BUF_ADDR), sizeof(struct vesafb_vbe_ib)); + + /* The OEM fields were not defined prior to VBE 2.0 */ + if (p_vbe(tsk->buf)->vbe_version >= 0x200) { + vesafb_get_string(oem_string_ptr); + vesafb_get_string(oem_vendor_name_ptr); + vesafb_get_string(oem_product_name_ptr); + vesafb_get_string(oem_product_rev_ptr); + } + + /* This is basically the same as vesafb_get_string() */ + addr = ((p_vbe(tsk->buf)->mode_list_ptr & 0xffff0000) >> 12) + + (p_vbe(tsk->buf)->mode_list_ptr & 0x0000ffff); + + if (addr >= 0xa0000) { + p_vbe(tsk->buf)->mode_list_ptr = (u32) __va(addr); + } else if (addr > BUF_ADDR && addr < BUF_ADDR + + sizeof(struct vesafb_vbe_ib)) + { + addr -= BUF_ADDR; + p_vbe(tsk->buf)->mode_list_ptr = (u32) (tsk->buf + addr); + } else { + res = 0; + printk(KERN_WARNING "vesafb: warning, copying modelist " + "from somewhere in RAM!\n"); + while (*(u16*)(addr+res) != 0xffff && + res < (sizeof(p_vbe(tsk->buf)->reserved) - 2) ) + { + *(u16*) ((u32)&(p_vbe(tsk->buf)->reserved) + res) = + *(u16*)(addr+res); + res += 2; } - - task->done = 1; - if (task->flags & VESAFB_FLAG_FREESTRUCT) { - kfree(task); + *(u16*) ((u32)&(p_vbe(tsk->buf)->reserved) + res) = 0xffff; + } +} + +int vesafb_handle_tasks(void) +{ + struct vesafb_task *tsk; + struct list_head *curr, *next; + int ret = 0; + + down(&vesafb_task_list_sem); + list_for_each_safe(curr, next, &vesafb_task_list) { + tsk = list_entry(curr, struct vesafb_task, node); + + if (tsk->flags & TF_EXIT) { + ret = 1; + goto task_done; + } + if (tsk->flags & TF_GETVBEIB) { + vesafb_handle_getvbeib(tsk); + goto task_done; } + if (tsk->flags & TF_MASK_BUF) { + tsk->regs.es = (BUF_ADDR >> 4); + tsk->regs.edi = (BUF_ADDR & 0x000f); + } + if (tsk->flags & TF_USE_BUF) + memcpy((void*)BUF_ADDR, tsk->buf, tsk->buf_len); + + vesafb_do_vm86(&tsk->regs); + + if (tsk->flags & TF_RETURN_BUF) + memcpy(tsk->buf, (void*)BUF_ADDR, tsk->buf_len); + +task_done: list_del(curr); + complete(&tsk->done); } - up(&vesafb_sem); - list_del_init(node); + /* If we're going to kill this thread, don't allow any elements + * to be added to the task list. */ + if (!ret) + up(&vesafb_task_list_sem); + + return ret; } +/* + * This 'hybrid' thread serves as a backend for vesafb-tng, handling all vm86 + * calls. It is started as a kernel thread. It then creates its own mm struct, + * thus separating itself from any userspace processes. At this moment, it + * stops being a kernel thread (kernel threads have mm = NULL) and becomes + * a 'hybrid' thread -- one that has full access to kernel space, yet runs + * with its own address space. + * + * This is necessary because in order to make vm86 calls some parts of the + * first 1MB of RAM have to be setup to mimic the real mode. These are: + * - interrupt vector table [0x00000-0x003ff] + * - BIOS data area [0x00400-0x004ff] + * - video BIOS [0xc0000-0xcffff] + * - motherboard BIOS [0xf0000-0xfffff] + * and possibly also: + * - the video memory segments [0xa0000-0xbffff] + */ int vesafb_thread(void *unused) { - struct vm_area_struct vma; - struct page *page; - - int ret, err = 0; - void *mem; + void *mem = NULL; + int err = 0; set_fs(KERNEL_DS); daemonize("vesafb"); - DPRINTK("started vesafb thread\n"); - - current->mm = current->active_mm; - mem = kmalloc(REAL_MEM_SIZE,GFP_KERNEL); - - if (!mem) - return -ENOMEM; - - for (page = virt_to_page(mem); page < virt_to_page(mem+REAL_MEM_SIZE); page++) { - SetPageReserved(page); - } - -#ifdef CONFIG_SMP - cpus_clear(current->active_mm->cpu_vm_mask); - cpu_set(smp_processor_id(), current->active_mm->cpu_vm_mask); -#endif - - vma.vm_mm = current->active_mm; - vma.vm_page_prot.pgprot = PROT_READ | PROT_EXEC | PROT_WRITE; - - ret = io_remap_page_range(&vma, 0x000000, __pa(mem), REAL_MEM_SIZE, vma.vm_page_prot); - ret += io_remap_page_range(&vma, 0x0a0000, 0x0a0000, 0x100000 - 0x0a0000, vma.vm_page_prot); - if (ret) { - printk(KERN_ERR "vesafb thread: memory remapping failed\n"); - err = -EINVAL; + if (set_new_mm()) { + err = -ENOMEM; + goto thr_end; + } + mem = vesafb_init_mem(); + if (!mem) { + err = -ENOMEM; goto thr_end; } - /* copy the first 0x20000 bytes from low mem to our private memory, which is - * then used for the vm86 calls */ - memcpy((void*)0x0, __va(0x0), REAL_MEM_SIZE); + DPRINTK("started vesafb thread\n"); - ioperm(0,1024,1); /* we can live if it fails, so don't bother checking for errors */ + /* Having an IO bitmap makes things faster as we avoid GPFs + * when running vm86 code. We can live if it fails, though, + * so don't bother checking for errors. */ + ioperm(0,1024,1); set_user_nice(current, -10); - + + complete(&vesafb_th_completion); + while (1) { - vesafb_handle_tasks(); - wait_event_interruptible(vesafb_wait, !list_empty(&vesafb_task_list)); - try_to_freeze(); - if (signal_pending(current)) + if (vesafb_handle_tasks()) break; + wait_event_interruptible(vesafb_wait, + !list_empty(&vesafb_task_list)); + try_to_freeze(); } -thr_end: - DPRINTK("exiting the vesafb thread\n"); - vesafb_pid = 0; - - for (page = virt_to_page(mem); page < virt_to_page(mem+REAL_MEM_SIZE); page++) { - ClearPageReserved(page); - } - - kfree(mem); +out: DPRINTK("exiting the vesafb thread\n"); + vesafb_pid = -1; + + /* Now that all callers know this thread is no longer running + * (pid < 0), allow them to continue. */ + up(&vesafb_task_list_sem); + vesafb_deinit_mem(mem); return err; +thr_end: + down(&vesafb_task_list_sem); + complete(&vesafb_th_completion); + goto out; } -int vesafb_init_thread(void) +int vesafb_queue_task(struct vesafb_task *tsk) +{ + down(&vesafb_task_list_sem); + if (vesafb_pid < 0) + return -1; + list_add_tail(&tsk->node, &vesafb_task_list); + up(&vesafb_task_list_sem); + wake_up(&vesafb_wait); + return 0; +} + +int vesafb_wait_for_thread(void) +{ + /* PID 0 means that the thread is still initializing. */ + if (vesafb_pid < 0) + return -1; + wait_for_completion(&vesafb_th_completion); + return 0; +} + +int __init vesafb_init_thread(void) { vesafb_pid = kernel_thread(vesafb_thread,NULL,0); return 0; } -EXPORT_SYMBOL(vesafb_pid); -EXPORT_SYMBOL(vesafb_queue_task); +#ifdef MODULE +void __exit vesafb_kill_thread(void) +{ + struct vesafb_task *tsk; + if (vesafb_pid <= 0) + return; + + vesafb_create_task(tsk); + if (!tsk) + return; + tsk->flags |= TF_EXIT; + vesafb_queue_task(tsk); + vesafb_wait_for_task(tsk); + kfree(tsk); + return; +} +module_exit(vesafb_kill_thread); +#endif +module_init(vesafb_init_thread); + +EXPORT_SYMBOL_GPL(vesafb_queue_task); +EXPORT_SYMBOL_GPL(vesafb_wait_for_thread); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michal Januszewski"); + diff -Naurp linux-2.6.13-gentoo-r2/drivers/video/vesafb-tng.c linux-2.6.13-gentoo-r2-vesa/drivers/video/vesafb-tng.c --- linux-2.6.13-gentoo-r2/drivers/video/vesafb-tng.c 2005-09-23 15:46:45.000000000 +0200 +++ linux-2.6.13-gentoo-r2-vesa/drivers/video/vesafb-tng.c 2005-09-23 15:43:44.000000000 +0200 @@ -1,17 +1,11 @@ /* * Framebuffer driver for VBE 2.0+ compliant graphic boards * - * (c) 2004-2005 Michal Januszewski - * Based upon vesafb code by Gerd Knorr - * + * (c) 2004-2005 Michal Januszewski #include #include @@ -23,9 +17,10 @@ #include #include #include +#include #include