[prefix] Add A20-enabling code in libflat
authorMichael Brown <mcb30@ipxe.org>
Tue, 20 Apr 2010 17:49:43 +0000 (18:49 +0100)
committerStefan Hajnoczi <stefanha@gmail.com>
Wed, 7 Jul 2010 19:14:36 +0000 (20:14 +0100)
gPXE currently insists on residing in an even megabyte.  This imposes
undesirably severe constraints upon our PMM allocation strategy, and
limits our options for mechanisms to access ROMs greater than 64kB in
size.

Add A20 handling code to libflat so that prefixes are able to access
memory even in odd megabytes.

The algorithms and tuning parameters in the new A20 handling code are
based upon a mixture of the existing gPXE A20 code and the A20 code
from the 2.6.32 Linux kernel.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Stefan Hajnoczi <stefanha@gmail.com>
src/arch/i386/prefix/libprefix.S
src/arch/i386/transitions/libflat.S

index df71a9d..6a23a96 100644 (file)
@@ -512,13 +512,24 @@ install_prealloc:
 
        /* Open up access to payload */
 #ifndef KEEP_IT_REAL
-       /* Flatten real mode */
+       /* Access high memory */
        pushw   %cs
        pushw   $1f
        pushw   %ax
-       pushw   $flatten_real_mode
+       pushw   $access_highmem
        lret
-1:
+1:     /* Die if we could not access high memory */
+       jnc     3f
+       movw    $a20_death_message, %si
+       xorw    %di, %di
+       call    print_message
+2:     jmp     2b
+       .section ".prefix.data", "aw", @progbits
+a20_death_message:
+       .asciz  "Gate A20 stuck - cannot continue\n"
+       .size   a20_death_message, . - a20_death_message
+       .previous
+3:
 #endif
 
        /* Calculate physical address of payload (i.e. first source) */
@@ -570,13 +581,13 @@ install_prealloc:
        popl    %edx /* discard */
 
        /* Copy code to new location */
+       pushl   %edi
        xorw    %ax, %ax
        movw    %ax, %es
-       movl    %ebp, %edi
        es rep addr32 movsb
+       popl    %edi
 
        /* Initialise librm at new location */
-       movl    %ebp, %edi
        lcall   *init_librm_vector
 #endif
 
index 9cb4c8a..9062b74 100644 (file)
@@ -24,7 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER )
 #define CR0_PE 1
 
 /****************************************************************************
- * flatten_real_mode (real-mode far call)
+ * flatten_real_mode
  *
  * Set up 4GB segment limits
  *
@@ -63,7 +63,6 @@ flatten_saved_gdt:
 
        .section ".text16.early", "awx", @progbits
        .code16
-       .globl  flatten_real_mode
 flatten_real_mode:
        /* Preserve registers and flags */
        pushfl
@@ -126,7 +125,7 @@ flatten_real_mode:
        popw    %si
        popl    %eax
        popfl
-       lret
+       ret
        .size flatten_real_mode, . - flatten_real_mode
 
        .section ".text16.early", "awx", @progbits
@@ -139,3 +138,281 @@ set_seg_base:
        andb    $0x0f, 4(%si)
        ret
        .size set_seg_base, . - set_seg_base
