Skip to content

Commit

Permalink
[prefix] Add A20-enabling code in libflat
Browse files Browse the repository at this point in the history
iPXE 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 iPXE A20 code and the A20 code
from the 2.6.32 Linux kernel.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Apr 20, 2010
1 parent fb754ce commit 24b52ae
Show file tree
Hide file tree
Showing 2 changed files with 296 additions and 8 deletions.
21 changes: 16 additions & 5 deletions src/arch/i386/prefix/libprefix.S
Expand Up @@ -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) */
Expand Down Expand Up @@ -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

Expand Down
283 changes: 280 additions & 3 deletions src/arch/i386/transitions/libflat.S
Expand Up @@ -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
*
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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

0 comments on commit 24b52ae

Please sign in to comment.