diff options
Diffstat (limited to 'kexec_test/kexec_test16.S')
-rw-r--r-- | kexec_test/kexec_test16.S | 1004 |
1 files changed, 1004 insertions, 0 deletions
diff --git a/kexec_test/kexec_test16.S b/kexec_test/kexec_test16.S new file mode 100644 index 0000000..4d37915 --- /dev/null +++ b/kexec_test/kexec_test16.S @@ -0,0 +1,1004 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * 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 (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + .text + .code16 + + .globl test16 + .balign 16 + .globl _start16 +_start16: +test16: + pushw $s_in_real_mode - _start16 + call print_string16 + addw $2, %sp + +#if 0 + /* Disable interrupts */ + movb $0xff, %al + outb %al, $0x21 + outb %al, $0xa1 +#endif + /* Enable interrupts, BIOS calls may fail if we don't */ + sti + pushw $s_interrupts_enabled - _start16 + call print_string16 + addw $2, %sp + + /* Get the base memory size, via a bios call */ + /* This is to test BIOS calls more than to achieve anything practical */ + xorw %ax, %ax + int $0x12 + pushw %ax + pushw $s_base_memory_size - _start16 + call print_string16 + addw $2, %sp + call print_hex16 + addw $2, %sp + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + + /* Some things do not like a20 being enabled so disable it */ + call disable_a20 + + /* Here we test various BIOS calls to determine how much of the system is working */ + call get_meme820 + call print_meme820 + call print_meme801 + call print_mem88 + call disable_apm + call print_equipment_list + call print_sysdesc + call print_video + call print_cursor + call print_video_mode + call set_auto_repeat_rate + call print_dasd_type + call print_edd + + /* Enable a20 */ + call enable_a20 + pushw $s_a20_enabled - _start16 + call print_string16 + addw $2, %sp + + /* Disable interrupts */ + cli + pushw $s_interrupts_disabled - _start16 + call print_string16 + addw $2, %sp + + retw + +# +# Enable A20. This is at the very best an annoying procedure. +# A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin. +# + +A20_TEST_LOOPS = 32 # Iterations per wait +A20_ENABLE_LOOPS = 255 # Total loops to try +A20_DISABLE_LOOPS = 255 # Total loops to try + +enable_a20: + .code16 + movb $A20_ENABLE_LOOPS, a20_tries - _start16 +a20_try_loop: + + # First, see if we are on a system with no A20 gate. +a20_none: + call a20_test + jnz a20_done + + # Next, try the BIOS (INT 0x15, AX=0x2401) +a20_bios: + movw $0x2401, %ax + pushfl # Be paranoid about flags + int $0x15 + popfl + + call a20_test + jnz a20_done + + # Try enabling A20 through the keyboard controller +a20_kbc: + call empty_8042 + + call a20_test # Just in case the BIOS worked + jnz a20_done # but had a delayed reaction. + + movb $0xD1, %al # command write + outb %al, $0x64 + call empty_8042 + + movb $0xDF, %al # A20 on + outb %al, $0x60 + call empty_8042 + + # Wait until a20 really *is* enabled; it can take a fair amount of + # time on certain systems; Toshiba Tecras are known to have this + # problem. +a20_kbc_wait: + xorw %cx, %cx +a20_kbc_wait_loop: + call a20_test + jnz a20_done + loop a20_kbc_wait_loop + + # Final attempt: use "configuration port A" +a20_fast: + inb $0x92, %al # Configuration Port A + orb $0x02, %al # "fast A20" version + andb $0xFE, %al # dont accidentally reset + outb %al, $0x92 + + # Wait for configuration port A to take effect +a20_fast_wait: + xorw %cx, %cx +a20_fast_wait_loop: + call a20_test + jnz a20_done + loop a20_fast_wait_loop + + # A20 is still not responding. Try frobbing it again. + # + decb (a20_tries - _start16) + jnz a20_try_loop + jmp a20_die + +a20_die: + pushw $s_a20_err_msg - _start16 + call print_string16 + jmp halt16 + + # If we get here, all is good +a20_done: + ret + + + +# This routine tests whether or not A20 is enabled. If so, it +# exits with zf = 0. +# +# The memory address used, 0x200, is the int $0x80 vector, which +# should be safe. + +A20_TEST_ADDR = 4*0x80 + +a20_test: + .code16 + pushw %cx + pushw %ax + xorw %cx, %cx + movw %cx, %fs # Low memory + decw %cx + movw %cx, %gs # High memory area + movw $A20_TEST_LOOPS, %cx + movw %fs:(A20_TEST_ADDR), %ax + pushw %ax +a20_test_wait: + incw %ax + movw %ax, %fs:(A20_TEST_ADDR) + call delay # Serialize and make delay constant + cmpw %gs:(A20_TEST_ADDR+0x10), %ax + loope a20_test_wait + + popw %fs:(A20_TEST_ADDR) + popw %ax + popw %cx + + ret + +# +# Disable A20 +# + +disable_a20: + .code16 + movb $A20_DISABLE_LOOPS, a20_disable_tries - _start16 +a20_disable_loop: + + # First see if gate A20 is already disabled + call a20_test + jz a20_disabled + + + # Next, try the BIOS (INT 0x15, AX= 0x2400) + movw $0x2400, %ax + pushfl # Be paranoid about flags + int $0x15 + popfl + + call a20_test + jz a20_disabled + + # Try disabling A20 through the keyboard controller + call empty_8042 + + call a20_test # Just in case the BIOS worked + jz a20_disabled # but had a delayed reaction. + + movb $0xD1, %al # command write + outb %al, $0x64 + call empty_8042 + + movb $0xDD, %al # A20 off + outb %al, $0x60 + call empty_8042 + + # Wait until a20 really *is* disabled + xorw %cx, %cx +a20_kbc_disable_loop: + call a20_test + jz a20_disabled + loop a20_kbc_disable_loop + + # Final attempt: use "configuration port A" + inb $0x92, %al # Configuratin Port A + andb $0xFD, %al # "fast A20" version + andb $0xFE, %al # dont accidentally reset + outb %al, $0x92 + + # Wait for configuration port A to take affect + xorw %cx, %cx +a20_fast_disable_loop: + call a20_test + jz a20_disabled + loop a20_fast_disable_loop + + # A20 is still not responding. Try it again + decb (a20_disable_tries - _start16) + jnz a20_disable_loop + + pushw $s_a20_cant_disable - _start16 + call print_string16 + addw $2, %sp + retw + + # If we get here, all is good +a20_disabled: + pushw $s_a20_disabled - _start16 + call print_string16 + addw $2, %sp + retw + + +# This routine checks that the keyboard command queue is empty +# (after emptying the output buffers) +# +# Some machines have delusions that the keyboard buffer is always full +# with no keyboard attached... +# +# If there is no keyboard controller, we will usually get 0xff +# to all the reads. With each IO taking a microsecond and +# a timeout of 100,000 iterations, this can take about half a +# second ("delay" == outb to port 0x80). That should be ok, +# and should also be plenty of time for a real keyboard controller +# to empty. +# + +empty_8042: + .code16 + pushl %ecx + movl $100000, %ecx + +empty_8042_loop: + decl %ecx + jz empty_8042_end_loop + + call delay + + inb $0x64, %al # 8042 status port + testb $1, %al # output buffer? + jz no_output + + call delay + inb $0x60, %al # read it + jmp empty_8042_loop + +no_output: + testb $2, %al # is input buffer full? + jnz empty_8042_loop # yes - loop +empty_8042_end_loop: + popl %ecx + ret + + + + +# method E820H: +# the memory map from hell. e820h returns memory classified into +# a whole bunch of different types, and allows memory holes and +# everything. We scan through this memory map and build a list +# of the first 32 memory areas, which we return at [E820MAP]. +# This is documented at http://www.teleport.com/~acpi/acpihtml/topic245.htm + +#define SMAP 0x534d4150 +#define E820_MAX 32 +#define E820_SIZE 20 + +get_meme820: + .code16 + pushw %bp + movw %sp, %bp + pushw %ds + pushw %es + pushl %esi + pushl %edi + pushl %ebx + + xorl %eax, %eax + movb %al, e820nr - _start16 + xorl %ebx, %ebx # continuation counter + movw $e820_map - _start16, %di # point into the whitelist + # so we can have the bios + # directly write into it. + +jmpe820: + movl $0x0000e820, %eax # e820, upper word zeroed + movl $SMAP, %edx # ascii SMAP + movl $E820_SIZE, %ecx # size of the e820rec + pushw %ds # data record. + popw %es + int $0x15 # make the call + jc bail820 # fall to e801 if it fails + + cmpl $SMAP, %eax # check the return is SMAP + jne bail820 # fall to e801 if it fails + +# cmpl $1, 16(%di) # is this usable memory? +# jne again820 + + # If this is usable memory, we save it by simply advancing %di by + # sizeof(e820rec). + # +good820: + movb e820nr - _start16, %al # up to 32 entries + cmpb $E820_MAX, %al + jnl bail820 + + incb e820nr - _start16 + movw %di, %ax + addw $20, %ax + movw %ax, %di +again820: + cmpl $0, %ebx # check to see if + jne jmpe820 # %ebx is set to EOF +bail820: + popl %ebx + popl %edi + popl %esi + popw %es + popw %ds + popw %bp + retw + + +print_meme820: + .code16 + pushw %si + xorw %cx, %cx + movb (e820nr - _start16), %cl + movw $e820_map - _start16, %si + + pushw $s_meme820 - _start16 + call print_string16 + addw $2, %sp + +print_meme820.1: + pushw %cx + + pushw 8(%si) + pushw 10(%si) + pushw 12(%si) + pushw 14(%si) + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + + pushw $s_at - _start16 + call print_string16 + addw $2, %sp + + pushw 0(%si) + pushw 2(%si) + pushw 4(%si) + pushw 6(%si) + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + + pushw $s_type - _start16 + call print_string16 + addw $2, %sp + + pushw 16(%si) + pushw 18(%si) + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + popw %cx + addw $E820_SIZE, %si + subw $1, %cx + jnz print_meme820.1 + + popw %si + retw + + + +print_meme801: + .code16 + pushw %bp + movw %sp, %bp + pushw %bx + pushl $0 + +# method E801H: +# memory size is in 1k chunksizes + + stc # fix to work around buggy + xorw %cx,%cx # BIOSes which dont clear/set + xorw %dx,%dx # carry on pass/error of + # e801h memory size call + # or merely pass cx,dx though + # without changing them. + movw $0xe801, %ax + int $0x15 + jc print_meme801.2 + + cmpw $0x0, %cx # Kludge to handle BIOSes + jne e801usecxdx # which report their extended + cmpw $0x0, %dx # memory in AX/BX rather than + jne e801usecxdx # CX/DX. The spec I have read + movw %ax, %cx # seems to indicate AX/BX + movw %bx, %dx # are more reasonable anyway... + +e801usecxdx: + andl $0xffff, %edx # clear sign extend + shll $6, %edx # and go from 64k to 1k chunks + movl %edx, -6(%bp) # store extended memory size + andl $0xffff, %ecx # clear sign extend + addl %ecx, -6(%bp) # and add lower memory into + + pushw $s_meme801 - _start16 + call print_string16 + addw $2, %sp + + pushw -6(%bp) + pushw -4(%bp) + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + +print_meme801.2: + addw $4, %sp + popw %bx + popw %bp + retw + +print_mem88: + .code16 +# Ye Olde Traditional Methode. Returns the memory size (up to 16mb or +# 64mb, depending on the bios) in ax. + movb $0x88, %ah + int $0x15 + + pushw %ax + pushw $s_mem88 - _start16 + call print_string16 + addw $2, %sp + call print_hex16 + addw $2, %sp + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + retw + +print_dasd_type: + .code16 + pushw $s_dasd_type - _start16 + call print_string16 + addw $2, %sp + + movw $0x1500, %ax + movb $0x81, %dl + int $0x13 + jc print_dasd_type.1 + + pushw %dx + pushw %cx + pushw $s_space - _start16 + pushw %ax + + call print_hex16 + addw $2, %sp + call print_string16 + addw $2, %sp + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + jmp print_dasd_type.2 +print_dasd_type.1: + pushw $s_none - _start16 + call print_string16 + addw $2, %sp + +print_dasd_type.2: + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + retw + +print_equipment_list: + .code16 + pushw $s_equipment_list - _start16 + call print_string16 + addw $2, %sp + + int $0x11 + pushw %ax + call print_hex16 + addw $2, %sp + + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + retw + +print_sysdesc: + .code16 + pushw $s_sysdesc - _start16 + call print_string16 + addw $2, %sp + + pushw %es + movb $0xc0, %ah + stc + int $0x15 + movw %es, %ax + popw %es + jc print_sysdesc.1 + + pushw %bx + pushw $s_colon - _start16 + pushw %ax + call print_hex16 + addw $2, %sp + call print_string16 + addw $2, %sp + call print_hex16 + addw $2, %sp + jmp print_sysdesc.2 + +print_sysdesc.1: + pushw $s_none - _start16 + call print_string16 + addw $2, %sp + +print_sysdesc.2: + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + retw + +print_edd: + .code16 + pushw $s_edd - _start16 + call print_string16 + add $2, %sp + + movb $0x80, %dl + movb $0x41, %ah # Function 41 + movw $0x55aa, %bx # magic + int $0x13 # make the call + jc print_edd.1 # no more BIOS devices + + cmpw $0xAA55, %bx # is magic right? + jne print_edd.1 # nope + + pushw $s_ok - _start16 + call print_string16 + add $2, %sp + jmp print_edd.2 + +print_edd.1: + pushw $s_none - _start16 + call print_string16 + add $2, %sp + +print_edd.2: + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + retw + +set_auto_repeat_rate: + .code16 + pushw $s_auto_repeat_rate - _start16 + call print_string16 + add $2, %sp + +# Set the keyboard repeat rate to the max + movw $0x0305, %ax + xorw %bx, %bx + int $0x16 + + pushw $s_done - _start16 + call print_string16 + add $2, %sp + + retw + +print_video: + .code16 + pushw $s_video_type - _start16 + call print_string16 + add $2, %sp + + movb $0x12, %ah # Check EGA/VGA + movb $0x10, %bl + int $0x10 + movw $s_video_pre_ega - _start16, %cx + cmpb $0x10, %bl + je print_video.1 + + movw $0x1a00, %ax # Check EGA or VGA? + int $0x10 + movw $s_video_vga - _start16, %cx + cmpb $0x1a, %al # 1a means VGA... + je print_video.1 # anything else is EGA. + + movw $s_video_ega - _start16, %cx + +print_video.1: + pushw %cx + call print_string16 + addw $2, %sp + + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + retw + +print_cursor: + .code16 + pushw $s_cursor - _start16 + call print_string16 + add $2, %sp + + movb $0x03, %ah # Read cursor position + xorb %bh, %bh + int $0x10 + + xorw %ax, %ax + movb %dl, %al + pushw %ax + pushw $s_space - _start16 + movb %dh, %al + pushw %ax + + call print_hex16 + add $2, %sp + call print_string16 + add $2, %sp + call print_hex16 + add $2, %sp + + pushw $s_crlf - _start16 + call print_string16 + add $2, %sp + + retw + +print_video_mode: + .code16 + pushw $s_video_mode - _start16 + call print_string16 + add $2, %sp + + movb $0x0f, %ah # Read cursor position + int $0x10 + + xorb %ah, %ah + pushw %ax + call print_hex16 + add $2, %sp + + pushw $s_crlf - _start16 + call print_string16 + add $2, %sp + + retw + + +disable_apm: + push %bp + movw %sp, %bp + pushw %bx + + pushw $s_testing_for_apm - _start16 + call print_string16 + add $2, %sp + + # check for APM BIOS + movw $0x5300, %ax # APM BIOS installation check + xorw %bx, %bx + int $0x15 + jc done_apm_bios # error -> no APM BIOS + + cmpw $0x504d, %bx # check for "PM" signature + jne done_apm_bios # no signature -> no APM BIOS + + pushw $s_apm_found_disconnecting - _start16 + call print_string16 + add $2, %sp + + movw $0x5304, %ax # Disconnect first just in case + xorw %bx, %bx + int $0x15 # ignore return code + + pushw $s_apm_connecting - _start16 + call print_string16 + add $2, %sp + + movw $0x5301, %ax # Real Mode connect + xorw %bx, %bx + int $0x15 + jc done_apm_bios # error + + pushw $s_apm_disabling - _start16 + call print_string16 + add $2, %sp + + movw $0x5308, %ax # Disable APM + mov $0xffff, %bx + xorw %cx, %cx + int $0x15 + + pushw $s_apm_disconnecting - _start16 + call print_string16 + add $2, %sp + + movw $0x5304, %ax # Do a final disconnect + xorw %bx, %bx + int $0x15 + +done_apm_bios: + pushw $s_apm_test_done - _start16 + call print_string16 + add $2, %sp + + popw %bx + popw %bp + retw + + +# Delay is needed after doing I/O +delay: + .code16 + outb %al,$0x80 + retw + +halt16: + .code16 + hlt + jmp halt16 + + +print_string16: + .code16 + pushw %bp + movw %sp, %bp + pushw %si + movw 4(%bp), %si + xorw %ax, %ax +print_string16.1: + lodsb %ds:(%si), %al + testb $0xff, %al + jz print_string16.2 + call print_char16 + jmp print_string16.1 +print_string16.2: + popw %si + popw %bp + ret + +print_hex16: + .code16 + pushw %bp + movw %sp, %bp + movw $16, %cx +print_hex16.1: + movw 4(%bp), %ax + subb $4, %cl + shrw %cl, %ax + andb $0x0f, %al + cmpb $9, %al + ja print_hex16.2 + addb $'0', %al + jmp print_hex16.3 +print_hex16.2: + addb $'A' - 10, %al +print_hex16.3: + pushw %cx + call print_char16 + popw %cx + testb %cl, %cl + jnz print_hex16.1 + + popw %bp + ret + +print_char16: + .code16 + # The character to print is in al + call serial_print_char16 + retw + + +#define TTYS0_BASE 0x3f8 +#define TTYS0_RBR (TTYS0_BASE + 0x00) +#define TTYS0_TBR (TTYS0_BASE + 0x00) +#define TTYS0_LSR (TTYS0_BASE + 0x05) + +serial_print_char16: + .code16 + pushw %bp + movw %sp, %bp + # The character to print is in al + pushw %ax + + # Wait until the serial port is ready to receive characters +serial_print_char16.1: + movw $TTYS0_LSR, %dx + inb %dx, %al + testb $0x20, %al + jz serial_print_char16.1 + + # Output the character + movw $TTYS0_TBR, %dx + movb -2(%bp), %al + outb %al, %dx + + # Wait until the serial port has transmitted the character +serial_print_char16.2: + movw $TTYS0_LSR, %dx + inb %dx, %al + testb $0x40, %al + jz serial_print_char16.2 + + # Restore %eax + popw %ax + # Return to caller + popw %bp + retw + + +s_a20_err_msg: + .asciz "A20 gate not responding!\r\n" + +s_in_real_mode: + .asciz "In real mode.\r\n" +s_base_memory_size: + .asciz "Base memory size: " +s_interrupts_enabled: + .asciz "Interrupts enabled.\r\n" +s_a20_disabled: + .asciz "A20 disabled.\r\n" +s_a20_cant_disable: + .asciz "Can not A20 line.\r\n" +s_a20_enabled: + .asciz "A20 enabled\r\n" +s_interrupts_disabled: + .asciz "Interrupts disabled.\r\n" + +s_meme820: .asciz "E820 Memory Map.\r\n" +s_at: .asciz " @ " +s_type: .asciz " type: " +s_space: .asciz " " +s_colon: .asciz ":" +s_none: .asciz " none " +s_ok: .asciz " ok " +s_done: .asciz " done\r\n" + +s_meme801: + .asciz "E801 Memory size: " +s_mem88: + .asciz "Mem88 Memory size: " + +s_dasd_type: + .asciz "DASD type: " +s_equipment_list: + .asciz "Equiptment list: " +s_sysdesc: + .asciz "Sysdesc: " +s_edd: + .asciz "EDD: " +s_auto_repeat_rate: + .asciz "Setting auto repeat rate " + + +s_video_type: + .asciz "Video type: " +s_video_pre_ega: + .asciz "CGA/MDA/HGA" +s_video_ega: + .asciz "EGA" +s_video_vga: + .asciz "VGA" + +s_cursor: + .asciz "Cursor Position(Row,Column): " + +s_video_mode: + .asciz "Video Mode: " + +s_testing_for_apm: + .asciz "Testing for APM.\r\n" +s_apm_found_disconnecting: + .asciz "APM Found disconnecting.\r\n" +s_apm_connecting: + .asciz "APM connecting.\r\n" +s_apm_disabling: + .asciz "APM disabling.\r\n" +s_apm_disconnecting: + .asciz "APM disconnecting.\r\n" +s_apm_test_done: + .asciz "APM test done.\r\n" + +s_crlf: .asciz "\r\n" + + + +a20_tries: .byte A20_ENABLE_LOOPS +a20_disable_tries: .byte A20_DISABLE_LOOPS + + +e820nr: .byte 0 +e820_map: .fill E820_MAX * E820_SIZE, 1, 0 |