blob: 4d379159552135a0bb103df8124ad9abd10efe03 [file] [log] [blame]
/*
* 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