These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / arch / i386 / firmware / pcbios / bios_console.c
1 /*
2  * Copyright (C) 2006 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 #include <assert.h>
27 #include <realmode.h>
28 #include <bios.h>
29 #include <ipxe/console.h>
30 #include <ipxe/ansiesc.h>
31 #include <ipxe/keymap.h>
32 #include <config/console.h>
33
34 #define ATTR_BOLD               0x08
35
36 #define ATTR_FCOL_MASK          0x07
37 #define ATTR_FCOL_BLACK         0x00
38 #define ATTR_FCOL_BLUE          0x01
39 #define ATTR_FCOL_GREEN         0x02
40 #define ATTR_FCOL_CYAN          0x03
41 #define ATTR_FCOL_RED           0x04
42 #define ATTR_FCOL_MAGENTA       0x05
43 #define ATTR_FCOL_YELLOW        0x06
44 #define ATTR_FCOL_WHITE         0x07
45
46 #define ATTR_BLINK              0x80
47
48 #define ATTR_BCOL_MASK          0x70
49 #define ATTR_BCOL_BLACK         0x00
50 #define ATTR_BCOL_BLUE          0x10
51 #define ATTR_BCOL_GREEN         0x20
52 #define ATTR_BCOL_CYAN          0x30
53 #define ATTR_BCOL_RED           0x40
54 #define ATTR_BCOL_MAGENTA       0x50
55 #define ATTR_BCOL_YELLOW        0x60
56 #define ATTR_BCOL_WHITE         0x70
57
58 #define ATTR_DEFAULT            ATTR_FCOL_WHITE
59
60 /* Set default console usage if applicable */
61 #if ! ( defined ( CONSOLE_PCBIOS ) && CONSOLE_EXPLICIT ( CONSOLE_PCBIOS ) )
62 #undef CONSOLE_PCBIOS
63 #define CONSOLE_PCBIOS ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
64 #endif
65
66 /** Current character attribute */
67 static unsigned int bios_attr = ATTR_DEFAULT;
68
69 /**
70  * Handle ANSI CUP (cursor position)
71  *
72  * @v ctx               ANSI escape sequence context
73  * @v count             Parameter count
74  * @v params[0]         Row (1 is top)
75  * @v params[1]         Column (1 is left)
76  */
77 static void bios_handle_cup ( struct ansiesc_context *ctx __unused,
78                               unsigned int count __unused, int params[] ) {
79         int cx = ( params[1] - 1 );
80         int cy = ( params[0] - 1 );
81
82         if ( cx < 0 )
83                 cx = 0;
84         if ( cy < 0 )
85                 cy = 0;
86
87         __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
88                                            "int $0x10\n\t"
89                                            "cli\n\t" )
90                                : : "a" ( 0x0200 ), "b" ( 1 ),
91                                    "d" ( ( cy << 8 ) | cx ) );
92 }
93
94 /**
95  * Handle ANSI ED (erase in page)
96  *
97  * @v ctx               ANSI escape sequence context
98  * @v count             Parameter count
99  * @v params[0]         Region to erase
100  */
101 static void bios_handle_ed ( struct ansiesc_context *ctx __unused,
102                              unsigned int count __unused,
103                              int params[] __unused ) {
104         /* We assume that we always clear the whole screen */
105         assert ( params[0] == ANSIESC_ED_ALL );
106
107         __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
108                                            "int $0x10\n\t"
109                                            "cli\n\t" )
110                                : : "a" ( 0x0600 ), "b" ( bios_attr << 8 ),
111                                    "c" ( 0 ),
112                                    "d" ( ( ( console_height - 1 ) << 8 ) |
113                                          ( console_width - 1 ) ) );
114 }
115
116 /**
117  * Handle ANSI SGR (set graphics rendition)
118  *
119  * @v ctx               ANSI escape sequence context
120  * @v count             Parameter count
121  * @v params            List of graphic rendition aspects
122  */
123 static void bios_handle_sgr ( struct ansiesc_context *ctx __unused,
124                               unsigned int count, int params[] ) {
125         static const uint8_t bios_attr_fcols[10] = {
126                 ATTR_FCOL_BLACK, ATTR_FCOL_RED, ATTR_FCOL_GREEN,
127                 ATTR_FCOL_YELLOW, ATTR_FCOL_BLUE, ATTR_FCOL_MAGENTA,
128                 ATTR_FCOL_CYAN, ATTR_FCOL_WHITE,
129                 ATTR_FCOL_WHITE, ATTR_FCOL_WHITE /* defaults */
130         };
131         static const uint8_t bios_attr_bcols[10] = {
132                 ATTR_BCOL_BLACK, ATTR_BCOL_RED, ATTR_BCOL_GREEN,
133                 ATTR_BCOL_YELLOW, ATTR_BCOL_BLUE, ATTR_BCOL_MAGENTA,
134                 ATTR_BCOL_CYAN, ATTR_BCOL_WHITE,
135                 ATTR_BCOL_BLACK, ATTR_BCOL_BLACK /* defaults */
136         };
137         unsigned int i;
138         int aspect;
139
140         for ( i = 0 ; i < count ; i++ ) {
141                 aspect = params[i];
142                 if ( aspect == 0 ) {
143                         bios_attr = ATTR_DEFAULT;
144                 } else if ( aspect == 1 ) {
145                         bios_attr |= ATTR_BOLD;
146                 } else if ( aspect == 5 ) {
147                         bios_attr |= ATTR_BLINK;
148                 } else if ( aspect == 22 ) {
149                         bios_attr &= ~ATTR_BOLD;
150                 } else if ( aspect == 25 ) {
151                         bios_attr &= ~ATTR_BLINK;
152                 } else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) {
153                         bios_attr &= ~ATTR_FCOL_MASK;
154                         bios_attr |= bios_attr_fcols[ aspect - 30 ];
155                 } else if ( ( aspect >= 40 ) && ( aspect <= 49 ) ) {
156                         bios_attr &= ~ATTR_BCOL_MASK;
157                         bios_attr |= bios_attr_bcols[ aspect - 40 ];
158                 }
159         }
160 }
161
162 /**
163  * Handle ANSI DECTCEM set (show cursor)
164  *
165  * @v ctx               ANSI escape sequence context
166  * @v count             Parameter count
167  * @v params            List of graphic rendition aspects
168  */
169 static void bios_handle_dectcem_set ( struct ansiesc_context *ctx __unused,
170                                       unsigned int count __unused,
171                                       int params[] __unused ) {
172         uint8_t height;
173
174         /* Get character height */
175         get_real ( height, BDA_SEG, BDA_CHAR_HEIGHT );
176
177         __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
178                                            "int $0x10\n\t"
179                                            "cli\n\t" )
180                                : : "a" ( 0x0100 ),
181                                    "c" ( ( ( height - 2 ) << 8 ) |
182                                          ( height - 1 ) ) );
183 }
184
185 /**
186  * Handle ANSI DECTCEM reset (hide cursor)
187  *
188  * @v ctx               ANSI escape sequence context
189  * @v count             Parameter count
190  * @v params            List of graphic rendition aspects
191  */
192 static void bios_handle_dectcem_reset ( struct ansiesc_context *ctx __unused,
193                                         unsigned int count __unused,
194                                         int params[] __unused ) {
195
196         __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
197                                            "int $0x10\n\t"
198                                            "cli\n\t" )
199                                : : "a" ( 0x0100 ), "c" ( 0x2000 ) );
200 }
201
202 /** BIOS console ANSI escape sequence handlers */
203 static struct ansiesc_handler bios_ansiesc_handlers[] = {
204         { ANSIESC_CUP, bios_handle_cup },
205         { ANSIESC_ED, bios_handle_ed },
206         { ANSIESC_SGR, bios_handle_sgr },
207         { ANSIESC_DECTCEM_SET, bios_handle_dectcem_set },
208         { ANSIESC_DECTCEM_RESET, bios_handle_dectcem_reset },
209         { 0, NULL }
210 };
211
212 /** BIOS console ANSI escape sequence context */
213 static struct ansiesc_context bios_ansiesc_ctx = {
214         .handlers = bios_ansiesc_handlers,
215 };
216
217 /**
218  * Print a character to BIOS console
219  *
220  * @v character         Character to be printed
221  */
222 static void bios_putchar ( int character ) {
223         int discard_a, discard_b, discard_c;
224
225         /* Intercept ANSI escape sequences */
226         character = ansiesc_process ( &bios_ansiesc_ctx, character );
227         if ( character < 0 )
228                 return;
229
230         /* Print character with attribute */
231         __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
232                                            "sti\n\t"
233                                            /* Skip non-printable characters */
234                                            "cmpb $0x20, %%al\n\t"
235                                            "jb 1f\n\t"
236                                            /* Read attribute */
237                                            "movb %%al, %%cl\n\t"
238                                            "movb $0x08, %%ah\n\t"
239                                            "int $0x10\n\t"
240                                            "xchgb %%al, %%cl\n\t"
241                                            /* Skip if attribute matches */
242                                            "cmpb %%ah, %%bl\n\t"
243                                            "je 1f\n\t"
244                                            /* Set attribute */
245                                            "movw $0x0001, %%cx\n\t"
246                                            "movb $0x09, %%ah\n\t"
247                                            "int $0x10\n\t"
248                                            "\n1:\n\t"
249                                            /* Print character */
250                                            "xorw %%bx, %%bx\n\t"
251                                            "movb $0x0e, %%ah\n\t"
252                                            "int $0x10\n\t"
253                                            "cli\n\t"
254                                            "popl %%ebp\n\t" /* gcc bug */ )
255                                : "=a" ( discard_a ), "=b" ( discard_b ),
256                                  "=c" ( discard_c )
257                                : "a" ( character ), "b" ( bios_attr ) );
258 }
259
260 /**
261  * Pointer to current ANSI output sequence
262  *
263  * While we are in the middle of returning an ANSI sequence for a
264  * special key, this will point to the next character to return.  When
265  * not in the middle of such a sequence, this will point to a NUL
266  * (note: not "will be NULL").
267  */
268 static const char *ansi_input = "";
269
270 /** A mapping from a BIOS scan code to an ANSI escape sequence */
271 #define BIOS_KEY( key, ansi ) key ansi "\0"
272
273 /** Mapping from BIOS scan codes to ANSI escape sequences */
274 static const char ansi_sequences[] = {
275         BIOS_KEY ( "\x53", "[3~" )      /* Delete */
276         BIOS_KEY ( "\x48", "[A" )       /* Up arrow */
277         BIOS_KEY ( "\x50", "[B" )       /* Down arrow */
278         BIOS_KEY ( "\x4b", "[D" )       /* Left arrow */
279         BIOS_KEY ( "\x4d", "[C" )       /* Right arrow */
280         BIOS_KEY ( "\x47", "[H" )       /* Home */
281         BIOS_KEY ( "\x4f", "[F" )       /* End */
282         BIOS_KEY ( "\x49", "[5~" )      /* Page up */
283         BIOS_KEY ( "\x51", "[6~" )      /* Page down */
284         BIOS_KEY ( "\x3f", "[15~" )     /* F5 */
285         BIOS_KEY ( "\x40", "[17~" )     /* F6 */
286         BIOS_KEY ( "\x41", "[18~" )     /* F7 */
287         BIOS_KEY ( "\x42", "[19~" )     /* F8 (required for PXE) */
288         BIOS_KEY ( "\x43", "[20~" )     /* F9 */
289         BIOS_KEY ( "\x44", "[21~" )     /* F10 */
290         BIOS_KEY ( "\x85", "[23~" )     /* F11 */
291         BIOS_KEY ( "\x86", "[24~" )     /* F12 */
292 };
293
294 /**
295  * Get ANSI escape sequence corresponding to BIOS scancode
296  *
297  * @v scancode          BIOS scancode
298  * @ret ansi_seq        ANSI escape sequence, if any, otherwise NULL
299  */
300 static const char * scancode_to_ansi_seq ( unsigned int scancode ) {
301         const char *seq = ansi_sequences;
302
303         while ( *seq ) {
304                 if ( *(seq++) == ( ( char ) scancode ) )
305                         return seq;
306                 seq += ( strlen ( seq ) + 1 );
307         }
308         DBG ( "Unrecognised BIOS scancode %02x\n", scancode );
309         return NULL;
310 }
311
312 /**
313  * Map a key
314  *
315  * @v character         Character read from console
316  * @ret character       Mapped character
317  */
318 static int bios_keymap ( unsigned int character ) {
319         struct key_mapping *mapping;
320
321         for_each_table_entry ( mapping, KEYMAP ) {
322                 if ( mapping->from == character )
323                         return mapping->to;
324         }
325         return character;
326 }
327
328 /**
329  * Get character from BIOS console
330  *
331  * @ret character       Character read from console
332  */
333 static int bios_getchar ( void ) {
334         uint16_t keypress;
335         unsigned int character;
336         const char *ansi_seq;
337
338         /* If we are mid-sequence, pass out the next byte */
339         if ( ( character = *ansi_input ) ) {
340                 ansi_input++;
341                 return character;
342         }
343
344         /* Read character from real BIOS console */
345         __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
346                                            "int $0x16\n\t"
347                                            "cli\n\t" )
348                                : "=a" ( keypress ) : "a" ( 0x1000 ) );
349         character = ( keypress & 0xff );
350
351         /* If it's a normal character, just map and return it */
352         if ( character && ( character < 0x80 ) )
353                 return bios_keymap ( character );
354
355         /* Otherwise, check for a special key that we know about */
356         if ( ( ansi_seq = scancode_to_ansi_seq ( keypress >> 8 ) ) ) {
357                 /* Start of escape sequence: return ESC (0x1b) */
358                 ansi_input = ansi_seq;
359                 return 0x1b;
360         }
361
362         return 0;
363 }
364
365 /**
366  * Check for character ready to read from BIOS console
367  *
368  * @ret True            Character available to read
369  * @ret False           No character available to read
370  */
371 static int bios_iskey ( void ) {
372         unsigned int discard_a;
373         unsigned int flags;
374
375         /* If we are mid-sequence, we are always ready */
376         if ( *ansi_input )
377                 return 1;
378
379         /* Otherwise check the real BIOS console */
380         __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
381                                            "int $0x16\n\t"
382                                            "pushfw\n\t"
383                                            "popw %w0\n\t"
384                                            "cli\n\t" )
385                                : "=r" ( flags ), "=a" ( discard_a )
386                                : "a" ( 0x1100 ) );
387         return ( ! ( flags & ZF ) );
388 }
389
390 struct console_driver bios_console __console_driver = {
391         .putchar = bios_putchar,
392         .getchar = bios_getchar,
393         .iskey = bios_iskey,
394         .usage = CONSOLE_PCBIOS,
395 };