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