mirror of
https://github.com/xcat2/xNBA.git
synced 2024-12-15 07:41:45 +00:00
8bff0f0619
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.
90 lines
2.2 KiB
C
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 */
|
|
}
|