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