| /* |
| * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of the |
| * License, or any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| * 02110-1301, USA. |
| * |
| * You can also choose to distribute this program under the terms of |
| * the Unmodified Binary Distribution Licence (as given in the file |
| * COPYING.UBDL), provided that you have satisfied its requirements. |
| * |
| */ |
| |
| FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) |
| |
| .section ".note.GNU-stack", "", @progbits |
| .code16 |
| .arch i386 |
| |
| /**************************************************************************** |
| * 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 |
| pushw %ds |
| pushw %es |
| |
| /* Set up segment registers for access across the 1MB boundary */ |
| xorw %ax, %ax |
| movw %ax, %ds |
| decw %ax |
| movw %ax, %es |
| |
| 2: /* Modify and check test pattern; succeed if we see a difference */ |
| pushfw |
| cli |
| xchgw %ds:0, %cx |
| movw %es:0x10, %ax |
| xchgw %ds:0, %cx |
| popfw |
| cmpw %ax, %cx |
| clc |
| jnz 99f |
| |
| /* Delay and retry */ |
| outb %al, $0x80 |
| addr32 loop 2b |
| stc |
| |
| 99: /* Restore registers and return */ |
| popw %es |
| popw %ds |
| popw %ax |
| popl %ecx |
| ret |
| .size test_a20_long, . - test_a20_long |
| |
| /**************************************************************************** |
| * 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. Be very paranoid, since some BIOSes |
| * are reported to clobber %ebx |
| */ |
| pushal |
| |
| /* 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 */ |
| popal |
| 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 |
| .globl enable_a20 |
| 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 |
| .balign 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 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 |
| lret |
| .size access_highmem, . - access_highmem |