From 24b52ae47655109de38d8a40f91efb21aed788ef Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 20 Apr 2010 18:49:43 +0100 Subject: [PATCH] [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 --- src/arch/i386/prefix/libprefix.S | 21 ++- src/arch/i386/transitions/libflat.S | 283 +++++++++++++++++++++++++++- 2 files changed, 296 insertions(+), 8 deletions(-) diff --git a/src/arch/i386/prefix/libprefix.S b/src/arch/i386/prefix/libprefix.S index df71a9d7..6a23a96c 100644 --- a/src/arch/i386/prefix/libprefix.S +++ b/src/arch/i386/prefix/libprefix.S @@ -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 diff --git a/src/arch/i386/transitions/libflat.S b/src/arch/i386/transitions/libflat.S index 9cb4c8af..9062b74f 100644 --- a/src/arch/i386/transitions/libflat.S +++ b/src/arch/i386/transitions/libflat.S @@ -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