Browse Source

[bios] Add support for injecting keypresses

When USB network card drivers are used, the BIOS' legacy USB
capability is necessarily disabled since there is no way to share the
host controller between the BIOS and iPXE.

Commit 3726722 ("[usb] Add basic support for USB keyboards") added
support allowing a USB keyboard to be used within iPXE.  However,
external code such as a PXE NBP has no way to utilise this support,
and so a USB keyboard cannot be used to control a PXE NBP loaded from
a USB network card.

Add support for injecting keypresses from any iPXE console into the
BIOS keyboard buffer.  This allows external code such as a PXE NBP to
function with a USB keyboard even after the BIOS' legacy USB
capability has been disabled.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
0f67f2edb7
1 changed files with 209 additions and 38 deletions
  1. 209
    38
      src/arch/i386/firmware/pcbios/bios_console.c

+ 209
- 38
src/arch/i386/firmware/pcbios/bios_console.c View File

26
 #include <assert.h>
26
 #include <assert.h>
27
 #include <realmode.h>
27
 #include <realmode.h>
28
 #include <bios.h>
28
 #include <bios.h>
29
+#include <biosint.h>
29
 #include <ipxe/console.h>
30
 #include <ipxe/console.h>
30
 #include <ipxe/ansiesc.h>
31
 #include <ipxe/ansiesc.h>
32
+#include <ipxe/keys.h>
31
 #include <ipxe/keymap.h>
33
 #include <ipxe/keymap.h>
34
+#include <ipxe/init.h>
32
 #include <config/console.h>
35
 #include <config/console.h>
33
 
36
 
34
 #define ATTR_BOLD		0x08
37
 #define ATTR_BOLD		0x08
66
 /** Current character attribute */
69
 /** Current character attribute */
67
 static unsigned int bios_attr = ATTR_DEFAULT;
70
 static unsigned int bios_attr = ATTR_DEFAULT;
68
 
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
+
69
 /**
83
 /**
70
  * Handle ANSI CUP (cursor position)
84
  * Handle ANSI CUP (cursor position)
71
  *
85
  *
265
  * not in the middle of such a sequence, this will point to a NUL
279
  * not in the middle of such a sequence, this will point to a NUL
266
  * (note: not "will be NULL").
280
  * (note: not "will be NULL").
267
  */
281
  */
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 */
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 },
292
 };
311
 };
293
 
312
 
294
 /**
313
 /**
297
  * @v scancode		BIOS scancode
316
  * @v scancode		BIOS scancode
298
  * @ret ansi_seq	ANSI escape sequence, if any, otherwise NULL
317
  * @ret ansi_seq	ANSI escape sequence, if any, otherwise NULL
299
  */
318
  */
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 );
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;
307
 	}
346
 	}
347
+
308
 	DBG ( "Unrecognised BIOS scancode %02x\n", scancode );
348
 	DBG ( "Unrecognised BIOS scancode %02x\n", scancode );
309
 	return NULL;
349
 	return NULL;
310
 }
350
 }
336
 	const char *ansi_seq;
376
 	const char *ansi_seq;
337
 
377
 
338
 	/* If we are mid-sequence, pass out the next byte */
378
 	/* If we are mid-sequence, pass out the next byte */
339
-	if ( ( character = *ansi_input ) ) {
340
-		ansi_input++;
379
+	if ( ( character = *bios_ansi_input ) ) {
380
+		bios_ansi_input++;
341
 		return character;
381
 		return character;
342
 	}
382
 	}
343
 
383
 
384
+	/* Do nothing if injection is in progress */
385
+	if ( bios_inject_lock )
386
+		return 0;
387
+
344
 	/* Read character from real BIOS console */
388
 	/* Read character from real BIOS console */
389
+	bios_inject_lock++;
345
 	__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
390
 	__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
346
 					   "int $0x16\n\t"
391
 					   "int $0x16\n\t"
347
 					   "cli\n\t" )
392
 					   "cli\n\t" )
348
-			       : "=a" ( keypress ) : "a" ( 0x1000 ) );
393
+			       : "=a" ( keypress )
394
+			       : "a" ( 0x1000 ), "m" ( bios_inject_lock ) );
395
+	bios_inject_lock--;
349
 	character = ( keypress & 0xff );
396
 	character = ( keypress & 0xff );
350
 
397
 
351
 	/* If it's a normal character, just map and return it */
398
 	/* If it's a normal character, just map and return it */
353
 		return bios_keymap ( character );
400
 		return bios_keymap ( character );
354
 
401
 
355
 	/* Otherwise, check for a special key that we know about */
402
 	/* Otherwise, check for a special key that we know about */
356
-	if ( ( ansi_seq = scancode_to_ansi_seq ( keypress >> 8 ) ) ) {
403
+	if ( ( ansi_seq = bios_ansi_seq ( keypress >> 8 ) ) ) {
357
 		/* Start of escape sequence: return ESC (0x1b) */
404
 		/* Start of escape sequence: return ESC (0x1b) */
358
-		ansi_input = ansi_seq;
405
+		bios_ansi_input = ansi_seq;
359
 		return 0x1b;
406
 		return 0x1b;
360
 	}
407
 	}
361
 
408
 
373
 	unsigned int flags;
420
 	unsigned int flags;
374
 
421
 
375
 	/* If we are mid-sequence, we are always ready */
422
 	/* If we are mid-sequence, we are always ready */
376
-	if ( *ansi_input )
423
+	if ( *bios_ansi_input )
377
 		return 1;
424
 		return 1;
378
 
425
 
426
+	/* Do nothing if injection is in progress */
427
+	if ( bios_inject_lock )
428
+		return 0;
429
+
379
 	/* Otherwise check the real BIOS console */
430
 	/* Otherwise check the real BIOS console */
431
+	bios_inject_lock++;
380
 	__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
432
 	__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
381
 					   "int $0x16\n\t"
433
 					   "int $0x16\n\t"
382
 					   "pushfw\n\t"
434
 					   "pushfw\n\t"
383
 					   "popw %w0\n\t"
435
 					   "popw %w0\n\t"
384
 					   "cli\n\t" )
436
 					   "cli\n\t" )
385
 			       : "=r" ( flags ), "=a" ( discard_a )
437
 			       : "=r" ( flags ), "=a" ( discard_a )
386
-			       : "a" ( 0x1100 ) );
438
+			       : "a" ( 0x1100 ), "m" ( bios_inject_lock ) );
439
+	bios_inject_lock--;
387
 	return ( ! ( flags & ZF ) );
440
 	return ( ! ( flags & ZF ) );
388
 }
441
 }
389
 
442
 
443
+/** BIOS console */
390
 struct console_driver bios_console __console_driver = {
444
 struct console_driver bios_console __console_driver = {
391
 	.putchar = bios_putchar,
445
 	.putchar = bios_putchar,
392
 	.getchar = bios_getchar,
446
 	.getchar = bios_getchar,
393
 	.iskey = bios_iskey,
447
 	.iskey = bios_iskey,
394
 	.usage = CONSOLE_PCBIOS,
448
 	.usage = CONSOLE_PCBIOS,
395
 };
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, ( ( unsigned int ) 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, ( ( unsigned int ) 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
+};

Loading…
Cancel
Save