david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[prefix] Add A20-enabling code in libflat

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>
This commit is contained in:
Michael Brown 2010-04-20 18:49:43 +01:00
parent fb754ced56
commit 24b52ae476
2 changed files with 296 additions and 8 deletions

View 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

View 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