/* * 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 */ #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: