blob: db6c2b673ce49a2817cfb55cd228b8e778de11b4 [file] [log] [blame]
/*
* Extended Boot Option ROM
*
* 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
* (at your option) 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright IBM Corporation, 2007
* Authors: Anthony Liguori <aliguori@us.ibm.com>
*/
#define OLD_INT19 (0x80 * 4) /* re-use INT 0x80 BASIC vector */
#define OLD_INT13 (0x81 * 4) /* re-use INT 0x81 BASIC vector */
.code16
.text
.global _start
_start:
.short 0xaa55
.byte (_end - _start) / 512
push %eax
push %ds
/* setup ds so we can access the IVT */
xor %ax, %ax
mov %ax, %ds
/* there is one more bootable HD */
incb 0x0475
/* save old int 19 */
mov (0x19*4), %eax
mov %eax, (OLD_INT19)
/* install out int 19 handler */
movw $int19_handler, (0x19*4)
mov %cs, (0x19*4+2)
pop %ds
pop %eax
lret
int19_handler:
push %eax /* reserve space for lret */
push %eax
push %bx
push %cx
push %dx
push %ds
/* setup ds to access IVT */
xor %ax, %ax
mov %ax, %ds
/* save old int 13 to int 2c */
mov (0x13*4), %eax
mov %eax, (OLD_INT13)
/* install our int 13 handler */
movw $int13_handler, (0x13*4)
mov %cs, (0x13*4+2)
/* restore previous int $0x19 handler */
mov (OLD_INT19),%eax
mov %eax,(0x19*4)
/* write old handler as return address onto stack */
push %bp
mov %sp, %bp
mov %eax, 14(%bp)
pop %bp
pop %ds
pop %dx
pop %cx
pop %bx
pop %eax
lret
#define FLAGS_CF 0x01
/* The two macro below clear/set the carry flag to indicate the status
* of the interrupt execution. It is not enough to issue a clc/stc instruction,
* since the value of the flags register will be overwritten by whatever is
* in the stack frame
*/
.macro clc_stack
push %bp
mov %sp, %bp
/* 8 = 2 (bp, just pushed) + 2 (ip) + 3 (real mode interrupt frame) */
and $(~FLAGS_CF), 8(%bp)
pop %bp
.endm
.macro stc_stack
push %bp
/* 8 = 2 (bp, just pushed) + 2 (ip) + 3 (real mode interrupt frame) */
or $(FLAGS_CF), 8(%bp)
pop %bp
.endm
/* we clobber %bx */
.macro alloca size
push %ds
push %bp
mov %sp, %bp /* remember the current stack position */
mov %ss, %bx
mov %bx, %ds
sub \size, %sp
and $(~0x0F), %sp
mov %sp, %bx
push %bp
mov 0(%bp), %bp
.endm
/* we clobber %bp */
.macro allocbpa size
mov %sp, %bp /* remember the current stack position */
sub \size, %sp
and $(~0x0F), %sp
push %bp
mov %sp, %bp
add $2, %bp
.endm
.macro freea
pop %sp
add $2, %sp
pop %ds
.endm
.macro freebpa
pop %sp
.endm
.macro dump reg
push %ax
push %dx
mov \reg, %ax
mov $0x406, %dx
outw %ax, %dx
pop %dx
pop %ax
.endm
.macro callout value
push %bp
push %bx
mov %sp, %bp
alloca $16
push %ax
push %dx
mov %ax, 0(%bx) /* ax */
mov 0(%bp), %ax /* bx */
mov %ax, 2(%bx)
mov %cx, 4(%bx) /* cx */
mov %dx, 6(%bx) /* dx */
mov %si, 8(%bx) /* si */
mov %ds, 10(%bx) /* ds */
mov %es, 12(%bx) /* ds */
movw \value, 14(%bx) /* value */
mov %bx, %ax
shr $4, %ax
mov %ds, %dx
add %dx, %ax
mov $0x407, %dx
outw %ax, %dx
pop %dx
pop %ax
freea
pop %bx
pop %bp
.endm
send_command:
push %bp
mov %sp, %bp
push %ax
push %bx
push %dx
mov 4(%bp), %ax
shr $4, %ax
and $0x0FFF, %ax
mov %ss, %bx
add %bx, %ax
mov $0x405, %dx
outw %ax, %dx
pop %dx
pop %bx
pop %ax
pop %bp
push %ax
mov 2(%bx), %ax
pop %ax
ret
add32: /* lo, hi, lo, hi */
push %bp
mov %sp, %bp
movw 4(%bp), %cx /* hi */
movw 6(%bp), %dx /* lo */
add 10(%bp), %dx
jnc 1f
add $1, %cx
1: add 8(%bp), %cx
pop %bp
ret
mul32: /* lo, hi, lo, hi */
/* 10(%bp), 8(%bp), 6(%bp), 4(%bp) */
push %bp
mov %sp, %bp
push %ax
push %bx
xor %cx, %cx
xor %dx, %dx
/* for (i = 0; i < 16;) */
xor %bx, %bx
0:
cmp $16, %bx
jge 2f
mov 6(%bp), %ax
and $1, %ax
cmp $1, %ax
jne 1f
push 10(%bp)
push 8(%bp)
push %dx
push %cx
call add32
add $8, %sp
1:
shlw $1, 8(%bp)
movw 10(%bp), %ax
and $0x8000, %ax
cmp $0x8000, %ax
jne 1f
orw $1, 8(%bp)
1:
shlw $1, 10(%bp)
shrw $1, 6(%bp)
/* i++) { */
add $1, %bx
jmp 0b
2:
pop %bx
pop %ax
pop %bp
ret
disk_reset:
movb $0, %ah
clc_stack
ret
/* this really should be a function, not a macro but i'm lazy */
.macro read_write_disk_sectors cmd
push %ax
push %bx
push %cx
push %dx
push %si
push %bp
sub $10, %sp
mov %sp, %bp
/* save nb_sectors */
mov %al, 6(%bp)
movb $0, 7(%bp)
/* save buffer */
mov %bx, 8(%bp)
/* cylinders */
xor %ax, %ax
mov %cl, %al
shl $2, %ax
and $0x300, %ax
mov %ch, %al
mov %ax, 0(%bp)
/* heads */
xor %ax, %ax
mov %dh, %al
mov %ax, 2(%bp)
/* sectors - 1 */
xor %ax, %ax
mov %cl, %al
and $0x3F, %al
sub $1, %ax
mov %ax, 4(%bp)
alloca $16
movw $0, 0(%bx) /* read c,h,s */
push %bx
call send_command
add $2, %sp
mov 6(%bx), %ax /* total_sectors */
mov 2(%bp), %si /* *= heads */
mul %si
add 4(%bp), %ax /* += sectors - 1 */
push 4(%bx) /* total_heads */
push $0
push 6(%bx) /* total_sectors */
push $0
call mul32
add $8, %sp
push 0(%bp) /* cylinders */
push $0
push %dx
push %cx
call mul32
add $8, %sp
add %ax, %dx
jnc 1f
add $1, %cx
1:
freea
alloca $16
movw \cmd, 0(%bx) /* read */
movw 6(%bp), %ax /* nb_sectors */
movw %ax, 2(%bx)
movw %es, 4(%bx) /* segment */
movw 8(%bp), %ax /* offset */
mov %ax, 6(%bx)
movw %dx, 8(%bx) /* sector */
movw %cx, 10(%bx)
movw $0, 12(%bx)
movw $0, 14(%bx)
push %bx
call send_command
add $2, %sp
freea
add $10, %sp
pop %bp
pop %si
pop %dx
pop %cx
pop %bx
pop %ax
mov $0, %ah
clc_stack
ret
.endm
read_disk_sectors:
read_write_disk_sectors $0x01
write_disk_sectors:
read_write_disk_sectors $0x02
read_disk_drive_parameters:
push %bx
/* allocate memory for packet, pointer gets returned in bx */
alloca $16
/* issue command */
movw $0, 0(%bx) /* cmd = 0, read c,h,s */
push %bx
call send_command
add $2, %sp
/* normalize sector value */
movb 6(%bx), %cl
andb $0x3F, %cl
movb %cl, 6(%bx)
/* normalize cylinders */
subw $2, 2(%bx)
/* normalize heads */
subw $1, 4(%bx)
/* return code */
mov $0, %ah
/* cylinders */
movb 2(%bx), %ch
movb 3(%bx), %cl
shlb $6, %cl
andb $0xC0, %cl
/* sectors */
orb 6(%bx), %cl
/* heads */
movb 4(%bx), %dh
/* drives */
movb $1, %dl
/* status */
mov $0, %ah
freea
pop %bx
/* do this last since it's the most sensitive */
clc_stack
ret
alternate_disk_reset:
movb $0, %ah
clc_stack
ret
read_disk_drive_size:
push %bx
alloca $16
movw $0, 0(%bx) /* cmd = 0, read c,h,s */
push %bx
call send_command
add $2, %sp
/* cylinders - 1 to cx:dx */
mov 2(%bx), %dx
xor %cx, %cx
sub $1, %dx
/* heads */
push 4(%bx)
push $0
push %dx
push %cx
call mul32
add $8, %sp
/* sectors */
push 6(%bx)
push $0
push %dx
push %cx
call mul32
add $8, %sp
/* status */
mov $3, %ah
freea
pop %bx
clc_stack
ret
check_if_extensions_present:
mov $0x30, %ah
mov $0xAA55, %bx
mov $0x07, %cx
clc_stack
ret
.macro extended_read_write_sectors cmd
cmpb $10, 0(%si)
jg 1f
mov $1, %ah
stc_stack
ret
1:
push %ax
push %bp
allocbpa $16
movw \cmd, 0(%bp) /* read */
movw 2(%si), %ax /* nb_sectors */
movw %ax, 2(%bp)
movw 4(%si), %ax /* offset */
movw %ax, 6(%bp)
movw 6(%si), %ax /* segment */
movw %ax, 4(%bp)
movw 8(%si), %ax /* block */
movw %ax, 8(%bp)
movw 10(%si), %ax
movw %ax, 10(%bp)
movw 12(%si), %ax
movw %ax, 12(%bp)
movw 14(%si), %ax
movw %ax, 14(%bp)
push %bp
call send_command
add $2, %sp
freebpa
pop %bp
pop %ax
mov $0, %ah
clc_stack
ret
.endm
extended_read_sectors:
extended_read_write_sectors $0x01
extended_write_sectors:
extended_read_write_sectors $0x02
get_extended_drive_parameters:
push %ax
push %bp
push %cx
push %dx
allocbpa $16
movw $0, 0(%bp) /* read c,h,s */
push %bp
call send_command
add $2, %sp
/* write size */
movw $26, 0(%si)
/* set flags to 2 */
movw $2, 2(%si)
/* cylinders */
mov 2(%bp), %ax
mov %ax, 4(%si)
xor %ax, %ax
mov %ax, 6(%si)
/* heads */
mov 4(%bp), %ax
mov %ax, 8(%si)
xor %ax, %ax
mov %ax, 10(%si)
/* sectors */
mov 6(%bp), %ax
mov %ax, 12(%si)
xor %ax, %ax
mov %ax, 14(%si)
/* set total number of sectors */
mov 8(%bp), %ax
mov %ax, 16(%si)
mov 10(%bp), %ax
mov %ax, 18(%si)
mov 12(%bp), %ax
mov %ax, 20(%si)
mov 14(%bp), %ax
mov %ax, 22(%si)
/* number of bytes per sector */
movw $512, 24(%si)
freebpa
pop %dx
pop %cx
pop %bp
pop %ax
mov $0, %ah
clc_stack
ret
terminate_disk_emulation:
mov $1, %ah
stc_stack
ret
int13_handler:
cmp $0x80, %dl
je 1f
/* write old handler as return address onto stack */
push %eax
push %eax
push %ds
push %bp
mov %sp, %bp
xor %ax, %ax
mov %ax, %ds
mov (OLD_INT13), %eax
mov %eax, 8(%bp)
pop %bp
pop %ds
pop %eax
lret
1:
cmp $0x0, %ah
jne 1f
call disk_reset
iret
1:
cmp $0x2, %ah
jne 1f
call read_disk_sectors
iret
1:
cmp $0x8, %ah
jne 1f
call read_disk_drive_parameters
iret
1:
cmp $0x15, %ah
jne 1f
call read_disk_drive_size
iret
1:
cmp $0x41, %ah
jne 1f
call check_if_extensions_present
iret
1:
cmp $0x42, %ah
jne 1f
call extended_read_sectors
iret
1:
cmp $0x48, %ah
jne 1f
call get_extended_drive_parameters
iret
1:
cmp $0x4b, %ah
jne 1f
call terminate_disk_emulation
iret
1:
cmp $0x0d, %ah
jne 1f
call alternate_disk_reset
iret
1:
cmp $0x03, %ah
jne 1f
call write_disk_sectors
iret
1:
cmp $0x43, %ah
jne 1f
call extended_write_sectors
iret
1:
int $0x18 /* boot failed */
iret
.align 512, 0
_end: