These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / arch / i386 / interface / pcbios / vesafb.c
1 /*
2  * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 /** @file
27  *
28  * VESA frame buffer console
29  *
30  */
31
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <limits.h>
35 #include <realmode.h>
36 #include <ipxe/console.h>
37 #include <ipxe/io.h>
38 #include <ipxe/ansicol.h>
39 #include <ipxe/fbcon.h>
40 #include <ipxe/vesafb.h>
41 #include <config/console.h>
42
43 /* Avoid dragging in BIOS console if not otherwise used */
44 extern struct console_driver bios_console;
45 struct console_driver bios_console __attribute__ (( weak ));
46
47 /* Disambiguate the various error causes */
48 #define EIO_FAILED __einfo_error ( EINFO_EIO_FAILED )
49 #define EINFO_EIO_FAILED                                                \
50         __einfo_uniqify ( EINFO_EIO, 0x01,                              \
51                           "Function call failed" )
52 #define EIO_HARDWARE __einfo_error ( EINFO_EIO_HARDWARE )
53 #define EINFO_EIO_HARDWARE                                              \
54         __einfo_uniqify ( EINFO_EIO, 0x02,                              \
55                           "Not supported in current configuration" )
56 #define EIO_MODE __einfo_error ( EINFO_EIO_MODE )
57 #define EINFO_EIO_MODE                                                  \
58         __einfo_uniqify ( EINFO_EIO, 0x03,                              \
59                           "Invalid in current video mode" )
60 #define EIO_VBE( code )                                                 \
61         EUNIQ ( EINFO_EIO, (code), EIO_FAILED, EIO_HARDWARE, EIO_MODE )
62
63 /* Set default console usage if applicable */
64 #if ! ( defined ( CONSOLE_VESAFB ) && CONSOLE_EXPLICIT ( CONSOLE_VESAFB ) )
65 #undef CONSOLE_VESAFB
66 #define CONSOLE_VESAFB ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
67 #endif
68
69 /** Font corresponding to selected character width and height */
70 #define VESAFB_FONT VBE_FONT_8x16
71
72 /* Forward declaration */
73 struct console_driver vesafb_console __console_driver;
74
75 /** A VESA frame buffer */
76 struct vesafb {
77         /** Frame buffer console */
78         struct fbcon fbcon;
79         /** Physical start address */
80         physaddr_t start;
81         /** Pixel geometry */
82         struct fbcon_geometry pixel;
83         /** Margin */
84         struct fbcon_margin margin;
85         /** Colour mapping */
86         struct fbcon_colour_map map;
87         /** Font definition */
88         struct fbcon_font font;
89         /** Saved VGA mode */
90         uint8_t saved_mode;
91 };
92
93 /** The VESA frame buffer */
94 static struct vesafb vesafb;
95
96 /** Base memory buffer used for VBE calls */
97 union vbe_buffer {
98         /** VBE controller information block */
99         struct vbe_controller_info controller;
100         /** VBE mode information block */
101         struct vbe_mode_info mode;
102 };
103 static union vbe_buffer __bss16 ( vbe_buf );
104 #define vbe_buf __use_data16 ( vbe_buf )
105
106 /**
107  * Convert VBE status code to iPXE status code
108  *
109  * @v status            VBE status code
110  * @ret rc              Return status code
111  */
112 static int vesafb_rc ( unsigned int status ) {
113         unsigned int code;
114
115         if ( ( status & 0xff ) != 0x4f )
116                 return -ENOTSUP;
117         code = ( ( status >> 8 ) & 0xff );
118         return ( code ? -EIO_VBE ( code ) : 0 );
119 }
120
121 /**
122  * Get font definition
123  *
124  */
125 static void vesafb_font ( void ) {
126         struct segoff font;
127
128         /* Get font information
129          *
130          * Working around gcc bugs is icky here.  The value we want is
131          * returned in %ebp, but there's no way to specify %ebp in an
132          * output constraint.  We can't put %ebp in the clobber list,
133          * because this tends to cause random build failures on some
134          * gcc versions.  We can't manually push/pop %ebp and return
135          * the value via a generic register output constraint, because
136          * gcc might choose to use %ebp to satisfy that constraint
137          * (and we have no way to prevent it from so doing).
138          *
139          * Work around this hideous mess by using %ecx and %edx as the
140          * output registers, since they get clobbered anyway.
141          */
142         __asm__ __volatile__ ( REAL_CODE ( "pushw %%bp\n\t" /* gcc bug */
143                                            "int $0x10\n\t"
144                                            "movw %%es, %%cx\n\t"
145                                            "movw %%bp, %%dx\n\t"
146                                            "popw %%bp\n\t" /* gcc bug */ )
147                                : "=c" ( font.segment ),
148                                  "=d" ( font.offset )
149                                : "a" ( VBE_GET_FONT ),
150                                  "b" ( VESAFB_FONT ) );
151         DBGC ( &vbe_buf, "VESAFB has font %04x at %04x:%04x\n",
152                VESAFB_FONT, font.segment, font.offset );
153         vesafb.font.start = real_to_user ( font.segment, font.offset );
154 }
155
156 /**
157  * Get VBE mode list
158  *
159  * @ret mode_numbers    Mode number list (terminated with VBE_MODE_END)
160  * @ret rc              Return status code
161  *
162  * The caller is responsible for eventually freeing the mode list.
163  */
164 static int vesafb_mode_list ( uint16_t **mode_numbers ) {
165         struct vbe_controller_info *controller = &vbe_buf.controller;
166         userptr_t video_mode_ptr;
167         uint16_t mode_number;
168         uint16_t status;
169         size_t len;
170         int rc;
171
172         /* Avoid returning uninitialised data on error */
173         *mode_numbers = NULL;
174
175         /* Get controller information block */
176         controller->vbe_signature = 0;
177         __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
178                                : "=a" ( status )
179                                : "a" ( VBE_CONTROLLER_INFO ),
180                                  "D" ( __from_data16 ( controller ) )
181                                : "memory", "ebx", "edx" );
182         if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
183                 DBGC ( &vbe_buf, "VESAFB could not get controller information: "
184                        "[%04x] %s\n", status, strerror ( rc ) );
185                 return rc;
186         }
187         if ( controller->vbe_signature != VBE_CONTROLLER_SIGNATURE ) {
188                 DBGC ( &vbe_buf, "VESAFB invalid controller signature "
189                        "\"%c%c%c%c\"\n", ( controller->vbe_signature >> 0 ),
190                        ( controller->vbe_signature >> 8 ),
191                        ( controller->vbe_signature >> 16 ),
192                        ( controller->vbe_signature >> 24 ) );
193                 DBGC_HDA ( &vbe_buf, 0, controller, sizeof ( *controller ) );
194                 return -EINVAL;
195         }
196         DBGC ( &vbe_buf, "VESAFB found VBE version %d.%d with mode list at "
197                "%04x:%04x\n", controller->vbe_major_version,
198                controller->vbe_minor_version,
199                controller->video_mode_ptr.segment,
200                controller->video_mode_ptr.offset );
201
202         /* Calculate length of mode list */
203         video_mode_ptr = real_to_user ( controller->video_mode_ptr.segment,
204                                         controller->video_mode_ptr.offset );
205         len = 0;
206         do {
207                 copy_from_user ( &mode_number, video_mode_ptr, len,
208                                  sizeof ( mode_number ) );
209                 len += sizeof ( mode_number );
210         } while ( mode_number != VBE_MODE_END );
211
212         /* Allocate and fill mode list */
213         *mode_numbers = malloc ( len );
214         if ( ! *mode_numbers )
215                 return -ENOMEM;
216         copy_from_user ( *mode_numbers, video_mode_ptr, 0, len );
217
218         return 0;
219 }
220
221 /**
222  * Get video mode information
223  *
224  * @v mode_number       Mode number
225  * @ret rc              Return status code
226  */
227 static int vesafb_mode_info ( unsigned int mode_number ) {
228         struct vbe_mode_info *mode = &vbe_buf.mode;
229         uint16_t status;
230         int rc;
231
232         /* Get mode information */
233         __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
234                                : "=a" ( status )
235                                : "a" ( VBE_MODE_INFO ),
236                                  "c" ( mode_number ),
237                                  "D" ( __from_data16 ( mode ) )
238                                : "memory" );
239         if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
240                 DBGC ( &vbe_buf, "VESAFB could not get mode %04x information: "
241                        "[%04x] %s\n", mode_number, status, strerror ( rc ) );
242                 return rc;
243         }
244         DBGC ( &vbe_buf, "VESAFB mode %04x %dx%d %dbpp(%d:%d:%d:%d) model "
245                "%02x [x%d]%s%s%s%s%s\n", mode_number, mode->x_resolution,
246                mode->y_resolution, mode->bits_per_pixel, mode->rsvd_mask_size,
247                mode->red_mask_size, mode->green_mask_size, mode->blue_mask_size,
248                mode->memory_model, ( mode->number_of_image_pages + 1 ),
249                ( ( mode->mode_attributes & VBE_MODE_ATTR_SUPPORTED ) ?
250                  "" : " [unsupported]" ),
251                ( ( mode->mode_attributes & VBE_MODE_ATTR_TTY ) ?
252                  " [tty]" : "" ),
253                ( ( mode->mode_attributes & VBE_MODE_ATTR_GRAPHICS ) ?
254                  "" : " [text]" ),
255                ( ( mode->mode_attributes & VBE_MODE_ATTR_LINEAR ) ?
256                  "" : " [nonlinear]" ),
257                ( ( mode->mode_attributes & VBE_MODE_ATTR_TRIPLE_BUF ) ?
258                  " [buf]" : "" ) );
259
260         return 0;
261 }
262
263 /**
264  * Set video mode
265  *
266  * @v mode_number       Mode number
267  * @ret rc              Return status code
268  */
269 static int vesafb_set_mode ( unsigned int mode_number ) {
270         struct vbe_mode_info *mode = &vbe_buf.mode;
271         uint16_t status;
272         int rc;
273
274         /* Get mode information */
275         if ( ( rc = vesafb_mode_info ( mode_number ) ) != 0 )
276                 return rc;
277
278         /* Record mode parameters */
279         vesafb.start = mode->phys_base_ptr;
280         vesafb.pixel.width = mode->x_resolution;
281         vesafb.pixel.height = mode->y_resolution;
282         vesafb.pixel.len = ( ( mode->bits_per_pixel + 7 ) / 8 );
283         vesafb.pixel.stride = mode->bytes_per_scan_line;
284         DBGC ( &vbe_buf, "VESAFB mode %04x has frame buffer at %08x\n",
285                mode_number, mode->phys_base_ptr );
286
287         /* Initialise font colours */
288         vesafb.map.red_scale = ( 8 - mode->red_mask_size );
289         vesafb.map.green_scale = ( 8 - mode->green_mask_size );
290         vesafb.map.blue_scale = ( 8 - mode->blue_mask_size );
291         vesafb.map.red_lsb = mode->red_field_position;
292         vesafb.map.green_lsb = mode->green_field_position;
293         vesafb.map.blue_lsb = mode->blue_field_position;
294
295         /* Select this mode */
296         __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
297                                : "=a" ( status )
298                                : "a" ( VBE_SET_MODE ),
299                                  "b" ( mode_number ) );
300         if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
301                 DBGC ( &vbe_buf, "VESAFB could not set mode %04x: [%04x] %s\n",
302                        mode_number, status, strerror ( rc ) );
303                 return rc;
304         }
305
306         return 0;
307 }
308
309 /**
310  * Select video mode
311  *
312  * @v mode_numbers      Mode number list (terminated with VBE_MODE_END)
313  * @v min_width         Minimum required width (in pixels)
314  * @v min_height        Minimum required height (in pixels)
315  * @v min_bpp           Minimum required colour depth (in bits per pixel)
316  * @ret mode_number     Mode number, or negative error
317  */
318 static int vesafb_select_mode ( const uint16_t *mode_numbers,
319                                 unsigned int min_width, unsigned int min_height,
320                                 unsigned int min_bpp ) {
321         struct vbe_mode_info *mode = &vbe_buf.mode;
322         int best_mode_number = -ENOENT;
323         unsigned int best_score = INT_MAX;
324         unsigned int score;
325         uint16_t mode_number;
326         int rc;
327
328         /* Find the first suitable mode */
329         while ( ( mode_number = *(mode_numbers++) ) != VBE_MODE_END ) {
330
331                 /* Force linear mode variant */
332                 mode_number |= VBE_MODE_LINEAR;
333
334                 /* Get mode information */
335                 if ( ( rc = vesafb_mode_info ( mode_number ) ) != 0 )
336                         continue;
337
338                 /* Skip unusable modes */
339                 if ( ( mode->mode_attributes & ( VBE_MODE_ATTR_SUPPORTED |
340                                                  VBE_MODE_ATTR_GRAPHICS |
341                                                  VBE_MODE_ATTR_LINEAR ) ) !=
342                      ( VBE_MODE_ATTR_SUPPORTED | VBE_MODE_ATTR_GRAPHICS |
343                        VBE_MODE_ATTR_LINEAR ) ) {
344                         continue;
345                 }
346                 if ( mode->memory_model != VBE_MODE_MODEL_DIRECT_COLOUR )
347                         continue;
348
349                 /* Skip modes not meeting the requirements */
350                 if ( ( mode->x_resolution < min_width ) ||
351                      ( mode->y_resolution < min_height ) ||
352                      ( mode->bits_per_pixel < min_bpp ) ) {
353                         continue;
354                 }
355
356                 /* Select this mode if it has the best (i.e. lowest)
357                  * score.  We choose the scoring system to favour
358                  * modes close to the specified width and height;
359                  * within modes of the same width and height we prefer
360                  * a higher colour depth.
361                  */
362                 score = ( ( mode->x_resolution * mode->y_resolution ) -
363                           mode->bits_per_pixel );
364                 if ( score < best_score ) {
365                         best_mode_number = mode_number;
366                         best_score = score;
367                 }
368         }
369
370         if ( best_mode_number >= 0 ) {
371                 DBGC ( &vbe_buf, "VESAFB selected mode %04x\n",
372                        best_mode_number );
373         } else {
374                 DBGC ( &vbe_buf, "VESAFB found no suitable mode\n" );
375         }
376
377         return best_mode_number;
378 }
379
380 /**
381  * Restore video mode
382  *
383  */
384 static void vesafb_restore ( void ) {
385         uint32_t discard_a;
386
387         /* Restore saved VGA mode */
388         __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
389                                : "=a" ( discard_a )
390                                : "a" ( VBE_SET_VGA_MODE | vesafb.saved_mode ) );
391         DBGC ( &vbe_buf, "VESAFB restored VGA mode %#02x\n",
392                vesafb.saved_mode );
393 }
394
395 /**
396  * Initialise VESA frame buffer
397  *
398  * @v config            Console configuration, or NULL to reset
399  * @ret rc              Return status code
400  */
401 static int vesafb_init ( struct console_configuration *config ) {
402         uint32_t discard_b;
403         uint16_t *mode_numbers;
404         unsigned int xgap;
405         unsigned int ygap;
406         unsigned int left;
407         unsigned int right;
408         unsigned int top;
409         unsigned int bottom;
410         int mode_number;
411         int rc;
412
413         /* Record current VGA mode */
414         __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
415                                : "=a" ( vesafb.saved_mode ), "=b" ( discard_b )
416                                : "a" ( VBE_GET_VGA_MODE ) );
417         DBGC ( &vbe_buf, "VESAFB saved VGA mode %#02x\n", vesafb.saved_mode );
418
419         /* Get VESA mode list */
420         if ( ( rc = vesafb_mode_list ( &mode_numbers ) ) != 0 )
421                 goto err_mode_list;
422
423         /* Select mode */
424         if ( ( mode_number = vesafb_select_mode ( mode_numbers, config->width,
425                                                   config->height,
426                                                   config->depth ) ) < 0 ) {
427                 rc = mode_number;
428                 goto err_select_mode;
429         }
430
431         /* Set mode */
432         if ( ( rc = vesafb_set_mode ( mode_number ) ) != 0 )
433                 goto err_set_mode;
434
435         /* Calculate margin.  If the actual screen size is larger than
436          * the requested screen size, then update the margins so that
437          * the margin remains relative to the requested screen size.
438          * (As an exception, if a zero margin was specified then treat
439          * this as meaning "expand to edge of actual screen".)
440          */
441         xgap = ( vesafb.pixel.width - config->width );
442         ygap = ( vesafb.pixel.height - config->height );
443         left = ( xgap / 2 );
444         right = ( xgap - left );
445         top = ( ygap / 2 );
446         bottom = ( ygap - top );
447         vesafb.margin.left = ( config->left + ( config->left ? left : 0 ) );
448         vesafb.margin.right = ( config->right + ( config->right ? right : 0 ) );
449         vesafb.margin.top = ( config->top + ( config->top ? top : 0 ) );
450         vesafb.margin.bottom =
451                 ( config->bottom + ( config->bottom ? bottom : 0 ) );
452
453         /* Get font data */
454         vesafb_font();
455
456         /* Initialise frame buffer console */
457         if ( ( rc = fbcon_init ( &vesafb.fbcon, phys_to_user ( vesafb.start ),
458                                  &vesafb.pixel, &vesafb.margin, &vesafb.map,
459                                  &vesafb.font, config->pixbuf ) ) != 0 )
460                 goto err_fbcon_init;
461
462         free ( mode_numbers );
463         return 0;
464
465         fbcon_fini ( &vesafb.fbcon );
466  err_fbcon_init:
467  err_set_mode:
468         vesafb_restore();
469  err_select_mode:
470         free ( mode_numbers );
471  err_mode_list:
472         return rc;
473 }
474
475 /**
476  * Finalise VESA frame buffer
477  *
478  */
479 static void vesafb_fini ( void ) {
480
481         /* Finalise frame buffer console */
482         fbcon_fini ( &vesafb.fbcon );
483
484         /* Restore saved VGA mode */
485         vesafb_restore();
486 }
487
488 /**
489  * Print a character to current cursor position
490  *
491  * @v character         Character
492  */
493 static void vesafb_putchar ( int character ) {
494
495         fbcon_putchar ( &vesafb.fbcon, character );
496 }
497
498 /**
499  * Configure console
500  *
501  * @v config            Console configuration, or NULL to reset
502  * @ret rc              Return status code
503  */
504 static int vesafb_configure ( struct console_configuration *config ) {
505         int rc;
506
507         /* Reset console, if applicable */
508         if ( ! vesafb_console.disabled ) {
509                 vesafb_fini();
510                 bios_console.disabled &= ~CONSOLE_DISABLED_OUTPUT;
511                 ansicol_reset_magic();
512         }
513         vesafb_console.disabled = CONSOLE_DISABLED;
514
515         /* Do nothing more unless we have a usable configuration */
516         if ( ( config == NULL ) ||
517              ( config->width == 0 ) || ( config->height == 0 ) ) {
518                 return 0;
519         }
520
521         /* Initialise VESA frame buffer */
522         if ( ( rc = vesafb_init ( config ) ) != 0 )
523                 return rc;
524
525         /* Mark console as enabled */
526         vesafb_console.disabled = 0;
527         bios_console.disabled |= CONSOLE_DISABLED_OUTPUT;
528
529         /* Set magic colour to transparent if we have a background picture */
530         if ( config->pixbuf )
531                 ansicol_set_magic_transparent();
532
533         return 0;
534 }
535
536 /** VESA frame buffer console driver */
537 struct console_driver vesafb_console __console_driver = {
538         .usage = CONSOLE_VESAFB,
539         .putchar = vesafb_putchar,
540         .configure = vesafb_configure,
541         .disabled = CONSOLE_DISABLED,
542 };