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