2
0
mirror of https://github.com/xcat2/xNBA.git synced 2024-12-15 07:41:45 +00:00
xNBA/src/arch/i386/firmware/pcbios/gateA20.c
Michael Brown 8bff0f0619 Avoid draining the keyboard buffer during gateA20_set(). It shouldn't
technically be necessary, because the "enable A20" command requires
only that the keyboard controller is ready to accept input (i.e. that
its input buffer is empty), and shouldn't also require that the
keyboard is ready to send output (i.e. that its output buffer is also
empty).  See http://www.smsc.com/main/tools/io-bios/42i.pdf section
3.1 ("Command Invocation") for a justification.

gateA20_set() is called on every real-mode transition (in case some
idiot piece of external code such as Intel's PXE stack decided it
would be fun to re-disable A20), so draining the keyboard buffer means
that we end up losing keypresses on some systems.  In particular, this
makes typing at the command line almost impossible, and causes
Etherboot to ignore Ctrl-Alt-Del.

We should really implement a gateA20_test() function to verify that
gate A20 has been correctly enabled, and think about adding other
commonly-used methods such as Fast Gate A20.
2006-12-05 09:45:39 +00:00

90 lines
2.2 KiB
C

#include "realmode.h"
#include "timer.h"
#include "latch.h"
#include "bios.h"
#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */
#define K_STATUS 0x64 /* keyboard status */
#define K_CMD 0x64 /* keybd ctlr command (write-only) */
#define K_OBUF_FUL 0x01 /* output buffer full */
#define K_IBUF_FUL 0x02 /* input buffer full */
#define KC_CMD_WIN 0xd0 /* read output port */
#define KC_CMD_WOUT 0xd1 /* write output port */
#define KB_SET_A20 0xdf /* enable A20,
enable output buffer full interrupt
enable data line
disable clock line */
#define KB_UNSET_A20 0xdd /* enable A20,
enable output buffer full interrupt
enable data line
disable clock line */
enum { Disable_A20 = 0x2400, Enable_A20 = 0x2401, Query_A20_Status = 0x2402,
Query_A20_Support = 0x2403 };
#define CF ( 1 << 0 )
#ifndef IBM_L40
static void empty_8042 ( void ) {
unsigned long time;
time = currticks() + TICKS_PER_SEC; /* max wait of 1 second */
while ( ( inb ( K_CMD ) & K_IBUF_FUL ) &&
currticks() < time ) {
/* Do nothing. In particular, do *not* read from
* K_RDWR, because that will drain the keyboard buffer
* and lose keypresses.
*/
}
}
#endif /* IBM_L40 */
/*
* Gate A20 for high memory
*
* Note that this function gets called as part of the return path from
* librm's real_call, which is used to make the int15 call if librm is
* being used. To avoid an infinite recursion, we make gateA20_set
* return immediately if it is already part of the call stack.
*/
void gateA20_set ( void ) {
static char reentry_guard = 0;
unsigned int discard_a;
unsigned int flags;
if ( reentry_guard )
return;
reentry_guard = 1;
__asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
"stc\n\t"
"int $0x15\n\t"
"pushfw\n\t"
"popw %w0\n\t"
"cli\n\t" )
: "=r" ( flags ), "=a" ( discard_a )
: "a" ( Enable_A20 ) );
if ( flags & CF ) {
/* INT 15 method failed, try alternatives */
#ifdef IBM_L40
outb(0x2, 0x92);
#else /* IBM_L40 */
empty_8042();
outb(KC_CMD_WOUT, K_CMD);
empty_8042();
outb(KB_SET_A20, K_RDWR);
empty_8042();
#endif /* IBM_L40 */
}
reentry_guard = 0;
}
void gateA20_unset ( void ) {
/* Not currently implemented */
}