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