mirror of
https://github.com/ipxe/ipxe
synced 2026-05-04 03:03:43 +03:00
6039deb163
The special key range (from KEY_MIN upwards) currently overlaps with the valid range for Unicode characters, and therefore prohibits the use of Unicode key values outside the ASCII range. Create space for Unicode key values by moving the special keys to the range immediately above the maximum valid Unicode character. This allows the existing encoding of special keys as an efficiently packed representation of the equivalent ANSI escape sequence to be maintained almost as-is. Signed-off-by: Michael Brown <mcb30@ipxe.org>
598 lines
16 KiB
C
598 lines
16 KiB
C
/*
|
|
* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*
|
|
* You can also choose to distribute this program under the terms of
|
|
* the Unmodified Binary Distribution Licence (as given in the file
|
|
* COPYING.UBDL), provided that you have satisfied its requirements.
|
|
*/
|
|
|
|
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|
|
|
#include <assert.h>
|
|
#include <realmode.h>
|
|
#include <bios.h>
|
|
#include <biosint.h>
|
|
#include <ipxe/console.h>
|
|
#include <ipxe/ansiesc.h>
|
|
#include <ipxe/keys.h>
|
|
#include <ipxe/keymap.h>
|
|
#include <ipxe/init.h>
|
|
#include <config/console.h>
|
|
|
|
#define ATTR_BOLD 0x08
|
|
|
|
#define ATTR_FCOL_MASK 0x07
|
|
#define ATTR_FCOL_BLACK 0x00
|
|
#define ATTR_FCOL_BLUE 0x01
|
|
#define ATTR_FCOL_GREEN 0x02
|
|
#define ATTR_FCOL_CYAN 0x03
|
|
#define ATTR_FCOL_RED 0x04
|
|
#define ATTR_FCOL_MAGENTA 0x05
|
|
#define ATTR_FCOL_YELLOW 0x06
|
|
#define ATTR_FCOL_WHITE 0x07
|
|
|
|
#define ATTR_BLINK 0x80
|
|
|
|
#define ATTR_BCOL_MASK 0x70
|
|
#define ATTR_BCOL_BLACK 0x00
|
|
#define ATTR_BCOL_BLUE 0x10
|
|
#define ATTR_BCOL_GREEN 0x20
|
|
#define ATTR_BCOL_CYAN 0x30
|
|
#define ATTR_BCOL_RED 0x40
|
|
#define ATTR_BCOL_MAGENTA 0x50
|
|
#define ATTR_BCOL_YELLOW 0x60
|
|
#define ATTR_BCOL_WHITE 0x70
|
|
|
|
#define ATTR_DEFAULT ATTR_FCOL_WHITE
|
|
|
|
/** Maximum keycode subject to remapping
|
|
*
|
|
* This allows us to avoid remapping the numeric keypad, which is
|
|
* necessary for keyboard layouts such as "fr" that swap the shifted
|
|
* and unshifted digit keys.
|
|
*/
|
|
#define SCANCODE_RSHIFT 0x36
|
|
|
|
/** Scancode for the "non-US \ and |" key
|
|
*
|
|
* This is the key that appears between Left Shift and Z on non-US
|
|
* keyboards.
|
|
*/
|
|
#define SCANCODE_NON_US 0x56
|
|
|
|
/* Set default console usage if applicable */
|
|
#if ! ( defined ( CONSOLE_PCBIOS ) && CONSOLE_EXPLICIT ( CONSOLE_PCBIOS ) )
|
|
#undef CONSOLE_PCBIOS
|
|
#define CONSOLE_PCBIOS ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
|
|
#endif
|
|
|
|
/** Current character attribute */
|
|
static unsigned int bios_attr = ATTR_DEFAULT;
|
|
|
|
/** Keypress injection lock */
|
|
static uint8_t __text16 ( bios_inject_lock );
|
|
#define bios_inject_lock __use_text16 ( bios_inject_lock )
|
|
|
|
/** Vector for chaining to other INT 16 handlers */
|
|
static struct segoff __text16 ( int16_vector );
|
|
#define int16_vector __use_text16 ( int16_vector )
|
|
|
|
/** Assembly wrapper */
|
|
extern void int16_wrapper ( void );
|
|
|
|
/**
|
|
* Handle ANSI CUP (cursor position)
|
|
*
|
|
* @v ctx ANSI escape sequence context
|
|
* @v count Parameter count
|
|
* @v params[0] Row (1 is top)
|
|
* @v params[1] Column (1 is left)
|
|
*/
|
|
static void bios_handle_cup ( struct ansiesc_context *ctx __unused,
|
|
unsigned int count __unused, int params[] ) {
|
|
int cx = ( params[1] - 1 );
|
|
int cy = ( params[0] - 1 );
|
|
|
|
if ( cx < 0 )
|
|
cx = 0;
|
|
if ( cy < 0 )
|
|
cy = 0;
|
|
|
|
__asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" )
|
|
: : "a" ( 0x0200 ), "b" ( 1 ),
|
|
"d" ( ( cy << 8 ) | cx ) );
|
|
}
|
|
|
|
/**
|
|
* Handle ANSI ED (erase in page)
|
|
*
|
|
* @v ctx ANSI escape sequence context
|
|
* @v count Parameter count
|
|
* @v params[0] Region to erase
|
|
*/
|
|
static void bios_handle_ed ( struct ansiesc_context *ctx __unused,
|
|
unsigned int count __unused,
|
|
int params[] __unused ) {
|
|
/* We assume that we always clear the whole screen */
|
|
assert ( params[0] == ANSIESC_ED_ALL );
|
|
|
|
__asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" )
|
|
: : "a" ( 0x0600 ), "b" ( bios_attr << 8 ),
|
|
"c" ( 0 ),
|
|
"d" ( ( ( console_height - 1 ) << 8 ) |
|
|
( console_width - 1 ) ) );
|
|
}
|
|
|
|
/**
|
|
* Handle ANSI SGR (set graphics rendition)
|
|
*
|
|
* @v ctx ANSI escape sequence context
|
|
* @v count Parameter count
|
|
* @v params List of graphic rendition aspects
|
|
*/
|
|
static void bios_handle_sgr ( struct ansiesc_context *ctx __unused,
|
|
unsigned int count, int params[] ) {
|
|
static const uint8_t bios_attr_fcols[10] = {
|
|
ATTR_FCOL_BLACK, ATTR_FCOL_RED, ATTR_FCOL_GREEN,
|
|
ATTR_FCOL_YELLOW, ATTR_FCOL_BLUE, ATTR_FCOL_MAGENTA,
|
|
ATTR_FCOL_CYAN, ATTR_FCOL_WHITE,
|
|
ATTR_FCOL_WHITE, ATTR_FCOL_WHITE /* defaults */
|
|
};
|
|
static const uint8_t bios_attr_bcols[10] = {
|
|
ATTR_BCOL_BLACK, ATTR_BCOL_RED, ATTR_BCOL_GREEN,
|
|
ATTR_BCOL_YELLOW, ATTR_BCOL_BLUE, ATTR_BCOL_MAGENTA,
|
|
ATTR_BCOL_CYAN, ATTR_BCOL_WHITE,
|
|
ATTR_BCOL_BLACK, ATTR_BCOL_BLACK /* defaults */
|
|
};
|
|
unsigned int i;
|
|
int aspect;
|
|
|
|
for ( i = 0 ; i < count ; i++ ) {
|
|
aspect = params[i];
|
|
if ( aspect == 0 ) {
|
|
bios_attr = ATTR_DEFAULT;
|
|
} else if ( aspect == 1 ) {
|
|
bios_attr |= ATTR_BOLD;
|
|
} else if ( aspect == 5 ) {
|
|
bios_attr |= ATTR_BLINK;
|
|
} else if ( aspect == 22 ) {
|
|
bios_attr &= ~ATTR_BOLD;
|
|
} else if ( aspect == 25 ) {
|
|
bios_attr &= ~ATTR_BLINK;
|
|
} else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) {
|
|
bios_attr &= ~ATTR_FCOL_MASK;
|
|
bios_attr |= bios_attr_fcols[ aspect - 30 ];
|
|
} else if ( ( aspect >= 40 ) && ( aspect <= 49 ) ) {
|
|
bios_attr &= ~ATTR_BCOL_MASK;
|
|
bios_attr |= bios_attr_bcols[ aspect - 40 ];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle ANSI DECTCEM set (show cursor)
|
|
*
|
|
* @v ctx ANSI escape sequence context
|
|
* @v count Parameter count
|
|
* @v params List of graphic rendition aspects
|
|
*/
|
|
static void bios_handle_dectcem_set ( struct ansiesc_context *ctx __unused,
|
|
unsigned int count __unused,
|
|
int params[] __unused ) {
|
|
uint8_t height;
|
|
|
|
/* Get character height */
|
|
get_real ( height, BDA_SEG, BDA_CHAR_HEIGHT );
|
|
|
|
__asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" )
|
|
: : "a" ( 0x0100 ),
|
|
"c" ( ( ( height - 2 ) << 8 ) |
|
|
( height - 1 ) ) );
|
|
}
|
|
|
|
/**
|
|
* Handle ANSI DECTCEM reset (hide cursor)
|
|
*
|
|
* @v ctx ANSI escape sequence context
|
|
* @v count Parameter count
|
|
* @v params List of graphic rendition aspects
|
|
*/
|
|
static void bios_handle_dectcem_reset ( struct ansiesc_context *ctx __unused,
|
|
unsigned int count __unused,
|
|
int params[] __unused ) {
|
|
|
|
__asm__ __volatile__ ( REAL_CODE ( "int $0x10\n\t" )
|
|
: : "a" ( 0x0100 ), "c" ( 0x2000 ) );
|
|
}
|
|
|
|
/** BIOS console ANSI escape sequence handlers */
|
|
static struct ansiesc_handler bios_ansiesc_handlers[] = {
|
|
{ ANSIESC_CUP, bios_handle_cup },
|
|
{ ANSIESC_ED, bios_handle_ed },
|
|
{ ANSIESC_SGR, bios_handle_sgr },
|
|
{ ANSIESC_DECTCEM_SET, bios_handle_dectcem_set },
|
|
{ ANSIESC_DECTCEM_RESET, bios_handle_dectcem_reset },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
/** BIOS console ANSI escape sequence context */
|
|
static struct ansiesc_context bios_ansiesc_ctx = {
|
|
.handlers = bios_ansiesc_handlers,
|
|
};
|
|
|
|
/**
|
|
* Print a character to BIOS console
|
|
*
|
|
* @v character Character to be printed
|
|
*/
|
|
static void bios_putchar ( int character ) {
|
|
int discard_a, discard_b, discard_c;
|
|
|
|
/* Intercept ANSI escape sequences */
|
|
character = ansiesc_process ( &bios_ansiesc_ctx, character );
|
|
if ( character < 0 )
|
|
return;
|
|
|
|
/* Print character with attribute */
|
|
__asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
|
|
/* Skip non-printable characters */
|
|
"cmpb $0x20, %%al\n\t"
|
|
"jb 1f\n\t"
|
|
/* Read attribute */
|
|
"movb %%al, %%cl\n\t"
|
|
"movb $0x08, %%ah\n\t"
|
|
"int $0x10\n\t"
|
|
"xchgb %%al, %%cl\n\t"
|
|
/* Skip if attribute matches */
|
|
"cmpb %%ah, %%bl\n\t"
|
|
"je 1f\n\t"
|
|
/* Set attribute */
|
|
"movw $0x0001, %%cx\n\t"
|
|
"movb $0x09, %%ah\n\t"
|
|
"int $0x10\n\t"
|
|
"\n1:\n\t"
|
|
/* Print character */
|
|
"xorw %%bx, %%bx\n\t"
|
|
"movb $0x0e, %%ah\n\t"
|
|
"int $0x10\n\t"
|
|
"popl %%ebp\n\t" /* gcc bug */ )
|
|
: "=a" ( discard_a ), "=b" ( discard_b ),
|
|
"=c" ( discard_c )
|
|
: "a" ( character ), "b" ( bios_attr ) );
|
|
}
|
|
|
|
/**
|
|
* Pointer to current ANSI output sequence
|
|
*
|
|
* While we are in the middle of returning an ANSI sequence for a
|
|
* special key, this will point to the next character to return. When
|
|
* not in the middle of such a sequence, this will point to a NUL
|
|
* (note: not "will be NULL").
|
|
*/
|
|
static const char *bios_ansi_input = "";
|
|
|
|
/** A BIOS key */
|
|
struct bios_key {
|
|
/** Scancode */
|
|
uint8_t scancode;
|
|
/** Relative key value */
|
|
uint16_t rkey;
|
|
} __attribute__ (( packed ));
|
|
|
|
/**
|
|
* Define a BIOS key mapping
|
|
*
|
|
* @v scancode Scancode
|
|
* @v key iPXE key code
|
|
* @v bioskey BIOS key mapping
|
|
*/
|
|
#define BIOS_KEY( scancode, key ) { scancode, KEY_REL ( key ) }
|
|
|
|
/** Mapping from BIOS scan codes to iPXE key codes */
|
|
static const struct bios_key bios_keys[] = {
|
|
BIOS_KEY ( 0x53, KEY_DC ),
|
|
BIOS_KEY ( 0x48, KEY_UP ),
|
|
BIOS_KEY ( 0x50, KEY_DOWN ),
|
|
BIOS_KEY ( 0x4b, KEY_LEFT ),
|
|
BIOS_KEY ( 0x4d, KEY_RIGHT ),
|
|
BIOS_KEY ( 0x47, KEY_HOME ),
|
|
BIOS_KEY ( 0x4f, KEY_END ),
|
|
BIOS_KEY ( 0x49, KEY_PPAGE ),
|
|
BIOS_KEY ( 0x51, KEY_NPAGE ),
|
|
BIOS_KEY ( 0x3f, KEY_F5 ),
|
|
BIOS_KEY ( 0x40, KEY_F6 ),
|
|
BIOS_KEY ( 0x41, KEY_F7 ),
|
|
BIOS_KEY ( 0x42, KEY_F8 ),
|
|
BIOS_KEY ( 0x43, KEY_F9 ),
|
|
BIOS_KEY ( 0x44, KEY_F10 ),
|
|
BIOS_KEY ( 0x85, KEY_F11 ),
|
|
BIOS_KEY ( 0x86, KEY_F12 ),
|
|
};
|
|
|
|
/**
|
|
* Get ANSI escape sequence corresponding to BIOS scancode
|
|
*
|
|
* @v scancode BIOS scancode
|
|
* @ret ansi_seq ANSI escape sequence, if any, otherwise NULL
|
|
*/
|
|
static const char * bios_ansi_seq ( unsigned int scancode ) {
|
|
static char buf[ 5 /* "[" + two digits + terminator + NUL */ ];
|
|
unsigned int rkey;
|
|
unsigned int terminator;
|
|
unsigned int n;
|
|
unsigned int i;
|
|
char *tmp = buf;
|
|
|
|
/* Construct ANSI escape sequence for scancode, if known */
|
|
for ( i = 0 ; i < ( sizeof ( bios_keys ) /
|
|
sizeof ( bios_keys[0] ) ) ; i++ ) {
|
|
|
|
/* Look for matching scancode */
|
|
if ( bios_keys[i].scancode != scancode )
|
|
continue;
|
|
|
|
/* Construct escape sequence */
|
|
rkey = bios_keys[i].rkey;
|
|
n = KEY_ANSI_N ( rkey );
|
|
terminator = KEY_ANSI_TERMINATOR ( rkey );
|
|
*(tmp++) = '[';
|
|
if ( n )
|
|
tmp += sprintf ( tmp, "%d", n );
|
|
*(tmp++) = terminator;
|
|
*(tmp++) = '\0';
|
|
assert ( tmp <= &buf[ sizeof ( buf ) ] );
|
|
return buf;
|
|
}
|
|
|
|
DBG ( "Unrecognised BIOS scancode %02x\n", scancode );
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Get character from BIOS console
|
|
*
|
|
* @ret character Character read from console
|
|
*/
|
|
static int bios_getchar ( void ) {
|
|
uint16_t keypress;
|
|
uint8_t kb0;
|
|
uint8_t kb2;
|
|
unsigned int scancode;
|
|
unsigned int character;
|
|
const char *ansi_seq;
|
|
|
|
/* If we are mid-sequence, pass out the next byte */
|
|
if ( ( character = *bios_ansi_input ) ) {
|
|
bios_ansi_input++;
|
|
return character;
|
|
}
|
|
|
|
/* Do nothing if injection is in progress */
|
|
if ( bios_inject_lock )
|
|
return 0;
|
|
|
|
/* Read character from real BIOS console */
|
|
bios_inject_lock++;
|
|
__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
|
|
"int $0x16\n\t"
|
|
"cli\n\t" )
|
|
: "=a" ( keypress )
|
|
: "a" ( 0x1000 ), "m" ( bios_inject_lock ) );
|
|
bios_inject_lock--;
|
|
scancode = ( keypress >> 8 );
|
|
character = ( keypress & 0xff );
|
|
get_real ( kb0, BDA_SEG, BDA_KB0 );
|
|
get_real ( kb2, BDA_SEG, BDA_KB2 );
|
|
|
|
/* If it's a normal character, map (if applicable) and return it */
|
|
if ( character && ( character < 0x80 ) ) {
|
|
|
|
/* Handle special scancodes */
|
|
if ( scancode == SCANCODE_NON_US ) {
|
|
/* Treat as "\|" with high bit set */
|
|
character |= KEYMAP_PSEUDO;
|
|
} else if ( scancode >= SCANCODE_RSHIFT ) {
|
|
/* Non-remappable scancode (e.g. numeric keypad) */
|
|
return character;
|
|
}
|
|
|
|
/* Apply modifiers */
|
|
if ( kb0 & BDA_KB0_CTRL )
|
|
character |= KEYMAP_CTRL;
|
|
if ( kb0 & BDA_KB0_CAPSLOCK )
|
|
character |= KEYMAP_CAPSLOCK_REDO;
|
|
if ( kb2 & BDA_KB2_RALT )
|
|
character |= KEYMAP_ALTGR;
|
|
|
|
/* Treat LShift+RShift as AltGr since many BIOSes will
|
|
* not return ASCII characters when AltGr is pressed.
|
|
*/
|
|
if ( ( kb0 & ( BDA_KB0_LSHIFT | BDA_KB0_RSHIFT ) ) ==
|
|
( BDA_KB0_LSHIFT | BDA_KB0_RSHIFT ) ) {
|
|
character |= KEYMAP_ALTGR;
|
|
}
|
|
|
|
/* Map and return */
|
|
return key_remap ( character );
|
|
}
|
|
|
|
/* Otherwise, check for a special key that we know about */
|
|
if ( ( ansi_seq = bios_ansi_seq ( keypress >> 8 ) ) ) {
|
|
/* Start of escape sequence: return ESC (0x1b) */
|
|
bios_ansi_input = ansi_seq;
|
|
return 0x1b;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Check for character ready to read from BIOS console
|
|
*
|
|
* @ret True Character available to read
|
|
* @ret False No character available to read
|
|
*/
|
|
static int bios_iskey ( void ) {
|
|
unsigned int discard_a;
|
|
unsigned int flags;
|
|
|
|
/* If we are mid-sequence, we are always ready */
|
|
if ( *bios_ansi_input )
|
|
return 1;
|
|
|
|
/* Do nothing if injection is in progress */
|
|
if ( bios_inject_lock )
|
|
return 0;
|
|
|
|
/* Otherwise check the real BIOS console */
|
|
bios_inject_lock++;
|
|
__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
|
|
"int $0x16\n\t"
|
|
"pushfw\n\t"
|
|
"popw %w0\n\t"
|
|
"cli\n\t" )
|
|
: "=R" ( flags ), "=a" ( discard_a )
|
|
: "a" ( 0x1100 ), "m" ( bios_inject_lock ) );
|
|
bios_inject_lock--;
|
|
return ( ! ( flags & ZF ) );
|
|
}
|
|
|
|
/** BIOS console */
|
|
struct console_driver bios_console __console_driver = {
|
|
.putchar = bios_putchar,
|
|
.getchar = bios_getchar,
|
|
.iskey = bios_iskey,
|
|
.usage = CONSOLE_PCBIOS,
|
|
};
|
|
|
|
/**
|
|
* Inject keypresses
|
|
*
|
|
* @v ix86 Registers as passed to INT 16
|
|
*/
|
|
static __asmcall __used void bios_inject ( struct i386_all_regs *ix86 ) {
|
|
unsigned int discard_a;
|
|
unsigned int scancode;
|
|
unsigned int rkey;
|
|
unsigned int i;
|
|
uint16_t keypress;
|
|
int key;
|
|
|
|
/* If this is a blocking call, then loop until the
|
|
* non-blocking variant of the call indicates that a keypress
|
|
* is available. Do this without acquiring the injection
|
|
* lock, so that injection may take place.
|
|
*/
|
|
if ( ( ix86->regs.ah & ~0x10 ) == 0x00 ) {
|
|
__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
|
|
"\n1:\n\t"
|
|
"pushw %%ax\n\t"
|
|
"int $0x16\n\t"
|
|
"popw %%ax\n\t"
|
|
"jc 2f\n\t"
|
|
"jz 1b\n\t"
|
|
"\n2:\n\t"
|
|
"cli\n\t" )
|
|
: "=a" ( discard_a )
|
|
: "a" ( ix86->regs.eax | 0x0100 ),
|
|
"m" ( bios_inject_lock ) );
|
|
}
|
|
|
|
/* Acquire injection lock */
|
|
bios_inject_lock++;
|
|
|
|
/* Check for keypresses */
|
|
if ( iskey() ) {
|
|
|
|
/* Get key */
|
|
key = getkey ( 0 );
|
|
|
|
/* Reverse internal CR->LF mapping */
|
|
if ( key == '\n' )
|
|
key = '\r';
|
|
|
|
/* Convert to keypress */
|
|
keypress = ( ( key << 8 ) | key );
|
|
|
|
/* Handle special keys */
|
|
if ( key >= KEY_MIN ) {
|
|
rkey = KEY_REL ( key );
|
|
for ( i = 0 ; i < ( sizeof ( bios_keys ) /
|
|
sizeof ( bios_keys[0] ) ) ; i++ ) {
|
|
if ( bios_keys[i].rkey == rkey ) {
|
|
scancode = bios_keys[i].scancode;
|
|
keypress = ( scancode << 8 );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Inject keypress */
|
|
DBGC ( &bios_console, "BIOS injecting keypress %04x\n",
|
|
keypress );
|
|
__asm__ __volatile__ ( REAL_CODE ( "int $0x16\n\t" )
|
|
: "=a" ( discard_a )
|
|
: "a" ( 0x0500 ), "c" ( keypress ),
|
|
"m" ( bios_inject_lock ) );
|
|
}
|
|
|
|
/* Release injection lock */
|
|
bios_inject_lock--;
|
|
}
|
|
|
|
/**
|
|
* Start up keypress injection
|
|
*
|
|
*/
|
|
static void bios_inject_startup ( void ) {
|
|
|
|
/* Assembly wrapper to call bios_inject() */
|
|
__asm__ __volatile__ (
|
|
TEXT16_CODE ( "\nint16_wrapper:\n\t"
|
|
"pushfw\n\t"
|
|
"cmpb $0, %%cs:bios_inject_lock\n\t"
|
|
"jnz 1f\n\t"
|
|
VIRT_CALL ( bios_inject )
|
|
"\n1:\n\t"
|
|
"popfw\n\t"
|
|
"ljmp *%%cs:int16_vector\n\t" ) : );
|
|
|
|
/* Hook INT 16 */
|
|
hook_bios_interrupt ( 0x16, ( ( intptr_t ) int16_wrapper ),
|
|
&int16_vector );
|
|
}
|
|
|
|
/**
|
|
* Shut down keypress injection
|
|
*
|
|
* @v booting System is shutting down for OS boot
|
|
*/
|
|
static void bios_inject_shutdown ( int booting __unused ) {
|
|
|
|
/* Unhook INT 16 */
|
|
unhook_bios_interrupt ( 0x16, ( ( intptr_t ) int16_wrapper ),
|
|
&int16_vector );
|
|
}
|
|
|
|
/** Keypress injection startup function */
|
|
struct startup_fn bios_inject_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
|
|
.name = "bios_inject",
|
|
.startup = bios_inject_startup,
|
|
.shutdown = bios_inject_shutdown,
|
|
};
|