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