iPXE
bios_console.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 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 #include <assert.h>
00027 #include <realmode.h>
00028 #include <bios.h>
00029 #include <biosint.h>
00030 #include <ipxe/console.h>
00031 #include <ipxe/ansiesc.h>
00032 #include <ipxe/keys.h>
00033 #include <ipxe/keymap.h>
00034 #include <ipxe/init.h>
00035 #include <config/console.h>
00036 
00037 #define ATTR_BOLD               0x08
00038 
00039 #define ATTR_FCOL_MASK          0x07
00040 #define ATTR_FCOL_BLACK         0x00
00041 #define ATTR_FCOL_BLUE          0x01
00042 #define ATTR_FCOL_GREEN         0x02
00043 #define ATTR_FCOL_CYAN          0x03
00044 #define ATTR_FCOL_RED           0x04
00045 #define ATTR_FCOL_MAGENTA       0x05
00046 #define ATTR_FCOL_YELLOW        0x06
00047 #define ATTR_FCOL_WHITE         0x07
00048 
00049 #define ATTR_BLINK              0x80
00050 
00051 #define ATTR_BCOL_MASK          0x70
00052 #define ATTR_BCOL_BLACK         0x00
00053 #define ATTR_BCOL_BLUE          0x10
00054 #define ATTR_BCOL_GREEN         0x20
00055 #define ATTR_BCOL_CYAN          0x30
00056 #define ATTR_BCOL_RED           0x40
00057 #define ATTR_BCOL_MAGENTA       0x50
00058 #define ATTR_BCOL_YELLOW        0x60
00059 #define ATTR_BCOL_WHITE         0x70
00060 
00061 #define ATTR_DEFAULT            ATTR_FCOL_WHITE
00062 
00063 /* Set default console usage if applicable */
00064 #if ! ( defined ( CONSOLE_PCBIOS ) && CONSOLE_EXPLICIT ( CONSOLE_PCBIOS ) )
00065 #undef CONSOLE_PCBIOS
00066 #define CONSOLE_PCBIOS ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
00067 #endif
00068 
00069 /** Current character attribute */
00070 static unsigned int bios_attr = ATTR_DEFAULT;
00071 
00072 /** Keypress injection lock */
00073 static uint8_t __text16 ( bios_inject_lock );
00074 #define bios_inject_lock __use_text16 ( bios_inject_lock )
00075 
00076 /** Vector for chaining to other INT 16 handlers */
00077 static struct segoff __text16 ( int16_vector );
00078 #define int16_vector __use_text16 ( int16_vector )
00079 
00080 /** Assembly wrapper */
00081 extern void int16_wrapper ( void );
00082 
00083 /**
00084  * Handle ANSI CUP (cursor position)
00085  *
00086  * @v ctx               ANSI escape sequence context
00087  * @v count             Parameter count
00088  * @v params[0]         Row (1 is top)
00089  * @v params[1]         Column (1 is left)
00090  */
00091 static void bios_handle_cup ( struct ansiesc_context *ctx __unused,
00092                               unsigned int count __unused, int params[] ) {
00093         int cx = ( params[1] - 1 );
00094         int cy = ( params[0] - 1 );
00095 
00096         if ( cx < 0 )
00097                 cx = 0;
00098         if ( cy < 0 )
00099                 cy = 0;
00100 
00101         __asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" )
00102                                : : "a" ( 0x0200 ), "b" ( 1 ),
00103                                    "d" ( ( cy << 8 ) | cx ) );
00104 }
00105 
00106 /**
00107  * Handle ANSI ED (erase in page)
00108  *
00109  * @v ctx               ANSI escape sequence context
00110  * @v count             Parameter count
00111  * @v params[0]         Region to erase
00112  */
00113 static void bios_handle_ed ( struct ansiesc_context *ctx __unused,
00114                              unsigned int count __unused,
00115                              int params[] __unused ) {
00116         /* We assume that we always clear the whole screen */
00117         assert ( params[0] == ANSIESC_ED_ALL );
00118 
00119         __asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" )
00120                                : : "a" ( 0x0600 ), "b" ( bios_attr << 8 ),
00121                                    "c" ( 0 ),
00122                                    "d" ( ( ( console_height - 1 ) << 8 ) |
00123                                          ( console_width - 1 ) ) );
00124 }
00125 
00126 /**
00127  * Handle ANSI SGR (set graphics rendition)
00128  *
00129  * @v ctx               ANSI escape sequence context
00130  * @v count             Parameter count
00131  * @v params            List of graphic rendition aspects
00132  */
00133 static void bios_handle_sgr ( struct ansiesc_context *ctx __unused,
00134                               unsigned int count, int params[] ) {
00135         static const uint8_t bios_attr_fcols[10] = {
00136                 ATTR_FCOL_BLACK, ATTR_FCOL_RED, ATTR_FCOL_GREEN,
00137                 ATTR_FCOL_YELLOW, ATTR_FCOL_BLUE, ATTR_FCOL_MAGENTA,
00138                 ATTR_FCOL_CYAN, ATTR_FCOL_WHITE,
00139                 ATTR_FCOL_WHITE, ATTR_FCOL_WHITE /* defaults */
00140         };
00141         static const uint8_t bios_attr_bcols[10] = {
00142                 ATTR_BCOL_BLACK, ATTR_BCOL_RED, ATTR_BCOL_GREEN,
00143                 ATTR_BCOL_YELLOW, ATTR_BCOL_BLUE, ATTR_BCOL_MAGENTA,
00144                 ATTR_BCOL_CYAN, ATTR_BCOL_WHITE,
00145                 ATTR_BCOL_BLACK, ATTR_BCOL_BLACK /* defaults */
00146         };
00147         unsigned int i;
00148         int aspect;
00149 
00150         for ( i = 0 ; i < count ; i++ ) {
00151                 aspect = params[i];
00152                 if ( aspect == 0 ) {
00153                         bios_attr = ATTR_DEFAULT;
00154                 } else if ( aspect == 1 ) {
00155                         bios_attr |= ATTR_BOLD;
00156                 } else if ( aspect == 5 ) {
00157                         bios_attr |= ATTR_BLINK;
00158                 } else if ( aspect == 22 ) {
00159                         bios_attr &= ~ATTR_BOLD;
00160                 } else if ( aspect == 25 ) {
00161                         bios_attr &= ~ATTR_BLINK;
00162                 } else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) {
00163                         bios_attr &= ~ATTR_FCOL_MASK;
00164                         bios_attr |= bios_attr_fcols[ aspect - 30 ];
00165                 } else if ( ( aspect >= 40 ) && ( aspect <= 49 ) ) {
00166                         bios_attr &= ~ATTR_BCOL_MASK;
00167                         bios_attr |= bios_attr_bcols[ aspect - 40 ];
00168                 }
00169         }
00170 }
00171 
00172 /**
00173  * Handle ANSI DECTCEM set (show cursor)
00174  *
00175  * @v ctx               ANSI escape sequence context
00176  * @v count             Parameter count
00177  * @v params            List of graphic rendition aspects
00178  */
00179 static void bios_handle_dectcem_set ( struct ansiesc_context *ctx __unused,
00180                                       unsigned int count __unused,
00181                                       int params[] __unused ) {
00182         uint8_t height;
00183 
00184         /* Get character height */
00185         get_real ( height, BDA_SEG, BDA_CHAR_HEIGHT );
00186 
00187         __asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" )
00188                                : : "a" ( 0x0100 ),
00189                                    "c" ( ( ( height - 2 ) << 8 ) |
00190                                          ( height - 1 ) ) );
00191 }
00192 
00193 /**
00194  * Handle ANSI DECTCEM reset (hide cursor)
00195  *
00196  * @v ctx               ANSI escape sequence context
00197  * @v count             Parameter count
00198  * @v params            List of graphic rendition aspects
00199  */
00200 static void bios_handle_dectcem_reset ( struct ansiesc_context *ctx __unused,
00201                                         unsigned int count __unused,
00202                                         int params[] __unused ) {
00203 
00204         __asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" )
00205                                : : "a" ( 0x0100 ), "c" ( 0x2000 ) );
00206 }
00207 
00208 /** BIOS console ANSI escape sequence handlers */
00209 static struct ansiesc_handler bios_ansiesc_handlers[] = {
00210         { ANSIESC_CUP, bios_handle_cup },
00211         { ANSIESC_ED, bios_handle_ed },
00212         { ANSIESC_SGR, bios_handle_sgr },
00213         { ANSIESC_DECTCEM_SET, bios_handle_dectcem_set },
00214         { ANSIESC_DECTCEM_RESET, bios_handle_dectcem_reset },
00215         { 0, NULL }
00216 };
00217 
00218 /** BIOS console ANSI escape sequence context */
00219 static struct ansiesc_context bios_ansiesc_ctx = {
00220         .handlers = bios_ansiesc_handlers,
00221 };
00222 
00223 /**
00224  * Print a character to BIOS console
00225  *
00226  * @v character         Character to be printed
00227  */
00228 static void bios_putchar ( int character ) {
00229         int discard_a, discard_b, discard_c;
00230 
00231         /* Intercept ANSI escape sequences */
00232         character = ansiesc_process ( &bios_ansiesc_ctx, character );
00233         if ( character < 0 )
00234                 return;
00235 
00236         /* Print character with attribute */
00237         __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
00238                                            /* Skip non-printable characters */
00239                                            "cmpb $0x20, %%al\n\t"
00240                                            "jb 1f\n\t"
00241                                            /* Read attribute */
00242                                            "movb %%al, %%cl\n\t"
00243                                            "movb $0x08, %%ah\n\t"
00244                                            "int $0x10\n\t"
00245                                            "xchgb %%al, %%cl\n\t"
00246                                            /* Skip if attribute matches */
00247                                            "cmpb %%ah, %%bl\n\t"
00248                                            "je 1f\n\t"
00249                                            /* Set attribute */
00250                                            "movw $0x0001, %%cx\n\t"
00251                                            "movb $0x09, %%ah\n\t"
00252                                            "int $0x10\n\t"
00253                                            "\n1:\n\t"
00254                                            /* Print character */
00255                                            "xorw %%bx, %%bx\n\t"
00256                                            "movb $0x0e, %%ah\n\t"
00257                                            "int $0x10\n\t"
00258                                            "popl %%ebp\n\t" /* gcc bug */ )
00259                                : "=a" ( discard_a ), "=b" ( discard_b ),
00260                                  "=c" ( discard_c )
00261                                : "a" ( character ), "b" ( bios_attr ) );
00262 }
00263 
00264 /**
00265  * Pointer to current ANSI output sequence
00266  *
00267  * While we are in the middle of returning an ANSI sequence for a
00268  * special key, this will point to the next character to return.  When
00269  * not in the middle of such a sequence, this will point to a NUL
00270  * (note: not "will be NULL").
00271  */
00272 static const char *bios_ansi_input = "";
00273 
00274 /** A BIOS key */
00275 struct bios_key {
00276         /** Scancode */
00277         uint8_t scancode;
00278         /** Key code */
00279         uint16_t key;
00280 } __attribute__ (( packed ));
00281 
00282 /** Mapping from BIOS scan codes to iPXE key codes */
00283 static const struct bios_key bios_keys[] = {
00284         { 0x53, KEY_DC },
00285         { 0x48, KEY_UP },
00286         { 0x50, KEY_DOWN },
00287         { 0x4b, KEY_LEFT },
00288         { 0x4d, KEY_RIGHT },
00289         { 0x47, KEY_HOME },
00290         { 0x4f, KEY_END },
00291         { 0x49, KEY_PPAGE },
00292         { 0x51, KEY_NPAGE },
00293         { 0x3f, KEY_F5 },
00294         { 0x40, KEY_F6 },
00295         { 0x41, KEY_F7 },
00296         { 0x42, KEY_F8 },
00297         { 0x43, KEY_F9 },
00298         { 0x44, KEY_F10 },
00299         { 0x85, KEY_F11 },
00300         { 0x86, KEY_F12 },
00301 };
00302 
00303 /**
00304  * Get ANSI escape sequence corresponding to BIOS scancode
00305  *
00306  * @v scancode          BIOS scancode
00307  * @ret ansi_seq        ANSI escape sequence, if any, otherwise NULL
00308  */
00309 static const char * bios_ansi_seq ( unsigned int scancode ) {
00310         static char buf[ 5 /* "[" + two digits + terminator + NUL */ ];
00311         unsigned int key;
00312         unsigned int terminator;
00313         unsigned int n;
00314         unsigned int i;
00315         char *tmp = buf;
00316 
00317         /* Construct ANSI escape sequence for scancode, if known */
00318         for ( i = 0 ; i < ( sizeof ( bios_keys ) /
00319                             sizeof ( bios_keys[0] ) ) ; i++ ) {
00320 
00321                 /* Look for matching scancode */
00322                 if ( bios_keys[i].scancode != scancode )
00323                         continue;
00324 
00325                 /* Construct escape sequence */
00326                 key = bios_keys[i].key;
00327                 n = KEY_ANSI_N ( key );
00328                 terminator = KEY_ANSI_TERMINATOR ( key );
00329                 *(tmp++) = '[';
00330                 if ( n )
00331                         tmp += sprintf ( tmp, "%d", n );
00332                 *(tmp++) = terminator;
00333                 *(tmp++) = '\0';
00334                 assert ( tmp <= &buf[ sizeof ( buf ) ] );
00335                 return buf;
00336         }
00337 
00338         DBG ( "Unrecognised BIOS scancode %02x\n", scancode );
00339         return NULL;
00340 }
00341 
00342 /**
00343  * Map a key
00344  *
00345  * @v character         Character read from console
00346  * @ret character       Mapped character
00347  */
00348 static int bios_keymap ( unsigned int character ) {
00349         struct key_mapping *mapping;
00350 
00351         for_each_table_entry ( mapping, KEYMAP ) {
00352                 if ( mapping->from == character )
00353                         return mapping->to;
00354         }
00355         return character;
00356 }
00357 
00358 /**
00359  * Get character from BIOS console
00360  *
00361  * @ret character       Character read from console
00362  */
00363 static int bios_getchar ( void ) {
00364         uint16_t keypress;
00365         unsigned int character;
00366         const char *ansi_seq;
00367 
00368         /* If we are mid-sequence, pass out the next byte */
00369         if ( ( character = *bios_ansi_input ) ) {
00370                 bios_ansi_input++;
00371                 return character;
00372         }
00373 
00374         /* Do nothing if injection is in progress */
00375         if ( bios_inject_lock )
00376                 return 0;
00377 
00378         /* Read character from real BIOS console */
00379         bios_inject_lock++;
00380         __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
00381                                            "int $0x16\n\t"
00382                                            "cli\n\t" )
00383                                : "=a" ( keypress )
00384                                : "a" ( 0x1000 ), "m" ( bios_inject_lock ) );
00385         bios_inject_lock--;
00386         character = ( keypress & 0xff );
00387 
00388         /* If it's a normal character, just map and return it */
00389         if ( character && ( character < 0x80 ) )
00390                 return bios_keymap ( character );
00391 
00392         /* Otherwise, check for a special key that we know about */
00393         if ( ( ansi_seq = bios_ansi_seq ( keypress >> 8 ) ) ) {
00394                 /* Start of escape sequence: return ESC (0x1b) */
00395                 bios_ansi_input = ansi_seq;
00396                 return 0x1b;
00397         }
00398 
00399         return 0;
00400 }
00401 
00402 /**
00403  * Check for character ready to read from BIOS console
00404  *
00405  * @ret True            Character available to read
00406  * @ret False           No character available to read
00407  */
00408 static int bios_iskey ( void ) {
00409         unsigned int discard_a;
00410         unsigned int flags;
00411 
00412         /* If we are mid-sequence, we are always ready */
00413         if ( *bios_ansi_input )
00414                 return 1;
00415 
00416         /* Do nothing if injection is in progress */
00417         if ( bios_inject_lock )
00418                 return 0;
00419 
00420         /* Otherwise check the real BIOS console */
00421         bios_inject_lock++;
00422         __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
00423                                            "int $0x16\n\t"
00424                                            "pushfw\n\t"
00425                                            "popw %w0\n\t"
00426                                            "cli\n\t" )
00427                                : "=R" ( flags ), "=a" ( discard_a )
00428                                : "a" ( 0x1100 ), "m" ( bios_inject_lock ) );
00429         bios_inject_lock--;
00430         return ( ! ( flags & ZF ) );
00431 }
00432 
00433 /** BIOS console */
00434 struct console_driver bios_console __console_driver = {
00435         .putchar = bios_putchar,
00436         .getchar = bios_getchar,
00437         .iskey = bios_iskey,
00438         .usage = CONSOLE_PCBIOS,
00439 };
00440 
00441 /**
00442  * Inject keypresses
00443  *
00444  * @v ix86              Registers as passed to INT 16
00445  */
00446 static __asmcall void bios_inject ( struct i386_all_regs *ix86 ) {
00447         unsigned int discard_a;
00448         unsigned int scancode;
00449         unsigned int i;
00450         uint16_t keypress;
00451         int key;
00452 
00453         /* If this is a blocking call, then loop until the
00454          * non-blocking variant of the call indicates that a keypress
00455          * is available.  Do this without acquiring the injection
00456          * lock, so that injection may take place.
00457          */
00458         if ( ( ix86->regs.ah & ~0x10 ) == 0x00 ) {
00459                 __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
00460                                                    "\n1:\n\t"
00461                                                    "pushw %%ax\n\t"
00462                                                    "int $0x16\n\t"
00463                                                    "popw %%ax\n\t"
00464                                                    "jc 2f\n\t"
00465                                                    "jz 1b\n\t"
00466                                                    "\n2:\n\t"
00467                                                    "cli\n\t" )
00468                                        : "=a" ( discard_a )
00469                                        : "a" ( ix86->regs.eax | 0x0100 ),
00470                                          "m" ( bios_inject_lock ) );
00471         }
00472 
00473         /* Acquire injection lock */
00474         bios_inject_lock++;
00475 
00476         /* Check for keypresses */
00477         if ( iskey() ) {
00478 
00479                 /* Get key */
00480                 key = getkey ( 0 );
00481 
00482                 /* Reverse internal CR->LF mapping */
00483                 if ( key == '\n' )
00484                         key = '\r';
00485 
00486                 /* Convert to keypress */
00487                 keypress = ( ( key << 8 ) | key );
00488 
00489                 /* Handle special keys */
00490                 if ( key >= KEY_MIN ) {
00491                         for ( i = 0 ; i < ( sizeof ( bios_keys ) /
00492                                             sizeof ( bios_keys[0] ) ) ; i++ ) {
00493                                 if ( bios_keys[i].key == key ) {
00494                                         scancode = bios_keys[i].scancode;
00495                                         keypress = ( scancode << 8 );
00496                                         break;
00497                                 }
00498                         }
00499                 }
00500 
00501                 /* Inject keypress */
00502                 DBGC ( &bios_console, "BIOS injecting keypress %04x\n",
00503                        keypress );
00504                 __asm__ __volatile__ ( REAL_CODE ( "int $0x16\n\t" )
00505                                        : "=a" ( discard_a )
00506                                        : "a" ( 0x0500 ), "c" ( keypress ),
00507                                          "m" ( bios_inject_lock ) );
00508         }
00509 
00510         /* Release injection lock */
00511         bios_inject_lock--;
00512 }
00513 
00514 /**
00515  * Start up keypress injection
00516  *
00517  */
00518 static void bios_inject_startup ( void ) {
00519 
00520         /* Assembly wrapper to call bios_inject() */
00521         __asm__ __volatile__ (
00522                 TEXT16_CODE ( "\nint16_wrapper:\n\t"
00523                               "pushfw\n\t"
00524                               "cmpb $0, %%cs:bios_inject_lock\n\t"
00525                               "jnz 1f\n\t"
00526                               VIRT_CALL ( bios_inject )
00527                               "\n1:\n\t"
00528                               "popfw\n\t"
00529                               "ljmp *%%cs:int16_vector\n\t" ) : );
00530 
00531         /* Hook INT 16 */
00532         hook_bios_interrupt ( 0x16, ( ( intptr_t ) int16_wrapper ),
00533                               &int16_vector );
00534 }
00535 
00536 /**
00537  * Shut down keypress injection
00538  *
00539  * @v booting           System is shutting down for OS boot
00540  */
00541 static void bios_inject_shutdown ( int booting __unused ) {
00542 
00543         /* Unhook INT 16 */
00544         unhook_bios_interrupt ( 0x16, ( ( intptr_t ) int16_wrapper ),
00545                                 &int16_vector );
00546 }
00547 
00548 /** Keypress injection startup function */
00549 struct startup_fn bios_inject_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
00550         .startup = bios_inject_startup,
00551         .shutdown = bios_inject_shutdown,
00552 };