Преглед изворни кода

[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 година
родитељ
комит
0f67f2edb7
1 измењених фајлова са 209 додато и 38 уклоњено
  1. 209
    38
      src/arch/i386/firmware/pcbios/bios_console.c

+ 209
- 38
src/arch/i386/firmware/pcbios/bios_console.c Прегледај датотеку

@@ -26,9 +26,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
26 26
 #include <assert.h>
27 27
 #include <realmode.h>
28 28
 #include <bios.h>
29
+#include <biosint.h>
29 30
 #include <ipxe/console.h>
30 31
 #include <ipxe/ansiesc.h>
32
+#include <ipxe/keys.h>
31 33
 #include <ipxe/keymap.h>
34
+#include <ipxe/init.h>
32 35
 #include <config/console.h>
33 36
 
34 37
 #define ATTR_BOLD		0x08
@@ -66,6 +69,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
66 69
 /** Current character attribute */
67 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 84
  * Handle ANSI CUP (cursor position)
71 85
  *
@@ -265,30 +279,35 @@ static void bios_putchar ( int character ) {
265 279
  * not in the middle of such a sequence, this will point to a NUL
266 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,14 +316,35 @@ static const char ansi_sequences[] = {
297 316
  * @v scancode		BIOS scancode
298 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 348
 	DBG ( "Unrecognised BIOS scancode %02x\n", scancode );
309 349
 	return NULL;
310 350
 }
@@ -336,16 +376,23 @@ static int bios_getchar ( void ) {
336 376
 	const char *ansi_seq;
337 377
 
338 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 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 388
 	/* Read character from real BIOS console */
389
+	bios_inject_lock++;
345 390
 	__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
346 391
 					   "int $0x16\n\t"
347 392
 					   "cli\n\t" )
348
-			       : "=a" ( keypress ) : "a" ( 0x1000 ) );
393
+			       : "=a" ( keypress )
394
+			       : "a" ( 0x1000 ), "m" ( bios_inject_lock ) );
395
+	bios_inject_lock--;
349 396
 	character = ( keypress & 0xff );
350 397
 
351 398
 	/* If it's a normal character, just map and return it */
@@ -353,9 +400,9 @@ static int bios_getchar ( void ) {
353 400
 		return bios_keymap ( character );
354 401
 
355 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 404
 		/* Start of escape sequence: return ESC (0x1b) */
358
-		ansi_input = ansi_seq;
405
+		bios_ansi_input = ansi_seq;
359 406
 		return 0x1b;
360 407
 	}
361 408
 
@@ -373,23 +420,147 @@ static int bios_iskey ( void ) {
373 420
 	unsigned int flags;
374 421
 
375 422
 	/* If we are mid-sequence, we are always ready */
376
-	if ( *ansi_input )
423
+	if ( *bios_ansi_input )
377 424
 		return 1;
378 425
 
426
+	/* Do nothing if injection is in progress */
427
+	if ( bios_inject_lock )
428
+		return 0;
429
+
379 430
 	/* Otherwise check the real BIOS console */
431
+	bios_inject_lock++;
380 432
 	__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
381 433
 					   "int $0x16\n\t"
382 434
 					   "pushfw\n\t"
383 435
 					   "popw %w0\n\t"
384 436
 					   "cli\n\t" )
385 437
 			       : "=r" ( flags ), "=a" ( discard_a )
386
-			       : "a" ( 0x1100 ) );
438
+			       : "a" ( 0x1100 ), "m" ( bios_inject_lock ) );
439
+	bios_inject_lock--;
387 440
 	return ( ! ( flags & ZF ) );
388 441
 }
389 442
 
443
+/** BIOS console */
390 444
 struct console_driver bios_console __console_driver = {
391 445
 	.putchar = bios_putchar,
392 446
 	.getchar = bios_getchar,
393 447
 	.iskey = bios_iskey,
394 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…
Откажи
Сачувај