|
@@ -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
|
+};
|