mirror of
https://github.com/xcat2/xNBA.git
synced 2024-11-22 01:21:45 +00:00
[librm] Use libflat to enable A20 line on each real-to-protected transition
Use the shared code in libflat to perform the A20 transitions automatically on each transition from real to protected mode. This allows us to remove all explicit calls to gateA20_set(). The old warnings about avoiding automatically enabling A20 are essentially redundant; they date back to the time when we would always start hammering the keyboard controller without first checking to see if gate A20 was already enabled (which it almost always is). Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
parent
24b52ae476
commit
38cd2035ff
@ -104,16 +104,6 @@ int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) {
|
||||
: "a" ( __from_data16 ( &undi_loader ) )
|
||||
: "ebx", "ecx", "edx", "esi", "edi", "ebp" );
|
||||
|
||||
/* UNDI API calls may rudely change the status of A20 and not
|
||||
* bother to restore it afterwards. Intel is known to be
|
||||
* guilty of this.
|
||||
*
|
||||
* Note that we will return to this point even if A20 gets
|
||||
* screwed up by the UNDI driver, because Etherboot always
|
||||
* resides in an even megabyte of RAM.
|
||||
*/
|
||||
gateA20_set();
|
||||
|
||||
if ( exit != PXENV_EXIT_SUCCESS ) {
|
||||
/* Clear entry point */
|
||||
memset ( &undi_loader_entry, 0, sizeof ( undi_loader_entry ) );
|
||||
|
@ -1,176 +0,0 @@
|
||||
FILE_LICENCE ( GPL2_OR_LATER );
|
||||
|
||||
#include <stdio.h>
|
||||
#include <realmode.h>
|
||||
#include <bios.h>
|
||||
#include <ipxe/io.h>
|
||||
#include <ipxe/timer.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 KC_CMD_NULL 0xff /* null command ("pulse nothing") */
|
||||
#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 */
|
||||
|
||||
#define SCP_A 0x92 /* System Control Port A */
|
||||
|
||||
enum { Disable_A20 = 0x2400, Enable_A20 = 0x2401, Query_A20_Status = 0x2402,
|
||||
Query_A20_Support = 0x2403 };
|
||||
|
||||
enum a20_methods {
|
||||
A20_UNKNOWN = 0,
|
||||
A20_INT15,
|
||||
A20_KBC,
|
||||
A20_SCPA,
|
||||
};
|
||||
|
||||
#define A20_MAX_RETRIES 32
|
||||
#define A20_INT15_RETRIES 32
|
||||
#define A20_KBC_RETRIES (2^21)
|
||||
#define A20_SCPA_RETRIES (2^21)
|
||||
|
||||
/**
|
||||
* Drain keyboard controller
|
||||
*/
|
||||
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 | K_OBUF_FUL ) ) &&
|
||||
currticks() < time ) {
|
||||
iodelay();
|
||||
( void ) inb_p ( K_RDWR );
|
||||
iodelay();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast test to see if gate A20 is already set
|
||||
*
|
||||
* @v retries Number of times to retry before giving up
|
||||
* @ret set Gate A20 is set
|
||||
*/
|
||||
static int gateA20_is_set ( int retries ) {
|
||||
static uint32_t test_pattern = 0xdeadbeef;
|
||||
physaddr_t test_pattern_phys = virt_to_phys ( &test_pattern );
|
||||
physaddr_t verify_pattern_phys = ( test_pattern_phys ^ 0x100000 );
|
||||
userptr_t verify_pattern_user = phys_to_user ( verify_pattern_phys );
|
||||
uint32_t verify_pattern;
|
||||
|
||||
do {
|
||||
/* Check for difference */
|
||||
copy_from_user ( &verify_pattern, verify_pattern_user, 0,
|
||||
sizeof ( verify_pattern ) );
|
||||
if ( verify_pattern != test_pattern )
|
||||
return 1;
|
||||
|
||||
/* Avoid false negatives */
|
||||
test_pattern++;
|
||||
|
||||
iodelay();
|
||||
|
||||
/* Always retry at least once, to avoid false negatives */
|
||||
} while ( retries-- >= 0 );
|
||||
|
||||
/* Pattern matched every time; gate A20 is not set */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
static int a20_method = A20_UNKNOWN;
|
||||
unsigned int discard_a;
|
||||
unsigned int scp_a;
|
||||
int retries = 0;
|
||||
|
||||
/* Avoid potential infinite recursion */
|
||||
if ( reentry_guard )
|
||||
return;
|
||||
reentry_guard = 1;
|
||||
|
||||
/* Fast check to see if gate A20 is already enabled */
|
||||
if ( gateA20_is_set ( 0 ) )
|
||||
goto out;
|
||||
|
||||
for ( ; retries < A20_MAX_RETRIES ; retries++ ) {
|
||||
switch ( a20_method ) {
|
||||
case A20_UNKNOWN:
|
||||
case A20_INT15:
|
||||
/* Try INT 15 method */
|
||||
__asm__ __volatile__ ( REAL_CODE ( "int $0x15" )
|
||||
: "=a" ( discard_a )
|
||||
: "a" ( Enable_A20 ) );
|
||||
if ( gateA20_is_set ( A20_INT15_RETRIES ) ) {
|
||||
DBG ( "Enabled gate A20 using BIOS\n" );
|
||||
a20_method = A20_INT15;
|
||||
goto out;
|
||||
}
|
||||
/* fall through */
|
||||
case A20_KBC:
|
||||
/* Try keyboard controller method */
|
||||
empty_8042();
|
||||
outb ( KC_CMD_WOUT, K_CMD );
|
||||
empty_8042();
|
||||
outb ( KB_SET_A20, K_RDWR );
|
||||
empty_8042();
|
||||
outb ( KC_CMD_NULL, K_CMD );
|
||||
empty_8042();
|
||||
if ( gateA20_is_set ( A20_KBC_RETRIES ) ) {
|
||||
DBG ( "Enabled gate A20 using "
|
||||
"keyboard controller\n" );
|
||||
a20_method = A20_KBC;
|
||||
goto out;
|
||||
}
|
||||
/* fall through */
|
||||
case A20_SCPA:
|
||||
/* Try "Fast gate A20" method */
|
||||
scp_a = inb ( SCP_A );
|
||||
scp_a &= ~0x01; /* Avoid triggering a reset */
|
||||
scp_a |= 0x02; /* Enable A20 */
|
||||
iodelay();
|
||||
outb ( scp_a, SCP_A );
|
||||
iodelay();
|
||||
if ( gateA20_is_set ( A20_SCPA_RETRIES ) ) {
|
||||
DBG ( "Enabled gate A20 using "
|
||||
"Fast Gate A20\n" );
|
||||
a20_method = A20_SCPA;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Better to die now than corrupt memory later */
|
||||
printf ( "FATAL: Gate A20 stuck\n" );
|
||||
while ( 1 ) {}
|
||||
|
||||
out:
|
||||
if ( retries )
|
||||
DBG ( "%d attempts were required to enable A20\n",
|
||||
( retries + 1 ) );
|
||||
reentry_guard = 0;
|
||||
}
|
||||
|
||||
void gateA20_unset ( void ) {
|
||||
/* Not currently implemented */
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <realmode.h>
|
||||
#include <gateA20.h>
|
||||
#include <memsizes.h>
|
||||
#include <basemem_packet.h>
|
||||
#include <ipxe/uaccess.h>
|
||||
@ -306,8 +305,6 @@ static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) {
|
||||
imgheader->execaddr.segoff.segment,
|
||||
imgheader->execaddr.segoff.offset );
|
||||
|
||||
gateA20_unset();
|
||||
|
||||
__asm__ __volatile__ (
|
||||
REAL_CODE ( "pushw %%ds\n\t" /* far pointer to bootp data */
|
||||
"pushw %%bx\n\t"
|
||||
@ -327,8 +324,6 @@ static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) {
|
||||
"b" ( __from_data16 ( basemem_packet ) )
|
||||
: "ecx", "edx", "ebp" );
|
||||
|
||||
gateA20_set();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -345,8 +340,6 @@ static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) {
|
||||
DBGC ( image, "NBI %p executing 32-bit image at %lx\n",
|
||||
image, imgheader->execaddr.linear );
|
||||
|
||||
/* no gateA20_unset for PM call */
|
||||
|
||||
/* Jump to OS with flat physical addressing */
|
||||
__asm__ __volatile__ (
|
||||
PHYS_CODE ( "pushl %%ebx\n\t" /* bootp data */
|
||||
|
@ -1,7 +0,0 @@
|
||||
#ifndef GATEA20_H
|
||||
#define GATEA20_H
|
||||
|
||||
extern void gateA20_set ( void );
|
||||
extern void gateA20_unset ( void );
|
||||
|
||||
#endif /* GATEA20_H */
|
@ -157,11 +157,6 @@ extern uint16_t __data16 ( rm_cs );
|
||||
extern uint16_t __text16 ( rm_ds );
|
||||
#define rm_ds __use_text16 ( rm_ds )
|
||||
|
||||
/* Functions that librm expects to be able to link to. Included here
|
||||
* so that the compiler will catch prototype mismatches.
|
||||
*/
|
||||
extern void gateA20_set ( void );
|
||||
|
||||
/**
|
||||
* Convert segment:offset address to user buffer
|
||||
*
|
||||
|
@ -147,16 +147,6 @@ int pxeparent_call ( SEGOFF16_t entry, unsigned int function,
|
||||
"D" ( __from_data16 ( &pxeparent_params ) )
|
||||
: "ecx", "edx", "esi", "ebp" );
|
||||
|
||||
/* PXE API calls may rudely change the status of A20 and not
|
||||
* bother to restore it afterwards. Intel is known to be
|
||||
* guilty of this.
|
||||
*
|
||||
* Note that we will return to this point even if A20 gets
|
||||
* screwed up by the parent PXE stack, because Etherboot always
|
||||
* resides in an even megabyte of RAM.
|
||||
*/
|
||||
gateA20_set();
|
||||
|
||||
/* Determine return status code based on PXENV_EXIT and
|
||||
* PXENV_STATUS
|
||||
*/
|
||||
|
@ -348,6 +348,7 @@ enable_a20_fast:
|
||||
#define ENABLE_A20_RETRIES 255
|
||||
.section ".text16.early", "awx", @progbits
|
||||
.code16
|
||||
.globl enable_a20
|
||||
enable_a20:
|
||||
/* Preserve registers */
|
||||
pushl %ecx
|
||||
|
@ -170,10 +170,18 @@ idt_init: /* Reuse the return opcode here */
|
||||
.section ".text16", "ax", @progbits
|
||||
.code16
|
||||
real_to_prot:
|
||||
/* Enable A20 line */
|
||||
call enable_a20
|
||||
/* A failure at this point is fatal, and there's nothing we
|
||||
* can do about it other than lock the machine to make the
|
||||
* problem immediately visible.
|
||||
*/
|
||||
1: jc 1b
|
||||
|
||||
/* Make sure we have our data segment available */
|
||||
movw %cs:rm_ds, %ax
|
||||
movw %ax, %ds
|
||||
|
||||
|
||||
/* Add _virt_offset, _text16 and _data16 to stack to be
|
||||
* copied, and also copy the return address.
|
||||
*/
|
||||
@ -181,7 +189,7 @@ real_to_prot:
|
||||
pushl _text16
|
||||
pushl _data16
|
||||
addw $16, %cx /* %ecx must be less than 64kB anyway */
|
||||
|
||||
|
||||
/* Real-mode %ss:%sp => %ebp:%edx and virtual address => %esi */
|
||||
xorl %ebp, %ebp
|
||||
movw %ss, %bp
|
||||
@ -396,9 +404,6 @@ prot_call:
|
||||
.section ".text", "ax", @progbits
|
||||
.code32
|
||||
1:
|
||||
/* Set up environment expected by C code */
|
||||
call gateA20_set
|
||||
|
||||
/* Call function */
|
||||
leal PC_OFFSET_IX86(%esp), %eax
|
||||
pushl %eax
|
||||
@ -442,13 +447,7 @@ prot_call:
|
||||
* function will be passed back to the protected-mode caller. A
|
||||
* result of this is that this routine cannot be called directly from
|
||||
* C code, since it clobbers registers that the C ABI expects the
|
||||
* callee to preserve. Gate A20 will *not* be automatically
|
||||
* re-enabled. Since we always run from an even megabyte of memory,
|
||||
* we are guaranteed to return successfully to the protected-mode
|
||||
* code, which should then call gateA20_set() if it suspects that gate
|
||||
* A20 may have been disabled. Note that enabling gate A20 is a
|
||||
* potentially slow operation that may also cause keyboard input to be
|
||||
* lost; this is why it is not done automatically.
|
||||
* callee to preserve.
|
||||
*
|
||||
* librm.h defines a convenient macro REAL_CODE() for using real_call.
|
||||
* See librm.h and realmode.h for details and examples.
|
||||
|
Loading…
Reference in New Issue
Block a user