+
+/****************************************************************************
+ * test_a20_short, test_a20_long
+ *
+ * Check to see if A20 line is enabled
+ *
+ * Parameters:
+ *   none
+ * Returns:
+ *   CF set if A20 line is not enabled
+ * Corrupts:
+ *   none
+ ****************************************************************************
+ */
+#define TEST_A20_SHORT_MAX_RETRIES 0x20
+#define TEST_A20_LONG_MAX_RETRIES 0x200000
+       .section ".text16.early", "awx", @progbits
+       .code16
+test_a20_short:
+       pushl   %ecx
+       movl    $TEST_A20_SHORT_MAX_RETRIES, %ecx
+       jmp     1f
+       .size   test_a20_short, . - test_a20_short
+test_a20_long:
+       pushl   %ecx
+       movl    $TEST_A20_LONG_MAX_RETRIES, %ecx
+1:     pushw   %ax
+
+       /* Flatten real mode so we can access the test pattern's 1MB offset */
+       call    flatten_real_mode
+
+2:     /* Modify and check test pattern; succeed if we see a difference */
+       incw    %cs:test_a20_data
+       addr32 movw %cs:(test_a20_data + 0x100000 ), %ax
+       cmpw    %cs:test_a20_data, %ax
+       clc
+       jnz     99f
+
+       /* Delay and retry */
+       outb    %al, $0x80
+       addr32 loop 2b
+       stc
+
+99:    /* Restore registers and return */
+       popw    %ax
+       popl    %ecx
+       ret
+       .size   test_a20_long, . - test_a20_long
+
+       .section ".text16.early.data", "aw", @progbits
+       .align  2
+test_a20_data:
+       .word   0xdead
+       .size   test_a20_data, . - test_a20_data
+
+/****************************************************************************
+ * enable_a20_bios
+ *
+ * Try enabling A20 line via BIOS
+ *
+ * Parameters:
+ *   none
+ * Returns:
+ *   CF set if A20 line is not enabled
+ * Corrupts:
+ *   none
+ ****************************************************************************
+ */
+       .section ".text16.early", "awx", @progbits
+       .code16
+enable_a20_bios:
+       /* Preserve registers */
+       pushw   %ax
+
+       /* Attempt INT 15,2401 */
+       movw    $0x2401, %ax
+       int     $0x15
+       jc      99f
+
+       /* Check that success was really successful */
+       call    test_a20_short
+
+99:    /* Restore registers and return */
+       popw    %ax
+       ret
+       .size   enable_a20_bios, . - enable_a20_bios
+
+/****************************************************************************
+ * enable_a20_kbc
+ *
+ * Try enabling A20 line via keyboard controller
+ *
+ * Parameters:
+ *   none
+ * Returns:
+ *   CF set if A20 line is not enabled
+ * Corrupts:
+ *   none
+ ****************************************************************************
+ */
+#define KC_RDWR                0x60
+#define KC_RDWR_SET_A20                0xdf
+#define        KC_CMD          0x64
+#define KC_CMD_WOUT            0xd1
+#define KC_CMD_NULL            0xff
+#define KC_STATUS      0x64
+#define KC_STATUS_OBUF_FULL    0x01
+#define KC_STATUS_IBUF_FULL    0x02
+#define KC_MAX_RETRIES 100000
+       .section ".text16.early", "awx", @progbits
+       .code16
+enable_a20_kbc:
+       /* Preserve registers */
+       pushw   %ax
+
+       /* Try keyboard controller */
+       call    empty_kbc
+       movb    $KC_CMD_WOUT, %al
+       outb    %al, $KC_CMD
+       call    empty_kbc
+       movb    $KC_RDWR_SET_A20, %al
+       outb    %al, $KC_RDWR
+       call    empty_kbc
+       movb    $KC_CMD_NULL, %al
+       outb    %al, $KC_CMD
+       call    empty_kbc
+
+       /* Check to see if it worked */
+       call    test_a20_long
+
+       /* Restore registers and return */
+       popw    %ax
+       ret
+       .size   enable_a20_kbc, . - enable_a20_kbc
+
+       .section ".text16.early", "awx", @progbits
+       .code16
+empty_kbc:
+       /* Preserve registers */
+       pushl   %ecx
+       pushw   %ax
+
+       /* Wait for KBC to become empty */
+       movl    $KC_MAX_RETRIES, %ecx
+1:     outb    %al, $0x80
+       inb     $KC_STATUS, %al
+       testb   $( KC_STATUS_OBUF_FULL | KC_STATUS_IBUF_FULL ), %al
+       jz      99f
+       testb   $KC_STATUS_OBUF_FULL, %al
+       jz      2f
+       outb    %al, $0x80
+       inb     $KC_RDWR, %al
+2:     addr32 loop 1b
+
+99:    /* Restore registers and return */
+       popw    %ax
+       popl    %ecx
+       ret
+       .size   empty_kbc, . - empty_kbc
+
+/****************************************************************************
+ * enable_a20_fast
+ *
+ * Try enabling A20 line via "Fast Gate A20"
+ *
+ * Parameters:
+ *   none
+ * Returns:
+ *   CF set if A20 line is not enabled
+ * Corrupts:
+ *   none
+ ****************************************************************************
+ */
+#define SCP_A 0x92
+       .section ".text16.early", "awx", @progbits
+       .code16
+enable_a20_fast:
+       /* Preserve registers */
+       pushw   %ax
+
+       /* Try "Fast Gate A20" */
+       inb     $SCP_A, %al
+       orb     $0x02, %al
+       andb    $~0x01, %al
+       outb    %al, $SCP_A
+
+       /* Check to see if it worked */
+       call    test_a20_long
+
+       /* Restore registers and return */
+       popw    %ax
+       ret
+       .size   enable_a20_fast, . - enable_a20_fast
+
+/****************************************************************************
+ * enable_a20
+ *
+ * Try enabling A20 line via any available method
+ *
+ * Parameters:
+ *   none
+ * Returns:
+ *   CF set if A20 line is not enabled
+ * Corrupts:
+ *   none
+ ****************************************************************************
+ */
+#define ENABLE_A20_RETRIES 255
+       .section ".text16.early", "awx", @progbits
+       .code16
+enable_a20:
+       /* Preserve registers */
+       pushl   %ecx
+       pushw   %ax
+
+       /* Check to see if A20 is already enabled */
+       call    test_a20_short
+       jnc     99f
+
+       /* Use known working method, if we have one */
+       movw    %cs:enable_a20_method, %ax
+       testw   %ax, %ax
+       jz      1f
+       call    *%ax
+       jmp     99f
+1:
+       /* Try all methods in turn until one works */
+       movl    $ENABLE_A20_RETRIES, %ecx
+2:     movw    $enable_a20_bios, %ax
+       movw    %ax, %cs:enable_a20_method
+       call    *%ax
+       jnc     99f
+       movw    $enable_a20_kbc, %ax
+       movw    %ax, %cs:enable_a20_method
+       call    *%ax
+       jnc     99f
+       movw    $enable_a20_fast, %ax
+       movw    %ax, %cs:enable_a20_method
+       call    *%ax
+       jnc     99f
+       addr32 loop 2b
+       /* Failure; exit with carry set */
+       movw    $0, %cs:enable_a20_method
+       stc
+
+99:    /* Restore registers and return */
+       popw    %ax
+       popl    %ecx
+       ret
+
+       .section ".text16.early.data", "aw", @progbits
+       .align  2
+enable_a20_method:
+       .word   0
+       .size   enable_a20_method, . - enable_a20_method
+
+/****************************************************************************
+ * access_highmem (real mode far call)
+ *
+ * Open up access to high memory in flat real mode with A20 enabled
+ *
+ * Parameters:
+ *   none
+ * Returns:
+ *   CF set if high memory could not be accessed
+ * Corrupts:
+ *   none
+ ****************************************************************************
+ */
+       .section ".text16.early", "awx", @progbits
+       .code16
+       .globl  access_highmem
+access_highmem:
+       /* Enable A20 line */
+       call    enable_a20
+       /* CPU will be in flat real mode as a result of this call */
+       lret
+       .size   access_highmem, . - access_highmem