[bios] Allow bios_console.c to be compiled for x86_64
[ipxe.git] / 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 <biosint.h>
30 #include <ipxe/console.h>
31 #include <ipxe/ansiesc.h>
32 #include <ipxe/keys.h>
33 #include <ipxe/keymap.h>
34 #include <ipxe/init.h>
35 #include <config/console.h>
36
37 #define ATTR_BOLD 0x08
38
39 #define ATTR_FCOL_MASK 0x07
40 #define ATTR_FCOL_BLACK 0x00
41 #define ATTR_FCOL_BLUE 0x01
42 #define ATTR_FCOL_GREEN 0x02
43 #define ATTR_FCOL_CYAN 0x03
44 #define ATTR_FCOL_RED 0x04
45 #define ATTR_FCOL_MAGENTA 0x05
46 #define ATTR_FCOL_YELLOW 0x06
47 #define ATTR_FCOL_WHITE 0x07
48
49 #define ATTR_BLINK 0x80
50
51 #define ATTR_BCOL_MASK 0x70
52 #define ATTR_BCOL_BLACK 0x00
53 #define ATTR_BCOL_BLUE 0x10
54 #define ATTR_BCOL_GREEN 0x20
55 #define ATTR_BCOL_CYAN 0x30
56 #define ATTR_BCOL_RED 0x40
57 #define ATTR_BCOL_MAGENTA 0x50
58 #define ATTR_BCOL_YELLOW 0x60
59 #define ATTR_BCOL_WHITE 0x70
60
61 #define ATTR_DEFAULT ATTR_FCOL_WHITE
62
63 /* Set default console usage if applicable */
64 #if ! ( defined ( CONSOLE_PCBIOS ) && CONSOLE_EXPLICIT ( CONSOLE_PCBIOS ) )
65 #undef CONSOLE_PCBIOS
66 #define CONSOLE_PCBIOS ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
67 #endif
68
69 /** Current character attribute */
70 static unsigned int bios_attr = ATTR_DEFAULT;
71
72 /** Keypress injection lock */
73 static uint8_t __text16 ( bios_inject_lock );
74 #define bios_inject_lock __use_text16 ( bios_inject_lock )
75
76 /** Vector for chaining to other INT 16 handlers */
77 static struct segoff __text16 ( int16_vector );
78 #define int16_vector __use_text16 ( int16_vector )
79
80 /** Assembly wrapper */
81 extern void int16_wrapper ( void );
82
83 /**
84 * Handle ANSI CUP (cursor position)
85 *
86 * @v ctx ANSI escape sequence context
87 * @v count Parameter count
88 * @v params[0] Row (1 is top)
89 * @v params[1] Column (1 is left)
90 */
91 static void bios_handle_cup ( struct ansiesc_context *ctx __unused,
92 unsigned int count __unused, int params[] ) {
93 int cx = ( params[1] - 1 );
94 int cy = ( params[0] - 1 );
95
96 if ( cx < 0 )
97 cx = 0;
98 if ( cy < 0 )
99 cy = 0;
100
101 __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
102 "int $0x10\n\t"
103 "cli\n\t" )
104 : : "a" ( 0x0200 ), "b" ( 1 ),
105 "d" ( ( cy << 8 ) | cx ) );
106 }
107
108 /**
109 * Handle ANSI ED (erase in page)
110 *
111 * @v ctx ANSI escape sequence context
112 * @v count Parameter count
113 * @v params[0] Region to erase
114 */
115 static void bios_handle_ed ( struct ansiesc_context *ctx __unused,
116 unsigned int count __unused,
117 int params[] __unused ) {
118 /* We assume that we always clear the whole screen */
119 assert ( params[0] == ANSIESC_ED_ALL );
120
121 __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
122 "int $0x10\n\t"
123 "cli\n\t" )
124 : : "a" ( 0x0600 ), "b" ( bios_attr << 8 ),
125 "c" ( 0 ),
126 "d" ( ( ( console_height - 1 ) << 8 ) |
127 ( console_width - 1 ) ) );
128 }
129
130 /**
131 * Handle ANSI SGR (set graphics rendition)
132 *
133 * @v ctx ANSI escape sequence context
134 * @v count Parameter count
135 * @v params List of graphic rendition aspects
136 */
137 static void bios_handle_sgr ( struct ansiesc_context *ctx __unused,
138 unsigned int count, int params[] ) {
139 static const uint8_t bios_attr_fcols[10] = {
140 ATTR_FCOL_BLACK, ATTR_FCOL_RED, ATTR_FCOL_GREEN,
141 ATTR_FCOL_YELLOW, ATTR_FCOL_BLUE, ATTR_FCOL_MAGENTA,
142 ATTR_FCOL_CYAN, ATTR_FCOL_WHITE,
143 ATTR_FCOL_WHITE, ATTR_FCOL_WHITE /* defaults */
144 };
145 static const uint8_t bios_attr_bcols[10] = {
146 ATTR_BCOL_BLACK, ATTR_BCOL_RED, ATTR_BCOL_GREEN,
147 ATTR_BCOL_YELLOW, ATTR_BCOL_BLUE, ATTR_BCOL_MAGENTA,
148 ATTR_BCOL_CYAN, ATTR_BCOL_WHITE,
149 ATTR_BCOL_BLACK, ATTR_BCOL_BLACK /* defaults */
150 };
151 unsigned int i;
152 int aspect;
153
154 for ( i = 0 ; i < count ; i++ ) {
155 aspect = params[i];
156 if ( aspect == 0 ) {
157 bios_attr = ATTR_DEFAULT;
158 } else if ( aspect == 1 ) {
159 bios_attr |= ATTR_BOLD;
160 } else if ( aspect == 5 ) {
161 bios_attr |= ATTR_BLINK;
162 } else if ( aspect == 22 ) {
163 bios_attr &= ~ATTR_BOLD;
164 } else if ( aspect == 25 ) {
165 bios_attr &= ~ATTR_BLINK;
166 } else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) {
167 bios_attr &= ~ATTR_FCOL_MASK;
168 bios_attr |= bios_attr_fcols[ aspect - 30 ];
169 } else if ( ( aspect >= 40 ) && ( aspect <= 49 ) ) {
170 bios_attr &= ~ATTR_BCOL_MASK;
171 bios_attr |= bios_attr_bcols[ aspect - 40 ];
172 }
173 }
174 }
175
176 /**
177 * Handle ANSI DECTCEM set (show cursor)
178 *
179 * @v ctx ANSI escape sequence context
180 * @v count Parameter count
181 * @v params List of graphic rendition aspects
182 */
183 static void bios_handle_dectcem_set ( struct ansiesc_context *ctx __unused,
184 unsigned int count __unused,
185 int params[] __unused ) {
186 uint8_t height;
187
188 /* Get character height */
189 get_real ( height, BDA_SEG, BDA_CHAR_HEIGHT );
190
191 __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
192 "int $0x10\n\t"
193 "cli\n\t" )
194 : : "a" ( 0x0100 ),
195 "c" ( ( ( height - 2 ) << 8 ) |
196 ( height - 1 ) ) );
197 }
198
199 /**
200 * Handle ANSI DECTCEM reset (hide cursor)
201 *
202 * @v ctx ANSI escape sequence context
203 * @v count Parameter count
204 * @v params List of graphic rendition aspects
205 */
206 static void bios_handle_dectcem_reset ( struct ansiesc_context *ctx __unused,
207 unsigned int count __unused,
208 int params[] __unused ) {
209
210 __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
211 "int $0x10\n\t"
212 "cli\n\t" )
213 : : "a" ( 0x0100 ), "c" ( 0x2000 ) );
214 }
215
216 /** BIOS console ANSI escape sequence handlers */
217 static struct ansiesc_handler bios_ansiesc_handlers[] = {
218 { ANSIESC_CUP, bios_handle_cup },
219 { ANSIESC_ED, bios_handle_ed },
220 { ANSIESC_SGR, bios_handle_sgr },
221 { ANSIESC_DECTCEM_SET, bios_handle_dectcem_set },
222 { ANSIESC_DECTCEM_RESET, bios_handle_dectcem_reset },
223 { 0, NULL }
224 };
225
226 /** BIOS console ANSI escape sequence context */
227 static struct ansiesc_context bios_ansiesc_ctx = {
228 .handlers = bios_ansiesc_handlers,
229 };
230
231 /**
232 * Print a character to BIOS console
233 *
234 * @v character Character to be printed
235 */
236 static void bios_putchar ( int character ) {
237 int discard_a, discard_b, discard_c;
238
239 /* Intercept ANSI escape sequences */
240 character = ansiesc_process ( &bios_ansiesc_ctx, character );
241 if ( character < 0 )
242 return;
243
244 /* Print character with attribute */
245 __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
246 "sti\n\t"
247 /* Skip non-printable characters */
248 "cmpb $0x20, %%al\n\t"
249 "jb 1f\n\t"
250 /* Read attribute */
251 "movb %%al, %%cl\n\t"
252 "movb $0x08, %%ah\n\t"
253 "int $0x10\n\t"
254 "xchgb %%al, %%cl\n\t"
255 /* Skip if attribute matches */
256 "cmpb %%ah, %%bl\n\t"
257 "je 1f\n\t"
258 /* Set attribute */
259 "movw $0x0001, %%cx\n\t"
260 "movb $0x09, %%ah\n\t"
261 "int $0x10\n\t"
262 "\n1:\n\t"
263 /* Print character */
264 "xorw %%bx, %%bx\n\t"
265 "movb $0x0e, %%ah\n\t"
266 "int $0x10\n\t"
267 "cli\n\t"
268 "popl %%ebp\n\t" /* gcc bug */ )
269 : "=a" ( discard_a ), "=b" ( discard_b ),
270 "=c" ( discard_c )
271 : "a" ( character ), "b" ( bios_attr ) );
272 }
273
274 /**
275 * Pointer to current ANSI output sequence
276 *
277 * While we are in the middle of returning an ANSI sequence for a
278 * special key, this will point to the next character to return. When
279 * not in the middle of such a sequence, this will point to a NUL
280 * (note: not "will be NULL").
281 */
282 static const char *bios_ansi_input = "";
283
284 /** A BIOS key */
285 struct bios_key {
286 /** Scancode */
287 uint8_t scancode;
288 /** Key code */
289 uint16_t key;
290 } __attribute__ (( packed ));
291
292 /** Mapping from BIOS scan codes to iPXE key codes */
293 static const struct bios_key bios_keys[] = {
294 { 0x53, KEY_DC },
295 { 0x48, KEY_UP },
296 { 0x50, KEY_DOWN },
297 { 0x4b, KEY_LEFT },
298 { 0x4d, KEY_RIGHT },
299 { 0x47, KEY_HOME },
300 { 0x4f, KEY_END },
301 { 0x49, KEY_PPAGE },
302 { 0x51, KEY_NPAGE },
303 { 0x3f, KEY_F5 },
304 { 0x40, KEY_F6 },
305 { 0x41, KEY_F7 },
306 { 0x42, KEY_F8 },
307 { 0x43, KEY_F9 },
308 { 0x44, KEY_F10 },
309 { 0x85, KEY_F11 },
310 { 0x86, KEY_F12 },
311 };
312
313 /**
314 * Get ANSI escape sequence corresponding to BIOS scancode
315 *
316 * @v scancode BIOS scancode
317 * @ret ansi_seq ANSI escape sequence, if any, otherwise NULL
318 */
319 static const char * bios_ansi_seq ( unsigned int scancode ) {
320 static char buf[ 5 /* "[" + two digits + terminator + NUL */ ];
321 unsigned int key;
322 unsigned int terminator;
323 unsigned int n;
324 unsigned int i;
325 char *tmp = buf;
326
327 /* Construct ANSI escape sequence for scancode, if known */
328 for ( i = 0 ; i < ( sizeof ( bios_keys ) /
329 sizeof ( bios_keys[0] ) ) ; i++ ) {
330
331 /* Look for matching scancode */
332 if ( bios_keys[i].scancode != scancode )
333 continue;
334
335 /* Construct escape sequence */
336 key = bios_keys[i].key;
337 n = KEY_ANSI_N ( key );
338 terminator = KEY_ANSI_TERMINATOR ( key );
339 *(tmp++) = '[';
340 if ( n )
341 tmp += sprintf ( tmp, "%d", n );
342 *(tmp++) = terminator;
343 *(tmp++) = '\0';
344 assert ( tmp <= &buf[ sizeof ( buf ) ] );
345 return buf;
346 }
347
348 DBG ( "Unrecognised BIOS scancode %02x\n", scancode );
349 return NULL;
350 }
351
352 /**
353 * Map a key
354 *
355 * @v character Character read from console
356 * @ret character Mapped character
357 */
358 static int bios_keymap ( unsigned int character ) {
359 struct key_mapping *mapping;
360
361 for_each_table_entry ( mapping, KEYMAP ) {
362 if ( mapping->from == character )
363 return mapping->to;
364 }
365 return character;
366 }
367
368 /**
369 * Get character from BIOS console
370 *
371 * @ret character Character read from console
372 */
373 static int bios_getchar ( void ) {
374 uint16_t keypress;
375 unsigned int character;
376 const char *ansi_seq;
377
378 /* If we are mid-sequence, pass out the next byte */
379 if ( ( character = *bios_ansi_input ) ) {
380 bios_ansi_input++;
381 return character;
382 }
383
384 /* Do nothing if injection is in progress */
385 if ( bios_inject_lock )
386 return 0;
387
388 /* Read character from real BIOS console */
389 bios_inject_lock++;
390 __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
391 "int $0x16\n\t"
392 "cli\n\t" )
393 : "=a" ( keypress )
394 : "a" ( 0x1000 ), "m" ( bios_inject_lock ) );
395 bios_inject_lock--;
396 character = ( keypress & 0xff );
397
398 /* If it's a normal character, just map and return it */
399 if ( character && ( character < 0x80 ) )
400 return bios_keymap ( character );
401
402 /* Otherwise, check for a special key that we know about */
403 if ( ( ansi_seq = bios_ansi_seq ( keypress >> 8 ) ) ) {
404 /* Start of escape sequence: return ESC (0x1b) */
405 bios_ansi_input = ansi_seq;
406 return 0x1b;
407 }
408
409 return 0;
410 }
411
412 /**
413 * Check for character ready to read from BIOS console
414 *
415 * @ret True Character available to read
416 * @ret False No character available to read
417 */
418 static int bios_iskey ( void ) {
419 unsigned int discard_a;
420 unsigned int flags;
421
422 /* If we are mid-sequence, we are always ready */
423 if ( *bios_ansi_input )
424 return 1;
425
426 /* Do nothing if injection is in progress */
427 if ( bios_inject_lock )
428 return 0;
429
430 /* Otherwise check the real BIOS console */
431 bios_inject_lock++;
432 __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
433 "int $0x16\n\t"
434 "pushfw\n\t"
435 "popw %w0\n\t"
436 "cli\n\t" )
437 : "=R" ( flags ), "=a" ( discard_a )
438 : "a" ( 0x1100 ), "m" ( bios_inject_lock ) );
439 bios_inject_lock--;
440 return ( ! ( flags & ZF ) );
441 }
442
443 /** BIOS console */
444 struct console_driver bios_console __console_driver = {
445 .putchar = bios_putchar,
446 .getchar = bios_getchar,
447 .iskey = bios_iskey,
448 .usage = CONSOLE_PCBIOS,
449 };
450
451 /**
452 * Inject keypresses
453 *
454 * @v ix86 Registers as passed to INT 16
455 */
456 static __asmcall void bios_inject ( struct i386_all_regs *ix86 ) {
457 unsigned int discard_a;
458 unsigned int scancode;
459 unsigned int i;
460 uint16_t keypress;
461 int key;
462
463 /* If this is a blocking call, then loop until the
464 * non-blocking variant of the call indicates that a keypress
465 * is available. Do this without acquiring the injection
466 * lock, so that injection may take place.
467 */
468 if ( ( ix86->regs.ah & ~0x10 ) == 0x00 ) {
469 __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
470 "\n1:\n\t"
471 "pushw %%ax\n\t"
472 "int $0x16\n\t"
473 "popw %%ax\n\t"
474 "jc 2f\n\t"
475 "jz 1b\n\t"
476 "\n2:\n\t"
477 "cli\n\t" )
478 : "=a" ( discard_a )
479 : "a" ( ix86->regs.eax | 0x0100 ),
480 "m" ( bios_inject_lock ) );
481 }
482
483 /* Acquire injection lock */
484 bios_inject_lock++;
485
486 /* Check for keypresses */
487 if ( iskey() ) {
488
489 /* Get key */
490 key = getkey ( 0 );
491
492 /* Reverse internal CR->LF mapping */
493 if ( key == '\n' )
494 key = '\r';
495
496 /* Convert to keypress */
497 keypress = ( ( key << 8 ) | key );
498
499 /* Handle special keys */
500 if ( key >= KEY_MIN ) {
501 for ( i = 0 ; i < ( sizeof ( bios_keys ) /
502 sizeof ( bios_keys[0] ) ) ; i++ ) {
503 if ( bios_keys[i].key == key ) {
504 scancode = bios_keys[i].scancode;
505 keypress = ( scancode << 8 );
506 break;
507 }
508 }
509 }
510
511 /* Inject keypress */
512 DBGC ( &bios_console, "BIOS injecting keypress %04x\n",
513 keypress );
514 __asm__ __volatile__ ( REAL_CODE ( "int $0x16\n\t" )
515 : "=a" ( discard_a )
516 : "a" ( 0x0500 ), "c" ( keypress ),
517 "m" ( bios_inject_lock ) );
518 }
519
520 /* Release injection lock */
521 bios_inject_lock--;
522 }
523
524 /**
525 * Start up keypress injection
526 *
527 */
528 static void bios_inject_startup ( void ) {
529
530 /* Assembly wrapper to call bios_inject() */
531 __asm__ __volatile__ (
532 TEXT16_CODE ( "\nint16_wrapper:\n\t"
533 "pushfw\n\t"
534 "cmpb $0, %%cs:bios_inject_lock\n\t"
535 "jnz 1f\n\t"
536 "pushl %0\n\t"
537 "pushw %%cs\n\t"
538 "call prot_call\n\t"
539 "addw $4, %%sp\n\t"
540 "\n1:\n\t"
541 "popfw\n\t"
542 "ljmp *%%cs:int16_vector\n\t" )
543 : : "i" ( bios_inject ) );
544
545 /* Hook INT 16 */
546 hook_bios_interrupt ( 0x16, ( ( intptr_t ) int16_wrapper ),
547 &int16_vector );
548 }
549
550 /**
551 * Shut down keypress injection
552 *
553 * @v booting System is shutting down for OS boot
554 */
555 static void bios_inject_shutdown ( int booting __unused ) {
556
557 /* Unhook INT 16 */
558 unhook_bios_interrupt ( 0x16, ( ( intptr_t ) int16_wrapper ),
559 &int16_vector );
560 }
561
562 /** Keypress injection startup function */
563 struct startup_fn bios_inject_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
564 .startup = bios_inject_startup,
565 .shutdown = bios_inject_shutdown,
566 };