iPXE
|
00001 /* 00002 * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. 00003 * 00004 * This program is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU General Public License as 00006 * published by the Free Software Foundation; either version 2 of the 00007 * License, or any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, but 00010 * WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 * General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program; if not, write to the Free Software 00016 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 * 02110-1301, USA. 00018 * 00019 * You can also choose to distribute this program under the terms of 00020 * the Unmodified Binary Distribution Licence (as given in the file 00021 * COPYING.UBDL), provided that you have satisfied its requirements. 00022 */ 00023 00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); 00025 00026 /** @file 00027 * 00028 * VESA frame buffer console 00029 * 00030 */ 00031 00032 #include <stdlib.h> 00033 #include <errno.h> 00034 #include <limits.h> 00035 #include <realmode.h> 00036 #include <ipxe/console.h> 00037 #include <ipxe/io.h> 00038 #include <ipxe/ansicol.h> 00039 #include <ipxe/fbcon.h> 00040 #include <ipxe/vesafb.h> 00041 #include <config/console.h> 00042 00043 /* Avoid dragging in BIOS console if not otherwise used */ 00044 extern struct console_driver bios_console; 00045 struct console_driver bios_console __attribute__ (( weak )); 00046 00047 /* Disambiguate the various error causes */ 00048 #define EIO_FAILED __einfo_error ( EINFO_EIO_FAILED ) 00049 #define EINFO_EIO_FAILED \ 00050 __einfo_uniqify ( EINFO_EIO, 0x01, \ 00051 "Function call failed" ) 00052 #define EIO_HARDWARE __einfo_error ( EINFO_EIO_HARDWARE ) 00053 #define EINFO_EIO_HARDWARE \ 00054 __einfo_uniqify ( EINFO_EIO, 0x02, \ 00055 "Not supported in current configuration" ) 00056 #define EIO_MODE __einfo_error ( EINFO_EIO_MODE ) 00057 #define EINFO_EIO_MODE \ 00058 __einfo_uniqify ( EINFO_EIO, 0x03, \ 00059 "Invalid in current video mode" ) 00060 #define EIO_VBE( code ) \ 00061 EUNIQ ( EINFO_EIO, (code), EIO_FAILED, EIO_HARDWARE, EIO_MODE ) 00062 00063 /* Set default console usage if applicable 00064 * 00065 * We accept either CONSOLE_FRAMEBUFFER or CONSOLE_VESAFB. 00066 */ 00067 #if ( defined ( CONSOLE_FRAMEBUFFER ) && ! defined ( CONSOLE_VESAFB ) ) 00068 #define CONSOLE_VESAFB CONSOLE_FRAMEBUFFER 00069 #endif 00070 #if ! ( defined ( CONSOLE_VESAFB ) && CONSOLE_EXPLICIT ( CONSOLE_VESAFB ) ) 00071 #undef CONSOLE_VESAFB 00072 #define CONSOLE_VESAFB ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG ) 00073 #endif 00074 00075 /** Character height */ 00076 #define VESAFB_CHAR_HEIGHT 16 00077 00078 /** Font corresponding to selected character width and height */ 00079 #define VESAFB_FONT VBE_FONT_8x16 00080 00081 /* Forward declaration */ 00082 struct console_driver vesafb_console __console_driver; 00083 00084 /** A VESA frame buffer */ 00085 struct vesafb { 00086 /** Frame buffer console */ 00087 struct fbcon fbcon; 00088 /** Physical start address */ 00089 physaddr_t start; 00090 /** Pixel geometry */ 00091 struct fbcon_geometry pixel; 00092 /** Colour mapping */ 00093 struct fbcon_colour_map map; 00094 /** Font definition */ 00095 struct fbcon_font font; 00096 /** Character glyphs */ 00097 struct segoff glyphs; 00098 /** Saved VGA mode */ 00099 uint8_t saved_mode; 00100 }; 00101 00102 /** The VESA frame buffer */ 00103 static struct vesafb vesafb; 00104 00105 /** Base memory buffer used for VBE calls */ 00106 union vbe_buffer { 00107 /** VBE controller information block */ 00108 struct vbe_controller_info controller; 00109 /** VBE mode information block */ 00110 struct vbe_mode_info mode; 00111 }; 00112 static union vbe_buffer __bss16 ( vbe_buf ); 00113 #define vbe_buf __use_data16 ( vbe_buf ) 00114 00115 /** 00116 * Convert VBE status code to iPXE status code 00117 * 00118 * @v status VBE status code 00119 * @ret rc Return status code 00120 */ 00121 static int vesafb_rc ( unsigned int status ) { 00122 unsigned int code; 00123 00124 if ( ( status & 0xff ) != 0x4f ) 00125 return -ENOTSUP; 00126 code = ( ( status >> 8 ) & 0xff ); 00127 return ( code ? -EIO_VBE ( code ) : 0 ); 00128 } 00129 00130 /** 00131 * Get character glyph 00132 * 00133 * @v character Character 00134 * @v glyph Character glyph to fill in 00135 */ 00136 static void vesafb_glyph ( unsigned int character, uint8_t *glyph ) { 00137 size_t offset = ( character * VESAFB_CHAR_HEIGHT ); 00138 00139 copy_from_real ( glyph, vesafb.glyphs.segment, 00140 ( vesafb.glyphs.offset + offset ), VESAFB_CHAR_HEIGHT); 00141 } 00142 00143 /** 00144 * Get font definition 00145 * 00146 */ 00147 static void vesafb_font ( void ) { 00148 00149 /* Get font information 00150 * 00151 * Working around gcc bugs is icky here. The value we want is 00152 * returned in %ebp, but there's no way to specify %ebp in an 00153 * output constraint. We can't put %ebp in the clobber list, 00154 * because this tends to cause random build failures on some 00155 * gcc versions. We can't manually push/pop %ebp and return 00156 * the value via a generic register output constraint, because 00157 * gcc might choose to use %ebp to satisfy that constraint 00158 * (and we have no way to prevent it from so doing). 00159 * 00160 * Work around this hideous mess by using %ecx and %edx as the 00161 * output registers, since they get clobbered anyway. 00162 */ 00163 __asm__ __volatile__ ( REAL_CODE ( "pushw %%bp\n\t" /* gcc bug */ 00164 "int $0x10\n\t" 00165 "movw %%es, %%cx\n\t" 00166 "movw %%bp, %%dx\n\t" 00167 "popw %%bp\n\t" /* gcc bug */ ) 00168 : "=c" ( vesafb.glyphs.segment ), 00169 "=d" ( vesafb.glyphs.offset ) 00170 : "a" ( VBE_GET_FONT ), 00171 "b" ( VESAFB_FONT ) ); 00172 DBGC ( &vbe_buf, "VESAFB has font %04x at %04x:%04x\n", 00173 VESAFB_FONT, vesafb.glyphs.segment, vesafb.glyphs.offset ); 00174 vesafb.font.height = VESAFB_CHAR_HEIGHT; 00175 vesafb.font.glyph = vesafb_glyph; 00176 } 00177 00178 /** 00179 * Get VBE mode list 00180 * 00181 * @ret mode_numbers Mode number list (terminated with VBE_MODE_END) 00182 * @ret rc Return status code 00183 * 00184 * The caller is responsible for eventually freeing the mode list. 00185 */ 00186 static int vesafb_mode_list ( uint16_t **mode_numbers ) { 00187 struct vbe_controller_info *controller = &vbe_buf.controller; 00188 userptr_t video_mode_ptr; 00189 uint16_t mode_number; 00190 uint16_t status; 00191 size_t len; 00192 int rc; 00193 00194 /* Avoid returning uninitialised data on error */ 00195 *mode_numbers = NULL; 00196 00197 /* Get controller information block */ 00198 controller->vbe_signature = 0; 00199 __asm__ __volatile__ ( REAL_CODE ( "int $0x10" ) 00200 : "=a" ( status ) 00201 : "a" ( VBE_CONTROLLER_INFO ), 00202 "D" ( __from_data16 ( controller ) ) 00203 : "memory", "ebx", "edx" ); 00204 if ( ( rc = vesafb_rc ( status ) ) != 0 ) { 00205 DBGC ( &vbe_buf, "VESAFB could not get controller information: " 00206 "[%04x] %s\n", status, strerror ( rc ) ); 00207 return rc; 00208 } 00209 if ( controller->vbe_signature != VBE_CONTROLLER_SIGNATURE ) { 00210 DBGC ( &vbe_buf, "VESAFB invalid controller signature " 00211 "\"%c%c%c%c\"\n", ( controller->vbe_signature >> 0 ), 00212 ( controller->vbe_signature >> 8 ), 00213 ( controller->vbe_signature >> 16 ), 00214 ( controller->vbe_signature >> 24 ) ); 00215 DBGC_HDA ( &vbe_buf, 0, controller, sizeof ( *controller ) ); 00216 return -EINVAL; 00217 } 00218 DBGC ( &vbe_buf, "VESAFB found VBE version %d.%d with mode list at " 00219 "%04x:%04x\n", controller->vbe_major_version, 00220 controller->vbe_minor_version, 00221 controller->video_mode_ptr.segment, 00222 controller->video_mode_ptr.offset ); 00223 00224 /* Calculate length of mode list */ 00225 video_mode_ptr = real_to_user ( controller->video_mode_ptr.segment, 00226 controller->video_mode_ptr.offset ); 00227 len = 0; 00228 do { 00229 copy_from_user ( &mode_number, video_mode_ptr, len, 00230 sizeof ( mode_number ) ); 00231 len += sizeof ( mode_number ); 00232 } while ( mode_number != VBE_MODE_END ); 00233 00234 /* Allocate and fill mode list */ 00235 *mode_numbers = malloc ( len ); 00236 if ( ! *mode_numbers ) 00237 return -ENOMEM; 00238 copy_from_user ( *mode_numbers, video_mode_ptr, 0, len ); 00239 00240 return 0; 00241 } 00242 00243 /** 00244 * Get video mode information 00245 * 00246 * @v mode_number Mode number 00247 * @ret rc Return status code 00248 */ 00249 static int vesafb_mode_info ( unsigned int mode_number ) { 00250 struct vbe_mode_info *mode = &vbe_buf.mode; 00251 uint16_t status; 00252 int rc; 00253 00254 /* Get mode information */ 00255 __asm__ __volatile__ ( REAL_CODE ( "int $0x10" ) 00256 : "=a" ( status ) 00257 : "a" ( VBE_MODE_INFO ), 00258 "c" ( mode_number ), 00259 "D" ( __from_data16 ( mode ) ) 00260 : "memory" ); 00261 if ( ( rc = vesafb_rc ( status ) ) != 0 ) { 00262 DBGC ( &vbe_buf, "VESAFB could not get mode %04x information: " 00263 "[%04x] %s\n", mode_number, status, strerror ( rc ) ); 00264 return rc; 00265 } 00266 DBGC ( &vbe_buf, "VESAFB mode %04x %dx%d %dbpp(%d:%d:%d:%d) model " 00267 "%02x [x%d]%s%s%s%s%s\n", mode_number, mode->x_resolution, 00268 mode->y_resolution, mode->bits_per_pixel, mode->rsvd_mask_size, 00269 mode->red_mask_size, mode->green_mask_size, mode->blue_mask_size, 00270 mode->memory_model, ( mode->number_of_image_pages + 1 ), 00271 ( ( mode->mode_attributes & VBE_MODE_ATTR_SUPPORTED ) ? 00272 "" : " [unsupported]" ), 00273 ( ( mode->mode_attributes & VBE_MODE_ATTR_TTY ) ? 00274 " [tty]" : "" ), 00275 ( ( mode->mode_attributes & VBE_MODE_ATTR_GRAPHICS ) ? 00276 "" : " [text]" ), 00277 ( ( mode->mode_attributes & VBE_MODE_ATTR_LINEAR ) ? 00278 "" : " [nonlinear]" ), 00279 ( ( mode->mode_attributes & VBE_MODE_ATTR_TRIPLE_BUF ) ? 00280 " [buf]" : "" ) ); 00281 00282 return 0; 00283 } 00284 00285 /** 00286 * Set video mode 00287 * 00288 * @v mode_number Mode number 00289 * @ret rc Return status code 00290 */ 00291 static int vesafb_set_mode ( unsigned int mode_number ) { 00292 struct vbe_mode_info *mode = &vbe_buf.mode; 00293 uint16_t status; 00294 int rc; 00295 00296 /* Get mode information */ 00297 if ( ( rc = vesafb_mode_info ( mode_number ) ) != 0 ) 00298 return rc; 00299 00300 /* Record mode parameters */ 00301 vesafb.start = mode->phys_base_ptr; 00302 vesafb.pixel.width = mode->x_resolution; 00303 vesafb.pixel.height = mode->y_resolution; 00304 vesafb.pixel.len = ( ( mode->bits_per_pixel + 7 ) / 8 ); 00305 vesafb.pixel.stride = mode->bytes_per_scan_line; 00306 DBGC ( &vbe_buf, "VESAFB mode %04x has frame buffer at %08x\n", 00307 mode_number, mode->phys_base_ptr ); 00308 00309 /* Initialise font colours */ 00310 vesafb.map.red_scale = ( 8 - mode->red_mask_size ); 00311 vesafb.map.green_scale = ( 8 - mode->green_mask_size ); 00312 vesafb.map.blue_scale = ( 8 - mode->blue_mask_size ); 00313 vesafb.map.red_lsb = mode->red_field_position; 00314 vesafb.map.green_lsb = mode->green_field_position; 00315 vesafb.map.blue_lsb = mode->blue_field_position; 00316 00317 /* Select this mode */ 00318 __asm__ __volatile__ ( REAL_CODE ( "int $0x10" ) 00319 : "=a" ( status ) 00320 : "a" ( VBE_SET_MODE ), 00321 "b" ( mode_number ) ); 00322 if ( ( rc = vesafb_rc ( status ) ) != 0 ) { 00323 DBGC ( &vbe_buf, "VESAFB could not set mode %04x: [%04x] %s\n", 00324 mode_number, status, strerror ( rc ) ); 00325 return rc; 00326 } 00327 00328 return 0; 00329 } 00330 00331 /** 00332 * Select video mode 00333 * 00334 * @v mode_numbers Mode number list (terminated with VBE_MODE_END) 00335 * @v min_width Minimum required width (in pixels) 00336 * @v min_height Minimum required height (in pixels) 00337 * @v min_bpp Minimum required colour depth (in bits per pixel) 00338 * @ret mode_number Mode number, or negative error 00339 */ 00340 static int vesafb_select_mode ( const uint16_t *mode_numbers, 00341 unsigned int min_width, unsigned int min_height, 00342 unsigned int min_bpp ) { 00343 struct vbe_mode_info *mode = &vbe_buf.mode; 00344 int best_mode_number = -ENOENT; 00345 unsigned int best_score = INT_MAX; 00346 unsigned int score; 00347 uint16_t mode_number; 00348 int rc; 00349 00350 /* Find the first suitable mode */ 00351 while ( ( mode_number = *(mode_numbers++) ) != VBE_MODE_END ) { 00352 00353 /* Force linear mode variant */ 00354 mode_number |= VBE_MODE_LINEAR; 00355 00356 /* Get mode information */ 00357 if ( ( rc = vesafb_mode_info ( mode_number ) ) != 0 ) 00358 continue; 00359 00360 /* Skip unusable modes */ 00361 if ( ( mode->mode_attributes & ( VBE_MODE_ATTR_SUPPORTED | 00362 VBE_MODE_ATTR_GRAPHICS | 00363 VBE_MODE_ATTR_LINEAR ) ) != 00364 ( VBE_MODE_ATTR_SUPPORTED | VBE_MODE_ATTR_GRAPHICS | 00365 VBE_MODE_ATTR_LINEAR ) ) { 00366 continue; 00367 } 00368 if ( mode->memory_model != VBE_MODE_MODEL_DIRECT_COLOUR ) 00369 continue; 00370 00371 /* Skip modes not meeting the requirements */ 00372 if ( ( mode->x_resolution < min_width ) || 00373 ( mode->y_resolution < min_height ) || 00374 ( mode->bits_per_pixel < min_bpp ) ) { 00375 continue; 00376 } 00377 00378 /* Select this mode if it has the best (i.e. lowest) 00379 * score. We choose the scoring system to favour 00380 * modes close to the specified width and height; 00381 * within modes of the same width and height we prefer 00382 * a higher colour depth. 00383 */ 00384 score = ( ( mode->x_resolution * mode->y_resolution ) - 00385 mode->bits_per_pixel ); 00386 if ( score < best_score ) { 00387 best_mode_number = mode_number; 00388 best_score = score; 00389 } 00390 } 00391 00392 if ( best_mode_number >= 0 ) { 00393 DBGC ( &vbe_buf, "VESAFB selected mode %04x\n", 00394 best_mode_number ); 00395 } else { 00396 DBGC ( &vbe_buf, "VESAFB found no suitable mode\n" ); 00397 } 00398 00399 return best_mode_number; 00400 } 00401 00402 /** 00403 * Restore video mode 00404 * 00405 */ 00406 static void vesafb_restore ( void ) { 00407 uint32_t discard_a; 00408 00409 /* Restore saved VGA mode */ 00410 __asm__ __volatile__ ( REAL_CODE ( "int $0x10" ) 00411 : "=a" ( discard_a ) 00412 : "a" ( VBE_SET_VGA_MODE | vesafb.saved_mode ) ); 00413 DBGC ( &vbe_buf, "VESAFB restored VGA mode %#02x\n", 00414 vesafb.saved_mode ); 00415 } 00416 00417 /** 00418 * Initialise VESA frame buffer 00419 * 00420 * @v config Console configuration, or NULL to reset 00421 * @ret rc Return status code 00422 */ 00423 static int vesafb_init ( struct console_configuration *config ) { 00424 uint32_t discard_b; 00425 uint16_t *mode_numbers; 00426 int mode_number; 00427 int rc; 00428 00429 /* Record current VGA mode */ 00430 __asm__ __volatile__ ( REAL_CODE ( "int $0x10" ) 00431 : "=a" ( vesafb.saved_mode ), "=b" ( discard_b ) 00432 : "a" ( VBE_GET_VGA_MODE ) ); 00433 DBGC ( &vbe_buf, "VESAFB saved VGA mode %#02x\n", vesafb.saved_mode ); 00434 00435 /* Get VESA mode list */ 00436 if ( ( rc = vesafb_mode_list ( &mode_numbers ) ) != 0 ) 00437 goto err_mode_list; 00438 00439 /* Select mode */ 00440 if ( ( mode_number = vesafb_select_mode ( mode_numbers, config->width, 00441 config->height, 00442 config->depth ) ) < 0 ) { 00443 rc = mode_number; 00444 goto err_select_mode; 00445 } 00446 00447 /* Set mode */ 00448 if ( ( rc = vesafb_set_mode ( mode_number ) ) != 0 ) 00449 goto err_set_mode; 00450 00451 /* Get font data */ 00452 vesafb_font(); 00453 00454 /* Initialise frame buffer console */ 00455 if ( ( rc = fbcon_init ( &vesafb.fbcon, phys_to_user ( vesafb.start ), 00456 &vesafb.pixel, &vesafb.map, &vesafb.font, 00457 config ) ) != 0 ) 00458 goto err_fbcon_init; 00459 00460 free ( mode_numbers ); 00461 return 0; 00462 00463 fbcon_fini ( &vesafb.fbcon ); 00464 err_fbcon_init: 00465 err_set_mode: 00466 vesafb_restore(); 00467 err_select_mode: 00468 free ( mode_numbers ); 00469 err_mode_list: 00470 return rc; 00471 } 00472 00473 /** 00474 * Finalise VESA frame buffer 00475 * 00476 */ 00477 static void vesafb_fini ( void ) { 00478 00479 /* Finalise frame buffer console */ 00480 fbcon_fini ( &vesafb.fbcon ); 00481 00482 /* Restore saved VGA mode */ 00483 vesafb_restore(); 00484 } 00485 00486 /** 00487 * Print a character to current cursor position 00488 * 00489 * @v character Character 00490 */ 00491 static void vesafb_putchar ( int character ) { 00492 00493 fbcon_putchar ( &vesafb.fbcon, character ); 00494 } 00495 00496 /** 00497 * Configure console 00498 * 00499 * @v config Console configuration, or NULL to reset 00500 * @ret rc Return status code 00501 */ 00502 static int vesafb_configure ( struct console_configuration *config ) { 00503 int rc; 00504 00505 /* Reset console, if applicable */ 00506 if ( ! vesafb_console.disabled ) { 00507 vesafb_fini(); 00508 bios_console.disabled &= ~CONSOLE_DISABLED_OUTPUT; 00509 ansicol_reset_magic(); 00510 } 00511 vesafb_console.disabled = CONSOLE_DISABLED; 00512 00513 /* Do nothing more unless we have a usable configuration */ 00514 if ( ( config == NULL ) || 00515 ( config->width == 0 ) || ( config->height == 0 ) ) { 00516 return 0; 00517 } 00518 00519 /* Initialise VESA frame buffer */ 00520 if ( ( rc = vesafb_init ( config ) ) != 0 ) 00521 return rc; 00522 00523 /* Mark console as enabled */ 00524 vesafb_console.disabled = 0; 00525 bios_console.disabled |= CONSOLE_DISABLED_OUTPUT; 00526 00527 /* Set magic colour to transparent if we have a background picture */ 00528 if ( config->pixbuf ) 00529 ansicol_set_magic_transparent(); 00530 00531 return 0; 00532 } 00533 00534 /** VESA frame buffer console driver */ 00535 struct console_driver vesafb_console __console_driver = { 00536 .usage = CONSOLE_VESAFB, 00537 .putchar = vesafb_putchar, 00538 .configure = vesafb_configure, 00539 .disabled = CONSOLE_DISABLED, 00540 };