[librm] Use libflat to enable A20 line on each real-to-protected transition
authorMichael Brown <mcb30@ipxe.org>
Tue, 20 Apr 2010 18:20:26 +0000 (19:20 +0100)
committerStefan Hajnoczi <stefanha@gmail.com>
Wed, 7 Jul 2010 19:14:36 +0000 (20:14 +0100)
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>
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
src/arch/i386/drivers/net/undiload.c
src/arch/i386/firmware/pcbios/gateA20.c [deleted file]
src/arch/i386/image/nbi.c
src/arch/i386/include/gateA20.h [deleted file]
src/arch/i386/include/librm.h
src/arch/i386/interface/pxeparent/pxeparent.c
src/arch/i386/transitions/libflat.S
src/arch/i386/transitions/librm.S

index 47a2bae..8271b50 100644 (file)
@@ -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 ) );
diff --git a/src/arch/i386/firmware/pcbios/gateA20.c b/src/arch/i386/firmware/pcbios/gateA20.c
deleted file mode 100644 (file)
index 1a71472..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdio.h>
-#include <realmode.h>
-#include <bios.h>
-#include <gpxe/io.h>
-#include <gpxe/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 */
-}
index a4ee442..7244e22 100644 (file)
@@ -1,7 +1,6 @@
 #include <errno.h>
 #include <assert.h>
 #include <realmode.h>
-#include <gateA20.h>
 #include <memsizes.h>
 #include <basemem_packet.h>
 #include <gpxe/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 */
diff --git a/src/arch/i386/include/gateA20.h b/src/arch/i386/include/gateA20.h
deleted file mode 100644 (file)
index 297ad6f..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef GATEA20_H
-#define GATEA20_H
-
-extern void gateA20_set ( void );
-extern void gateA20_unset ( void );
-
-#endif /* GATEA20_H */
index f193f5e..c6992f6 100644 (file)
@@ -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
  *
index 582db5d..4786c7e 100644 (file)
@@ -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
         */
index 9062b74..5e5f7b9 100644 (file)
@@ -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
index a07ffc5..5eb82b4 100644 (file)
@@ -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.