| /* |
| * GRUB -- GRand Unified Bootloader |
| * Copyright (C) 1999,2000,2001,2002,2004 Free Software Foundation, Inc. |
| * |
| * 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| |
| /* |
| * Note: These functions defined in this file may be called from C. |
| * Be careful of that you must not modify some registers. Quote |
| * from gcc-2.95.2/gcc/config/i386/i386.h: |
| |
| 1 for registers not available across function calls. |
| These must include the FIXED_REGISTERS and also any |
| registers that can be used without being saved. |
| The latter must include the registers where values are returned |
| and the register where structure-value addresses are passed. |
| Aside from that, you can include as many other registers as you like. |
| |
| ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg |
| { 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } |
| */ |
| |
| #define ASM_FILE |
| |
| #include "shared.h" |
| |
| #ifdef STAGE1_5 |
| # define ABS(x) ((x) - EXT_C(main) + 0x2200) |
| #else |
| # define ABS(x) ((x) - EXT_C(main) + 0x8200) |
| #endif |
| |
| .file "asm.S" |
| |
| .text |
| |
| /* Tell GAS to generate 16-bit instructions so that this code works |
| in real mode. */ |
| .code16 |
| |
| #ifndef STAGE1_5 |
| /* |
| * In stage2, do not link start.S with the rest of the source |
| * files directly, so define the start symbols here just to |
| * force ld quiet. These are not referred anyway. |
| */ |
| .globl start, _start |
| start: |
| _start: |
| #endif /* ! STAGE1_5 */ |
| |
| ENTRY(main) |
| /* |
| * Guarantee that "main" is loaded at 0x0:0x8200 in stage2 and |
| * at 0x0:0x2200 in stage1.5. |
| */ |
| ljmp $0, $ABS(codestart) |
| |
| // /* control byte |
| // * Currently only bit 0 is used. |
| // * bit 0 disable the "unconditional command-line entrance" feature |
| // */ |
| // |
| // . = EXT_C(main) + 0x5 |
| // .byte 0 |
| |
| /* |
| * Compatibility version number |
| * |
| * These MUST be at byte offset 6 and 7 of the executable |
| * DO NOT MOVE !!! |
| */ |
| . = EXT_C(main) + 0x6 |
| .byte COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR |
| |
| /* |
| * This is a special data area 8 bytes from the beginning. |
| */ |
| |
| . = EXT_C(main) + 0x8 |
| |
| VARIABLE(install_partition) |
| .long 0xFFFFFF |
| /* This variable is here only because of a historical reason. */ |
| VARIABLE(saved_entryno) |
| #if defined(STAGE1_5) /* || ! defined(PRESET_MENU_STRING) */ |
| .long 0 |
| #else |
| |
| /* Note: GRUB for DOS uses this for the commandline preset_menu. |
| * A preset_menu can be embedded in the commandline of GRUB.EXE. |
| * This new preset_menu overrides the built-in preset_menu. |
| * If the variable is not touched, and the first byte at config_file is 0, |
| * then the new menu at 0x0800 will work. |
| * If the variable here is cleared to 0, or the first byte at config_file is |
| * not 0, then the built-in preset_menu will work. |
| * |
| * Do NOT change this variable to other value than 0. |
| */ |
| |
| .long preset_menu |
| #endif |
| VARIABLE(stage2_id) |
| .byte STAGE2_ID |
| VARIABLE(force_lba) |
| .byte 0 |
| VARIABLE(version_string) |
| .string VERSION |
| VARIABLE(config_file) |
| #ifndef STAGE1_5 |
| .string "/boot/grub/menu.lst" |
| #else /* STAGE1_5 */ |
| .long 0xffffffff |
| .string "/boot/grub/stage2" |
| #endif /* STAGE1_5 */ |
| |
| /* |
| * Leave some breathing room for the config file name. |
| */ |
| |
| . = EXT_C(main) + 0x6C #; bss starting address |
| #ifndef STAGE1_5 |
| #if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL) |
| //.word (__bss_start - main) & 0x0F, (__bss_start - main) >> 4 |
| .long __bss_start |
| #elif defined(HAVE_USCORE_EDATA_SYMBOL) |
| //.word (_edata - main) & 0x0F, (_edata - main) >> 4 |
| .long _edata |
| #elif defined(HAVE_EDATA_SYMBOL) |
| //.word (edata - main) & 0x0F, (edata - main) >> 4 |
| .long edata |
| #else |
| #error no bss starting address |
| #endif |
| #endif |
| |
| . = EXT_C(main) + 0x70 |
| |
| /* the real mode code continues... */ |
| codestart: |
| cli /* we're not safe here! */ |
| cld |
| |
| /* set up %ds, %ss, and %es */ |
| xorw %ax, %ax |
| movw %ax, %ds |
| movw %ax, %es |
| movw %ax, %ss |
| movl $STACKOFF, %esp |
| |
| #ifndef STAGE1_5 |
| movb 0x0410, %al |
| movb %al, ABS(floppies_orig) |
| movb 0x0475, %al |
| movb %al, ABS(harddrives_orig) |
| |
| //movb $((int13_handler_end - int13_handler + 0x3ff) / 0x400), ABS(int13_handler) |
| |
| movl 0x4C, %eax |
| movl %eax, ABS(int13_offset) |
| movl %eax, ABS(int13_offset) + 0x113 - 0x1C |
| cmpl $0xC0000000, %eax |
| jnb 1f |
| cmpl $0x9A000000, %eax |
| jb 1f |
| andl $0x3FFFFF, %eax |
| cmpl $0x100, %eax |
| jnz 1f |
| movw 0x413, %ax |
| shlw $6, %ax |
| cmpw 0x4E, %ax |
| jne 1f |
| |
| movw %ax, %ds /* DS=old int13 code segment */ |
| movl 0x1C, %eax /* ROM int 13 vector */ |
| cmpl $0x9A000000, %eax |
| jb 2f |
| |
| movl 0x0C, %eax /* ROM int 15 vector */ |
| cmpl $0x9A000000, %eax |
| jb 2f |
| |
| /* restore old emu data, except the first byte of handler size. */ |
| movw $(0x140 - 1), %cx |
| movw $1, %si |
| movw $ABS(int13_handler + 1), %di |
| repz movsb |
| |
| ///* restore old int13_dup */ |
| //movl (int13_dup - int13_handler), %eax |
| //movl %eax, %es:ABS(int13_dup) |
| |
| ///* restore old safe_mbr_hook */ |
| //movl (safe_mbr_hook - int13_handler), %eax |
| //movl %eax, %es:ABS(safe_mbr_hook) |
| |
| 2: |
| xorw %ax, %ax |
| movw %ax, %ds /* DS=0 */ |
| 1: |
| movl ABS(ROM_int15), %eax |
| cmpl $0x9A000000, %eax |
| jnb 1f |
| movl 0x0054, %eax |
| movl %eax, ABS(ROM_int15) |
| 1: |
| /* check the BIOS type (currently only for Bochs) */ |
| movw $0xF000, %ax |
| movw %ax, %es /* ES=0xF000 */ |
| movw $0xFF00, %di |
| movw $ABS(bochs_copygrght_string), %si |
| movw $0x22, %cx |
| repz cmpsw |
| setz ABS(bios_id) /* 1 for bochs, 0 for unknown. */ |
| xorw %ax, %ax |
| movw %ax, %es /* ES=0 */ |
| |
| #endif /* STAGE1_5 */ |
| #ifndef SUPPORT_DISKLESS |
| /* |
| * Save the sector number of the second sector (i.e. this sector) |
| * in INSTALL_SECOND_SECTOR. See also "stage2/start.S". |
| */ |
| ADDR32 movl %ebp, EXT_C(install_second_sector) |
| #endif |
| |
| ///* set up the real mode/BIOS stack */ |
| //movl $STACKOFF, %ebp |
| //movl %ebp, %esp |
| |
| /* for usb keyboard hack here, disable interrupt */ |
| //sti /* we're safe again */ |
| |
| /* the active mouse will hang the machine */ |
| |
| #if 0 |
| cli |
| pushal |
| pushw %ds |
| pushw %es |
| pushw %cs |
| call 1f |
| popw %es |
| popw %ds |
| popal |
| jmp 2f |
| 1: |
| pushw $0xADDA |
| //ljmp $0xF000, $0x98E1 |
| ljmp $0xF000, $0xA5F2 |
| |
| 2: |
| ljmp $0xF000, $0xFFF0 |
| #endif |
| |
| pushw %dx /* DL=boot drive */ |
| |
| /* qemu-0.8.0 could hang on mouse init here. */ |
| |
| #if 0 |
| /* reset mouse */ |
| movw $0xC201, %ax |
| int $0x15 |
| #endif |
| |
| #if 0 |
| /* disable mouse */ |
| movw $0xC200, %ax |
| xorw %bx, %bx /* BH=0 means disable */ |
| int $0x15 |
| #endif |
| |
| #if 0 |
| /* set mouse handler address */ |
| movw $0xC207, %ax |
| xorw %bx, %bx /* ES:BX=0000:0000 to cancel the handler */ |
| int $0x15 |
| #endif |
| |
| #if 0 |
| /* disable monitor clock (Watch-Dog) */ |
| movw $0xC300, %ax |
| int $0x15 |
| #endif |
| |
| #if 0 |
| /* restart all adaptors */ |
| movb $0xFF, %al |
| outb %al, $0x96 |
| xorw %cx, %cx |
| 1: loop 1b |
| movb $0xF0, %al |
| outb %al, $0x96 |
| xorw %cx, %cx |
| 1: loop 1b |
| movb $0x00, %al |
| outb %al, $0x96 |
| xorw %cx, %cx |
| 1: loop 1b |
| |
| /* initialize all adaptors */ |
| movw $0xC000, %bx |
| 2: |
| movw %bx, %ds |
| xorw %si, %si |
| lodsw |
| cmpw $0xAA55, %ax |
| jne 1f |
| xorw %ax, %ax |
| lodsb #; ROM size in sectors |
| addw $3, %ax |
| andw $0xFFFC, %ax |
| subw $4, %ax |
| shlw $5, %ax |
| pushaw |
| pushw %bx |
| pushw $3 |
| movw %sp, %bp |
| lcall *(%bp) |
| popw %bx |
| popw %bx |
| popaw |
| 1: |
| addw $0x80, %ax |
| addw %ax, %bx |
| cmpw $0xF000, %bx |
| jb 2b |
| |
| #endif |
| |
| xorw %ax, %ax |
| movw %ax, %ds |
| movw %ax, %es |
| |
| #if 0 |
| /* cancel alarm clock */ |
| movb $0x07, %ah |
| int $0x1A |
| #endif |
| |
| /* clear VDS */ |
| andb $0xD7, 0x47B |
| |
| #ifndef STAGE1_5 |
| |
| sti |
| movw $1, %bx /* BL=1, BH=0 */ |
| movl 0x46c, %eax /* initial tick */ |
| addl $5, %eax /* wait 0.27 seconds */ |
| pushl %eax |
| |
| 2: |
| /* checkkey 'c' */ |
| |
| pushw %bx |
| movb $0x01, %ah /* checkkey */ |
| int $0x16 |
| popw %bx |
| |
| jz 1f /* no keypress */ |
| |
| /* getkey */ |
| |
| pushw %bx |
| movb $0x00, %ah /* getkey */ |
| int $0x16 |
| popw %bx |
| |
| cmpw $KEY_IC, %ax /* insert char */ |
| jne 3f |
| movb $1, %bh /* DEBUG_KEY pressed */ |
| 3: |
| orb $0x20, %al |
| cmpb $0x63, %al /* is "C"? */ |
| jne 2b /* no, get next key */ |
| |
| /* "C" is pressed. */ |
| |
| /* check the DUCE indicator */ |
| |
| cmpl $0x45435544, 0x5FC |
| //jz 1f |
| jz 2b |
| |
| /* Bypass all config files */ |
| movb $0, %bl |
| jmp 2b |
| 1: |
| |
| popl %eax |
| pushl %eax |
| movl 0x46c, %ecx /* current tick */ |
| cmpl %eax, %ecx |
| jnb 1f |
| subl $5, %eax |
| cmpl %eax, %ecx |
| jnb 2b |
| cmpl $5, %ecx |
| jb 2b |
| 1: |
| popl %eax |
| movb %bh, debug_boot |
| #endif |
| |
| popw %dx /* DL=boot drive */ |
| |
| cli |
| |
| pushw %bx |
| |
| #ifndef SUPPORT_DISKLESS |
| /* save boot drive reference */ |
| ADDR32 movb %dl, EXT_C(boot_drive) |
| |
| testb %bh, %bh /* debug_boot? */ |
| jz 1f |
| movw $ABS(reset_disk_string),%si |
| call print_message /* will not change DX */ |
| 1: |
| xorw %ax, %ax |
| /* reset disk system (%ah = 0) */ |
| #ifdef STAGE1_5 |
| int $0x13 |
| #else |
| call safe_int13 |
| #endif |
| movw $ABS(reset_disk_failure_string),%si |
| jc 1f |
| movw $ABS(reset_disk_success_string),%si |
| 1: |
| popw %bx |
| pushw %bx |
| testb %bh, %bh /* debug_boot? */ |
| jz 1f |
| call print_message /* will not change DX */ |
| 1: |
| #endif |
| |
| popw %bx |
| |
| xorw %ax, %ax |
| movw %ax, %ds |
| movw %ax, %es |
| |
| pushw %bx |
| |
| /* clear keyboard buffer */ |
| 2: |
| movb $0x01, %ah /* checkkey */ |
| int $0x16 |
| |
| jz 1f /* no keypress */ |
| |
| movb $0x00, %ah /* getkey */ |
| int $0x16 |
| jmp 2b |
| 1: |
| |
| popw %bx |
| |
| /* transition to protected mode */ |
| DATA32 call EXT_C(real_to_prot) |
| |
| /* The ".code32" directive takes GAS out of 16-bit mode. */ |
| .code32 |
| |
| #ifndef STAGE1_5 |
| testb %bl, %bl |
| jnz 1f |
| movl $0, EXT_C(use_config_file) |
| 1: |
| |
| /* before clearing the bss, we move preset_menu to 0x800 */ |
| |
| movl preset_menu, %eax |
| |
| #if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL) |
| cmpl $__bss_start, %eax |
| #elif defined(HAVE_USCORE_EDATA_SYMBOL) |
| cmpl $_edata, %eax |
| #elif defined(HAVE_EDATA_SYMBOL) |
| cmpl $edata, %eax |
| #else |
| #error no bss starting address |
| #endif |
| jnz 1f /* use old bootp for diskless */ |
| |
| xorl %eax, %eax |
| cmpb %al, config_file /* AL == 0 */ |
| jnz 2f |
| movl saved_entryno, %ebx |
| testl %ebx, %ebx |
| jnz 3f /* use menu embedded in commnad-line of grub.exe */ |
| 2: |
| /* use builtin preset_menu */ |
| |
| /* set the starting address of the preset_menu */ |
| |
| #if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL) |
| movl $__bss_start, %esi |
| #elif defined(HAVE_USCORE_EDATA_SYMBOL) |
| movl $_edata, %esi |
| #elif defined(HAVE_EDATA_SYMBOL) |
| movl $edata, %esi |
| #else |
| #error no bss starting address |
| #endif |
| |
| cld |
| addl $16, %esi /* skip 4 bytes of B0 02 1A CE */ |
| /* skip 4 bytes of reserved */ |
| /* skip 4 bytes of reserved */ |
| /* skip 4 bytes of zeroes */ |
| |
| movl $0x400, %ecx /* move 4KB of the menu ... */ |
| movl $0x800, %edi /* ... to 0x800 */ |
| repz movsl |
| |
| 3: |
| movl $0x0800, preset_menu /* use new menu at 0x800 */ |
| 1: |
| #endif /* !STAGE1_5 */ |
| |
| /* clean out the bss */ |
| |
| /* set %edi to the bss starting address */ |
| #if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL) |
| movl $__bss_start, %edi |
| #elif defined(HAVE_USCORE_EDATA_SYMBOL) |
| movl $_edata, %edi |
| #elif defined(HAVE_EDATA_SYMBOL) |
| movl $edata, %edi |
| #else |
| #error no bss starting address |
| #endif |
| |
| /* set %ecx to the bss end */ |
| #if defined(HAVE_END_SYMBOL) |
| movl $end, %ecx |
| #elif defined(HAVE_USCORE_END_SYMBOL) |
| movl $_end, %ecx |
| #else |
| #error no bss ending address |
| #endif |
| |
| /* compute the bss length */ |
| subl %edi, %ecx |
| |
| /* zero %al */ |
| xorb %al, %al |
| |
| /* set the direction */ |
| cld |
| |
| /* clean out */ |
| rep |
| stosb |
| |
| |
| /* |
| * Call the start of main body of C code, which does some |
| * of it's own initialization before transferring to "cmain". |
| */ |
| call EXT_C(init_bios_info) |
| |
| |
| /* |
| * This call is special... it never returns... in fact it should simply |
| * hang at this point! |
| */ |
| |
| ENTRY(stop) |
| call EXT_C(prot_to_real) |
| |
| /* |
| * This next part is sort of evil. It takes advantage of the |
| * byte ordering on the x86 to work in either 16-bit or 32-bit |
| * mode, so think about it before changing it. |
| */ |
| |
| /* No external program ever calls HARD_STOP. HARD_STOP is only called |
| * by the asm.S itself, and all calls are from real mode. So we |
| * could(and should) use .code16 here clearly. |
| */ |
| |
| .code16 |
| |
| //ENTRY(hard_stop) |
| hard_stop: |
| sti |
| hlt |
| //jmp EXT_C(hard_stop) |
| jmp hard_stop |
| |
| #ifndef STAGE1_5 |
| |
| /* If preset_menu == __bss_start, the new menu at end of pre_stage2 will be used. */ |
| |
| VARIABLE(preset_menu) |
| #if defined(PRESET_MENU_STRING) |
| #if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL) |
| //.word (__bss_start - main) & 0x0F, (__bss_start - main) >> 4 |
| .long __bss_start |
| #elif defined(HAVE_USCORE_EDATA_SYMBOL) |
| //.word (_edata - main) & 0x0F, (_edata - main) >> 4 |
| .long _edata |
| #elif defined(HAVE_EDATA_SYMBOL) |
| //.word (edata - main) & 0x0F, (edata - main) >> 4 |
| .long edata |
| #else /* ! HAVE_EDATA_SYMBOL */ |
| #error no bss starting address |
| #endif /* ! HAVE_EDATA_SYMBOL */ |
| #else /* ! PRESET_MENU_STRING */ |
| .long 0 |
| #endif /* PRESET_MENU_STRING */ |
| |
| VARIABLE(debug_boot) |
| .long 0 |
| #endif /* ! STAGE1_5 */ |
| .code16 |
| |
| /* real mode print string */ |
| |
| /* prints string DS:SI (modifies AX BX SI) */ |
| |
| print_message: |
| 1: |
| cld |
| lodsb (%si), %al /* get token */ |
| xorw %bx, %bx /* video page 0 */ |
| movb $0x0e, %ah /* print it */ |
| int $0x10 /* via TTY mode */ |
| cmpb $0, %al /* end of string? */ |
| jne 1b /* until done */ |
| ret |
| |
| reset_disk_string: |
| .ascii "Reseting the boot drive... \0" |
| reset_disk_success_string: |
| .ascii "Success.\r\n\0" |
| reset_disk_failure_string: |
| .ascii "Failure!\r\n\0" |
| |
| bochs_copygrght_string: |
| .ascii "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team.\0" |
| ENTRY(bios_id) |
| .long 0 /* 1 for bochs, 0 for unknown. */ |
| .code32 |
| |
| #ifndef STAGE1_5 |
| |
| /* unsigned long pxe_scan(void) |
| * |
| * scan pxe runtime |
| */ |
| ENTRY(pxe_scan) |
| pushw %es |
| pushl %ebx |
| |
| call EXT_C(prot_to_real) |
| .code16 |
| |
| movw $0x5650, %ax |
| int $0x1A |
| cmpw $0x564E, %ax |
| jnz 1f |
| cmpl $0x4E455850, %es:(%bx) // PXEN(V+) |
| jnz 1f |
| cmpw $0x201, %es:6(%bx) // API version |
| jb 1f |
| lesw %es:0x28(%bx), %bx // !PXE structure |
| cmpl $0x45585021, %es:(%bx) // !PXE |
| jnz 1f |
| lesw %es:0x10(%bx), %bx |
| movw %es, %cx |
| jmp 2f |
| 1: |
| xorw %bx, %bx |
| xorw %cx, %cx |
| 2: |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movl %ecx, %eax |
| shll $16, %eax |
| movw %bx, %ax |
| movl %eax, EXT_C(pxe_entry) |
| |
| popl %ebx |
| popw %es |
| ret |
| |
| /* int pxe_call(int func,void* data) |
| * |
| * PXE function call |
| */ |
| ENTRY(pxe_call) |
| pushl %ebp |
| movl %esp, %ebp |
| pushl %esi |
| pushl %edi |
| pushl %ebx |
| |
| movl 8(%ebp), %ecx |
| movl 12(%ebp), %edx |
| movl %edx, %eax |
| andl $0xF, %eax |
| shrl $4, %edx |
| shll $16, %edx |
| addl %eax, %edx |
| movl EXT_C(pxe_entry), %ebx |
| |
| call EXT_C(prot_to_real) |
| .code16 |
| |
| pushl %ebx |
| pushl %edx |
| pushw %cx |
| movw %sp, %bx |
| lcall *%ss:6(%bx) |
| cld |
| addw $10, %sp |
| movw %ax, %cx |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| xorl %eax, %eax |
| movw %cx, %ax |
| |
| popl %ebx |
| popl %edi |
| popl %esi |
| popl %ebp |
| ret |
| |
| #if PXE_FAST_READ |
| |
| /* int pxe_fast_read(void* data,int num) |
| * |
| * Read multiple packets |
| */ |
| ENTRY(pxe_fast_read) |
| pushl %ebp |
| movl %esp, %ebp |
| pushl %esi |
| pushl %edi |
| pushl %ebx |
| pushw %es |
| |
| movl 8(%ebp), %edx |
| movl 12(%ebp), %esi |
| movl EXT_C(pxe_blksize), %edi |
| movl %edx, %eax |
| andl $0xF, %eax |
| shrl $4, %edx |
| shll $16, %edx |
| addl %eax, %edx |
| movl EXT_C(pxe_entry), %ebx |
| |
| call EXT_C(prot_to_real) |
| .code16 |
| |
| pushl %ebx |
| pushl %edx |
| pushw $0x22 // PXENV_TFTP_READ |
| movw %sp, %bx |
| movw %si, %cx |
| lesw %ss:2(%bx), %si |
| |
| 1: |
| lcall *%ss:6(%bx) |
| cld |
| |
| cmpw $0, %es:(%si) |
| jnz 2f |
| movw %es:4(%si), %dx |
| addw %dx, %es:6(%si) |
| cmpw %di, %dx |
| jb 2f |
| loop 1b |
| |
| 2: |
| addw $10, %sp |
| movw %ax, %cx |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| xorl %eax, %eax |
| movw %cx, %ax |
| |
| popw %es |
| popl %ebx |
| popl %edi |
| popl %esi |
| popl %ebp |
| ret |
| #endif |
| |
| /* |
| * stop_floppy() |
| * |
| * Stops the floppy drive from spinning, so that other software is |
| * jumped to with a known state. |
| */ |
| ENTRY(stop_floppy) |
| pusha |
| call EXT_C(prot_to_real) |
| .code16 |
| sti #; added 2006-11-30 |
| xorb %dl, %dl |
| #ifdef STAGE1_5 |
| int $0x13 |
| #else |
| call safe_int13 |
| #endif |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| popa |
| ret |
| |
| /* |
| * grub_reboot() |
| * |
| * Reboot the system. At the moment, rely on BIOS. |
| */ |
| ENTRY(grub_reboot) |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| /* cold boot */ |
| |
| //sti /* needn't enable interrupt here. comment it out */ |
| |
| movw $0x0472, %di |
| movw %ax, (%di) |
| ljmp $0xFFFF, $0x0000 |
| |
| .code32 |
| |
| /* |
| * grub_halt(int no_apm) |
| * |
| * Halt the system, using APM if possible. If NO_APM is true, don't use |
| * APM even if it is available. |
| */ |
| ENTRY(grub_halt) |
| /* get the argument */ |
| movl 4(%esp), %eax |
| |
| /* see if zero */ |
| testl %eax, %eax |
| jnz EXT_C(stop) |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* this is not needed here, so comment it out. */ |
| sti #; added 2006-11-30 |
| |
| /* detect APM */ |
| movw $0x5300, %ax |
| xorw %bx, %bx |
| int $0x15 |
| //jc EXT_C(hard_stop) |
| jc hard_stop |
| /* don't check %bx for buggy BIOSes... */ |
| |
| /* disconnect APM first */ |
| movw $0x5304, %ax |
| xorw %bx, %bx |
| int $0x15 |
| |
| /* connect APM */ |
| movw $0x5301, %ax |
| xorw %bx, %bx |
| int $0x15 |
| //jc EXT_C(hard_stop) |
| jc hard_stop |
| |
| /* set APM protocol level - 1.1 or bust. (this covers APM 1.2 also) */ |
| movw $0x530E, %ax |
| xorw %bx, %bx |
| movw $0x0101, %cx |
| int $0x15 |
| //jc EXT_C(hard_stop) |
| jc hard_stop |
| |
| /* set the power state to off */ |
| movw $0x5307, %ax |
| movw $1, %bx |
| movw $3, %cx |
| int $0x15 |
| |
| /* shouldn't reach here */ |
| //jmp EXT_C(hard_stop) |
| jmp hard_stop |
| |
| |
| /* |
| * int check_64bit (void) |
| * |
| * Checks whether 64-bit mode is supported |
| * |
| * Stolen from a patch originaly intended for syslinux |
| * (http://syslinux.zytor.com/archives/2007-January/007832.html) |
| * |
| * Copyright (C) 2007 Byron Stanoszek <gandalf@winds.org> |
| * |
| * Adapted to AT&T syntax by Robert Millan <rmh@aybabtu.com> |
| */ |
| |
| ENTRY(check_64bit) |
| |
| .code32 |
| |
| pushl %ebp |
| pushl %ebx |
| pushl %edx |
| |
| /* Check if this CPU supports the CPUID command */ |
| pushfl |
| pushfl |
| popl %eax |
| movl %eax, %ebx |
| xorl $(1 << 21), %eax // CPUID bit |
| pushl %eax |
| popfl |
| pushfl |
| popl %eax |
| popfl // Restore the original flags |
| xorl %ebx, %eax |
| jz is_32bit |
| |
| /* Now check for the 64-bit flag in the CPU features byte ($0000_0001, edx) |
| This is bit 30 for Intel CPUs, and bit 29 for AMD CPUs */ |
| movl $0x00000000, %eax // Find last Intel cpuid # |
| cpuid |
| cmpl $0x00000000, %eax |
| je test_amd |
| movl $0x00000001, %eax // Read Intel CPU flags |
| cpuid |
| btl $30, %edx // 64-bit if bit 30 is set |
| jc is_64bit |
| |
| test_amd: |
| movl $0x80000000, %eax // Find last AMD cpuid # |
| cpuid |
| cmpl $0x80000000, %eax |
| jbe is_32bit |
| movl $0x80000001, %eax // Read AMD CPU flags |
| cpuid |
| btl $29, %edx // 64-bit if bit 29 is set |
| jnc is_32bit |
| |
| is_64bit: |
| movl $1, %eax |
| popl %edx |
| popl %ebx |
| popl %ebp |
| ret |
| is_32bit: |
| xorl %eax, %eax |
| popl %edx |
| popl %ebx |
| popl %ebp |
| ret |
| |
| |
| /* Catch CPU exceptions 0 - 7 |
| * 0 Divide |
| * 1 Debug |
| * 2 NMI |
| * 3 Break point |
| * 4 Overflow |
| * 5 Bound |
| * 6 Invalid Instruction |
| * 7 no coprocessor |
| */ |
| |
| set_fault_recovery_handler: |
| |
| .code16 |
| |
| pushfw |
| pushw %ds |
| pushw %es |
| pushaw |
| |
| /* backup int 00 - 07 */ |
| |
| xorw %ax, %ax |
| movw %ax, %ds |
| movw %ax, %es |
| xorw %si, %si |
| movw $ABS(int_00_07_vectors), %di |
| movw $16, %cx |
| cld |
| repz movsw |
| |
| /* set to new vector */ |
| |
| xorw %ax, %ax |
| movw %ax, %ds |
| movw %ax, %es |
| xorw %si, %si |
| movw $ABS(int_00_07_vectors), %di |
| |
| pushl %eax |
| |
| xorw %di, %di |
| movl $ABS(fault_recovery_handler), %eax /* 0000:fault_recovery_handler */ |
| movw $8, %cx |
| cld |
| repz stosl |
| |
| popl %eax |
| |
| popaw |
| popw %es |
| popw %ds |
| popfw |
| ret |
| |
| unset_fault_recovery_handler: |
| |
| .code16 |
| |
| pushfw |
| pushw %ds |
| pushw %es |
| pushaw |
| |
| /* restore int 00 - 07 */ |
| |
| xorw %ax, %ax |
| movw %ax, %ds |
| movw %ax, %es |
| xorw %di, %di |
| movw $ABS(int_00_07_vectors), %si |
| movw $16, %cx |
| cld |
| repz movsw |
| |
| popaw |
| popw %es |
| popw %ds |
| popfw |
| ret |
| |
| .align 4 |
| |
| int_00_07_vectors: |
| .space 32 |
| |
| original_registers: |
| .space 48 |
| |
| safe_int13: |
| |
| .code16 |
| |
| /* setup our fault recovery handler */ |
| |
| call set_fault_recovery_handler |
| |
| /* backup old registers. Note: CS=0 */ |
| |
| movw %ds, %cs:ABS(original_registers) |
| movw %es, %cs:ABS(original_registers) + 4 |
| movw %ss, %cs:ABS(original_registers) + 8 |
| movl %esp, %cs:ABS(original_registers) + 12 |
| movl %eax, %cs:ABS(original_registers) + 16 |
| movl %ebx, %cs:ABS(original_registers) + 20 |
| movl %ecx, %cs:ABS(original_registers) + 24 |
| movl %edx, %cs:ABS(original_registers) + 28 |
| movl %esi, %cs:ABS(original_registers) + 32 |
| movl %edi, %cs:ABS(original_registers) + 36 |
| movl %ebp, %cs:ABS(original_registers) + 40 |
| |
| pushw %bp |
| pushw %ax |
| movw %sp, %bp |
| movw (%bp), %ax |
| movw %ax, %cs:ABS(original_registers) + 44 #; return IP |
| popw %ax |
| popw %bp |
| |
| int $0x13 |
| |
| call unset_fault_recovery_handler |
| |
| ret |
| |
| fault_recovery_handler: |
| |
| .code16 |
| |
| /* restore old registers. Note: CS=0 */ |
| |
| movw %cs:ABS(original_registers), %ds |
| movw %cs:ABS(original_registers) + 4, %es |
| movw %cs:ABS(original_registers) + 8, %ss |
| movl %cs:ABS(original_registers) + 12, %esp |
| movl %cs:ABS(original_registers) + 16, %eax |
| movl %cs:ABS(original_registers) + 20, %ebx |
| movl %cs:ABS(original_registers) + 24, %ecx |
| movl %cs:ABS(original_registers) + 28, %edx |
| movl %cs:ABS(original_registers) + 32, %esi |
| movl %cs:ABS(original_registers) + 36, %edi |
| movl %cs:ABS(original_registers) + 40, %ebp |
| |
| /* stack is available, so we can push and pop. */ |
| |
| pushw %bp |
| pushw %ax |
| movw %sp, %bp |
| movw %cs:ABS(original_registers) + 44, %ax #; return IP |
| movw %ax, (%bp) |
| popw %ax |
| popw %bp |
| |
| pushl $1 #; CF=1 indicating error |
| popfl #; CLD, CLI, and many more... |
| |
| call unset_fault_recovery_handler |
| |
| ret |
| |
| /* never come here. */ |
| |
| iret |
| |
| .code32 |
| |
| /* |
| * track_int13(int drive) |
| * |
| * Track the int13 handler to probe I/O address space. |
| */ |
| ENTRY(track_int13) |
| pushl %ebp |
| movl %esp, %ebp |
| |
| pushl %ebx |
| pushl %edi |
| |
| /* copy the original int13 handler segment:offset */ |
| movl $0x4c, %edi |
| movl (%edi), %eax |
| movl %eax, track_int13_addr |
| |
| /* replace the int1 handler */ |
| movl $0x4, %edi |
| pushl (%edi) |
| movl $ABS(int1_handler), %eax |
| movl %eax, (%edi) |
| |
| /* read the MBR to call int13 successfully */ |
| movb 8(%ebp), %dl |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| movw $SCRATCHSEG, %ax |
| movw %ax, %es |
| xorw %bx, %bx |
| movw $1, %cx |
| xorb %dh, %dh |
| |
| /* save FLAGS on the stack to emulate int13 */ |
| pushfw |
| |
| /* set the TF flag */ |
| /* FIXME: this can be simplified not to use AX */ |
| pushfw |
| popw %ax |
| orw $0x100, %ax |
| pushw %ax |
| popfw |
| |
| movw $0x0201, %ax |
| |
| .byte 0x9a /* lcall */ |
| track_int13_addr: |
| .word 0 /* offset */ |
| .word 0 /* segment */ |
| |
| /* TF is cleared here automatically */ |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| /* restore the int1 handler */ |
| movl $0x4, %edi |
| popl (%edi) |
| |
| popl %edi |
| popl %ebx |
| popl %ebp |
| |
| ret |
| |
| |
| /* |
| * Check if the next instruction is I/O, and if this is true, add the |
| * port into the io map. |
| * |
| * Note: Probably this will make the execution of int13 very slow. |
| * |
| * Note2: In this implementation, all we can know is I/O-mapped I/O. It |
| * is impossible to detect memory-mapped I/O. |
| */ |
| int1_handler: |
| .code16 |
| |
| pushw %bp |
| movw %sp, %bp |
| pushw %ds |
| pushw %ax |
| pushw %si |
| pushw %dx |
| |
| /* IP */ |
| movw 2(%bp), %si |
| /* CS */ |
| movw 4(%bp), %ax |
| movw %ax, %ds |
| |
| /* examine the next instruction */ |
| 1: lodsb (%si), %al |
| /* skip this code if it is a prefix */ |
| cmpb $0x2E, %al |
| je 1b |
| cmpb $0x36, %al |
| je 1b |
| cmpb $0x3E, %al |
| je 1b |
| cmpb $0x26, %al |
| je 1b |
| cmpb $0x64, %al |
| jl 2f |
| cmpb $0x67, %al |
| jle 1b |
| 2: cmpb $0xF0, %al |
| jl 3f |
| cmpb $0xF3, %al |
| jle 1b |
| |
| 3: /* check if this code is out* or in* */ |
| |
| /* ins? or outs? */ |
| cmpb $0x6C, %al |
| jl 4f |
| cmpb $0x6F, %al |
| jle 5f |
| |
| 4: /* in? or out? (register operand version) */ |
| cmpb $0xEC, %al |
| jl 6f |
| cmpb $0xEF, %al |
| jle 5f |
| |
| 6: /* in? or out? (immediate operand version) */ |
| cmpb $0xE4, %al |
| jl 8f |
| cmpb $0xE7, %al |
| jg 8f |
| |
| 7: /* immediate has a port */ |
| lodsb (%si), %al |
| movzbw %al, %dx |
| |
| 5: /* %dx has a port */ |
| |
| /* set %ds to zero */ |
| xorw %ax, %ax |
| movw %ax, %ds |
| |
| /* set %si to the io map */ |
| movw $ABS(EXT_C(io_map)), %si |
| |
| |
| 9: /* check if the io map already has the port */ |
| lodsw (%si), %ax |
| /* check if this is the end */ |
| testw %ax, %ax |
| jz 1f |
| /* check if this matches the port */ |
| cmpw %ax, %dx |
| jne 9b |
| /* if so, leave from this handler */ |
| jmp 8f |
| |
| 1: /* check for the buffer overrun */ |
| cmpw $(ABS(EXT_C(io_map)) + (IO_MAP_SIZE + 1) * 2), %si |
| je 8f |
| /* add the port into the io map */ |
| movw %dx, -2(%si) |
| |
| 8: /* restore registers */ |
| popw %dx |
| popw %si |
| popw %ax |
| popw %ds |
| popw %bp |
| |
| iret |
| |
| .code32 |
| |
| ENTRY(io_map) |
| .space (IO_MAP_SIZE + 1) * 2 |
| |
| |
| /* |
| * set_int15_handler(void) |
| * |
| * Set up int15_handler. |
| */ |
| ENTRY(set_int15_handler) |
| pushl %edi |
| |
| /* save the original int15 handler */ |
| movl $0x54, %edi |
| movw (%edi), %ax |
| movw %ax, ABS(int15_offset) |
| movw 2(%edi), %ax |
| movw %ax, ABS(int15_segment) |
| |
| /* save the new int15 handler */ |
| movw $ABS(int15_handler), %ax |
| movw %ax, (%edi) |
| xorw %ax, %ax |
| movw %ax, 2(%edi) |
| |
| popl %edi |
| ret |
| |
| |
| /* |
| * unset_int15_handler(void) |
| * |
| * Restore the original int15 handler |
| */ |
| ENTRY(unset_int15_handler) |
| pushl %edi |
| |
| /* check if int15_handler is set */ |
| movl $0x54, %edi |
| movw $ABS(int15_handler), %ax |
| cmpw %ax, (%edi) |
| jne 1f |
| xorw %ax, %ax |
| cmpw %ax, 2(%edi) |
| jne 1f |
| |
| /* restore the original */ |
| movw ABS(int15_offset), %ax |
| movw %ax, (%edi) |
| movw ABS(int15_segment), %ax |
| movw %ax, 2(%edi) |
| |
| 1: |
| popl %edi |
| ret |
| |
| |
| /* |
| * Translate a key code to another. |
| * |
| * Note: This implementation cannot handle more than one length |
| * scancodes (such as Right Ctrl). |
| */ |
| .code16 |
| int15_handler: |
| /* if non-carrier, ignore it */ |
| jnc 1f |
| /* check if AH=4F */ |
| cmpb $0x4F, %ah |
| jne 1f |
| |
| /* E0 and E1 are special */ |
| cmpb $0xE1, %al |
| je 4f |
| cmpb $0xE0, %al |
| /* this flag is actually the machine code (je or jmp) */ |
| int15_skip_flag: |
| je 4f |
| |
| pushw %bp |
| movw %sp, %bp |
| |
| pushw %bx |
| pushw %dx |
| pushw %ds |
| pushw %si |
| |
| /* save bits 0-6 of %al in %dl */ |
| movw %ax, %dx |
| andb $0x7f, %dl |
| /* save the highest bit in %bl */ |
| movb %al, %bl |
| xorb %dl, %bl |
| /* set %ds to 0 */ |
| xorw %ax, %ax |
| movw %ax, %ds |
| /* set %si to the key map */ |
| movw $ABS(EXT_C(bios_key_map)), %si |
| |
| /* find the key code from the key map */ |
| 2: |
| lodsw |
| /* check if this is the end */ |
| testw %ax, %ax |
| jz 3f |
| /* check if this matches the key code */ |
| cmpb %al, %dl |
| jne 2b |
| /* if so, perform the mapping */ |
| movb %ah, %dl |
| 3: |
| /* restore %ax */ |
| movw %dx, %ax |
| orb %bl, %al |
| /* make sure that CF is set */ |
| orw $1, 6(%bp) |
| /* restore other registers */ |
| popw %si |
| popw %ds |
| popw %dx |
| popw %bx |
| popw %bp |
| iret |
| |
| 4: |
| /* tricky: jmp (0x74) <-> je (0xeb) */ |
| xorb $(0x74 ^ 0xeb), ABS(int15_skip_flag) |
| 1: |
| /* just cascade to the original */ |
| /* ljmp */ |
| .byte 0xea |
| int15_offset: .word 0 |
| int15_segment: .word 0 |
| |
| .code32 |
| |
| .align 4 |
| ENTRY(bios_key_map) |
| .space (KEY_MAP_SIZE + 1) * 2 |
| |
| |
| /* |
| * set_int13_handler(map) |
| * |
| * Copy MAP to the drive map and set up int13_handler. |
| */ |
| ENTRY(set_int13_handler) |
| pushl %ebp |
| movl %esp, %ebp |
| |
| pushl %edi |
| pushl %esi |
| |
| /* copy MAP to the drive map */ |
| movl $(DRIVE_MAP_SIZE * DRIVE_MAP_SLOT_SIZE / 4), %ecx |
| movl $ABS(hooked_drive_map), %edi |
| movl 8(%ebp), %esi |
| cld |
| repz movsl |
| |
| // Now initialized early at the beginning of this file |
| // |
| // /* save the original int13 handler */ |
| // movl $0x4c, %edi |
| // movl (%edi), %eax |
| // movl %eax, ABS(int13_offset) |
| |
| /* decrease the lower memory size and set it to the BIOS memory */ |
| movl $0x413, %edi |
| movl %edi, %esi |
| |
| lodsw |
| /* KBytes that int13 handler occupies */ |
| subb ABS(int13_handler), %al |
| stosw |
| |
| /* compute the segment(high word) */ |
| shll $(16 + 6), %eax |
| |
| /* the offset(low word) should be 0x100 */ |
| movw $0x100, %ax |
| |
| /* save the new int13 handler */ |
| movl $0x4c, %edi |
| stosl |
| |
| /* EDI points to the destination int13 handler in the reserved area */ |
| movl %eax, %edi /* the int13 vector just saved */ |
| shrl $12, %edi /* get base address of segment */ |
| |
| /* set ESI to the drive map */ |
| movl $ABS(hooked_drive_map), %esi |
| movl $(DRIVE_MAP_SIZE), %ecx |
| 1: |
| cmpb $0xff, 1(%esi) /* Is there a mapped memdrive? */ |
| je 1f /* Yes, hook int15 for e820 memory */ |
| /* try next slot */ |
| addl $DRIVE_MAP_SLOT_SIZE, %esi |
| loop 1b |
| jmp 2f /* no memdrives, don't hook int15 */ |
| |
| 1: |
| /* save the new int15 handler */ |
| movw $(int15_e820_handler - int13_handler), %ax /* segment still in high word */ |
| movl %eax, 0x54 |
| |
| 2: |
| /* copy int13_handler to the reserved area */ |
| movl $ABS(int13_handler), %esi |
| movl $((int13_handler_end - int13_handler) / 4), %ecx |
| cld |
| repz movsl |
| |
| popl %esi |
| popl %edi |
| popl %ebp |
| ret |
| |
| /* int |
| * unset_int13_handler(check_status_only) |
| * |
| * Restore the original int13 handler |
| * |
| * Return 0 for success and non-zero for failure. |
| */ |
| ENTRY(unset_int13_handler) |
| pushl %ebp |
| movl %esp, %ebp |
| |
| pushl %edi |
| |
| /* check if int13_handler is set */ |
| movl $0x413, %edi |
| movw (%edi), %ax |
| cmpw $640, %ax |
| jae 1f #; needn't unset |
| // cmpw $632, %ax |
| // jb 1f |
| shll $(16 + 6), %eax |
| |
| /* the offset(low word) should be 0x100 */ |
| movw $0x100, %ax |
| |
| cmpl %eax, 0x4c |
| jne 1f #; not hooked, unset failure |
| movl %eax, %edi |
| shrl $12, %edi /* segment base address */ |
| |
| cmpl $0x9A000000, 0x1C(%edi) /* old int 13 */ |
| jb 1f |
| |
| cmpl $0x9A000000, 0x0C(%edi) /* old int 15 */ |
| jb 1f |
| |
| movl ABS(int13_offset), %eax |
| cmpl 0x1C(%edi), %eax |
| jnz 1f |
| |
| movl ABS(ROM_int15), %eax |
| cmpl 0x0C(%edi), %eax |
| jnz 1f |
| |
| xorl %eax, %eax |
| |
| cmpl %eax, 8(%ebp) |
| jnz 1f |
| |
| /* increase the lower memory size */ |
| |
| movzbw (%edi), %ax |
| addw %ax, 0x413 |
| |
| /* restore the original int15 handler */ |
| movl ABS(ROM_int15), %eax |
| movl %eax, 0x54 |
| |
| /* restore the original int13 handler */ |
| movl ABS(int13_offset), %eax |
| movl %eax, 0x4c |
| 1: |
| /* return non-zero for failure */ |
| popl %edi |
| popl %ebp |
| ret |
| |
| |
| /* |
| * Map a drive to another drive or a disk image file. |
| */ |
| |
| .code16 |
| |
| /* align it this way so that int13_handler can be used as a segment |
| * base address. The `cdrom' command requires this. |
| */ |
| .align 16 |
| |
| int13_handler: |
| |
| /* memory size in K that int13 handler uses. */ |
| |
| .byte ((int13_handler_end - int13_handler + 0x3ff) / 0x400) |
| |
| /* 9-byte space reserved. */ |
| |
| . = int13_handler + 0x0A |
| |
| VARIABLE(floppies_orig) |
| .byte 0 /* original value at 0040:0010 */ |
| |
| . = int13_handler + 0x0B |
| |
| VARIABLE(harddrives_orig) |
| .byte 0 /* original value at 0040:0075 */ |
| |
| . = int13_handler + 0x0C |
| |
| ROM_int15: |
| .long 0 /* original int15 vector */ |
| |
| . = int13_handler + 0x10 |
| |
| /* 12-byte space reserved. */ |
| |
| . = int13_handler + 0x1C |
| |
| int13_offset: .word 0 /* original int13 vector */ |
| int13_segment: .word 0 |
| |
| . = int13_handler + 0x20 /* drive map table begins at 0x20 */ |
| |
| ENTRY(hooked_drive_map) |
| .space (DRIVE_MAP_SIZE + 1) * DRIVE_MAP_SLOT_SIZE |
| |
| /* 8-byte space reserved. */ |
| |
| . = int13_handler + 0x100 /* real int13 handler entry at 0x100 */ |
| |
| jmp 1f |
| |
| . = int13_handler + 0x103 |
| |
| /* SafeMBRHook structure begin */ |
| |
| .ascii "$INT13SF" /* Win9x use this! Don't touch! */ |
| |
| .ascii "GRUB4DOS" /* 8-byte Vender ID */ |
| |
| . = int13_handler + 0x113 |
| |
| int13_dup: |
| |
| .long 0 |
| |
| . = int13_handler + 0x117 |
| |
| VARIABLE(safe_mbr_hook) |
| |
| .long 0x00000001 /* safe MBR hook flag */ |
| |
| /* SafeMBRHook structure end */ |
| |
| /* But Win9x may expect additional data after SafeMBRHook structure. |
| * This is undocumented, and mysterious. If this area is not what |
| * Win9x expected, Win9x could hang. |
| */ |
| |
| . = int13_handler + 0x11B |
| |
| VARIABLE(int13_scheme) |
| |
| /* bit 0 controls how we access sectors in protected mode. |
| * bit0=0: use pushf and far call for ROM int13 service. |
| * bit0=1: use the `int $0x13' instruction. |
| */ |
| .long 0x00000001 |
| |
| /* space reserved. */ |
| |
| . = int13_handler + 0x140 |
| |
| 1: |
| cmpb $0x1a, %ah /* PS/2 low level format ESDI drive!!!! */ |
| je error_01_disable2 /* disabled in any case */ |
| |
| /* (BP+10): old Flags */ |
| /* (BP+8): return CS */ |
| /* (BP+6): return IP */ |
| cld |
| pushl %eax /* (BP+2): old EAX */ |
| pushw %bp /* (BP+0): old BP */ |
| movw %sp, %bp /* BP->old BP */ |
| |
| /* EDD30 code imported from edd30.asm of Smart Boot Manager. */ |
| |
| /* |
| #; asmsyntax=nasm |
| #; |
| #; CD-ROM Boot Extension v 1.1 for Smart Boot Manager |
| #; |
| #; Copyright (C) 2000, Christopher Li <chrisl@gnuchina.org>. |
| #; Copyright (C) 2000, James Su <suzhe@gnuchina.org> |
| #; |
| #; This is free software, you can redistribute it and/or modify it |
| #; under the terms of the GNU General Public License version 2 or above. |
| #; |
| #; The ATAPI driver is based on the source code of atadrv written by |
| #; Hale Landis <hlandis@ibm.net>, Thanks him a lot! |
| #; |
| #; Without his great program, we could not implement the CD-ROM Boot feature |
| #; so quickly. |
| #; |
| */ |
| |
| |
| //#define EDD_3_0 |
| |
| #define sane_check |
| #define check_extra_fail |
| |
| //#define CB_DATA 0 //; data reg in/out pio_base_addr1+0 |
| //#define CB_FR 1 //; feature reg out pio_base_addr1+1 |
| //#define CB_SC 2 //; sector count in/out pio_base_addr1+2 |
| //#define CB_SN 3 //; sector number in/out pio_base_addr1+3 |
| //#define CB_CL 4 //; cylinder low in/out pio_base_addr1+4 |
| //#define CB_CH 5 //; cylinder high in/out pio_base_addr1+5 |
| //#define CB_DH 6 //; device head in/out pio_base_addr1+6 |
| //#define CB_STAT 7 //; primary status in pio_base_addr1+7 |
| //#define CB_CMD 7 //; command out pio_base_addr1+7 |
| //#define CB_DC 8 //; device control out pio_base_addr2+6 |
| //#define CB_ASTAT 8 //; alternate status in pio_base_addr2+6 |
| //#define CB_DC_SRST 0x04 //; soft reset |
| #define CB_STAT_BSY 0x80 //; busy |
| #define CB_STAT_RDY 0x40 //; ready |
| #define CB_STAT_SKC 0x10 //; seek complete |
| |
| #define CB_STAT_DRQ 0x08 //; data request |
| #define CB_STAT_ERR 0x01 //; error |
| |
| #define CB_SC_P_TAG 0xf8 //; ATAPI tag (mask) |
| #define CB_SC_P_REL 0x04 //; ATAPI release |
| #define CB_SC_P_IO 0x02 //; ATAPI I/O |
| #define CB_SC_P_CD 0x01 //; ATAPI C/D |
| |
| #define FAILBIT8 0x0100 //; SC( CD/IO bits) wrong at end of cmd |
| #define FAILBIT6 0x0040 //; byte count wrong at data packet xfer time |
| #define FAILBIT5 0x0020 //; SC (IO bit) wrong at data packet xfer time |
| #define FAILBIT4 0x0010 //; SC (CD bit) wrong at data packet xfer time |
| #define FAILBIT3 0x0008 //; byte count wrong at cmd packet xfer time |
| #define FAILBIT2 0x0004 //; SC wrong at cmd packet xfer time |
| #define FAILBIT0 0x0001 //; slow setting BSY=1 or DRQ=1 after AO cmd |
| |
| #define CB_DC_HD15 0x08 //; bit should always be set to one |
| #define CB_DC_NIEN 0x02 //; disable interrupts |
| #define cmd_DC CB_DC_HD15 |
| #define cmd_DC_ni CB_DC_HD15 | CB_DC_NIEN |
| |
| #undef DEBUG |
| |
| #ifdef DEBUG |
| #include "debug.h" |
| #endif |
| |
| cmpb $0, %cs:(atapi_dev_count - int13_handler) |
| jz no_cdrom /* no cdrom */ |
| cmpb %cs:(min_cdrom_id - int13_handler), %dl |
| jb not_cdrom_drv /* not cdrom drive */ |
| cmpb %cs:(max_cdrom_id - int13_handler), %dl |
| jbe edd30_for_cdrom |
| |
| not_cdrom_drv: |
| // cmpb %cs:(min_cdrom512_id - int13_handler), %dl |
| // jb not_cdrom512_drv /* not cdrom512 drive */ |
| // cmpb %cs:(max_cdrom512_id - int13_handler), %dl |
| // jbe edd30_for_cdrom512 |
| // |
| //not_cdrom512_drv: |
| cmpb $0, %cs:(emu_disk_type - int13_handler) |
| je not_emu_drv |
| |
| cmpb %cs:(edd30_cdemu_spec - int13_handler + 2), %dl #; struc_cdemu_spec.emu_drvid |
| je cdemu_int13h |
| |
| not_emu_drv: |
| |
| no_cdrom: |
| |
| pushw %si /* BP-2: old SI */ |
| |
| /* set %si to the drive map */ |
| movw $(hooked_drive_map - int13_handler), %si |
| |
| /* find the drive number from the drive map */ |
| subw $DRIVE_MAP_SLOT_SIZE, %sp |
| 1: |
| addw $DRIVE_MAP_SLOT_SIZE, %sp |
| lodsl %cs:(%si), %eax |
| pushl %eax /* BP-6: FROM, TO, Hmax, Smax */ |
| lodsl %cs:(%si), %eax |
| pushl %eax /* BP-10: to_C, to_H, to_S */ |
| lodsl %cs:(%si), %eax |
| pushl %eax /* BP-14: StartLBA_Lo */ |
| lodsl %cs:(%si), %eax |
| pushl %eax /* BP-18: StartLBA_Hi */ |
| lodsl %cs:(%si), %eax |
| pushl %eax /* BP-22: S_count_Lo */ |
| lodsl %cs:(%si), %eax |
| pushl %eax /* BP-26: S_count_Hi */ |
| |
| movl -6(%bp), %eax /* FROM, TO, Hmax, Smax */ |
| |
| /* check if this is the end */ |
| testl %eax, %eax |
| jnz 3f /* not end, continue */ |
| |
| movl -14(%bp), %eax /* StartLBA_Lo */ |
| testl %eax, %eax |
| jnz 3f /* not end, continue */ |
| |
| movl -22(%bp), %eax /* S_count_Lo */ |
| testl %eax, %eax |
| jnz 3f /* not end, continue */ |
| movb %dl, %al /* FROM */ |
| movb %dl, %ah /* TO */ |
| movw %ax, -6(%bp) |
| jmp 2f /* map whole drive to itself signals the end */ |
| 3: |
| /* Now this is a valid drive map slot */ |
| movw -6(%bp), %ax /* AL=FROM, AH=TO */ |
| cmpb %al, %dl /* check if this matches the drive number */ |
| jne 1b /* no, continue to check the next map */ |
| |
| /* yes, found the map corresponding to drive DL */ |
| |
| #; movw -4(%bp), %ax /* AL=Hmax, AH=Smax */ |
| #; andb $0xC1, %ah /* clear bit 5 - bit 1 */ |
| testb $0x80, -7(%bp) /* TO_S */ |
| jnz 1f |
| |
| /* non-zero StartLBA signals emulation */ |
| |
| movl -14(%bp), %eax /* StartLBA_Lo */ |
| testl %eax, %eax |
| jnz drive_emulation |
| |
| /* S_count being not 1 signals emulation */ |
| |
| movl -22(%bp), %eax /* S_count_Lo */ |
| shrl $1, %eax |
| jnz drive_emulation |
| |
| /* now StartLBA=0 and sector count=1(for whole disk) */ |
| |
| /* if sectors per track > 1, this is force geometry screw. */ |
| |
| movw -4(%bp), %ax /* AL=Hmax, AH=Smax */ |
| testb $62, %ah /* Sectors > 1 means force geom, this -- */ |
| jnz drive_emulation /* -- also leads to drive emulation */ |
| |
| /* ignore geom and directly map a whole drive */ |
| |
| 1: |
| /* bit 7 of the TO_S is for in-situ primary partition(alter MBR) */ |
| |
| /* bits of AH: |
| * 7 bit set means readonly/fakewrite |
| * 6 bit set means disable LBA |
| * 5 - 1 bits already cleared(=0) |
| * 0 bit cleared means disable CHS |
| * So, if AH!=1, it is a restricted disk access; |
| * and if AH=1, it is a normal disk access. |
| */ |
| cmpb $1, %ah |
| je 1f |
| // testb $0x3F, %ah |
| // jz 3f |
| // testb $0xC0, %ah |
| // jz 1f |
| //3: |
| call restricted_map |
| 1: |
| /* map a whole drive, normal access */ |
| |
| /* but if --in-situ was used, we should avoid writing the MBR! */ |
| testb $0x80, -7(%bp) /* TO_S */ |
| jz 1f /* not in-situ, allow write */ |
| testb $0x40, -7(%bp) /* TO_S. bit 6 here means safe-boot */ |
| jz 1f /* unsafe-boot, allow write */ |
| movw 2(%bp), %ax /* restore AX */ |
| cmpb $0x03, %ah /* is it CHS write? */ |
| jne 3f |
| |
| /* check if it is a write to MBR, i.e., C/H/S=0/0/1 */ |
| cmpw $0x0001, %cx /* C=0, S=1 */ |
| jne 1f |
| cmpb $0x00, %dh /* H=0 */ |
| jne 1f |
| |
| /* deny the write and end the int 13 call */ |
| call readonly_fakewrite /* NO RETURN!! */ |
| 3: |
| cmpb $0x43, %ah /* is it LBA write? */ |
| jne 1f /* no, continue the normal access */ |
| |
| /* check if it is a write to MBR, i.e., LBA=0 */ |
| |
| xorl %eax, %eax |
| cmpl %eax, 12(%si) /* write to LBA_hi32bits=0? */ |
| jne 1f |
| cmpl %eax, 8(%si) /* write to LBA_lo32bits=0? */ |
| jne 1f |
| |
| /* deny the write and end the int 13 call */ |
| call readonly_fakewrite /* NO RETURN!! */ |
| |
| 1: |
| movw -6(%bp), %ax /* AL=FROM, AH=TO */ |
| movb %ah, %dl /* Let DL access TO instead of FROM */ |
| 2: |
| /* might map to itself, i.e., actually not mapped */ |
| |
| movw -2(%bp), %si /* restore SI */ |
| |
| pushw 10(%bp) /* pushfw, simulate the interrupt call */ |
| |
| movl 2(%bp), %eax /* restore EAX */ |
| movw (%bp), %bp /* restore BP */ |
| |
| // .byte 0x9a /* lcall, simulate the interrupt call */ |
| //int13_offset: .word 0 |
| //int13_segment: .word 0 |
| lcall %cs:*(int13_offset - int13_handler) |
| |
| // /* the original int13 vector must be stored in the first kilo bytes */ |
| // |
| // . = . - (. - int13_handler) / 0x401 |
| |
| pushw %ax /* save AX */ |
| pushfw /* save flags returned by int13 */ |
| popw %ax |
| movw %sp, %bp /* set new BP */ |
| |
| /* BP+38: old Flags */ |
| /* BP+36: return CS */ |
| /* BP+34: return IP */ |
| /* BP+30: old EAX */ |
| /* BP+28: old BP */ |
| /* BP+26: old SI */ |
| /* BP+22: FROM, TO, Hmax, Smax */ |
| /* BP+18: to_C, to_H, to_S */ |
| /* BP+14: StartLBA_Lo */ |
| /* BP+10: StartLBA_Hi */ |
| /* BP+ 6: S_count_Lo */ |
| /* BP+ 2: S_count_Hi */ |
| /* BP+ 0: new AX returned from int 13 */ |
| |
| #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| leaw 28(%bp), %bp // it is addw $28, %bp but won't touch flags |
| |
| /* BP+10: old Flags */ |
| /* BP+ 8: return CS */ |
| /* BP+ 6: return IP */ |
| /* BP+ 2: old EAX */ |
| /* BP+ 0: old BP */ |
| /* BP- 2: old SI */ |
| /* BP- 6: FROM, TO, Hmax, Smax */ |
| /* BP-10: to_C, to_H, to_S */ |
| /* BP-14: StartLBA_Lo */ |
| /* BP-18: StartLBA_Hi */ |
| /* BP-22: S_count_Lo */ |
| /* BP-26: S_count_Hi */ |
| /* BP-28: new AX returned from int 13 */ |
| #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| |
| movw %ax, 10(%bp) /* update the flags in the stack */ |
| |
| /* check if should restore(reversely map) the drive number */ |
| jc 1f /* restore DL on error */ |
| movw 2(%bp), %ax /* lower word of the saved EAX */ |
| |
| /* alter MBR after read */ |
| |
| testb $0x80, -7(%bp) |
| jz 3f |
| |
| cmpb $0x02, %ah |
| je alter_mbr |
| cmpb $0x42, %ah |
| je alter_mbr |
| 3: |
| cmpb $0x08, %ah /* int13 AH=08h, read drive parameters */ |
| jne 2f |
| |
| /* DL==number of drives, should not restore */ |
| pushw %ds |
| xorw %ax, %ax |
| movw %ax, %ds |
| movw -6(%bp), %ax /* AL=FROM, AH=TO */ |
| testb %al, %al /* hard drive? */ |
| movb 0x475, %dl /* DL=number of hard drives */ |
| js 3f /* yes, jump */ |
| //movw $0x10, %bx /* for floppy, refer to function 0x48 */ |
| movb 0x410, %al |
| rorb $1, %al |
| cbw |
| shlb $1, %al |
| shrb $6, %al |
| incw %ax |
| andb %ah, %al |
| movb %al, %dl /* DL=number of floppy drives */ |
| //lesw 0x0078, %di /* point to int 1E floppy parameters */ |
| 3: |
| popw %ds |
| jmp 3f |
| 2: |
| //cmpb $0x15, %ah /* int13 AH=15h, read drive type */ |
| //je 3f /* CX:DX==total sectors, should not restore */ |
| //jne 1f |
| xorb $0x15, %ah /* int13 AH=15h, read drive type */ |
| /* CF=0 for now. if ZF=1, then AH was 0x15, and it is "equal" |
| * for the jxx class of instructions. if ZF=0, then AH was not 0x15, |
| * and it is "above" for the jxx class of instructions. |
| */ |
| 1: |
| movw -6(%bp), %ax /* get the drive mapping */ |
| /* should not simply use "jne 2f" here because ZF is unknown when |
| * error occurred for the above int13 call |
| */ |
| jc 2f /* "below" means error occurred for int13 */ |
| ja 2f /* "above" means it is not int13 AH=15h */ |
| /* "equal" means int13 AH=15h */ |
| testb %al, %al /* the FROM drive is harddrive? */ |
| js 3f /* yes, do not restore DL */ |
| /* restore DL for floppy int13 AH=15h call */ |
| 2: |
| /* try to restore DL if possible */ |
| cmpb %al, %ah /* check if the mapping was performed */ |
| je 3f /* not performed, so need not restore DL */ |
| cmpb %dl, %ah |
| jne 3f /* DL changed by int13, so do not restore */ |
| movb %al, %dl /* restore DL back to the FROM drive */ |
| 3: |
| /* return */ |
| |
| movl 2(%bp), %eax /* restore the original EAX */ |
| popw %ax /* update the int13 status in AX */ |
| movw (%bp), %bp /* restore the original BP */ |
| addw $32, %sp |
| iret |
| |
| alter_mbr: |
| |
| call modify_in_situ |
| |
| movw -6(%bp), %ax /* get the drive mapping */ |
| jmp 2b |
| |
| restricted_map: |
| movw 2(%bp), %ax /* lower word of the saved EAX */ |
| |
| /* CHS read functions */ |
| cmpb $0x02, %ah /* read sectors */ |
| je 2f |
| cmpb $0x04, %ah /* verify sectors, also a read operation */ |
| je 2f |
| cmpb $0x0a, %ah /* read long sectors */ |
| je 2f |
| cmpb $0x0c, %ah /* seek to cylinder */ |
| je 2f |
| cmpb $0x21, %ah /* PS/1 and newer PS/2 - read multiple disk sectors */ |
| jne 1f |
| 2: |
| movb -3(%bp), %ah /* AH=Smax */ |
| testb $63, %ah /* check if Sectors=0, i.e., disable CHS */ |
| jz error_01_disable |
| ret |
| 1: |
| /* CHS write functions */ |
| cmpb $0x03, %ah /* CHS write sectors */ |
| je 2f |
| cmpb $0x05, %ah /* PC/XT/AT/EISA format tracks */ |
| je 2f |
| cmpb $0x06, %ah /* PC/XT format tracks with bad sectors */ |
| je 2f |
| cmpb $0x07, %ah /* PC/XT format multiple cylinders */ |
| je 2f |
| cmpb $0x0b, %ah /* PC/XT/AT/EISA write sectors with ECC */ |
| je 2f |
| cmpb $0x0f, %ah /* PC/XT/PS/1 write sector buffer */ |
| je 2f |
| cmpb $0x22, %ah /* PS/1 and newer PS/2 - write multiple disk sectors */ |
| jne 1f |
| 2: |
| movb -3(%bp), %ah /* AH=Smax */ |
| testb $63, %ah /* check if Sectors=0, i.e., disable CHS */ |
| jz error_01_disable |
| testb $0x80, %ah /* readonly access? */ |
| jnz readonly_fakewrite |
| ret |
| 1: |
| /* LBA read functions */ |
| cmpb $0x41, %ah /* Extensions - INSTALLATION CHECK */ |
| je 2f |
| cmpb $0x42, %ah /* Extensions - EXTENDED READ */ |
| je 2f |
| cmpb $0x44, %ah /* Extensions - verify sectors */ |
| je 2f |
| cmpb $0x45, %ah /* Extensions - LOCK/UNLOCK DRIVE */ |
| je 2f |
| cmpb $0x46, %ah /* Extensions - EJECT MEDIA */ |
| je 2f |
| cmpb $0x47, %ah /* Extensions - EXTENDED SEEK */ |
| je 2f |
| cmpb $0x48, %ah /* Extensions - GET DRIVE PARAMETERS */ |
| je 2f |
| cmpb $0x49, %ah /* Extensions - detect media change */ |
| je 2f |
| cmpb $0x4a, %ah /* Bootable CDROM - INITIATE DISK EMULATION */ |
| je 2f |
| cmpb $0x4b, %ah /* Bootable CDROM - TERMINATE DISK EMULATION */ |
| je 2f |
| cmpb $0x4c, %ah /* Bootable CDROM - INITIATE DISK EMULATION AND BOOT */ |
| je 2f |
| cmpb $0x4d, %ah /* Bootable CDROM - RETURN BOOT CATALOG */ |
| je 2f |
| cmpb $0x4e, %ah /* Extensions v2.1 - SET HARDWARE CONFIGURATION */ |
| jne 1f |
| 2: |
| movb -3(%bp), %ah /* AH=Smax */ |
| testb $64, %ah /* disable LBA? */ |
| jnz error_01_disable |
| ret |
| 1: |
| /* LBA write functions */ |
| cmpb $0x43, %ah /* Extensions - EXTENDED WRITE */ |
| jne 1f |
| 2: |
| movb -3(%bp), %ah /* AH=Smax */ |
| testb $64, %ah /* disable LBA? */ |
| jnz error_01_disable |
| testb $0x80, %ah /* readonly access? */ |
| jnz readonly_fakewrite |
| 1: |
| /* no restrictions, return and continue */ |
| ret |
| |
| error_01_disable: |
| /* function not supported, or the input CHS is invalid */ |
| addw $26, %sp /* adjust SP */ |
| popw %si |
| popw %bp |
| popl %eax |
| error_01_disable2: |
| movb $0x01, %ah /* signal invalid function call */ |
| stc /* signal error */ |
| lret $2 /* far return discard the original flags */ |
| |
| readonly_fakewrite: |
| addw $26, %sp /* adjust SP */ |
| popw %si |
| #;testb $1, -22(%bp) /* lowest bit of S_count_Lo */ |
| testb $0x40, -7(%bp) /* bit 6 of TO_S */ |
| popw %bp |
| popl %eax |
| jnz 1f |
| /* read only */ |
| movb $0x03, %ah /* signal write protection */ |
| stc /* signal error */ |
| lret $2 /* far return discard the original flags */ |
| 1: |
| /* fake write */ |
| xorb %ah, %ah /* signal write succeeded */ |
| /*clc*/ /* signal success, CF already cleared by XOR */ |
| lret $2 /* far return discard the original flags */ |
| |
| |
| drive_emulation: |
| movw -4(%bp), %ax /* AL=Hmax, AH=Smax */ |
| testb $63, %ah /* disable CHS? */ |
| jz 2f /* yes, call restricted map */ |
| testb $0xc0, %ah /* readonly or disable LBA? */ |
| jz 1f |
| 2: |
| call restricted_map |
| 1: |
| movw 2(%bp), %ax /* get original AX */ |
| |
| testb %ah, %ah /* reset disk system, always succeed */ |
| jnz 1f |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x01, %ah /* get status of last operation, always succeed */ |
| jnz 1f |
| xorb %ah, %ah |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x04, %ah /* verify sectors, always succeed */ |
| jnz 1f |
| xorb %ah, %ah |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x09, %ah /* INITIALIZE CONTROLLER WITH DRIVE PARAMETERS */ |
| jnz 1f |
| xorb %ah, %ah |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x0c, %ah /* SEEK TO CYLINDER */ |
| jnz 1f |
| xorb %ah, %ah |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x0d, %ah /* reset hard disks */ |
| jnz 1f |
| xorb %ah, %ah |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x10, %ah /* check if drive ready */ |
| jnz 1f |
| xorb %ah, %ah |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x11, %ah /* recalibrate drive */ |
| jnz 1f |
| xorb %ah, %ah |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x14, %ah /* CONTROLLER INTERNAL DIAGNOSTIC */ |
| jnz 1f |
| xorb %ah, %ah |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x05, %ah /* format track */ |
| jnz 1f |
| xorb %ah, %ah /* do nothing but return success */ |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x08, %ah /* get drive parameters */ |
| jnz 1f |
| pushw %ds |
| xorw %ax, %ax |
| movw %ax, %ds |
| testb %dl, %dl /* hard drive? */ |
| movb 0x475, %dl /* DL=number of hard drives */ |
| js 2f /* yes, jump */ |
| movw $0x10, %bx /* for floppy, refer to function 0x48 */ |
| movb 0x410, %al |
| rorb $1, %al |
| cbw |
| shlb $1, %al |
| shrb $6, %al |
| incw %ax |
| andb %ah, %al |
| xchgw %ax, %dx /* DL=number of floppy drives */ |
| lesw 0x0078, %di /* point to int1E floppy parameters */ |
| 2: |
| popw %ds |
| movw -4(%bp), %ax /* AL=Hmax, AH=Smax */ |
| movb %al, %dh /* max head number */ |
| andb $63, %ah |
| movb %ah, %cl /* max sector number */ |
| |
| ///* simply use 1024 cylinders, hope this would work well */ |
| //orw $0xffc0, %cx /* max cylinder number=1023 */ |
| |
| /* max cylinder number */ |
| |
| pushl %edx |
| pushl %ecx |
| movzbl %dh, %eax |
| movzbl %cl, %ecx |
| incl %eax |
| mull %ecx /* EDX=0, EAX=sectors per cylinder */ |
| xchgl %eax, %ecx |
| |
| movl -22(%bp), %eax /* S_count_Lo */ |
| #;andb $0xfe, %al |
| decl %eax |
| //xorl %edx, %edx |
| divl %ecx /* EAX=max cylinder number */ |
| popl %ecx |
| popl %edx |
| |
| movb %al, %ch /* low 8 bits of cylinder */ |
| shlb $6, %ah /* high 2 bits of cylinder */ |
| orb %ah, %cl |
| |
| //movb $0x08, %dl /* drives=8(HDs=8 as well as FDs=8) */ |
| xorw %ax, %ax |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x15, %ah /* get disk type */ |
| jnz 1f |
| testb %dl, %dl /* hard drive? */ |
| js 2f /* yes, jump */ |
| movb $0x02, %ah /* floppy (or other removable drive) with change-line support */ |
| clc |
| jmp int13_return |
| 2: |
| movb $0x03, %ah /* hard disk */ |
| |
| /* CX:DX=total number of sectors */ |
| movw -22(%bp), %dx /* lo word of S_count_Lo */ |
| movw -20(%bp), %cx /* hi word of S_count_Lo */ |
| #;andb $0xfe, %dl /* clear bit 0 */ |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x16, %ah |
| jnz 1f |
| xorb %ah, %ah /* AH=0 means disk not changed */ |
| /* clc */ /* signal success, CF already cleared by XOR */ |
| jmp int13_return |
| 1: |
| cmpb $0x17, %ah /* set floppy type for format */ |
| jnz 1f |
| movb $0x03, %al /* 1.44M drive, 1.44M floppy */ |
| xorb %ah, %ah |
| /* clc */ /* signal success, CF already cleared by XOR */ |
| jmp int13_return |
| 1: |
| cmpb $0x18, %ah /* set media type for format */ |
| jnz 1f |
| testb %dl, %dl /* hard drive? */ |
| js 2f /* yes, jump */ |
| pushw %ax |
| xorw %ax, %ax |
| movw %ax, %es |
| movw $0x0078, %di |
| movl %es:(%di), %eax |
| movw %ax, %di |
| shrl $0x10, %eax |
| movw %ax, %es |
| popw %ax |
| xorb %ah, %ah |
| /* clc */ /* signal success, CF already cleared by XOR */ |
| jmp int13_return |
| 2: |
| movb $1, %ah /* temp disabled for hard disks */ |
| stc |
| jmp int13_return |
| 1: |
| /* Now AH is neither 0 nor 1 */ |
| testb $0xfc, %ah /* CHS read/write sectors */ |
| jnz 1f |
| /* so AH is either 2(for read) or 3(for write) */ |
| #if 1 |
| cmpw $0x0301, %ax /* is it write 1 sector? */ |
| jne 2f |
| cmpw $0x0001, %cx /* write to cylinder 0, sector 1? */ |
| jne 2f |
| cmpb $0x01, %dh /* write to head 0 or 1? */ |
| ja 2f |
| /* cmpw $0xaa55, %es:0x1fe(%bx) |
| je 2f */ |
| testb %dl, %dl |
| js 3f /* protect hard disk head 0 and 1 */ |
| testb %dh, %dh /* write to floppy head 0? */ |
| jne 2f /* no, write permitted */ |
| 3: |
| #;movb -22(%bp), %ah /* lowest byte of S_count_Lo */ |
| movb -7(%bp), %ah /* TO_S, bit 6 for fake write */ |
| testb $0x40, %ah /* fake write here means safeboot */ |
| jz 2f /* unsafe boot allows the write */ |
| |
| /* disable the write */ |
| addw $24, %sp /* adjust SP */ |
| popw %si |
| popw %bp |
| popl %eax |
| xorb %ah, %ah /* signal write succeeded, this is a fake */ |
| /* clc */ /* signal success, CF already cleared by XOR */ |
| lret $2 /* far return discard the original flags */ |
| |
| #else |
| /* debug only. locate windows 98 unidentified write to floppy */ |
| pushaw |
| movw 8(%bp), %ax |
| movw %ax, %ds |
| movw 6(%bp), %si |
| subw $0x0400, %si |
| movw %bx, %di |
| movw $0x0400, %cx |
| cld |
| rep |
| movsw |
| movw %si, %ax |
| movw %di, %bx |
| subw $4, %di |
| stosw |
| movw %bx, %ax |
| stosw |
| popaw |
| movb $4, %al |
| /*3:*/ |
| /*hlt*/ /* invalid write to floppy, halt the machine */ |
| /*jmp 3b*/ |
| #endif |
| 2: |
| cmpb $0x7F, %al /* check if sectors exceed 127 */ |
| ja error_01_invalid |
| testb %al, %al /* read 0 sectors not allowed */ |
| jz error_01_invalid |
| testb $63, %cl /* beginning sector number 0 is invalid */ |
| jz error_01_invalid |
| movb -3(%bp), %ah /* AH=Smax */ |
| andb $63, %ah |
| pushw %cx |
| andb $63, %cl |
| cmpb %ah, %cl /* CL should not > max sector number */ |
| popw %cx |
| ja error_01_invalid |
| movb -4(%bp), %ah |
| cmpb %ah, %dh /* DH should not > max head number */ |
| ja error_01_invalid |
| pushw %ds |
| pushw %cs |
| popw %ds |
| movw $(EBIOS_disk_address_packet - int13_handler), %si |
| movb %al, 2(%si) /* sectors */ |
| movw %bx, 4(%si) |
| movw %es, 6(%si) |
| xorl %eax, %eax |
| movb %ah, 3(%si) /* sectors_hi_reserved */ |
| movl %eax, 12(%si) |
| movb %ch, %al /* cylinder number */ |
| movb %cl, %ah /* CL holds higher 2 bits */ |
| shrb $6, %ah /* AH lower holds the 2 bits */ |
| /* Now AX holds the 10 bit cylinder number */ |
| pushl %ebx |
| xorl %ebx, %ebx |
| movb -4(%bp), %bl /* max head number */ |
| /* xorb %bh, %bh */ |
| incw %bx /* BX=total heads, <=256 */ |
| pushl %edx |
| mull %ebx /* EDX:EAX=result, so EDX=0 */ |
| popl %edx |
| xorl %ebx, %ebx |
| movb %dh, %bl /* head number */ |
| addl %ebx, %eax |
| movb -3(%bp), %bl /* Max sector number */ |
| andb $63, %bl |
| pushl %edx |
| mull %ebx /* EDX:EAX=result, so EDX=0 */ |
| popl %edx |
| movb %cl, %bl /* sector number */ |
| andb $63, %bl |
| decb %bl |
| addl %ebx, %eax /* EAX=lo 32 bits of logical sector number */ |
| movl %eax, 8(%si) |
| popl %ebx |
| call bound_check |
| testb $0xfe, %ah /* allow all or part of sectors to tranfer */ |
| jnz 3f /* no sectors to transfer, skip */ |
| movl -14(%bp), %eax /* StartLBA_Lo */ |
| addl %eax, 8(%si) |
| setb 12(%si) |
| movb -5(%bp), %dl /* DL=TO_DRIVE */ |
| movb 3(%bp), %ah /* 0x02=read, 0x03=write */ |
| orb $0x40, %ah /* 0x42=EXT_read, 0x43=EXT_write */ |
| #if 1 |
| call real_int13_service |
| #else |
| pushfw |
| lcall *(int13_offset - int13_handler) /* call original int13 */ |
| #endif |
| |
| jc 2f /* on error we needn't use the bound check result */ |
| 4: |
| call modify_boot_sectors |
| popfw /* use bound check info */ |
| pushfw /* dummy push, for the following dummy pop */ |
| jnc 4f |
| movb $0x04, %ah /* sector not found */ |
| 2: |
| popfw /* dummy pop */ |
| stc |
| pushfw |
| 4: |
| movb 2(%si), %al /* number of sectors transferred */ |
| |
| /* It is not safe to use POPW %DS for running in protected mode */ |
| popfw /* dummy pop, skip the pushed flags */ |
| movb -6(%bp), %dl /* restore DL(=FROM_DRIVE) */ |
| 3: |
| popw %ds |
| jmp int13_return |
| 1: |
| cmpb $0x41, %ah /* EBIOS installation check */ |
| jnz 1f |
| cmpw $0x55aa, %bx |
| jnz error_01_invalid |
| // testb %dl, %dl |
| // jns error_01_invalid |
| movw $0xaa55, %bx |
| movb $0x21, %ah /* major version 2.1(EDD-1.1) */ |
| movw $0x01, %cx /* support functions 42h,43h,44h,47h,48h */ |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x42, %ah /* EBIOS read sectors */ |
| jz 2f |
| cmpb $0x43, %ah /* EBIOS write sectors */ |
| jnz 1f |
| 2: |
| movw -2(%bp), %si /* get old SI, disk address packet */ |
| movl (%si), %eax /* packet length, sectors, etc. */ |
| testb %ah, %ah |
| jnz error_01_invalid |
| testb $0xf0, %al |
| jz error_01_invalid |
| shrl $16, %eax |
| testw $0xff80, %ax |
| jnz error_01_invalid |
| call bound_check |
| testb $0xfe, %ah /* allow all or part of sectors to tranfer */ |
| jnz int13_return /* no sectors to transfer, skip */ |
| movl -14(%bp), %eax /* StartLBA_Lo */ |
| addl %eax, 8(%si) |
| jnc 2f |
| incl 12(%si) |
| 2: |
| movb -5(%bp), %dl /* DL=TO_DRIVE */ |
| movb 3(%bp), %ah /* 0x42=read, 0x43=write */ |
| #if 1 |
| call real_int13_service |
| #else |
| pushfw |
| lcall %cs:*(int13_offset - int13_handler) /* call original int13 */ |
| #endif |
| // pushfw |
| // pushl %eax |
| // movl -14(%bp), %eax /* StartLBA_Lo */ |
| // subl %eax, 8(%si) |
| // sbbl $0, 12(%si) |
| // popl %eax |
| // popfw |
| jc 2f /* on error need not use the bound check */ |
| // call modify_boot_sectors |
| popfw /* use bound check info */ |
| pushfw /* dummy push, for the following dummy pop */ |
| jnc 3f |
| movb $0x04, %ah /* sector not found */ |
| 2: |
| popfw /* dummy pop */ |
| stc |
| pushfw |
| 3: |
| #if 0 |
| /* this pair can be omitted */ |
| popfw /* dummy pop */ |
| pushfw |
| #endif |
| pushl %eax |
| movl -14(%bp), %eax /* StartLBA_Lo */ |
| subl %eax, 8(%si) |
| jnc 2f |
| decl 12(%si) |
| 2: |
| popl %eax |
| |
| call modify_boot_sectors |
| |
| movb -6(%bp), %dl /* restore DL(=FROM_DRIVE) */ |
| |
| popfw /* pop the pushed Flags by bound_check */ |
| jmp int13_return |
| 1: |
| cmpb $0x44, %ah /* EBIOS verify sectors */ |
| jnz 1f |
| xorb %ah, %ah |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x47, %ah /* EBIOS seek */ |
| jnz 1f |
| xorb %ah, %ah |
| clc |
| jmp int13_return |
| 1: |
| cmpb $0x48, %ah /* EBIOS GET DRIVE PARAMETERS */ |
| jnz error_01_invalid |
| movw -2(%bp), %si /* get old SI, extended drive parameter table */ |
| movw $26, (%si) /* buffer length */ |
| movw $2, 2(%si) /* info */ |
| xorl %eax, %eax |
| movl %eax, 8(%si) /* total heads */ |
| movl %eax, 12(%si) /* sectors per track */ |
| movl %eax, 16(%si) /* total sectors */ |
| movl %eax, 20(%si) /* hi 32 bits of total sectors */ |
| pushl %ebx |
| xorl %ebx, %ebx |
| movw -4(%bp), %ax /* AL=Hmax, AH=Smax */ |
| andb $63, %ah |
| movb %ah, 12(%si) /* sectors per track */ |
| movb %ah, %bl |
| xorb %ah, %ah |
| incw %ax /* total heads=Hmax+1 */ |
| movw %ax, 8(%si) /* total heads */ |
| pushl %edx |
| mulw %bx /* DX:AX=product, DX=0 */ |
| movw %ax, %bx /* BX=sectors per cylinder */ |
| movl -22(%bp), %eax /* S_count_Lo */ |
| #;andb $0xfe, %al |
| movl %eax, 16(%si) /* total sectors */ |
| xorl %edx, %edx /* EDX:EAX=64bit total sectors */ |
| testw %bx, %bx |
| jz 2f |
| divl %ebx /* EAX=quotient, EDX=residue */ |
| 2: |
| testl %edx, %edx |
| popl %edx |
| popl %ebx |
| jz 2f |
| incl %eax |
| 2: |
| movl %eax, 4(%si) /* total cylinders */ |
| movw $512, 24(%si) /* bytes per sector */ |
| xorb %ah, %ah |
| /*clc*/ /* signal success, CF already cleared by XOR */ |
| jmp int13_return |
| error_01_invalid: |
| movb $0x01, %ah /* unsupported function call */ |
| stc /* signal error */ |
| int13_return: |
| pushw %ax /* save status */ |
| //pushfw |
| //popw %ax |
| lahf /* Load Flags into AH */ |
| movb 10(%bp), %al |
| shrw $1, %ax |
| rolb $1, %al |
| |
| movb %al, 10(%bp) /* update flags in the stack */ |
| movw -2(%bp), %si /* restore the original SI */ |
| movl 2(%bp), %eax /* restore the original EAX */ |
| movw (%bp), %bp /* restore the original BP */ |
| popw %ax /* restore status */ |
| addw $32, %sp /* adjust SP */ |
| iret |
| |
| bound_check: |
| |
| /* |
| * check if the request exceeds the boundary of the emulated disk. |
| * |
| * input: DS:SI |
| * output: AH=0 no restrictions, all sectors transferred |
| * AH=1 sectors transferred, but not all |
| * AH=4 error exit immediately, no sectors transferred |
| * |
| * if S_count=StartLBA=0, then no restrictions |
| * else if 8(%si)>=S_count then error |
| * else if 8(%si)+2(%si)<=S_count then no restrictions |
| * else let 2(%si)=S_count-8(%si),call original int13,signal error |
| * |
| */ |
| movl -14(%bp), %eax /* StartLBA_Lo */ |
| testl %eax, %eax |
| jnz 2f |
| movl -22(%bp), %eax /* S_count_Lo */ |
| shrl $1, %eax |
| jnz 2f |
| clc /* map whole drive, signal no restrictions */ |
| popw %ax /* pop return IP */ |
| pushfw #; push additional Flags!! |
| pushw %ax /* push return IP */ |
| xorb %ah, %ah /* no restrictions, all sectors transferred */ |
| ret |
| 2: |
| movl 12(%si), %eax /* hi 32-bit of the requested StartLBA */ |
| testl %eax, %eax |
| jnz 3f /* non-zero is considered `too big' */ |
| movl -22(%bp), %eax /* S_count_Lo */ |
| #;andb $0xfe, %al |
| cmpl %eax, 8(%si) /* lo 32-bit of the requested StartLBA */ |
| jb 2f |
| 3: |
| /* all sectors exceed the bound, exit immediately */ |
| #; Return WITHOUT additional Flags pushed!! |
| |
| movb $4, %ah |
| xorb %al, %al /* no sectors transferred */ |
| movb %al, 2(%si) /* SI+2 retrieve sectors transferred */ |
| stc |
| ret |
| 2: |
| subl 8(%si), %eax |
| pushl %ebx |
| xorl %ebx, %ebx |
| movw 2(%si), %bx |
| cmpl %eax, %ebx |
| popl %ebx |
| ja 2f |
| clc /* signal no restrictions */ |
| popw %ax /* pop return IP */ |
| pushfw #; push additional Flags!! |
| pushw %ax /* push return IP */ |
| xorb %ah, %ah /* no restrictions, all sectors transferred */ |
| ret |
| 2: |
| movb %al, 2(%si) /* SI+2 retrieve sectors transferred */ |
| stc |
| popw %ax /* pop return IP */ |
| pushfw #; push additional Flags!! |
| pushw %ax /* push return IP */ |
| movb $1, %ah /* not all sectors tranferred */ |
| ret |
| |
| real_int13_service: |
| /* AH = 0x42 or 0x43 */ |
| |
| cmpb $0xff, %dl /* mem drive */ |
| jne 2f |
| cmpb $0x42, %ah |
| je 3f |
| cmpb $0x43, %ah |
| jne 5f |
| /* DS:SI points to disk address packet */ |
| /* 2(%si), byte, sectors(1-127) */ |
| /* 4(%si), word, offset */ |
| /* 6(%si), word, segment */ |
| /* 8(%si), qword, lba */ |
| 3: |
| /* AH = 0x42 or 0x43 */ |
| pushw %es |
| pushal |
| sahf /* Store AH into flags SF ZF xx AF xx PF xx CF */ |
| pushf |
| movzwl 4(%si), %ebx #; BX=offset, EBX_high_word=0 |
| movzwl 6(%si), %eax #; AX=segment |
| movl 8(%si), %ecx #; ECX=LBA of MEMDRIVE SECTOR |
| shll $9, %ecx #; ECX=linear address of SECTOR(above 1M) |
| shll $4, %eax #; EAX=linear base address of segment |
| addl %ebx, %eax #; EAX=linear address of BUFFER(below 1M) |
| popf |
| jc 4f /* write */ |
| xchgl %eax, %ecx |
| 4: |
| cmpl $0, %cs:(memdisk_raw - int13_handler) |
| je 4f /* do not use raw mode */ |
| |
| /* raw mode as in memdisk, contributed by Bean */ |
| |
| /* Data transfer: %eax -> %ecx, number of sector 2(%si) */ |
| |
| // pushw %ax |
| smsw %bx |
| testb $1, %bl |
| // popw %ax |
| jnz 4f /* protected mode */ |
| |
| /* switch to protected mode myself */ |
| /* ebx destroy */ |
| |
| pushfl |
| pushw %ds |
| pushw %es |
| |
| movl %ecx, %edi |
| movzbl 2(%si), %ecx #; ECX=number of sectors to transfer |
| movl %eax, %esi #; ESI changed!! |
| |
| cli |
| |
| #;xorl %ebx, %ebx |
| movw %cs, %bx #; EBX_high_word=0 |
| shll $4, %ebx |
| addl $(MyGDT - int13_handler), %ebx |
| movl %ebx, %cs:MyGDT - int13_handler + 2 |
| |
| /* Test to see if A20 is enabled or not */ |
| xorw %ax, %ax |
| movw %ax, %ds /* DS=0 */ |
| decw %ax |
| movw %ax, %es /* ES=0xFFFF */ |
| |
| movw %ds:0, %ax |
| movw %ax, %bx |
| xorw %es:0x10, %bx |
| notw %ax |
| movw %ax, %ds:0 |
| movw %ax, %dx |
| xorw %es:0x10, %dx |
| notw %ax |
| movw %ax, %ds:0 |
| |
| orw %bx, %dx |
| pushw %dx /* 0=A20 off, 1=A20 on */ |
| jnz 1f |
| movb $1, %dl /* A20 is off, so it need turn on. */ |
| call int13_enable_a20 |
| 1: |
| |
| shll $7, %ecx /* 128 dwords per sector */ |
| |
| lgdt %cs:MyGDT - int13_handler |
| movl %cr0, %eax |
| orb $1, %al |
| movl %eax, %cr0 /* Switch to protected mode */ |
| |
| movw $16, %bx /* Switch to 4G data segment */ |
| movw %bx, %ds |
| movw %bx, %es |
| |
| cld |
| |
| addr32 rep movsl |
| |
| // movw $8, %bx /* Switch back to 64K data segment */ |
| // movw %bx, %ds |
| // movw %bx, %es |
| |
| andb $0xFE, %al |
| movl %eax, %cr0 /* Back to real mode */ |
| |
| popw %dx |
| popw %es |
| popw %ds |
| |
| /* Keep A20 on. This should hurt nothing. */ |
| #if 0 |
| /* Disable A20 if necessary ! */ |
| andw %dx, %dx /* 0=orig A20 off, 1=orig A20 on */ |
| jnz 1f |
| call int13_enable_a20 |
| 1: |
| #endif |
| |
| popfl |
| |
| popal |
| popw %es |
| xorb %ah, %ah |
| ret |
| 4: |
| movb 2(%si), %bl /* number of sectors to be moved */ |
| pushw %cs |
| popw %es |
| movw $(GDT_data - int13_handler), %si /* SI changed!! */ |
| movw %ax, %es:0x12(%si) #; source physical address low 16 bits |
| shrl $16, %eax |
| movb %al, %es:0x14(%si) #; source physical address bit 16-23 |
| movb %ah, %es:0x17(%si) #; source physical address bit 24-32 |
| movw %cx, %es:0x1a(%si) #; dest physical address low 16 bits |
| shrl $16, %ecx |
| movb %cl, %es:0x1c(%si) #; dest physical address bit 16-23 |
| movb %ch, %es:0x1f(%si) #; dest physical address bit 24-32 |
| |
| xorw %cx, %cx /* ECX is 0 */ |
| movl %ecx, %es:(%si) |
| movl %ecx, %es:0x04(%si) |
| movl %ecx, %es:0x08(%si) |
| movl %ecx, %es:0x0c(%si) |
| movl %ecx, %es:0x20(%si) |
| movl %ecx, %es:0x24(%si) |
| movl %ecx, %es:0x28(%si) |
| movl %ecx, %es:0x2c(%si) |
| movb %bl, %ch /* CX=number of words to be moved */ |
| movb $0x87, %ah /* access extended memory */ |
| |
| pushw %ax |
| smsw %ax |
| testb $1, %al |
| popw %ax |
| jnz 4f /* protected mode */ |
| #if 0 |
| pushw %ax |
| pushw %ds |
| xorw %ax, %ax |
| movw %ax, %ds |
| movl 0x0054, %eax /* point to int15 vector */ |
| popw %ds |
| cmpl %eax, %cs:(ROM_int15 - int13_handler) |
| popw %ax |
| je 4f |
| /* real mode with int15 vector changed */ |
| #endif |
| /* real mode */ |
| pushfw |
| lcall %cs:*(ROM_int15 - int13_handler) |
| |
| #if 0 |
| /* ensure A20 is on when we return to caller. */ |
| |
| /* A20 control is a slow operation. Enabling A20 here in int13 handler |
| * could cause problems on some machines. |
| */ |
| |
| pushfw |
| |
| cli /* yes, keep interrupt off when controlling A20 */ |
| |
| movw $0x00ff, %cx # try so many times on failure |
| movw $0x0001, %dx # non-zero means `enable' |
| call enable_disable_a20 |
| //sete %dl # DL=1 means success |
| |
| popfw |
| #endif |
| |
| jmp 6f |
| 4: |
| ///* protected mode, or real mode with int15 vector unchanged */ |
| /* protected mode */ |
| int $0x15 |
| 6: |
| popal |
| popw %es |
| jc 6f |
| xorb %ah, %ah |
| ret |
| 6: |
| movb $0, 2(%si) /* signal no sectors successfully moved */ |
| 5: |
| /* sector not found */ |
| movb $4, %ah |
| stc |
| ret |
| 2: |
| /* normal disk drive */ |
| /* AH = 0x42 or 0x43 */ |
| #if 1 |
| pushw %ax |
| smsw %ax |
| testb $1, %al |
| popw %ax |
| jz real_mode_ROM_int13 |
| #else |
| pushw %ds |
| pushw %si |
| xorw %si, %si |
| movw %si, %ds /* DS=SI=0 */ |
| /* DS:[SI+0x4C] points to int 13 vector */ |
| ldsw 0x4C(%si), %si /* new DS:SI=int 13 vector, pointing to */ |
| /* int 13 service routine */ |
| cmpb $0x63, (%si) /* win 98 places ARPL here */ |
| popw %si |
| popw %ds |
| jne real_mode_ROM_int13 |
| #endif |
| /* protected mode(Windows 98, EMM386) */ |
| |
| #;/* now inside win 98, will call protected mode int 13 */ |
| |
| #if 0 |
| /* debug code to test the win98 behavior */ |
| |
| /* disable the disk access in protected mode. */ |
| testw $0x400, -10(%bp) /* to_C */ |
| jnz 1f |
| movb $0, 2(%si) |
| movb $2, %ah /* I/O error */ |
| stc |
| ret |
| 1: |
| #endif |
| pushw %si /* save SI */ |
| pushl %eax /* save EAX */ |
| pushl %ebx /* save EBX */ |
| |
| /* set SI to the drive map */ |
| movw $(hooked_drive_map - int13_handler), %si |
| /* find the drive number from the drive map */ |
| cld |
| subw $DRIVE_MAP_SLOT_SIZE - 4, %si |
| 2: |
| addw $DRIVE_MAP_SLOT_SIZE - 4, %si |
| lodsl %cs:(%si), %eax |
| testl %eax, %eax /* end of map table? */ |
| movl %eax, %ebx /* save the map to EBX */ |
| jz 2f /* yes, no map found */ |
| cmpb %dl, %ah /* found the map? */ |
| jne 2b /* no, check the next slot */ |
| |
| /* drive is mapped. check if map a whole drive */ |
| shrl $16, %eax |
| testb $62, %ah |
| jnz 2b /* no, check the next slot */ |
| movl %cs:4(%si), %eax /* StartLBA_Lo */ |
| testl %eax, %eax |
| jnz 2b /* no, check the next slot */ |
| movl %cs:12(%si), %eax /* S_count_Lo */ |
| shrl $1, %eax |
| jnz 2b /* no, check the next slot */ |
| 2: |
| testl %ebx, %ebx /* mapped or not mapped ? */ |
| jz 2f /* not mapped, do nothing */ |
| movb %bl, %dl /* use the mapped FROM_DRIVE for win 98 */ |
| 2: |
| movb -6(%bp), %al /* AL=FROM_DRIVE */ |
| testb %al, %al /* hard drive emulation? */ |
| jns 2f /* floppy, jump */ |
| cmpb %al, %dl |
| jb 2f |
| incb %dl |
| 2: |
| popl %ebx |
| popl %eax |
| popw %si |
| #if 0 |
| /* This does not work, although it seems it should work. */ |
| int $0x13 /* call win 98 protected-mode int 13 */ |
| ret |
| #else |
| /* This works on win98, but not tested under emm386. */ |
| // pushfw |
| // lcall %cs:*(int13_offset - int13_handler) |
| // ret |
| |
| /* so we can just fall through to real mode... */ |
| #endif |
| |
| real_mode_ROM_int13: |
| |
| /* in real mode DOS, call original real mode int13 */ |
| /* DS:SI points to disk address packet */ |
| /* 2(%si), byte, sectors(1-127) */ |
| /* 4(%si), word, offset */ |
| /* 6(%si), word, segment */ |
| /* 8(%si), qword, lba */ |
| #if 0 |
| /* debug code to test the win98 behavior */ |
| |
| /* disable the disk access in real mode. */ |
| testw $0x400, -10(%bp) /* to_C */ |
| jnz 1f |
| movb $0, 2(%si) |
| movb $2, %ah /* I/O error */ |
| stc |
| ret |
| 1: |
| #endif |
| pushw %ax /* 0x42 or 0x43 */ |
| |
| /* DS:SI is so important! Save it just in case it is destroyed by |
| * a buggy BIOS. |
| */ |
| |
| #;jmp 1f /* test the CHS translation code */ |
| |
| pushw %ds |
| pushw %si |
| pushw %ax |
| smsw %ax |
| testb $1, %al |
| popw %ax |
| jz 4f |
| |
| /* pmode */ |
| testb $1, %cs:(int13_scheme - int13_handler) |
| jz 4f |
| int $0x13 |
| //pushfw |
| //lcall %cs:*(int13_dup - int13_handler) |
| ////lcall $0, $0x4C |
| |
| jmp 2f |
| 4: |
| /* real mode */ |
| |
| pushfw |
| lcall %cs:*(int13_dup - int13_handler) |
| 2: |
| popw %si |
| popw %ds |
| |
| jc 1f |
| testb %ah, %ah |
| jnz 1f |
| addw $2, %sp /* discard AX on the stack */ |
| /* CF=0 */ |
| ret |
| 1: |
| /* try again using CHS mode */ |
| |
| /* byte at SI+2: number of sectors to access */ |
| /* 8 bytes at SI+8: StartLBA */ |
| |
| /* CHS mode cannot access large addresses */ |
| movl 12(%si), %eax |
| testl %eax, %eax |
| jnz 1f |
| |
| pushl %edx |
| pushl %ebx |
| movw -10(%bp), %ax /* to_C */ |
| andw $0x3FF, %ax /* get lo 10 bits */ |
| incw %ax /* cylinders */ |
| pushw %ax |
| movzbw -8(%bp), %ax /* to_H */ |
| incw %ax /* heads */ |
| movzbw -7(%bp), %bx /* to_S */ |
| andb $0x3F, %bl |
| mulw %bx /* DX=0, AX=heads*sectors */ |
| popw %bx /* cylinders */ |
| mulw %bx /* DX:AX=total sectors in drive */ |
| pushw %dx |
| pushw %ax |
| movzbl 2(%si), %eax |
| addl 8(%si), %eax /* last sector number + 1 */ |
| popl %edx /* total sectors in drive */ |
| cmpl %edx, %eax |
| popl %ebx |
| popl %edx |
| jnb 1f |
| |
| /* all requested sectors can be accessed by CHS */ |
| |
| /* we will access one sector at a time */ |
| |
| popw %ax /* AH=0x42 or 0x43 */ |
| pushw %ax /* AH=0x42 or 0x43 */ |
| |
| andb $0x0F, %ah /* AH=0x02 0r 0x03 */ |
| movb %ah, %cs:(rw_self_mod + 2 - int13_handler) |
| |
| pushw %es |
| pushw %bx |
| pushw %cx |
| pushl %edi |
| |
| movzbw 2(%si), %cx /* sectors to transfer */ |
| movl 8(%si), %edi /* start sector number(LBA) */ |
| 3: |
| /* translate LBA to CHS */ |
| |
| pushw %cx |
| |
| /* get sectors per cylinder */ |
| |
| pushw %dx |
| |
| pushl %edi /* lba */ |
| movzbw -8(%bp), %ax /* to_H */ |
| incw %ax /* heads */ |
| movzbw -7(%bp), %bx /* to_S */ |
| andb $0x3F, %bl |
| mulw %bx /* DX=0, AX=sectors per cylinder */ |
| popw %cx /* lba_lo */ |
| xchgw %ax, %cx /* CX=sectors per cylinder, AX=lba_lo */ |
| popw %dx /* DX:AX=lba */ |
| |
| divw %cx /* AX=cylinder number, DX=rem */ |
| xchgw %ax, %dx /* DX=cylinder number, AX=rem */ |
| |
| movb %dl, %ch /* CH=lo 8 bits of cylinder */ |
| |
| divb %bl /* AL=head number, AH=sector number - 1 */ |
| movb %ah, %cl |
| incw %cx /* CL=sector number */ |
| shlb $6, %dh /* hi 2 bits of cylinder */ |
| orb %dh, %cl |
| |
| popw %dx /* DL=drive number */ |
| pushw %dx |
| |
| movb %al, %dh /* DH=head number */ |
| |
| pushl %edi |
| |
| movw %di, %ax |
| subw 8(%si), %di /* sectors already read */ |
| shlw $5, %di /* paragraghs */ |
| addw 6(%si), %di |
| movw 4(%si), %bx |
| movw %bx, %ax |
| shrw $4, %ax |
| andw $0x000F, %bx |
| addw %di, %ax /* segment */ |
| movw %ax, %es |
| |
| rw_self_mod: |
| movw $0x0201, %ax |
| |
| /* DS:SI is so important! Save it just in case it is destroyed by |
| * a buggy BIOS. |
| */ |
| pushw %ds |
| pushw %si |
| pushw %ax |
| smsw %ax |
| testb $1, %al |
| popw %ax |
| jz 4f |
| |
| /* pmode */ |
| testb $1, %cs:(int13_scheme - int13_handler) |
| jz 4f |
| int $0x13 |
| //pushfw |
| //lcall %cs:*(int13_dup - int13_handler) |
| ////lcall $0, $0x4C |
| |
| jmp 2f |
| 4: |
| /* real mode */ |
| |
| pushfw |
| lcall %cs:*(int13_dup - int13_handler) |
| 2: |
| popw %si |
| popw %ds |
| |
| popl %edi |
| popw %dx |
| |
| incl %edi /* next sector */ |
| |
| popw %cx |
| jc 3f |
| testb %ah, %ah |
| jnz 3f |
| loop 3b |
| |
| popl %edi |
| popw %cx |
| popw %bx |
| popw %es |
| popw %ax |
| xorb %ah, %ah /* success */ |
| ret |
| 3: |
| popl %edi |
| popw %cx |
| popw %bx |
| popw %es |
| popw %ax |
| movb $0, 2(%si) |
| movb $2, %ah /* I/O error */ |
| stc |
| ret |
| |
| 1: |
| popw %ax |
| movb $0, 2(%si) |
| movb $4, %ah /* sector not found */ |
| stc |
| ret |
| |
| modify_boot_sectors: |
| |
| pushw %es |
| pushal |
| movw 2(%bp), %ax /* get original AX */ |
| /* check CHS read */ |
| cmpb $0x02, %ah /* is it read? */ |
| jne 3f |
| cmpw $0x0001, %cx /* read from cylinder 0, sector 1? */ |
| je 5f |
| 3: |
| /* check LBA read */ |
| cmpb $0x42, %ah /* is it extended read? */ |
| jne 4f |
| cmpl $0, 12(%si) /* read from LBA_high=0? */ |
| jne 4f |
| movb $0, %dh /* simulate CHS read of head 0 */ |
| cmpl $0, 8(%si) /* read from LBA_low=0? */ |
| je 3f |
| movzbl -3(%bp), %eax /* AL=Smax */ |
| andb $63, %al |
| movb $1, %dh /* simulate CHS read of head 1 */ |
| cmpl %eax, 8(%si) /* read from LBA_low=sectors per track? */ |
| jne 4f |
| 3: |
| movw 4(%si), %bx /* simulate CHS read buffer offset */ |
| movw 6(%si), %es /* simulate CHS read buffer segment */ |
| 5: |
| #; testb %dl, %dl /* The TO_DRIVE is hard drive? */ |
| #; jns 4f /* no, do nothing */ |
| cmpw $0xaa55, %es:0x1fe(%bx) |
| jne 4f |
| movl -22(%bp), %eax /* S_count_Lo */ |
| shrl $1, %eax |
| jz 4f /* map a whole drive, nothing need to change */ |
| movl -14(%bp), %eax /* StartLBA_Lo */ |
| testl %eax, %eax |
| jz 4f /* geometry translation only, needn't change */ |
| movl -6(%bp), %eax /* FROM_DRIVE, TO_DRIVE, H, S */ |
| testb %al, %al /* The FROM_DRIVE is hard drive? */ |
| jns modify_floppy /* no, goto floppy boot record modification */ |
| testb %dh, %dh /* read from head 0? */ |
| jnz modify_HD_DOS /* no, goto HD DOS boot record modification */ |
| |
| /* we have read an MBR, and we need to modify the partition table */ |
| |
| /* the partition table could be an extended partition table. if so, |
| * we need to turn it to be a primary partition table. |
| */ |
| |
| /* if all start_sectors are not sectors_per_track, then it is not |
| * an extended partition table. |
| */ |
| |
| shrl $16, %eax /* AL=MaxH, AH=MaxS */ |
| andb $63, %ah /* AH=MaxS */ |
| // cmpb $1, %ah |
| // jbe 4f /* do not modify partition table when disable CHS mode */ |
| |
| movzbl %ah, %eax |
| movw $0x1c6, %si /* SI=0x1c6 */ |
| cmpl %eax, %es:(%bx, %si) |
| jz 5f |
| addw $16, %si /* SI=0x1d6 */ |
| cmpl %eax, %es:(%bx, %si) |
| jz 5f |
| addw $16, %si /* SI=0x1e6 */ |
| cmpl %eax, %es:(%bx, %si) |
| jz 5f |
| addw $16, %si /* SI=0x1f6 */ |
| cmpl %eax, %es:(%bx, %si) |
| jz 5f |
| 3: |
| jmp 4f /* not an extended partition table */ |
| 5: |
| /* check if it is a primary partition table */ |
| |
| /* a primary partition table entry should have C/H/S=0/1/1 */ |
| |
| movl %es:-8(%bx, %si), %eax |
| shrl $8, %eax |
| cmpl $0x000101, %eax /* 0x00=C, 0x01=S, 0x01=H */ |
| je 3b /* yes, it is primary partition table */ |
| |
| /* now we are sure this is an extended partition table */ |
| |
| /* compose a master boot record routine */ |
| |
| cld |
| #if 1 |
| movw %bx, %di |
| movb $0xFA, %al /* cli */ |
| stosb |
| movl $0xD08EC033, %eax /* xor AX,AX; mov SS,AX */ |
| stosl |
| movl $0xFB7C00BC, %eax /* mov SP,7C00 ; sti */ |
| stosl |
| movl $0x07501F50, %eax /* push AX; pop DS; push AX; pop ES */ |
| stosl |
| movl $0x7C1CBEFC, %eax /* cld; mov SI,7C1C */ |
| stosl |
| movl $0x50061CBF, %eax /* mov DI,061C ; push AX */ |
| stosl |
| movl $0x01E4B957, %eax /* push DI ; mov CX, 01E4 */ |
| stosl |
| movl $0x1ECBA4F3, %eax /* repz movsb; retf; push DS */ |
| stosl |
| movl $0x537C00BB, %eax /* mov BX,7C00 ; push BX */ |
| stosl |
| movl $0x520180BA, %eax /* mov DX,0180 ; push DX */ |
| stosl |
| movl $0x530201B8, %eax /* mov AX,0201 ; push BX */ |
| stosl |
| movl $0x5F13CD41, %eax /* inc CX; int 13; pop DI */ |
| stosl |
| movl $0x5607BEBE, %eax /* mov SI,07BE ; push SI */ |
| stosl |
| movl $0xCBFA5A5D, %eax /* pop BP; pop DX; cli; retf */ |
| stosl |
| #endif |
| |
| /* empty all other entries except this one by 2 steps: */ |
| |
| /* step 1. move this entry onto the first one (overwrite it) */ |
| movl %es:-8(%bx, %si), %eax |
| movl %eax, %es:0x1be(%bx) |
| movl %es:-4(%bx, %si), %eax |
| movl %eax, %es:0x1c2(%bx) |
| movl %es:(%bx, %si), %eax |
| movl %eax, %es:0x1c6(%bx) |
| movl %es:4(%bx, %si), %eax |
| movl %eax, %es:0x1ca(%bx) |
| |
| #if 1 |
| /* step 2. empty the last 3 entries */ |
| xorw %ax, %ax |
| movw %bx, %di |
| addw $0x1ce, %di |
| movw $24, %cx |
| repz stosw /* DI=BX+0x1fe */ |
| #else |
| xorw %ax, %ax |
| movw %bx, %di |
| addw $0x1d1, %di |
| stosw /* DI=BX+0x1d3 */ |
| addw $2, %di /* DI=BX+0x1d5 */ |
| stosb /* DI=BX+0x1d6 */ |
| addw $0x28, %di /* DI=BX+0x1fe */ |
| #endif |
| |
| /* modify the start_CHS of the first entry */ |
| subw $0x40, %di /* DI=BX+0x1be */ |
| movb $0x80, %al /* set boot indicator */ |
| stosb /* DI=BX+0x1bf */ |
| movb $0x01, %al /* AX=1 */ |
| stosb /* DI=BX+0x1c0, H=0x01 */ |
| stosw /* DI=BX+0x1c2, S=0x01, C=0x00 */ |
| |
| #if 0 |
| movb $0x0B, %al |
| stosb /* DI=BX+0x1c3 */ |
| #else |
| /* modify the end_CHS of the first entry */ |
| incw %di /* DI=BX+0x1c3 */ |
| #endif |
| |
| /* get total sectors */ |
| movl %es:7(%di), %eax /* DI+7=BX+0x1ca */ |
| |
| /* calculate the end sector number */ |
| addl %es:3(%di), %eax /* DI+3=BX+0x1c6 */ |
| //decl %eax |
| |
| pushl %eax /* EAX-1=end sector number */ |
| movzwl -4(%bp), %eax /* H, S */ |
| andb $63, %ah |
| movzbl %ah, %ecx |
| mulb %cl |
| addw %cx, %ax /* EAX=sectors per cylinder */ |
| pushl %eax /* EAX=sectors per cylinder */ |
| movw $1024, %cx |
| mull %ecx /* EAX=CHS addressible total sectors */ |
| /* EDX=0 */ |
| popl %edx /* EDX=sectors per cylinder */ |
| popl %ecx /* ECX-1=end sector number */ |
| cmpl %eax, %ecx |
| jb 5f |
| |
| /* assign max end sector number */ |
| movl %eax, %ecx |
| //subl $0x3EC1, %ecx /* XXX: 0x3EC1=255*63 */ |
| 5: |
| pushl %ecx /* ECX-1=end sector number */ |
| movl %edx, %ecx /* ECX=sectors per cylinder */ |
| xorl %edx, %edx |
| popl %eax /* EAX-1=end sector number */ |
| decl %eax |
| |
| divl %ecx /* EAX=cylinder number */ |
| /* EDX=sector number in the last cylinder */ |
| /* EAX hi=0, EDX hi=0 */ |
| xchgw %ax, %cx /* CX=cylinder number */ |
| xchgw %ax, %dx /* AX=sector number in the last cylinder */ |
| movb -3(%bp), %dl /* S */ |
| andb $63, %dl |
| divb %dl /* AL=head number, AH=sector number - 1 */ |
| stosb /* DI=BX+0x1c4 */ |
| movb %ah, %al |
| incw %ax /* AL=sector number */ |
| movb %cl, %ah /* cylinder lo 8 bits */ |
| shlb $6, %ch /* cylinder hi 2 bits */ |
| orb %ch, %al |
| //movb $0, %ah /* XXX: let cylinder lo 8 bits=0 */ |
| stosw /* DI=BX+0x1c6 */ |
| |
| 4: |
| /* end partition table modification */ |
| popal |
| popw %es |
| ret |
| |
| modify_floppy: |
| |
| /* AL=FROM_DRIVE is the floppy drive number. */ |
| |
| cmpb $0x00, %dh /* read from head 0? */ |
| jne 4b |
| #; xorl %eax, %eax |
| #; cmpl %eax, %es:0x1c(%bx) /* Number of hidden sectors */ |
| #; je 4b |
| #; cmpl $0x33544146, %es:0x52(%bx) /* FAT32? */ |
| #; je 5f |
| #; /* NTFS drive number is also at offset 0x24 */ |
| #; //cmpl $0x31544146, %es:0x36(%bx) /* FAT16? */ |
| #; //jne 4b |
| #; cmpw $0x80, %es:0x24(%bx) /* Physical drive number */ |
| #; jne 4b |
| #; movb %al, %es:0x24(%bx) /* AL=floppy drive number */ |
| #; jmp 6f |
| #;5: |
| #; cmpb $0x80, %es:0x40(%bx) /* Physical drive number */ |
| #; jne 4b |
| #; movb %al, %es:0x40(%bx) /* AL=0 means floppy */ |
| #;6: |
| #; movl %eax, %es:0x1c(%bx) /* let number of hidden sectors=0 */ |
| #; /*movb $0xf0, %es:0x15(%bx)*/ /* set floppy media descriptor */ |
| #; jmp 4b |
| |
| pushl %ecx |
| pushw %si |
| |
| /* FAT12/FAT16/NTFS drive number is at offset 0x24. */ |
| movw $0x24, %si |
| |
| xorl %ecx, %ecx |
| |
| /* check if it is FAT32. */ |
| |
| /* FAT32 should have 0 root_dir_entries and total_sectors_short. */ |
| cmpl %ecx, %es:0x11(%bx) |
| jne 5f /* not FAT32 */ |
| |
| /* FAT32 should have 0 sectors_per_fat. */ |
| cmpw %cx, %es:0x16(%bx) |
| jne 5f /* not FAT32 */ |
| |
| /* FAT32 should have non-zero total_sectors_long. */ |
| cmpl %ecx, %es:0x20(%bx) |
| je 5f /* not FAT32 */ |
| |
| /* FAT32 should have non-zero sectors_per_fat32. */ |
| cmpl %ecx, %es:0x24(%bx) |
| je 5f /* not FAT32 */ |
| |
| /* Now it is FAT32, and the drive number is at offset 0x40. */ |
| movw $0x40, %si |
| 5: |
| movb %al, %es:(%bx, %si) /* modify the boot drive number. */ |
| |
| movl %ecx, %es:0x1c(%bx) /* let number of hidden sectors=0 */ |
| /*movb $0xf0, %es:0x15(%bx)*/ /* set floppy media descriptor */ |
| |
| popw %si |
| popl %ecx |
| |
| jmp 4b |
| |
| modify_HD_DOS: |
| |
| // cmpb $0x42, 3(%bp) /* is extended read? */ |
| // je 4b |
| cmpb $0x01, %dh /* read from head 1? */ |
| jne 4b |
| movl -14(%bp), %eax /* StartLBA_Lo */ |
| testl %eax, %eax |
| jz 4b |
| cmpl %eax, %es:0x1c(%bx) /* Number of hidden sectors */ |
| jbe 4b |
| subl %eax, %es:0x1c(%bx) |
| movzbl -3(%bp), %eax /* AL=Smax */ |
| andb $63, %al |
| cmpl %eax, %es:0x1c(%bx) /* Number of hidden sectors */ |
| jnb 4b |
| movl %eax, %es:0x1c(%bx) |
| jmp 4b |
| |
| modify_in_situ: |
| |
| pushw %es |
| pushal |
| //movw 2(%bp), %ax /* get original AX */ |
| |
| /* if FROM is not harddrive, do nothing. */ |
| testb $0x80, -6(%bp) /* FROM drive */ |
| jz 4f |
| |
| cmpb $0x02, %ah /* is it CHS read? */ |
| jne 3f |
| |
| /* AL=sectors read */ |
| |
| /* translate CHS to LBA */ |
| |
| movw %dx, %di /* save DX to DI */ |
| movw %cx, %si /* save CX to SI */ |
| |
| movb -7(%bp), %al /* TO_S */ |
| andb $63, %al |
| movzbw %al, %dx |
| mulb -8(%bp) |
| addw %dx, %ax /* AX=sectors_per_cylinder */ |
| |
| /* get current cylinder number */ |
| |
| shrb $6, %cl |
| xchgb %cl, %ch |
| |
| mulw %cx /* DX:AX=sectors */ |
| |
| pushw %dx |
| pushw %ax |
| |
| /* restore DX */ |
| |
| movw %di, %dx |
| |
| /* get current head number */ |
| |
| movb -7(%bp), %al /* TO_S */ |
| andb $63, %al |
| mulb %dh /* AX=sectors */ |
| popl %edx |
| movzwl %ax, %eax |
| addl %edx, %eax |
| |
| movw %si, %cx /* restore CX */ |
| andb $0x3F, %cl |
| decw %cx |
| movzbl %cl, %ecx |
| addl %ecx, %eax /* EAX=start sector number */ |
| movzbl 2(%bp), %ecx /* sectors read */ |
| jmp 6f |
| |
| 3: |
| cmpb $0x42, %ah /* is it LBA read? */ |
| jne 4f |
| cmpl $0, 12(%si) /* read from LBA_high=0? */ |
| jne 4f |
| movl 8(%si), %eax /* start sector number */ |
| movzbl 2(%si), %ecx /* sectors read */ |
| movw 4(%si), %bx /* simulate CHS read buffer offset */ |
| movw 6(%si), %es /* simulate CHS read buffer segment */ |
| 6: |
| #; testb %dl, %dl /* The TO_DRIVE is hard drive? */ |
| #; jns 4f /* no, do nothing */ |
| /* ECX=sectors read */ |
| cmpl $0, %eax /* start sector number 0 is for MBR */ |
| je 5f |
| |
| /* if EAX <= startLBA_Lo < EAX + ECX, then the boot sector is read. */ |
| |
| cmpl -14(%bp), %eax /* startLBA_Lo */ |
| ja 4f /* boot sector was not read */ |
| addl %eax, %ecx |
| cmpl -14(%bp), %ecx |
| jbe 4f /* boot sector was not read */ |
| |
| movl -14(%bp), %ecx |
| subl %eax, %ecx /* sectors between ES:BX and boot record */ |
| |
| /* modify hidden sectors of the partition boot record */ |
| cmpb $0x07, -10(%bp) /* NTFS */ |
| je 3f |
| cmpb $0x0C, -10(%bp) /* FAT32(LBA) */ |
| je 3f |
| cmpb $0x0E, -10(%bp) /* FAT12/16 */ |
| je 3f |
| cmpb $0x83, -10(%bp) /* EXT2 */ |
| jne 4f |
| 3: |
| movw %es, %ax |
| shlw $5, %cx /* 1 sector is 32 paragraphs */ |
| addw %cx, %ax |
| movw %ax, %es |
| cmpw $0xaa55, %es:0x1fe(%bx) |
| jne 4f |
| movl -14(%bp), %eax /* startLBA_Lo */ |
| movl %eax, %es:0x1c(%bx) |
| |
| jmp 4f |
| 5: |
| /* Modify partition table. Note that there are 4 partition entries. |
| * The first one is called entry 0, and the last is entry 3. */ |
| |
| cmpw $0xaa55, %es:0x1fe(%bx) |
| jne 4f |
| |
| leaw 0x1be(%bx), %di /* DI=BX+0x1be */ |
| |
| movw %di, %si |
| |
| /* if the first entry is empty, we simply put our new entry on it. */ |
| |
| cmpb $0, %es:4(%si) /* consider partition type 00 as empty */ |
| jz 5f |
| testb $63, %es:2(%si) /* invalid start sector number of 0 */ |
| jz 5f |
| testb $63, %es:6(%si) /* invalid end sector number of 0 */ |
| jz 5f |
| cmpl $0, %es:8(%si) /* invalid start LBA of 0 */ |
| jz 5f |
| cmpl $0, %es:12(%si) /* invalid sector count of 0 */ |
| jz 5f |
| |
| /* Now that the first entry is not empty, we should find an empty one |
| * in entries 1, 2, 3(the last 3 partition entries). */ |
| |
| 7: |
| addw $0x10, %si /* SI=BX+0x1ce or BX+0x1de or BX+0x1ee */ |
| |
| cmpb $0, %es:4(%si) /* consider partition type 00 as empty */ |
| jz 7f |
| testb $63, %es:2(%si) /* invalid start sector number of 0 */ |
| jz 7f |
| testb $63, %es:6(%si) /* invalid end sector number of 0 */ |
| jz 7f |
| cmpl $0, %es:8(%si) /* invalid start LBA of 0 */ |
| jz 7f |
| cmpl $0, %es:12(%si) /* invalid sector count of 0 */ |
| jz 7f |
| |
| leaw 0x1ee(%bx), %bx |
| cmpw %bx, %si |
| leaw -0x1ee(%bx), %bx |
| jb 7b /* try next entry */ |
| |
| /* Now SI=BX+0x1ee points to the last entry */ |
| |
| 7: |
| /* SI points to empty entry(or the last entry), we move old entry 0 |
| * onto this one. |
| */ |
| |
| movl %es:(%di), %eax |
| movl %eax, %es:(%si) |
| movl %es:4(%di), %eax |
| movl %eax, %es:4(%si) |
| movl %es:8(%di), %eax |
| movl %eax, %es:8(%si) |
| movl %es:12(%di), %eax |
| movl %eax, %es:12(%si) |
| |
| 5: |
| /* build our new entry 0 */ |
| |
| /* DI=BX+0x1be */ |
| |
| movl -14(%bp), %eax /* startLBA_Lo */ |
| |
| call lba_to_chs |
| |
| movb $0x80, %al /* set boot indicator */ |
| //movb $0x00, %al /* clear boot indicator */ |
| stosl /* DI=BX+0x1c2 */ |
| |
| movl -22(%bp), %eax |
| addl -14(%bp), %eax |
| decl %eax /* endLBA */ |
| |
| call lba_to_chs |
| |
| /* if in situ, TO_C holds the partition type: |
| * 0x07(NTFS), 0x0C(FAT32), 0x0E(FAT12/16), 0x83(EXT2/3) |
| */ |
| movb -10(%bp), %al /* TO_C */ |
| stosl /* DI=BX+0x1c6 */ |
| |
| /* modify start sector and sector count */ |
| |
| movl -14(%bp), %eax |
| stosl /* DI=BX+0x1ca */ |
| movl -22(%bp), %eax |
| stosl /* DI=BX+0x1ce */ |
| |
| #if 1 |
| /* clear other boot indicators in the last 3 entries */ |
| |
| movb $0, %al /* DI=BX+0x1ce */ |
| stosb /* DI=BX+0x1cf */ |
| |
| leaw 0x1de(%bx), %di /* DI=BX+0x1de */ |
| stosb /* DI=BX+0x1df */ |
| |
| leaw 0x1ee(%bx), %di /* DI=BX+0x1ee */ |
| stosb /* DI=BX+0x1ef */ |
| #endif |
| |
| //#if 0 |
| // /* hide entry 3 (test only) */ |
| // orb $0x10, %es:0x1f2(%bx) |
| //#endif |
| |
| #if 0 |
| /* move extended partition entries to last */ |
| |
| cmpb $0x0F, %es:0x1f2(%bx) |
| je 4f /* entry 3 is extended, do nothing */ |
| |
| cmpb $0x05, %es:0x1f2(%bx) |
| je 4f /* entry 3 is extended, do nothing */ |
| |
| cmpb $0x0F, %es:0x1e2(%bx) |
| je 7f /* entry 2 is extended */ |
| |
| cmpb $0x05, %es:0x1e2(%bx) |
| je 7f /* entry 2 is extended */ |
| |
| /* both entry 2 and entry 3 are not extended */ |
| |
| cmpb $0x0F, %es:0x1d2(%bx) |
| je 8f /* entry 1 is extended */ |
| |
| cmpb $0x05, %es:0x1d2(%bx) |
| jne 4f /* all entries 1, 2, 3 are not extended, do nothing */ |
| 8: |
| /* entry 1 is extended, but entry 2 and entry 3 are not extended. |
| * so exchange entry 1 and entry 3. */ |
| |
| xchgl %eax, %es:0x1ce(%bx) |
| xchgl %eax, %es:0x1ee(%bx) |
| xchgl %eax, %es:0x1ce(%bx) |
| |
| xchgl %eax, %es:0x1d2(%bx) |
| xchgl %eax, %es:0x1f2(%bx) |
| xchgl %eax, %es:0x1d2(%bx) |
| |
| xchgl %eax, %es:0x1d6(%bx) |
| xchgl %eax, %es:0x1f6(%bx) |
| xchgl %eax, %es:0x1d6(%bx) |
| |
| xchgl %eax, %es:0x1da(%bx) |
| xchgl %eax, %es:0x1fa(%bx) |
| xchgl %eax, %es:0x1da(%bx) |
| |
| jmp 4f |
| |
| 7: |
| /* entry 2 is extended, but entry 3 is not. if entry 1 is extended, |
| * we exchange entry 1 and 3, else, we exchange entry 2 and 3. */ |
| */ |
| |
| cmpb $0x0F, %es:0x1d2(%bx) |
| je 8b /* entry 1 is extended */ |
| |
| cmpb $0x05, %es:0x1d2(%bx) |
| je 8b /* entry 1 is extended */ |
| |
| /* exchange entry 2 and 3 */ |
| xchgl %eax, %es:0x1de(%bx) |
| xchgl %eax, %es:0x1ee(%bx) |
| xchgl %eax, %es:0x1de(%bx) |
| |
| xchgl %eax, %es:0x1e2(%bx) |
| xchgl %eax, %es:0x1f2(%bx) |
| xchgl %eax, %es:0x1e2(%bx) |
| |
| xchgl %eax, %es:0x1e6(%bx) |
| xchgl %eax, %es:0x1f6(%bx) |
| xchgl %eax, %es:0x1e6(%bx) |
| |
| xchgl %eax, %es:0x1ea(%bx) |
| xchgl %eax, %es:0x1fa(%bx) |
| xchgl %eax, %es:0x1ea(%bx) |
| #endif |
| |
| // /* empty the last 3 entries */ |
| // xorw %ax, %ax |
| // movw %bx, %di |
| // addw $0x1ce, %di |
| // movw $24, %cx |
| // repz stosw /* DI=BX+0x1fe */ |
| |
| 4: |
| /* end partition table and BPB modification */ |
| popal |
| popw %es |
| ret |
| |
| |
| lba_to_chs: |
| |
| /* input: |
| * |
| * EAX: LBA |
| * |
| * output: |
| * |
| * EAX: CHS |
| * |
| * |
| * |
| */ |
| |
| pushl %eax /* EAX=end sector number */ |
| movzwl -8(%bp), %eax /* TO_H, TO_S */ |
| andb $63, %ah |
| movzbl %ah, %ecx |
| mulb %cl |
| addw %cx, %ax /* EAX=sectors per cylinder */ |
| pushl %eax /* EAX=sectors per cylinder */ |
| movw $1024, %cx |
| mull %ecx /* EAX=CHS addressible total sectors */ |
| /* EDX=0 */ |
| popl %edx /* EDX=sectors per cylinder */ |
| popl %ecx /* ECX=end sector number */ |
| decl %eax |
| cmpl %eax, %ecx |
| jb 5f |
| |
| /* assign max end sector number */ |
| movl %eax, %ecx |
| //subl $0x3EC1, %ecx /* XXX: 0x3EC1=255*63 */ |
| 5: |
| pushl %ecx /* ECX=end sector number */ |
| movl %edx, %ecx /* ECX=sectors per cylinder */ |
| xorl %edx, %edx |
| popl %eax /* EAX=end sector number */ |
| |
| divl %ecx /* EAX=cylinder number */ |
| /* EDX=sector number in the last cylinder */ |
| /* EAX hi=0, EDX hi=0 */ |
| xchgw %ax, %cx /* CX=cylinder number */ |
| xchgw %ax, %dx /* AX=sector number in the last cylinder */ |
| movb -7(%bp), %dl /* TO_S */ |
| andb $63, %dl |
| divb %dl /* AL=head number, AH=sector number - 1 */ |
| //stosb /* DI=BX+0x1c4 */ |
| |
| xchgb %ah, %al /* AH=head number, AL=sector number - 1 */ |
| |
| pushw %ax |
| |
| //movb %ah, %al |
| incw %ax /* AL=sector number */ |
| movb %cl, %ah /* cylinder lo 8 bits */ |
| shlb $6, %ch /* cylinder hi 2 bits */ |
| orb %ch, %al |
| //movb $0, %ah /* XXX: let cylinder lo 8 bits=0 */ |
| //stosw /* DI=BX+0x1c6 */ |
| shll $16, %eax |
| |
| popw %ax |
| |
| ret |
| |
| ENTRY(atapi_dev_count) .long 0 |
| ENTRY(min_cdrom_id) .long 0xE0 |
| max_cdrom_id: .long 0xE0 |
| ENTRY(memdisk_raw) .long 1 /* set to 0 if accessing memdrives using int15/AH=87h */ |
| |
| minimum_mem_lo_in_map: |
| .long 0 /* min ram drive base below 16M */ |
| minimum_mem_hi_in_map: |
| .long 0 /* min ram drive base above 16M */ |
| |
| int15_e820_handler: |
| |
| /* Comments are mostly gotten from Ralf Brown's Interrupt List. */ |
| |
| cmpw $0xe820, %ax //cmpl $0x0000e820, %eax |
| jne 1f |
| |
| /* Newer BIOSes - GET SYSTEM MEMORY MAP */ |
| |
| //AX = E820h |
| //EAX = 0000E820h |
| //EDX = 534D4150h ('SMAP') |
| //EBX = continuation value or 00000000h to start at beginning of map |
| //ECX = size of buffer for result, in bytes (should be >= 20 bytes) |
| //ES:DI -> buffer for result |
| |
| //Return: |
| //CF clear if successful |
| //EAX = 534D4150h ('SMAP') |
| //ES:DI buffer filled |
| //EBX = next offset from which to copy or 00000000h if all done |
| //ECX = actual length returned in bytes |
| //CF set on error |
| //AH = error code (86h) |
| |
| /* Notes: Originally introduced with the Phoenix BIOS v4.0, this |
| * function is now supported by most newer BIOSes, since various |
| * versions of Windows call it to find out about the system memory. |
| * A maximum of 20 bytes will be transferred at one time, even if ECX |
| * is higher; some BIOSes (e.g. Award Modular BIOS v4.50PG) ignore the |
| * value of ECX on entry, and always copy 20 bytes. Some BIOSes expect |
| * the high word of EAX to be clear on entry, i.e. EAX=0000E820h. If |
| * this function is not supported, an application should fall back to |
| * AX=E802h, AX=E801h, and then AH=88h. The BIOS is permitted to return |
| * a nonzero continuation value in EBX and indicate that the end of the |
| * list has already been reached by returning with CF set on the next |
| * iteration. This function will return base memory and ISA/PCI memory |
| * contiguous with base memory as normal memory ranges; it will |
| * indicate chipset-defined address holes which are not in use and |
| * motherboard memory-mapped devices, and all occurrences of the system |
| * BIOS as reserved; standard PC address ranges will not be reported |
| */ |
| |
| //Format of Phoenix BIOS system memory map address range descriptor: |
| // |
| //Offset Size Description |
| //------ ----- ---------------------- |
| // 00h QWORD base address |
| // 08h QWORD length in bytes |
| // 10h DWORD type of address range |
| |
| //Values for System Memory Map address type: |
| //01h memory, available to OS |
| //02h reserved, not available (e.g. system ROM, memory-mapped device) |
| //03h ACPI Reclaim Memory (usable by OS after reading ACPI tables) |
| //04h ACPI NVS Memory (OS is required to save this memory between NVS sessions) |
| //other not defined yet -- treat as Reserved |
| |
| cmpl $0x534D4150, %edx /* "SMAP" */ |
| jne 1f |
| cmpl $20, %ecx |
| jb 2f |
| |
| pushfw |
| lcall %cs:*(ROM_int15 - int13_handler) |
| |
| jc 3f |
| cmpl $0x534D4150, %eax /* "SMAP" */ |
| jne 2f |
| pushal |
| movl %es:4(%di), %eax /* BaseAddrHigh */ |
| testl %eax, %eax |
| jnz 4f |
| movl %es:16(%di), %eax /* Type */ |
| decl %eax /* 1=usable memory, available to the operating system */ |
| jnz 4f |
| /* set %si to the drive map */ |
| movw $(hooked_drive_map - int13_handler), %si |
| movw $(DRIVE_MAP_SIZE), %cx |
| 6: |
| cmpb $0xff, %cs:1(%si) |
| jne 5f |
| movl %cs:8(%si), %ebx /* start_sector */ |
| shll $9, %ebx |
| movl %es:4(%di), %eax /* BaseAddrHigh */ |
| testl %eax, %eax |
| jnz 5f |
| cmpl %es:(%di), %ebx /* BaseAddrLow */ |
| jb 5f |
| movl %es:12(%di), %eax /* LengthHigh */ |
| testl %eax, %eax |
| jnz 7f |
| movl %es:8(%di), %eax /* LengthLow */ |
| addl %es:(%di), %eax |
| jc 7f |
| cmpl %eax, %ebx |
| jnb 5f |
| 7: |
| subl %es:(%di), %ebx /* new length */ |
| movl %ebx, %es:8(%di) /* LengthLow */ |
| xorl %ebx, %ebx |
| movl %ebx, %es:12(%di) /* LengthHigh */ |
| //jmp 4f |
| |
| 5: |
| /* try next slot */ |
| addw $DRIVE_MAP_SLOT_SIZE, %si |
| loop 6b |
| //memory block length update done |
| 4: |
| popal |
| clc |
| lret $2 |
| |
| 2: |
| movb $0x86, %ah /* function not supported */ |
| 3: |
| stc |
| lret $2 |
| #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 1: |
| cmpw $0xe801, %ax //cmpl $0x0000e801, %eax |
| je 1f |
| cmpw $0xe881, %ax //cmpl $0x0000e881, %eax |
| je 1f |
| cmpw $0xda88, %ax |
| je 1f |
| cmpb $0xc7, %ah |
| je 1f |
| cmpb $0x8a, %ah |
| je 1f |
| cmpb $0x88, %ah |
| je 1f |
| ljmp %cs:*(ROM_int15 - int13_handler) |
| #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 1: |
| |
| pushal |
| |
| /* find minimum mem ever used in the drive map slots */ |
| |
| movl $-1, %eax /* 0xffffffff */ |
| movl %eax, %cs:(minimum_mem_hi_in_map - int13_handler) |
| movl $0x1000000, %cs:(minimum_mem_lo_in_map - int13_handler) |
| /* set %si to the drive map */ |
| movw $(hooked_drive_map - int13_handler), %si |
| movw $(DRIVE_MAP_SIZE), %cx |
| 6: |
| cmpb $0xff, %cs:1(%si) |
| jne 5f |
| movl %cs:8(%si), %ebx /* StartLBA_Lo */ |
| shll $9, %ebx |
| |
| cmpl $0x1000000, %ebx /* 16M */ |
| jb 7f |
| /* hi mem */ |
| cmpl %ebx, %cs:(minimum_mem_hi_in_map - int13_handler) |
| jbe 5f |
| movl %ebx, %cs:(minimum_mem_hi_in_map - int13_handler) |
| jmp 5f |
| 7: |
| /* lo mem */ |
| cmpl %ebx, %cs:(minimum_mem_lo_in_map - int13_handler) |
| jbe 5f |
| movl %ebx, %cs:(minimum_mem_lo_in_map - int13_handler) |
| 5: |
| /* try next slot */ |
| addw $DRIVE_MAP_SLOT_SIZE, %si |
| loop 6b |
| |
| popal |
| |
| cmpl $-1, %cs:(minimum_mem_hi_in_map - int13_handler) |
| jne 1f |
| cmpl $0x1000000, %cs:(minimum_mem_lo_in_map - int13_handler) |
| jne 1f |
| |
| ljmp %cs:*(ROM_int15 - int13_handler) |
| |
| #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 1: |
| cmpw $0xe801, %ax //cmpl $0x0000e801, %eax |
| jne 1f |
| |
| /* Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS */ |
| |
| //AX = E801h |
| |
| //Return: |
| //CF clear if successful |
| //AX = extended memory between 1M and 16M, in K (max 3C00h = 15MB) |
| //BX = extended memory above 16M, in 64K blocks |
| //CX = configured memory 1M to 16M, in K |
| //DX = configured memory above 16M, in 64K blocks |
| //CF set on error |
| |
| /* Notes: Supported by the A03 level (6/14/94) and later XPS P90 |
| * BIOSes, as well as the Compaq Contura, 3/8/93 DESKPRO/i, and |
| * 7/26/93 LTE Lite 386 ROM BIOS. Supported by AMI BIOSes dated |
| * 8/23/94 or later. On some systems, the BIOS returns AX=BX=0000h; |
| * in this case, use CX and DX instead of AX and BX. This interface |
| * is used by Windows NT 3.1, OS/2 v2.11/2.20, and is used as a |
| * fall-back by newer versions if AX=E820h is not supported. This |
| * function is not used by MS-DOS 6.0 HIMEM.SYS when an EISA machine |
| * (for example with parameter /EISA) (see also MEM F000h:FFD9h), or |
| * no Compaq machine was detected, or parameter /NOABOVE16 was given. |
| */ |
| |
| //movw $0x3c00, %ax /* 1-16M mem in K (0x3c00KB = 15MB) */ |
| //movw $0x0300, %bx /* mem above 16M in 64K blocks */ |
| 4: |
| pushfw |
| |
| pushfw |
| lcall %cs:*(ROM_int15 - int13_handler) |
| |
| jc 3f |
| |
| pushl %eax |
| |
| /* between 16M and 4G, we modify BX, DX */ |
| |
| movl %cs:(minimum_mem_hi_in_map - int13_handler), %eax |
| cmpl $-1, %eax /* 4G - 1 */ |
| je 5f |
| subl $0x1000000, %eax /* 16M */ |
| shrl $16, %eax /* AX=mem above 16M in 64K blocks */ |
| movw %ax, %bx |
| movw %ax, %dx |
| popl %eax |
| popfw |
| pushfw |
| pushl %eax |
| jnc 5f |
| movzwl %bx, %ebx |
| movzwl %dx, %edx |
| 5: |
| popl %eax |
| pushl %eax |
| |
| /* between 1M and 16M, we modify AX, CX */ |
| |
| movl %cs:(minimum_mem_lo_in_map - int13_handler), %eax |
| cmpl $0x1000000, %eax /* 16M */ |
| je 5f |
| subl $0x0100000, %eax /* 1M */ |
| shrl $10, %eax /* AX=1-16M mem in K */ |
| movw %ax, %cx |
| popl %eax |
| movw %cx, %ax |
| popfw |
| pushfw |
| pushl %eax |
| jnc 5f |
| popl %eax |
| movzwl %ax, %eax |
| pushl %eax |
| movzwl %cx, %ecx |
| 5: |
| |
| popl %eax |
| |
| popfw |
| clc |
| lret $2 |
| 3: |
| popfw |
| stc |
| lret $2 |
| |
| #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 1: |
| cmpw $0xe881, %ax //cmpl $0x0000e881, %eax |
| jne 1f |
| |
| /* Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS (32-bit) */ |
| |
| //AX = E881h |
| |
| //Return: |
| //CF clear if successful |
| //EAX = extended memory between 1M and 16M, in K (max 3C00h = 15MB) |
| //EBX = extended memory above 16M, in 64K blocks |
| //ECX = configured memory 1M to 16M, in K |
| //EDX = configured memory above 16M, in 64K blocks |
| //CF set on error |
| |
| /* Notes: Supported by AMI BIOSes dated 8/23/94 or later. This |
| * interface is used by Windows NT 3.1, OS/2 v2.11/2.20, and is used |
| * as a fall-back by newer versions if AX=E820h is not supported |
| */ |
| |
| //movl $0x3c00, %eax /* 1-16M mem in K (0x3c00 = 15MB) */ |
| //movl $0x0300, %ebx /* mem above 16M in 64K blocks */ |
| |
| stc |
| jmp 4b |
| #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 1: |
| cmpw $0xda88, %ax |
| jne 1f |
| |
| /* AMI PCI BIOS - GET EXTENDED MEMORY SIZE */ |
| |
| //AX = DA88h |
| |
| //Return: |
| //CF clear (successful) |
| //AX = 0000h |
| //CL:BX = extended memory size in KBytes |
| |
| /* 63M = 3f00000 Bytes = 0xfc00 K */ |
| //movw $0xfc00, %bx |
| |
| #; pushfw |
| #; lcall %cs:*(ROM_int15 - int13_handler) |
| #; |
| #; jc 3f |
| |
| pushl %eax |
| |
| movl %cs:(minimum_mem_lo_in_map - int13_handler), %eax |
| cmpl $0x1000000, %eax /* 16M */ |
| jb 5f |
| movl %cs:(minimum_mem_hi_in_map - int13_handler), %eax |
| 5: |
| subl $0x0100000, %eax |
| shrl $10, %eax /* EAX=extened mem in K */ |
| xchgw %ax, %bx |
| shrl $16, %eax |
| movb %al, %cl |
| |
| popl %eax |
| |
| xorw %ax, %ax |
| clc |
| lret $2 |
| #;3: |
| #; stc |
| #; lret $2 |
| |
| #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 1: |
| cmpb $0xC7, %ah |
| jne 1f |
| |
| /* SYSTEM - later PS/2s - RETURN MEMORY-MAP INFORMATION */ |
| |
| //AH = C7h |
| //DS:SI -> user supplied memory map table |
| |
| //Return: |
| //CF set on error |
| //CF clear if successful |
| |
| /* Notes: Call AH=C0h and examine bit 4 of feature byte 2 to check if |
| * this function is supported. IBM classifies this function as |
| * optional. Windows95 OSR2 reportedly does not support this function |
| * even though INT 15/AH=C0h indicates that it is available (observed |
| * on several different systems) |
| */ |
| |
| //Format of memory-map table structure: |
| // |
| //Offset Size Description |
| //------ ----- -------------------------------------------------------------- |
| // 00h WORD length of table (excluding this word) |
| // 02h DWORD local memory between 1M and 16M, in 1K blocks |
| // 06h DWORD local memory between 16M and 4G, in 1K blocks |
| // 0Ah DWORD system memory between 1M and 16M, in 1K blocks |
| // 0Eh DWORD system memory between 16M and 4G, in 1K blocks |
| // 12h DWORD cacheable memory between 1M and 16M, in 1K blocks |
| // 16h DWORD cacheable memory between 16M and 4G, in 1K blocks |
| // 1Ah DWORD 1K blocks before start of non-system memory between 1M and 16M |
| // 1Eh DWORD 1K blocks before start of non-system memory between 16M and 4G |
| // 22h WORD start segment of largest free block from C0000h-DFFFFh |
| // 24h WORD size of largest free block |
| // 26h DWORD reserved |
| |
| //movw $0x0028, (%si) |
| pushw %ds |
| pushw %si |
| |
| pushfw |
| lcall %cs:*(ROM_int15 - int13_handler) |
| |
| popw %si |
| popw %ds |
| |
| jc 3f |
| |
| pushl %eax |
| |
| /* between 16M and 4G */ |
| |
| movl %cs:(minimum_mem_hi_in_map - int13_handler), %eax |
| cmpl $-1, %eax /* 4G - 1 */ |
| je 5f |
| subl $0x1000000, %eax /* 16M */ |
| shrl $10, %eax /* AX=mem above 16M in 1K blocks */ |
| movl %eax, 0x0e(%si) |
| movl %eax, 0x1e(%si) |
| 5: |
| |
| /* between 1M and 16M */ |
| |
| movl %cs:(minimum_mem_lo_in_map - int13_handler), %eax |
| cmpl $0x1000000, %eax /* 16M */ |
| je 5f |
| subl $0x0100000, %eax /* 1M */ |
| shrl $10, %eax /* AX=1-16M mem in K */ |
| movl %eax, 0x0a(%si) |
| movl %eax, 0x1a(%si) |
| 5: |
| |
| popl %eax |
| |
| clc |
| lret $2 |
| 3: |
| stc |
| lret $2 |
| |
| #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 1: |
| cmpb $0x8a, %ah |
| jne 1f |
| |
| /* Phoenix BIOS v4.0 - GET BIG MEMORY SIZE */ |
| |
| //AH = 8Ah |
| |
| //Return: |
| //DX:AX = extended memory size in K |
| //movw $0xfc00, %ax |
| |
| #; pushfw |
| #; lcall %cs:*(ROM_int15 - int13_handler) |
| #; |
| #; jc 3f |
| |
| pushl %eax |
| |
| movl %cs:(minimum_mem_lo_in_map - int13_handler), %eax |
| cmpl $0x1000000, %eax /* 16M */ |
| jb 5f |
| movl %cs:(minimum_mem_hi_in_map - int13_handler), %eax |
| 5: |
| subl $0x0100000, %eax |
| shrl $10, %eax /* EAX=extened mem in K */ |
| popw %dx |
| pushw %ax |
| shrl $16, %eax |
| xchgw %ax, %dx |
| |
| popl %eax |
| |
| clc |
| lret $2 |
| #;3: |
| #; stc |
| #; lret $2 |
| |
| #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 1: |
| cmpb $0x88, %ah |
| jne 1f |
| |
| /* SYSTEM - GET EXTENDED MEMORY SIZE (286+) */ |
| |
| //AH = 88h |
| |
| //Return: |
| //CF clear on success(Not all BIOSes correctly return the carry flag) |
| //AX = number of contiguous KB starting at absolute address 100000h |
| //CF set on error |
| //AH = status |
| // 80h invalid command (PC,PCjr) |
| // 86h unsupported function (XT,PS30) |
| //movw $0xfc00, %ax |
| |
| #; pushfw |
| #; lcall %cs:*(ROM_int15 - int13_handler) |
| #; |
| #; jc 3f |
| |
| pushl %eax |
| |
| movl %cs:(minimum_mem_lo_in_map - int13_handler), %eax |
| cmpl $0x1000000, %eax /* 16M */ |
| jb 5f |
| movl %cs:(minimum_mem_hi_in_map - int13_handler), %eax |
| 5: |
| subl $0x0100000, %eax |
| shrl $10, %eax /* EAX=extened mem in K */ |
| |
| cmpl $0x10000, %eax |
| jb 5f |
| movw $0xFFFF, %ax |
| 5: |
| addw $2, %sp |
| pushw %ax |
| popl %eax |
| |
| clc |
| lret $2 |
| #;3: |
| #; stc |
| #; lret $2 |
| |
| 1: |
| ljmp %cs:*(ROM_int15 - int13_handler) |
| |
| #include "a20.inc" |
| |
| // Input: |
| // dl=0 - Disable A20 |
| // dl=1 - Eanble A20 |
| |
| |
| int13_enable_a20: |
| movw $0x2400,%ax |
| addb %dl, %al |
| stc |
| pushfw |
| lcall %cs:*(ROM_int15 - int13_handler) |
| jnc int13_a20_gloop2ret |
| |
| call int13_a20_gloop1 |
| |
| movb $KC_CMD_WOUT, %al |
| outb $K_CMD |
| |
| int13_a20_gloopint1: |
| inb $K_STATUS |
| andb $K_IBUF_FUL, %al |
| jnz int13_a20_gloopint1 |
| |
| movb $KB_OUTPUT_MASK, %al |
| |
| orb %dl, %dl |
| jz int13_a20_gdoit |
| orb $KB_A20_ENABLE, %al |
| |
| int13_a20_gdoit: |
| outb $K_RDWR |
| |
| call int13_a20_gloop1 |
| |
| /* output a dummy command (USB keyboard hack) */ |
| movb $0xff, %al |
| outb $K_CMD |
| call int13_a20_gloop1 |
| |
| jmp int13_a20_gloop2ret |
| |
| int13_a20_gloop1: |
| inb $K_STATUS |
| andb $K_IBUF_FUL, %al |
| jnz int13_a20_gloop1 |
| |
| int13_a20_gloop2: |
| inb $K_STATUS |
| andb $K_OBUF_FUL, %al |
| jz int13_a20_gloop2ret |
| inb $K_RDWR |
| jmp int13_a20_gloop2 |
| |
| int13_a20_gloop2ret: |
| ret |
| |
| |
| __reg_select_dev: |
| #; ax = dev |
| #; simple version of the select dev |
| addb $0x0A, %al #; AL= 0x0A or 0x0B |
| shlb $4, %al #; AL= 0xA0 or 0xB0 |
| movw reg_addr - int13_handler + (6 * 2), %dx #; CB_DH = drive select |
| outb %al, %dx |
| |
| call delay400ns |
| ret |
| |
| |
| reg_select_dev: |
| #; input: ax = bx = dev |
| #; set_timeout first |
| // movw %ax, %bx |
| cmpb $2, (reg_dev_info - int13_handler)(%bx) # REG_CONFIG_TYPE_ATA |
| jb __reg_select_dev # not ATA |
| |
| /* The device is ATA */ |
| |
| // pushw %bx |
| |
| call reg_poll_busy # change AL, DX |
| |
| #if 0 |
| orw %ax, %ax |
| jnz 1f # return if,{or ax,ax},nz |
| #else |
| ja 1f # timeout, failure |
| #endif |
| |
| movw %bx, %ax |
| call __reg_select_dev # change AL, DX |
| |
| #ifndef USE_ATA |
| call reg_poll_busy # change AL, DX |
| #if 0 |
| jmp 1f |
| #else |
| #if 0 |
| orw %ax, %ax |
| jz 1f |
| #else |
| jna 1f |
| #endif |
| #endif |
| #else //; USE_ATA |
| 3: |
| movw reg_addr - int13_handler + (7 * 2), %dx #; CB_STAT |
| inb %dx, %al |
| |
| cmpb $2, (reg_dev_info - int13_handler)(%bx) # REG_CONFIG_TYPE_ATA |
| jne 4f |
| andb $(CB_STAT_BSY | CB_STAT_RDY | CB_STAT_SKC), %al |
| # return if,{cmp al,CB_STAT_RDY|CB_STAT_SKC},e |
| cmpb $(CB_STAT_RDY | CB_STAT_SKC), %al |
| je 1f |
| jmp 3b |
| 4: |
| # return if,{test al,CB_STAT_BSY},z |
| testb $CB_STAT_BSY, %al |
| jz 1f |
| jmp 3b |
| #endif //; USE_ATA |
| |
| |
| call __reg_select_dev |
| 1: |
| // popw %bx |
| ret |
| |
| |
| select_atapi: |
| cmpw atapi_cur_dev - int13_handler, %ax # device serial number |
| jne select_atapi_force |
| //clc #; CF already cleared |
| ret |
| |
| |
| select_atapi_force: |
| #; input: ax = device serial number |
| #; return: cf =0 success, cf =1 failed |
| pushaw |
| pushw %es |
| |
| pushw %cs |
| popw %es |
| |
| cmpw atapi_dev_count - int13_handler, %ax |
| cmc |
| jb 2f |
| |
| movw %ax, atapi_cur_dev - int13_handler # device serial number |
| |
| movw $atapi_dev_base - int13_handler, %si # array of ATAPI reg pointer and dev |
| shlw $2, %ax # each element of array has 4 bytes |
| addw %ax, %si |
| movw (%si), %bx # BX is the reg pointer of the current device |
| |
| call reg_setup_base_addr # fill the 10-word reg_addr area |
| # change no registers |
| |
| movw 2(%si), %bx # BX is the dev number of the current device |
| movw %bx, %ax |
| |
| call set_timeout # set timeout for reg_select_dev |
| # change no registers |
| |
| call reg_select_dev #; reg_select_dev |
| |
| addb $0x0A, %bl #; BL= 0x0A or 0x0B |
| shlb $4, %bl #; BL= 0xA0 or 0xB0 |
| movb %bl, reg_cur_dev - int13_handler |
| |
| clc |
| 2: |
| pop %es |
| popaw |
| ret |
| |
| |
| read_atapi: |
| #;input: es:di -> buffer, cx = sector count, edx = lba address |
| #;return: cf =0 success, cx = number of bytes actually read |
| pushaw |
| orw %cx, %cx |
| jz 3f |
| |
| 1: |
| pushw %cx |
| pushl %edx |
| |
| call clear_atapi_buffer |
| movb $0x28, atapi_cmd_buffer - int13_handler |
| |
| bswapl %edx |
| movl %edx, atapi_cmd_buffer - int13_handler + 2 |
| movb $1, atapi_cmd_buffer - int13_handler + 8 |
| |
| # invoke reg_packet,byte, 0, es, di, REG_ATAPI_MAX_BYTES |
| pushw $0x8000 #; REG_ATAPI_MAX_BYTES |
| pushw %di |
| pushw %es |
| pushw $0 |
| call reg_packet |
| addw $8, %sp |
| |
| orw %ax, %ax |
| jnz 2f |
| cmpw $0x800, %cx #; CDSECTOR_SIZE |
| jne 2f |
| popl %edx |
| incl %edx |
| addw %cx, %di |
| popw %cx |
| loop 1b |
| |
| clc |
| jmp 4f |
| |
| 2: |
| popl %edx |
| popw %cx |
| |
| 3: |
| stc |
| 4: |
| popaw |
| ret |
| |
| |
| read_bios_time: |
| #; return EAX as the long time |
| pushw %ds |
| xorw %ax, %ax |
| movw %ax, %ds |
| movl 0x46c, %eax |
| popw %ds |
| ret |
| |
| |
| edd30_read_cdrom: |
| #;return cf=0 success, cf=1 fail, ah = fail code |
| #;will change all registers, including ES!! |
| |
| call test_atapi_ready |
| movb $0xAA, %ah # error code if CF=1 |
| jc 4f |
| |
| movw %ds, %ax # save DS |
| |
| movw -18(%bp), %ds # get DS for the disk address packet |
| movw 2(%si), %cx # struc_int13ext.blk_count |
| movw 4(%si), %di # struc_int13ext.buf_addr_off |
| movw 6(%si), %bx # struc_int13ext.buf_addr_seg |
| movl 8(%si), %edx # struc_int13ext.blk_num_low1 |
| cmpb $16, (%si) # valid packet size is 16 |
| |
| movw %ax, %ds # restore DS |
| |
| setne %ah # AH=1 for invalid function call |
| stc # error out if ... |
| jne 4f # ... invalid packet size |
| |
| movw %bx, %es # ES changed!! |
| |
| call read_atapi |
| |
| movb $0x0C, %ah # error code if CF=1 |
| 4: |
| // /* debug print AX and FLAGS */ |
| // pushfw |
| // pushw %ax |
| // pushw $read_cdrom_ah_flags - int13_handler # the format string |
| // call realmode_printf |
| // popw %ax |
| // popw %ax |
| // popfw |
| ret |
| |
| //read_cdrom_ah_flags: |
| // .ascii "edd30_read_cdrom: AX=%X, FLAGS=%X\r\n\0" |
| |
| |
| reg_reset: |
| #; call after reg_probe_exist |
| pushaw |
| |
| #; mov_ax 0 |
| #; call __reg_select_dev |
| |
| #if 0 |
| /* The ATA software reset mechanism, SRST, (bit 2 in the Device Control |
| * Register) cannot be used for ATAPI Device, because resets issued by |
| * the ATAPI driver would also reset any attached hard disk and vice |
| * versa. To solve this, ATAPI defines an ATAPI Soft Reset command |
| * using a reserved ATA opcode which could be decoded by the interface |
| * controller hardware. |
| */ |
| |
| movb $cmd_DC, %al |
| orb $0x04, %al #; CB_DC_SRST = soft reset |
| |
| movw reg_addr - int13_handler + (8 * 2), %dx #; CB_DC |
| outb %al, %dx |
| |
| call delay400ns |
| #endif |
| |
| #if 0 |
| movb $0x08, %al #; ATAPI Soft Reset |
| movw reg_addr - int13_handler + (7 * 2), %dx #; CB_CMD |
| outb %al, %dx |
| |
| call delay400ns |
| #endif |
| |
| #if 1 |
| /* set features (0xEF to command port) */ |
| /* set transfer mode (0x03 in feature register) */ |
| /* mode value in Sector Count register (0 for PIO default mode) */ |
| |
| movb $0, %al #; PIO default transfer mode |
| movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register |
| outb %al, %dx |
| |
| call delay400ns |
| |
| movb $0x03, %al #; 0x03 = set transfer mode based on value in sector count register |
| movw reg_addr - int13_handler + (1 * 2), %dx #; CB_FR = Feature Register |
| outb %al, %dx |
| |
| call delay400ns |
| |
| movb $0xEF, %al #; Set Features Command |
| movw reg_addr - int13_handler + (7 * 2), %dx #; CB_CMD |
| outb %al, %dx |
| |
| call delay400ns |
| #endif |
| |
| #if 1 |
| movb $0x08, %al #; ATAPI Soft Reset |
| movw reg_addr - int13_handler + (7 * 2), %dx #; CB_CMD |
| outb %al, %dx |
| |
| call delay400ns |
| #endif |
| |
| #; Test only: do a hard reset and see if it works |
| #if 1 |
| /* The ATA software reset mechanism, SRST, (bit 2 in the Device Control |
| * Register) cannot be used for ATAPI Device, because resets issued by |
| * the ATAPI driver would also reset any attached hard disk and vice |
| * versa. To solve this, ATAPI defines an ATAPI Soft Reset command |
| * using a reserved ATA opcode which could be decoded by the interface |
| * controller hardware. |
| */ |
| |
| movb $cmd_DC, %al |
| orb $0x04, %al #; CB_DC_SRST = soft reset |
| |
| movw reg_addr - int13_handler + (8 * 2), %dx #; CB_DC |
| outb %al, %dx |
| |
| call delay400ns |
| #endif |
| |
| movb $cmd_DC, %al |
| movw reg_addr - int13_handler + (8 * 2), %dx #; CB_DC |
| outb %al, %dx |
| |
| call delay400ns |
| |
| call set_timeout |
| |
| cmpb $0, reg_dev_info - int13_handler |
| je 1f #; master not exist |
| |
| call sub_atapi_delay |
| |
| call reg_poll_busy |
| |
| 1: |
| cmpb $0, reg_dev_info - int13_handler + 1 |
| je 1f #; slave not exist |
| |
| call sub_atapi_delay |
| |
| //call reg_poll_busy #; added recently for test |
| |
| //jmp 1f #; added recently for test |
| |
| 2: |
| movw $1, %ax |
| |
| call __reg_select_dev |
| |
| movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register |
| inb %dx, %al |
| movb %al, %ah |
| |
| movw reg_addr - int13_handler + (3 * 2), %dx #; CB_SN = reserved for SAM TAG byte |
| inb %dx, %al |
| |
| cmpw $0x0101, %ax |
| je 1f |
| |
| call check_timeout |
| jna 2b |
| 1: |
| popaw |
| ret |
| |
| |
| test_atapi_ready: |
| #; return: cf =0 ready, cf =1 not ready |
| pushaw |
| movw $2, %cx |
| |
| 1: |
| pushw %cx |
| |
| call clear_atapi_buffer |
| |
| #; TEST UNIT READY Command |
| #; |
| #; The TEST UNIT READY command provides a means to check if the Device |
| #; is ready. This is not a request for a self-test. If the Device would |
| #; accept an appropriate medium-access command without returning CHECK |
| #; CONDITION status, this command shall return a GOOD status. If the |
| #; Device cannot become operational or is in a state such that a Host |
| #; Computer action(e.g. START/STOP UNIT command with LoEj = 0 and Start |
| #; = 1) is required to make the unit ready, the ATAPI CD-ROM Drive |
| #; shall return CHECK CONDITION status with a sense key of NOT READY. |
| #; |
| #; Byte |
| #; 0 Operation code (00h) |
| #; 1 Reserved |
| #; 2 Reserved |
| #; 3 Reserved |
| #; 4 Reserved |
| #; 5 Reserved |
| #; 6 Reserved |
| #; 7 Reserved |
| #; 8 Reserved |
| #; 9 Reserved |
| #; 10 Reserved |
| #; 11 Reserved |
| #; |
| #; Using the TEST UNIT READY Command |
| #; |
| #; The TEST UNIT READY command is useful in that it allows a Host |
| #; Computer to poll a Device until it is ready without the need to |
| #; allocate space for returned data. It is especially useful to check |
| #; cartridge status. ATAPI CD-ROM Drives are expected to respond |
| #; promptly to indicate the current status of the device. |
| |
| # invoke reg_packet,byte,0,cs, atapi_tmp_buffer, 128 |
| pushw $128 |
| pushw $atapi_tmp_buffer - int13_handler |
| pushw %cs |
| pushw $0 |
| call reg_packet |
| addw $8, %sp |
| |
| // /* debug print AX */ |
| // pushw %ax |
| // pushw $test_atapi_ax - int13_handler # the format string |
| // call realmode_printf |
| // popw %ax |
| // popw %ax |
| |
| orw %ax, %ax |
| jnz 2f |
| |
| call get_atapi_sense |
| |
| // /* debug print AL, FLAGS */ |
| // pushfw |
| // pushw %ax |
| // pushw $test_atapi_al_flags - int13_handler # the format string |
| // call realmode_printf |
| // popw %ax |
| // popw %ax |
| // popfw |
| |
| jc 2f |
| orb %al, %al |
| jnz 2f |
| |
| popw %cx |
| clc |
| popaw |
| ret |
| 2: |
| call start_stop_unit #; start unit |
| |
| popw %cx |
| loop 1b #; try again |
| |
| stc |
| popaw |
| ret |
| |
| //test_atapi_ax: |
| // .ascii "test_atapi_ax: AX=%X\r\n\0" |
| |
| //test_atapi_al_flags: |
| // .ascii "test_atapi_al_flags: AX=%X, FLAGS=%X\r\n\0" |
| |
| |
| start_stop_unit: |
| #; return: cf =0 ready, cf =1 not ready |
| pushaw |
| movw $1, %cx |
| |
| 1: |
| pushw %cx |
| |
| call clear_atapi_buffer |
| |
| #; START/STOP UNIT Command |
| #; |
| #; The START/STOP UNIT command requests that the ATAPI CD-ROM Drive |
| #; enable or disable media access operations. |
| #; |
| #; | bit bit bit bit bit bit bit bit |
| #; Byte | 7 6 5 4 3 2 1 0 |
| #; -----+-------------------------------------------------------------- |
| #; 0 | Operation code (1Bh) |
| #; -----+-------------------------------------------------------+------ |
| #; 1 | Reserved | Immed |
| #; -----+-------------------------------------------------------+------ |
| #; 2 | Reserved |
| #; -----+-------------------------------------------------------------- |
| #; 3 | Reserved |
| #; -----+-----------------------------------------------+-------+------ |
| #; 4 | Reserved | LoEj | Start |
| #; -----+-----------------------------------------------+-------+------ |
| #; 5 | |
| #; -----+-------------------------------------------------------------- |
| #; 6 | Reserved |
| #; -----+-------------------------------------------------------------- |
| #; 7 | Reserved |
| #; -----+-------------------------------------------------------------- |
| #; 8 | Reserved |
| #; -----+-------------------------------------------------------------- |
| #; 9 | Reserved |
| #; -----+-------------------------------------------------------------- |
| #; 10 | Reserved |
| #; -----+-------------------------------------------------------------- |
| #; 11 | Reserved |
| #; -----+-------------------------------------------------------------- |
| |
| movb $0x1B, atapi_cmd_buffer - int13_handler |
| movb $0x01, atapi_cmd_buffer - int13_handler + 4 #; start unit |
| |
| # invoke reg_packet,byte,0,cs, atapi_tmp_buffer, 128 |
| pushw $128 |
| pushw $atapi_tmp_buffer - int13_handler |
| pushw %cs |
| pushw $0 |
| call reg_packet |
| addw $8, %sp |
| |
| orw %ax, %ax |
| jnz 2f |
| |
| // call get_atapi_sense |
| // |
| // jc 2f |
| // orb %al, %al |
| // jnz 2f |
| |
| popw %cx |
| clc |
| popaw |
| ret |
| 2: |
| popw %cx |
| loop 1b |
| |
| stc |
| popaw |
| ret |
| |
| |
| |
| |
| reg_setup_base_addr: |
| #; input: bx = base addr pointer |
| pushw %ax |
| pushw %di |
| cld # store upward |
| movw $reg_addr - int13_handler, %di # points to 10-word space |
| movw (%bx), %ax # get base address |
| # store 8 ports(command block registers) |
| |
| #if 0 |
| pushw 18(%di) |
| pushw 16(%di) |
| pushw 14(%di) |
| pushw 12(%di) |
| pushw 10(%di) |
| pushw 8(%di) |
| pushw 6(%di) |
| pushw 4(%di) |
| pushw 2(%di) |
| pushw (%di) |
| pushw 2(%bx) |
| pushw %ax |
| pushw $reg_addresses - int13_handler # the format string |
| call realmode_printf |
| addw $26, %sp # adjust the stack pointer |
| #endif |
| |
| stosw |
| incw %ax |
| stosw |
| incw %ax |
| stosw |
| incw %ax |
| stosw |
| incw %ax |
| stosw |
| incw %ax |
| stosw |
| incw %ax |
| stosw |
| incw %ax |
| stosw |
| |
| movw 2(%bx), %ax # get base address |
| stosw # store 2 ports(control block registers) |
| incw %ax |
| stosw |
| popw %di |
| popw %ax |
| ret |
| |
| #if 0 |
| reg_addresses: |
| .ascii "reg_addresses: Data=%X, Ctrl=%X, di: %X, %X, %X, %X, %X, %X, %X, %X, %X, %X\r\n\0" |
| #endif |
| |
| set_timeout: |
| pushl %eax |
| call read_bios_time |
| addl $(5 * 18), %eax #; 20 seconds |
| # not 20 seconds, but 5 seconds |
| #; FIXME: Midnight overflow |
| # Midnight overflow is handled in check_timeout |
| movl %eax, time_out - int13_handler |
| popl %eax |
| ret |
| |
| |
| check_timeout: |
| #; you need to setup the timeout first |
| pushl %eax |
| pushl %ebx |
| pushl %edx |
| call read_bios_time |
| movl time_out - int13_handler, %ebx |
| movl %ebx, %edx |
| subl $(5 * 18), %ebx # the time when we set_timeout |
| cmpl %ebx, %eax |
| jnb 1f |
| /* the next day */ |
| addl $0x001800B0, %eax /* 24 hours = 0x1800B0 ticks */ |
| 1: |
| cmpl %edx, %eax |
| popl %edx |
| popl %ebx |
| popl %eax |
| ret |
| |
| |
| clear_atapi_buffer: |
| pushaw |
| pushw %es |
| pushw %cs |
| popw %es |
| cld |
| movw $atapi_tmp_buffer - int13_handler, %di |
| movw $128, %cx # but note the buffer is 256-byte long! |
| xorb %al, %al |
| rep stosb # added the lost rep prefix |
| movw $atapi_cmd_buffer - int13_handler, %di |
| movw $16, %cx |
| rep stosb # added the lost rep prefix |
| popw %es |
| popaw |
| ret |
| |
| |
| reg_packet: |
| #;proc reg_packet,withlocal,dir,packet_seg,packet_off,packet_len |
| #; input: |
| #; return ax = 0 noerror, ah = error code al= error bit |
| #; cx = len |
| pushaw |
| |
| movw %sp, %bp |
| |
| call set_timeout |
| movb $0, _pre_fail_bit7_ - int13_handler |
| |
| #; outbytes CB_DC,cmd_DC_ni,CB_FR,0,CB_SC,0,CB_SN,0, |
| # outbytes CB_DC,cmd_DC,CB_FR,0,CB_SC,0,CB_SN,0, |
| movb $cmd_DC, %al |
| movw reg_addr - int13_handler + (8 * 2), %dx #; CB_DC |
| outb %al, %dx |
| |
| call delay400ns #; added recently |
| |
| call sub_atapi_delay #; added recently |
| |
| movb $0, %al |
| movw reg_addr - int13_handler + (1 * 2), %dx #; CB_FR = Feature Register |
| outb %al, %dx |
| |
| movb $0, %al |
| movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register |
| outb %al, %dx |
| |
| movb $0, %al |
| movw reg_addr - int13_handler + (3 * 2), %dx #; CB_SN = reserved for SAM TAG byte |
| outb %al, %dx |
| |
| |
| # outbytes CB_CL,[.packet_len],CB_CH,[.packet_len+1] |
| movb 24(%bp), %al #; packet_len low byte |
| movw reg_addr - int13_handler + (4 * 2), %dx #; CB_CL = byte count low |
| outb %al, %dx |
| |
| movb 25(%bp), %al #; packet_len high byte |
| movw reg_addr - int13_handler + (5 * 2), %dx #; CB_CH = byte count high |
| outb %al, %dx |
| |
| # outbyte CB_DH,[reg_cur_dev] |
| movb reg_cur_dev - int13_handler, %al |
| movw reg_addr - int13_handler + (6 * 2), %dx #; CB_DH = drive select |
| outb %al, %dx |
| |
| # outbyte CB_CMD,CMD_PACKET |
| movb $0xA0, %al #; CMD_PACKET |
| movw reg_addr - int13_handler + (7 * 2), %dx #; CB_CMD |
| outb %al, %dx |
| |
| call delay400ns |
| |
| call sub_atapi_delay |
| |
| subw %bx, %bx #; clear error number |
| |
| # while {inbyte CB_ASTAT},{test al,CB_STAT_BSY|CB_STAT_ERR|CB_STAT_DRQ},z |
| 3: |
| # inbyte CB_ASTAT |
| movw reg_addr - int13_handler + (8 * 2), %dx #; CB_ASTAT=8, CB_STAT=7 |
| inb %dx, %al |
| |
| testb $(CB_STAT_BSY | CB_STAT_DRQ | CB_STAT_ERR), %al |
| jnz 3f |
| |
| orb $FAILBIT0, %bl #; error |
| call check_timeout |
| jna 3b |
| 3: |
| # endwhile |
| |
| # while {inbyte CB_ASTAT},{test al, CB_STAT_BSY}, nz |
| 3: |
| # inbyte CB_ASTAT |
| movw reg_addr - int13_handler + (8 * 2), %dx #; CB_ASTAT=8, CB_STAT=7 |
| inb %dx, %al |
| |
| testb $CB_STAT_BSY, %al |
| jz 3f |
| |
| call check_timeout |
| jna 3b |
| movw $-1, 18(%bp) |
| movb $51, %bh #; error |
| 3: |
| # endwhile |
| |
| cmpb $0, %bh |
| jnz 2f #; error |
| |
| # inbyte CB_STAT,[.status] |
| movw reg_addr - int13_handler + (7 * 2), %dx #; CB_STAT |
| inb %dx, %al |
| movb %al, _status_ - int13_handler |
| |
| # inbyte CB_SC, [.reason] |
| movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register |
| inb %dx, %al |
| movb %al, _reason_ - int13_handler |
| |
| # inbyte CB_CL, [.bcnt] |
| movw reg_addr - int13_handler + (4 * 2), %dx #; CB_CL = byte count low |
| inb %dx, %al |
| movb %al, _bcnt_ - int13_handler |
| |
| # inbyte CB_CH, [.bcnt+1] |
| movw reg_addr - int13_handler + (5 * 2), %dx #; CB_CH = byte count high |
| inb %dx, %al |
| movb %al, _bcnt_ - int13_handler + 1 |
| |
| movb _status_ - int13_handler, %al |
| andb $(CB_STAT_BSY | CB_STAT_DRQ), %al |
| cmpb $CB_STAT_DRQ, %al |
| jz 3f |
| movb $52, %bh #; error |
| |
| #ifdef DEBUG |
| print_stat [.status],"%b %s check BSY=0 DRQ=1 now\n",bx,STAT_BUF |
| #endif |
| |
| jmp 2f |
| 3: |
| |
| #ifdef sane_check |
| movb _reason_ - int13_handler, %al |
| testb $(CB_SC_P_TAG | CB_SC_P_REL | CB_SC_P_IO), %al |
| jnz 3f |
| testb $CB_SC_P_CD, %al |
| jnz 4f |
| 3: |
| orb $FAILBIT2, %bl #; error |
| #ifdef DEBUG |
| print_stat [.status],"%b %s %b check reason\n",bx,STAT_BUF,[.reason] |
| #endif |
| 4: |
| movw _bcnt_ - int13_handler, %ax |
| cmpw 24(%bp), %ax |
| jz 3f |
| orb $FAILBIT3, %bl #; error |
| |
| #ifdef DEBUG |
| print_stat [.status],"%b %s 0x%x check packet_len\n",bx,STAT_BUF,[.bcnt] |
| #endif |
| |
| 3: |
| |
| #endif //; sane_check |
| |
| #ifdef DEBUG |
| debug_print "sending cmd buffer" |
| #endif |
| |
| movw $atapi_cmd_buffer - int13_handler, %si |
| #if 0 |
| movw $12, %cx #; cmd_buff_len is 12 |
| shrw $1, %cx |
| #else |
| movw $6, %cx #; cmd_buff_len is 12 |
| #endif |
| movw reg_addr - int13_handler + (0 * 2), %dx #; CB_DATA = 16-bit data port |
| cld |
| repz outsw |
| |
| call delay400ns |
| |
| subw %cx, %cx |
| 3: |
| # while |
| |
| #ifdef DEBUG |
| debug_print " data transfer ----------------------------\n" |
| #endif |
| call sub_atapi_delay |
| 4: |
| # while {inbyte CB_ASTAT},{test al,CB_STAT_BSY},nz |
| # inbyte CB_ASTAT |
| movw reg_addr - int13_handler + (8 * 2), %dx #; CB_ASTAT=8, CB_STAT=7 |
| inb %dx, %al |
| |
| testb $CB_STAT_BSY, %al |
| jz 4f |
| call check_timeout |
| jna 4b |
| movb $54, %bh #; error |
| jmp 2f |
| # endwhile |
| 4: |
| |
| #ifdef DEBUG |
| print_stat al,"%b %s wait ASTAT BSY=0\n",bx,STAT_BUF |
| #endif |
| |
| #; Data transfer loop |
| #; read the primary state register |
| |
| # inbyte CB_STAT,[.status] |
| movw reg_addr - int13_handler + (7 * 2), %dx #; CB_STAT |
| inb %dx, %al |
| movb %al, _status_ - int13_handler |
| |
| # inbyte CB_SC, [.reason] |
| movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register |
| inb %dx, %al |
| movb %al, _reason_ - int13_handler |
| |
| # inbyte CB_CL, [.bcnt] |
| movw reg_addr - int13_handler + (4 * 2), %dx #; CB_CL = byte count low |
| inb %dx, %al |
| movb %al, _bcnt_ - int13_handler |
| |
| # inbyte CB_CH, [.bcnt+1] |
| movw reg_addr - int13_handler + (5 * 2), %dx #; CB_CH = byte count high |
| inb %dx, %al |
| movb %al, _bcnt_ - int13_handler + 1 |
| |
| #ifdef DEBUG |
| print_stat [.status],"%b %s pre-data reason=%b len=%d\n",bx,STAT_BUF,[.reason],[.bcnt] |
| |
| print_stat [.status]," stat " |
| debug_print " check the device said end of command" |
| #endif |
| |
| testb $(CB_STAT_BSY | CB_STAT_DRQ), _status_ - int13_handler |
| jnz 4f |
| orb $0x80, 18(%bp) #; NON_DATA |
| jmp 2f |
| 4: |
| #ifdef DEBUG |
| debug_print " device want transfer data BSY = 0 DRQ =1" |
| #endif |
| |
| movb _status_ - int13_handler, %al |
| andb $(CB_STAT_BSY | CB_STAT_DRQ), %al |
| cmpb $CB_STAT_DRQ, %al |
| jz 4f |
| movb $55, %bh #; error |
| jmp 2f |
| 4: |
| |
| #ifdef sane_check |
| |
| #ifdef DEBUG |
| print_stat al," stat " |
| debug_print " check: C/nD=0, IO=1 (read) or IO=0 (write)" |
| #endif |
| testb $(CB_SC_P_TAG | CB_SC_P_REL | CB_SC_P_CD), _reason_ - int13_handler |
| jz 4f |
| |
| orb $FAILBIT4, %bl #; error |
| |
| #ifdef DEBUG |
| print_stat al, " FAIL:%b %s reason=%b C/nD=0, IO=1 (read) or IO=0 (write)\n",bx,STAT_BUF,[.reason], |
| #endif |
| 4: |
| testb $CB_SC_P_IO, _reason_ - int13_handler |
| jz 4f |
| cmpb $0, 18(%bp) |
| jz 4f |
| orb $FAILBIT5, %bl #; error |
| |
| #ifdef DEBUG |
| print_stat al, " FAIL:%b %s reason=%b dir=%d\n",bx,STAT_BUF,[.reason],[.dir] |
| #endif |
| |
| 4: |
| |
| #endif //; sane_check |
| |
| #; do the slow data transfer |
| |
| #ifdef DEBUG |
| debug_print " do slow delay" |
| #endif |
| |
| call sub_atapi_delay |
| |
| #ifdef DEBUG |
| debug_print " check data len zero" |
| #endif |
| |
| movw _bcnt_ - int13_handler, %ax |
| orw %ax, %ax |
| jnz 4f |
| movb $60, %bh #; error |
| orb $0x80, 18(%bp) #; NON_DATA |
| jmp 2f |
| 4: |
| |
| #ifdef DEBUG |
| debug_print " check the buffer len" |
| #endif |
| |
| #ifdef sane_check |
| cmpw $0x8000, %ax #; REG_ATAPI_MAX_BYTES |
| jna 4f |
| orb $FAILBIT6, %bl #; error |
| 4: |
| #endif //; sane_check |
| movb _pre_fail_bit7_ - int13_handler, %dl |
| orb %dl, %bl #; error |
| testb $1, %al |
| setnz _pre_fail_bit7_ - int13_handler |
| |
| movw %cx, %dx |
| addw %ax, %dx |
| cmpw 24(%bp), %dx |
| jna 4f |
| movb $59, %bh #; error |
| orb $0x80, 18(%bp) #; NON_DATA |
| jmp 2f |
| 4: |
| |
| pushw %dx |
| movw %ax, %cx |
| incw %cx |
| shrw $1, %cx |
| movw reg_addr - int13_handler + (0 * 2), %dx #; CB_DATA = 16-bit data port |
| cld |
| cmpb $0, 18(%bp) |
| jz 4f |
| pushw %ds |
| movw 20(%bp), %ds #; packet_seg |
| movw 22(%bp), %si #; packet_off |
| repz outsw |
| popw %ds |
| jmp 5f |
| 4: |
| pushw %es |
| movw 20(%bp), %es #; packet_seg |
| movw 22(%bp), %di #; packet_off |
| repz insw |
| popw %es |
| 5: |
| popw %cx |
| |
| addw %ax, 22(%bp) #; packet_off |
| call delay400ns |
| jmp 3b |
| # endwhile |
| 3: |
| testb $0x80, 18(%bp) #; NON_DATA |
| jnz 3f |
| call sub_atapi_delay |
| |
| call check_timeout # !!added recently!! |
| jna 3f |
| |
| movb $57, %bh #; error: time is out |
| |
| #ifdef DEBUG |
| debug_print " end of command, wait for BSY=0\n" |
| #endif |
| |
| jmp 2f |
| 3: |
| |
| # inbyte CB_STAT, #; [.status] |
| movw reg_addr - int13_handler + (7 * 2), %dx #; CB_STAT |
| inb %dx, %al |
| |
| testb $(CB_STAT_BSY | CB_STAT_DRQ | CB_STAT_ERR), %al |
| jz 1f |
| |
| movb $58, %bh #; error |
| |
| #ifdef DEBUG |
| debug_print "Error: final check for stat al\n" |
| #endif |
| |
| jmp 2f |
| 1: |
| # inbyte CB_SC, [.reason] |
| movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register |
| inb %dx, %al |
| movb %al, _reason_ - int13_handler |
| 2: |
| |
| #ifdef check_extra_fail |
| movb _reason_ - int13_handler, %al |
| testb $(CB_SC_P_TAG | CB_SC_P_REL), %al |
| jnz 1f |
| testb $CB_SC_P_IO, %al |
| jz 1f |
| testb $CB_SC_P_CD, %al |
| jnz 2f |
| 1: |
| orw $FAILBIT8, %bx #; error |
| |
| #ifdef DEBUG |
| debug_print "FAIL:final check for protocol failures C/nD=1 IO=1\n" |
| #endif |
| |
| 2: |
| #endif //; check_extra_fail |
| |
| #; outbyte CB_DC,cmd_DC |
| |
| #ifdef DEBUG |
| debug_print " end of command, ec:failbit=%x, %d bytes data\n",bx,cx |
| #endif |
| |
| movw %bx, 14(%bp) # __AX #; error |
| movw %cx, 12(%bp) # __CX |
| |
| popaw |
| ret |
| |
| # local status,1,reason,1,bcnt,2,pre_fail_bit7,1 |
| _status_: .byte 0 |
| _reason_: .byte 0 |
| _bcnt_: .byte 0, 0 |
| _pre_fail_bit7_: .byte 0 |
| # //; end of function reg_packet |
| |
| |
| delay400ns: |
| |
| pushw %cx |
| movw $0x0040, %cx |
| 1: |
| pause |
| loop 1b |
| popw %cx |
| ret |
| |
| # inbyte CB_ASTAT |
| movw reg_addr - int13_handler + (8 * 2), %dx #; CB_ASTAT=8, CB_STAT=7 |
| inb %dx, %al |
| inb %dx, %al |
| inb %dx, %al |
| inb %dx, %al |
| ret |
| |
| |
| sub_atapi_delay: |
| #; delay a few clicks |
| cmpl $0, slow_atapi_device - int13_handler |
| jz 1f |
| |
| # delay 1 millisecond |
| |
| pushl %ecx |
| //pushfw |
| |
| //sti #; sti should already be done by the caller |
| |
| movl delay_repeat_num - int13_handler, %ecx # loops per millisecond |
| 2: |
| call read_bios_time |
| cmpw %ax, %ax # for more accurate timing |
| addr32 loope 2b |
| |
| //popfw |
| popl %ecx |
| |
| 1: |
| ret |
| |
| |
| reg_poll_busy: |
| #; need to setup the timeout first |
| #;return ax=0 ok |
| #; ax = 1 timeout |
| |
| 1: |
| movw reg_addr - int13_handler + (7 * 2), %dx #; CB_STAT |
| inb %dx, %al |
| |
| andb $0x80, %al # CB_STAT_BSY |
| jz 1f |
| call check_timeout |
| jna 1b |
| # timeout |
| 1: |
| ret |
| |
| |
| get_atapi_sense: |
| #; return: cf =0 success, al = sense key, bl = asc, bh = ascq |
| #; cf =1 failed |
| |
| call clear_atapi_buffer |
| |
| #; REQUEST SENSE Command |
| #; |
| #; The REQUEST SENSE command requests that the ATAPI CD-ROM Drive |
| #; transfer sense data to the Host Computer. |
| #; |
| #; Byte |
| #; 0 Operation code (03h) |
| #; 1 Reserved |
| #; 2 Reserved |
| #; 3 Reserved |
| #; 4 Allocation Length |
| #; 5 Reserved |
| #; 6 Reserved |
| #; 7 Reserved |
| #; 8 Reserved |
| #; 9 Reserved |
| #; 10 Reserved |
| #; 11 Reserved |
| |
| movb $0x03, atapi_cmd_buffer - int13_handler |
| movb $32, atapi_cmd_buffer - int13_handler + 4 |
| |
| # invoke reg_packet,byte,0,cs, atapi_tmp_buffer, 128 |
| pushw $128 |
| pushw $atapi_tmp_buffer - int13_handler |
| pushw %cs |
| pushw $0 |
| call reg_packet |
| addw $8, %sp |
| |
| orw %ax, %ax |
| jnz 1f |
| |
| movb atapi_tmp_buffer - int13_handler, %al |
| /* bit 7 is `Valid' bit, and should be 1 |
| * bit 0-6 is the error code and can be 70h or 71h |
| */ |
| andb $0x7f, %al |
| cmpb $0x70, %al |
| je 2f |
| cmpb $0x71, %al |
| jne 1f |
| 2: |
| movb atapi_tmp_buffer - int13_handler + 2, %al #; get sense key |
| andb $0x0f, %al #; low 4 bits are sense key |
| xorw %bx, %bx |
| cmpb $0x06, atapi_tmp_buffer - int13_handler + 7 #; additional sense length |
| jb 3f |
| movw atapi_tmp_buffer - int13_handler + 12, %bx |
| 3: |
| clc |
| ret |
| 1: |
| stc |
| ret |
| |
| |
| edd30_for_cdrom: |
| // ; Stack layout: |
| // ; +10 INT flags |
| // ; +8 INT CS |
| // ; +6 INT IP |
| // ; +2 EAX |
| // ; BP+0 BP |
| // ; -2 ax |
| // ; -4 cx |
| // ; -6 dx |
| // ; -8 bx |
| // ; -10 sp |
| // ; -12 bp |
| // ; -14 si |
| // ; -16 di |
| // ; -18 ds |
| // ; -20 es |
| // ; -24 edx |
| |
| sti #; for edd30_for_cdrom |
| |
| cld |
| |
| pushaw |
| pushw %ds |
| pushw %es |
| pushl %edx |
| pushw %cs |
| popw %ds |
| |
| pushw %ax |
| movzbw %dl, %ax |
| subb %cs:(min_cdrom_id - int13_handler), %al |
| call select_atapi #; select_atapi_force |
| popw %ax |
| |
| jnc 1f |
| movb $0xAA, %ah |
| jmp edd30_for_cdrom_fail_out |
| 1: |
| movw $(edd30_act_table_end - edd30_act_table), %cx |
| xorw %bx, %bx |
| 1: |
| cmpb %ah, (edd30_act_table - int13_handler)(%bx) |
| je 1f |
| incw %bx |
| loop 1b |
| jmp edd30_for_cdrom_invalid_cmd |
| 1: |
| shlw $1, %bx |
| jmp *(edd30_jmp_table - int13_handler)(%bx) |
| |
| edd30_for_cdrom_stop_disk_emu: |
| movw %si, %di |
| pushw -18(%bp) |
| popw %es |
| movw $edd30_cdemu_spec - int13_handler, %si |
| movw $0x13, %cx #; SIZE_OF_CDEMU_SPEC |
| repz movsb |
| orb %al, %al |
| jnz edd30_for_cdrom_success_out |
| |
| //.terminate_emu: |
| movb %al, emu_disk_type - int13_handler |
| jmp edd30_for_cdrom_success_out |
| |
| edd30_for_cdrom_init_disk_emu_and_boot: |
| call edd30_init_disk_emu |
| jc edd30_for_cdrom_fail_out |
| |
| movw edd30_cdemu_spec - int13_handler + 12, %ax #; struc_cdemu_spec.load_seg |
| movw %ax, %es |
| xorw %di, %di |
| movw edd30_cdemu_spec - int13_handler + 14, %cx #; struc_cdemu_spec.sect_count |
| addw $3, %cx #; convert virtual sect count ... |
| shrw $2, %cx #; ... to cdrom sect count |
| movl edd30_cdemu_spec - int13_handler + 4, %edx #; struc_cdemu_spec.image_lba |
| call read_atapi |
| jnc .emu_boot_read_ok |
| movb $0x04, %ah |
| jmp edd30_for_cdrom_fail_out |
| |
| .emu_boot_read_ok: |
| |
| // movw $copyright_msg, %si |
| // call draw_string_tty |
| |
| call read_bios_time |
| movw %ax, %dx |
| addw $(18 * 2), %dx |
| |
| #; sti |
| .loop_delay: |
| call read_bios_time |
| cmpw %ax, %dx |
| ja .loop_delay |
| #; cli |
| |
| movb edd30_cdemu_spec - int13_handler + 2, %dl #; struc_cdemu_spec.emu_drvid |
| movb %dl, -6(%bp) #; set the driver id to dl |
| movw %es, %ax |
| shlw $4, %ax |
| movw %ax, 6(%bp) #; set new ip |
| xorw %ax, %ax |
| movw %ax, 8(%bp) #; set new cs |
| movw %ax, (%bp) #; clear bp |
| movw $0xAA55, %ax |
| movw %ax, 2(%bp) |
| jmp edd30_for_cdrom_success_out_no_ah |
| |
| edd30_for_cdrom_init_disk_emu: |
| call edd30_init_disk_emu |
| jnc edd30_for_cdrom_success_out |
| jmp edd30_for_cdrom_fail_out |
| |
| edd30_for_cdrom_return_boot_catalog: |
| call edd30_return_boot_catalog |
| jnc edd30_for_cdrom_success_out |
| jmp edd30_for_cdrom_fail_out |
| |
| edd30_for_cdrom_get_drv_param: |
| call edd30_get_cdrom_param |
| jnc edd30_for_cdrom_success_out |
| jmp edd30_for_cdrom_invalid_cmd |
| |
| edd30_for_cdrom_ext_write: |
| movb $0x03, %ah |
| jmp edd30_for_cdrom_fail_out |
| |
| edd30_for_cdrom_ext_read: |
| call edd30_read_cdrom |
| jnc edd30_for_cdrom_success_out |
| jmp edd30_for_cdrom_fail_out |
| |
| edd30_for_cdrom_install_check: |
| movw $0xAA55, -8(%bp) #; bx=0xaa55 |
| |
| #ifdef EDD_3_0 |
| movb $0x30, %ah #; ax=0x30 edd-3.0 |
| #else |
| movb $0x21, %ah #; ax=0x21 edd-2.1 |
| #endif |
| |
| movb $0x07, -4(%bp) #; cx= 0x01 | 0x04, ext disk access and edd ok |
| jmp edd30_for_cdrom_success_out_no_ah |
| |
| edd30_for_cdrom_get_disk_type: |
| movb $0x02, %ah |
| jmp edd30_for_cdrom_success_out_no_ah |
| |
| edd30_for_cdrom_get_last_stat: |
| movb int13_last_stat - int13_handler, %ah |
| jmp edd30_for_cdrom_success_out_no_ah |
| |
| edd30_for_cdrom_reset: |
| call reg_reset |
| movw atapi_cur_dev - int13_handler, %ax # device serial number |
| call select_atapi_force |
| |
| edd30_for_cdrom_success_out: |
| xorb %ah, %ah |
| |
| edd30_for_cdrom_success_out_no_ah: |
| andb $0xFE, 10(%bp) |
| jmp .done |
| |
| edd30_for_cdrom_invalid_cmd: |
| movb $0x01, %ah |
| |
| edd30_for_cdrom_fail_out: |
| orb $1, 10(%bp) // set CF=1 for ERROR |
| |
| .done: |
| /* set error code AH */ |
| movb %ah, int13_last_stat - int13_handler |
| movb %ah, 3(%bp) |
| |
| popl %edx |
| popw %es |
| popw %ds |
| popaw |
| popw %bp |
| popl %eax |
| iret |
| |
| get_atapi_base_io: |
| #;input: ax = dev |
| #;return: bx = base io 1, dx = base io 2, cx = device number |
| # save si, ax |
| pushw %si |
| pushw %ax |
| shlw $2, %ax |
| movw $atapi_dev_base - int13_handler, %si |
| addw %ax, %si |
| movw %cs:(%si), %bx |
| movw %cs:2(%si), %cx |
| movw %cs:2(%bx), %dx |
| movw %cs:(%bx), %bx |
| popw %ax |
| popw %si |
| ret |
| |
| #;============================================================================ |
| edd30_get_cdrom_param: |
| #;return cf =0 ok, cf =1 fail |
| pushw %ds |
| movw -18(%bp), %ax #; restore ds |
| movw %ax, %ds |
| movw (%si), %ax |
| cmpw $26, %ax |
| jae .packet_ok3 |
| popw %ds |
| setb %ah |
| //.fail_out: |
| stc |
| ret |
| |
| .packet_ok3: |
| |
| #ifdef EDD_3_0 |
| cmpw $66, %ax |
| jb .below_3_0 |
| movw $66, %ax |
| jmp .set_packet_size |
| .below_3_0: |
| #endif |
| |
| cmpw $30, %ax |
| jb .below_2_0 |
| movw $30, %ax |
| jmp .set_packet_size |
| .below_2_0: |
| movw $26, %ax |
| .set_packet_size: |
| movw %ax, (%si) |
| movw $0x74, 2(%si) #; struc_extparam.flags: removable, lock, chg line |
| movw $0x800, 24(%si) #; CDSECTOR_SIZE, struc_extparam.bytes_per_sect |
| xorw %bx, %bx |
| decw %bx |
| movw %bx, 4(%si) #; struc_extparam.cylinders |
| movw %bx, 8(%si) #; struc_extparam.heads |
| movw %bx, 12(%si) #; struc_extparam.sectors |
| |
| #ifdef EDD_3_0 |
| cmpw $66, %ax |
| jb .no_dpi |
| movw $0xBEDD, 30(%si) #; struc_extparam.dpi_key: dpi signature |
| movw $0x24, 32(%si) #; struc_extparam.dpi_length: dpi length |
| movl $0x415349, 36(%si) #; 'ISA', struc_extparam.host_bus_type" |
| movl $0x50415441, 40(%si) #; 'ATAP', struc_extparam.interface_type |
| movw $0x49, 44(%si) #; 'I', struc_extparam.interface_type + 4 |
| |
| movw %cs:(atapi_cur_dev - int13_handler), %ax # device serial number |
| call get_atapi_base_io |
| movw %cx, 56(%si) #; struc_extparam.device_path |
| movw %bx, 48(%si) #; struc_extparam.interface_path |
| |
| pushw %si |
| addw $30, %si #; struc_extparam.dpi_key |
| movw $(57 - 30), %cx #; struc_extparam.checksum - struc_extparam.dpi_key |
| call edd30_checksum |
| movb %ah, (%si) |
| popw %si |
| .no_dpi: |
| #endif |
| |
| cmpw $30, %ax |
| jb .no_dpte |
| movw $atapi_dpte_buffer - int13_handler, 26(%si) #; struc_extparam.dpte_addr |
| movw %cs, 28(%si) #; struc_extparam.dpte_addr + 2 |
| |
| .no_dpte: |
| popw %ds |
| movw atapi_cur_dev - int13_handler, %ax # device serial number |
| call get_atapi_base_io |
| movw $atapi_dpte_buffer - int13_handler, %si |
| movw %bx, (%si) |
| movw %dx, 2(%si) |
| movb $0xE0, %al |
| shlb $4, %cl |
| orb %cl, %al |
| movb %al, 4(%si) #; struc_dpte.flags: LBA enable, bit 4 = slave drv |
| movb $0x60, 10(%si) #; struc_dpte.bios_spec: ATAPI and removable |
| movw $15, %cx #; struc_dpte.checksum |
| call edd30_checksum |
| movb %ah, (%si) |
| clc |
| ret |
| |
| edd30_checksum: |
| xorb %ah, %ah |
| |
| .loop_checksum: |
| lodsb |
| addb %al, %ah |
| loop .loop_checksum |
| negb %ah |
| ret |
| |
| #;============================================================================ |
| edd30_return_boot_catalog: |
| #;return cf =0 ok, cf =1 fail |
| call test_atapi_ready |
| jnc .atapi_ok2 |
| movb $0xAA, %ah |
| jmp .fail_out2 |
| |
| .atapi_ok2: |
| pushw %ds |
| movw -18(%bp), %ax #; restore ds |
| movw %ax, %ds |
| cmpb $8, (%si) |
| popw %ds |
| jae .packet_ok2 |
| setb %ah |
| |
| .fail_out2: |
| stc |
| ret |
| |
| .packet_ok2: |
| pushw %cs |
| popw %es |
| movw $edd30_disk_buffer - int13_handler, %di |
| xorw %cx, %cx |
| incw %cx |
| movw $0x11, %dx |
| movzwl %dx, %edx |
| call read_atapi #; read boot record volume descriptor |
| jc .read_fail |
| cmpb $0, (%di) |
| jne .non_bootable |
| cmpl $0x54204C45, 7(%di) #; 'EL T' |
| jne .non_bootable |
| cmpl $0x5449524F, 11(%di) #; 'ORIT' |
| jne .non_bootable |
| |
| movl 0x47(%di), %edx |
| orl %edx, %edx |
| jz .non_bootable |
| |
| pushw %ds |
| movw -18(%bp), %ax #; restore ds |
| movw %ax, %ds |
| movzwl 6(%si), %eax #; struc_cdbc_cmd.begnning_sect |
| addl %eax, %edx |
| |
| movb 1(%si), %cl #; struc_cdbc_cmd.sector_count |
| movw 4(%si), %bx #; struc_cdbc_cmd.buf_addr_seg |
| movw 2(%si), %di #; struc_cdbc_cmd.buf_addr_off |
| popw %ds |
| movw %bx, %es |
| call read_atapi |
| jc .read_fail |
| ret |
| |
| .non_bootable: |
| .read_fail: |
| movb $0x0C, %ah |
| stc |
| ret |
| |
| #;============================================================================ |
| edd30_init_disk_emu: |
| call test_atapi_ready |
| jnc .atapi_ok |
| movb $0xAA, %ah |
| jmp .fail_out1 |
| |
| .atapi_ok: |
| pushw %ds |
| movw -18(%bp), %ax #; restore ds |
| movw %ax, %ds |
| cmpb $0x13, (%si) #; SIZE_OF_CDEMU_SPEC |
| jae .packet_ok |
| popw %ds |
| |
| .invalid_cmd1: |
| setb %ah |
| .fail_out1: |
| stc |
| ret |
| |
| .packet_ok: |
| pushw %es |
| pushw %cs |
| popw %es |
| movw $edd30_cdemu_spec - int13_handler, %di |
| pushw %di |
| movw $0x13, %cx #; SIZE_OF_CDEMU_SPEC |
| repz movsb |
| popw %si |
| popw %es |
| popw %ds |
| |
| movb %dl, emu_cdrom_id - int13_handler |
| movb 2(%si), %al #; struc_cdemu_spec.emu_drvid |
| movb 1(%si), %al #; struc_cdemu_spec.media_type |
| andb $0x0F, %al |
| cmpb $4, %al |
| jae .invalid_cmd1 |
| shlb $1, %al |
| movb %al, emu_disk_type - int13_handler |
| movw 10(%si), %ax #; struc_cdemu_spec.user_bufseg |
| orw %ax, %ax |
| jnz .has_user_buf |
| movw %cs, emu_buf_seg - int13_handler |
| movw $edd30_disk_buffer - int13_handler, %ax |
| movw %ax, emu_buf_off - int13_handler |
| jmp .cont |
| |
| .has_user_buf: |
| movw %ax, emu_buf_seg - int13_handler |
| xorw %ax, %ax |
| movw %ax, emu_buf_off - int13_handler |
| .cont: |
| movw 12(%si), %ax #; struc_cdemu_spec.load_seg |
| orw %ax, %ax |
| jnz .has_load_seg |
| movw $0x07C0, 12(%si) #; struc_cdemu_spec.load_seg |
| |
| .has_load_seg: |
| movb 17(%si), %ah #; struc_cdemu_spec.sectors |
| movb %ah, %bl |
| shlb $6, %ah |
| movb 16(%si), %al #; struc_cdemu_spec.cylinders |
| movw %ax, emu_cyl - int13_handler |
| andw $63, %bx |
| movw %bx, emu_sec - int13_handler |
| movb 18(%si), %bl #; struc_cdemu_spec.heads |
| movw %bx, emu_head - int13_handler |
| |
| #if 1 |
| cmpb $0, emu_disk_type - int13_handler |
| jz .no_disk_swap |
| cmpb $0, 2(%si) #; struc_cdemu_spec.emu_drvid |
| jnz .no_disk_swap |
| |
| /* map (fd0) (fd1) */ |
| #if 0 |
| xorw %ax, %ax |
| movw %ax, drive_map - int13_handler + 2 |
| incw %ax |
| movw %ax, drive_map - int13_handler #; install the swap drive map |
| #else |
| /* find an empty slot */ |
| xorl %eax, %eax |
| movw $(hooked_drive_map - int13_handler - DRIVE_MAP_SLOT_SIZE), %bx |
| 1: |
| addw $DRIVE_MAP_SLOT_SIZE, %bx |
| cmpw $(hooked_drive_map - int13_handler + (DRIVE_MAP_SIZE*DRIVE_MAP_SLOT_SIZE)), %bx |
| jnb .no_disk_swap #; not found |
| cmpl %eax, (%bx) |
| jne 1b |
| cmpl %eax, 4(%bx) |
| jne 1b |
| cmpl %eax, 8(%bx) |
| jne 1b |
| cmpl %eax, 12(%bx) |
| jne 1b |
| cmpl %eax, 16(%bx) |
| jne 1b |
| cmpl %eax, 20(%bx) |
| jne 1b |
| |
| #; found |
| incw %ax |
| movw %ax, (%bx) |
| |
| #endif |
| |
| #; increase the floppy number |
| pushw %ds |
| pushw $0x0040 |
| popw %ds |
| orb $0x41, 0x0010 #; physical address 0x410 |
| popw %ds |
| |
| #endif |
| .no_disk_swap: |
| |
| clc |
| ret |
| |
| |
| #;============================================================================ |
| cdemu_int13h: |
| // ; Stack layout: |
| // ; +10 INT flags |
| // ; +8 INT CS |
| // ; +6 INT IP |
| // ; +2 EAX |
| // ; BP+0 BP |
| // ; -2 ax |
| // ; -4 cx |
| // ; -6 dx |
| // ; -8 bx |
| // ; -10 sp |
| // ; -12 bp |
| // ; -14 si |
| // ; -16 di |
| // ; -18 ds |
| // ; -20 es |
| // ; -24 edx |
| |
| sti #; for cdemu_int13h |
| |
| cld |
| |
| pushaw |
| pushw %ds |
| pushw %es |
| pushl %edx |
| pushw %cs |
| popw %ds |
| |
| pushw %ax |
| movzbw %cs:emu_cdrom_id - int13_handler, %ax |
| subb %cs:min_cdrom_id - int13_handler, %al |
| call select_atapi |
| popw %ax |
| |
| jnc 1f |
| movb $0xAA, %ah |
| jmp edd30_for_cdrom_fail_out |
| |
| 1: |
| movw $(cdemu_act_table_end - cdemu_act_table), %cx |
| xorw %di, %di |
| |
| 1: |
| cmpb %ah, (cdemu_act_table - int13_handler)(%di) |
| je 1f |
| incw %di |
| loop 1b |
| jmp edd30_for_cdrom_invalid_cmd |
| 1: |
| movw -4(%bp), %cx #; restore cx |
| shlw $1, %di |
| jmp *(cdemu_jmp_table - int13_handler)(%di) |
| |
| #;============================================================================ |
| cdemu_int13h_emu_get_param: |
| movb emu_disk_type - int13_handler, %ah |
| movb %ah, -8(%bp) #; bl = drive type |
| movw edd30_cdemu_spec - int13_handler + 16, %cx #; struc_cdemu_spec.cylinders |
| xchgb %cl, %ch |
| decb %ch |
| andb $0x3F, %cl |
| movb edd30_cdemu_spec - int13_handler + 18, %dh #; struc_cdemu_spec.heads |
| decb %dh |
| movb $0x02, %dl |
| movw %cx, -4(%bp) |
| movw %dx, -6(%bp) |
| xorw %ax, %ax |
| jmp edd30_for_cdrom_success_out |
| |
| cdemu_int13h_emu_get_type: |
| movb $0x02, %ah |
| jmp edd30_for_cdrom_success_out_no_ah |
| |
| cdemu_int13h_emu_read: |
| orb %al, %al |
| jz .emu_read_fail |
| |
| cmpb emu_head - int13_handler, %dh |
| jnb .emu_read_fail |
| |
| movb %cl, %dl #; dl = sector number |
| andb $63, %dl |
| orb %dl, %dl |
| jz .emu_read_fail |
| decb %dl |
| |
| cmpb emu_sec - int13_handler, %dl |
| jnb .emu_read_fail |
| |
| movzbw %ch, %cx |
| cmpw emu_cyl - int13_handler, %cx |
| jnb .emu_read_fail |
| |
| movw %cx, %ax #; ax = cylinder |
| mulb emu_head - int13_handler #; (cylinder*head |
| movb %dh, %cl |
| addw %cx, %ax #; (cylinder*head + head) |
| movb %dl, %cl #; cl = sector |
| mulw emu_sec - int13_handler #; (cylinder*head + head)*sect_per_track |
| addw %cx, %ax #; (cyl*head + head)*sect_p_t + sector |
| |
| movb 2(%bp), %cl #; number of sectors |
| |
| movw emu_buf_off - int13_handler, %di |
| xorl %edx, %edx |
| movl %edx, emu_last_read - int13_handler |
| |
| .emu_loop_read: |
| |
| pushw %ax |
| pushw %bx |
| pushw %cx |
| pushw %di |
| |
| movzwl %ax, %edx |
| |
| shrl $2, %edx |
| addl edd30_cdemu_spec - int13_handler + 4, %edx #; struc_cdemu_spec.image_lba |
| |
| cmpl emu_last_read - int13_handler, %edx |
| je .emu_have_read |
| movl %edx, emu_last_read - int13_handler |
| |
| movw $1, %cx |
| pushw %es |
| pushw emu_buf_seg - int13_handler |
| popw %es |
| |
| call read_atapi |
| |
| popw %es |
| jc .emu_atapi_fail |
| |
| .emu_have_read: |
| andw $3, %ax #; sector = sector % 4 |
| shlw $9, %ax #; sector = sector * 512 |
| movw emu_buf_off - int13_handler, %si |
| addw %ax, %si |
| movw %bx, %di |
| |
| pushw %ds |
| pushw emu_buf_seg - int13_handler |
| popw %ds |
| movw $512, %cx |
| repz movsb |
| popw %ds |
| |
| popw %di |
| popw %cx |
| popw %bx |
| popw %ax |
| addw $512, %bx |
| incw %ax |
| loop .emu_loop_read |
| jmp edd30_for_cdrom_success_out |
| |
| .emu_atapi_fail: |
| popw %di |
| popw %cx |
| popw %bx |
| popw %ax |
| |
| .emu_read_fail: |
| movb $0x04, %ah |
| jmp edd30_for_cdrom_fail_out |
| |
| #;============================================================================ |
| |
| |
| cdemu_act_table: |
| .byte 0x0, 0x1, 0x2, 0x4, 0x8, 0x15, 0x16, 0x4B |
| cdemu_act_table_end: |
| |
| cdemu_jmp_table: |
| .word edd30_for_cdrom_success_out - int13_handler #; 0 reset |
| .word edd30_for_cdrom_get_last_stat - int13_handler #; 1 get last state |
| .word cdemu_int13h_emu_read - int13_handler #; 2 read |
| .word cdemu_int13h_emu_read - int13_handler #; 4 verify |
| .word cdemu_int13h_emu_get_param - int13_handler #; 8 get param |
| .word cdemu_int13h_emu_get_type - int13_handler #; 0x15 get type |
| .word edd30_for_cdrom_success_out - int13_handler #; 0x16 detect disk change |
| .word edd30_for_cdrom_stop_disk_emu - int13_handler #; 0x4b stop disk emu |
| |
| |
| edd30_act_table: |
| .byte 0x0, 0x1, 0x15, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4D, 0x4A, 0x4C, 0x4E, 0x4B |
| edd30_act_table_end: |
| |
| edd30_jmp_table: |
| .word edd30_for_cdrom_reset - int13_handler |
| .word edd30_for_cdrom_get_last_stat - int13_handler |
| .word edd30_for_cdrom_get_disk_type - int13_handler |
| .word edd30_for_cdrom_install_check - int13_handler |
| .word edd30_for_cdrom_ext_read - int13_handler |
| .word edd30_for_cdrom_ext_write - int13_handler |
| .word edd30_for_cdrom_ext_read - int13_handler |
| .word edd30_for_cdrom_success_out - int13_handler #; lock / unlock |
| .word edd30_for_cdrom_success_out - int13_handler #; eject |
| .word edd30_for_cdrom_success_out - int13_handler #; extended seek |
| .word edd30_for_cdrom_get_drv_param - int13_handler |
| .word edd30_for_cdrom_success_out - int13_handler #; check media change |
| .word edd30_for_cdrom_return_boot_catalog - int13_handler |
| .word edd30_for_cdrom_init_disk_emu - int13_handler |
| .word edd30_for_cdrom_init_disk_emu_and_boot - int13_handler |
| .word edd30_for_cdrom_success_out - int13_handler #; set hardware configuration |
| .word edd30_for_cdrom_stop_disk_emu - int13_handler #; 0x4b stop disk emu |
| |
| int13_last_stat: .byte 0 #; the error code in AH |
| |
| //drive_map: .word 0, 0, 0 |
| |
| emu_buf_off: .word 0 |
| emu_buf_seg: .word 0 |
| emu_cdrom_id: .byte 0 |
| emu_disk_type: .byte 0 #;1=360 2=1.2 3=720 4=1.44 6=2.88 10h=atapi |
| emu_cyl: .word 0 |
| emu_sec: .word 0 |
| emu_head: .word 0 |
| emu_last_read: .long 0 |
| |
| // .bss |
| start_of_atapi_data: |
| |
| atapi_cur_dev: .word 0 #; device serial number |
| reg_cur_dev: .byte 0 #; 0xA0 for master, 0xB0 for slave |
| time_out: .long 0 |
| reg_dev_info: .byte 0, 0 |
| |
| |
| atapi_dev_base: .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| |
| atapi_dev_base_bak: .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| .word 0, 0 #; base pointer, device |
| |
| reg_addr: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
| atapi_cmd_buffer: .space 16 |
| atapi_tmp_buffer: .space 256 |
| //atapi_devinfo: .space 32 # SIZE_OF_ATAPI_DEVINFO |
| delay_repeat_num: .long 0 # loops per millisecond |
| |
| end_of_atapi_data: |
| |
| |
| |
| edd30_cdemu_spec: .space 19 # SIZE_OF_CDEMU_SPEC |
| atapi_dpte_buffer: .space 16 # SIZE_OF_DPTE |
| |
| .align 16 |
| |
| edd30_disk_buffer: .space 0x800 |
| |
| //cmd_select_dev: .byte 0xA0, 0xB0 #; CB_DH_DEV0, CB_DH_DEV1 |
| |
| ENTRY(slow_atapi_device) |
| .long 0 |
| |
| /* void realmode_printf(const char *format, ...) |
| * |
| * input: format is offset in CS segment |
| * |
| * Usage example: |
| * |
| * pushw IntegerN |
| * ... ... ... ... |
| * pushw Integer2 |
| * pushw Integer1 |
| * pushw $format_string - int13_handler |
| * call realmode_printf |
| * addw $(2*(N+1)), %sp |
| * |
| * where int13_handle should be the base of the CS segment, |
| * and format_string like this: |
| * |
| * format_string: |
| * .string "Int1=%x, Int2=%x, ..., IntN=%x\r\n" |
| * |
| * Currently only %d, %x and %X are implemented. |
| */ |
| |
| realmode_printf: |
| pushaw |
| movw %sp, %bp |
| # bp+18: format |
| # bp+20: variables |
| addw $18, %bp |
| movw (%bp), %si # points to format string |
| addw $2, %bp # (%bp) is the first variable |
| 1: |
| cs lodsb |
| testb %al, %al |
| jz 1f |
| cmpb $'%', %al |
| jne 2f |
| |
| #; %d, %x, %X |
| |
| cs lodsb |
| testb %al, %al |
| jz 1f |
| cmpb $'d', %al |
| movw $10, %bx # base 10 |
| jz 4f |
| cmpb $'x', %al |
| jz 3f |
| cmpb $'X', %al |
| jne 1b # unkown directive, continue |
| 3: |
| /* print hexa number */ |
| movw $16, %bx # base 16 |
| 4: |
| /* print decimal or hexa number */ |
| pushl %edi |
| |
| xorl %edi, %edi |
| xorw %cx, %cx # count the digits |
| movw (%bp), %ax |
| 5: |
| xorw %dx, %dx |
| divw %bx # AX=quo, DX=rem |
| movw %dx, %di |
| rorl $4, %edi |
| incw %cx |
| testw %ax, %ax # end? |
| jnz 5b |
| |
| /* print the digits in EDI */ |
| xorw %bx, %bx /* video page 0 */ |
| 5: |
| roll $4, %edi |
| movw %di, %ax # get digit in AL |
| andb $0x0f, %al |
| cmpb $9, %al |
| jbe 6f |
| addb $7, %al # A, B, C, D, E, F |
| 6: |
| addb $0x30, %al |
| movb $0x0e, %ah /* print it */ |
| int $0x10 /* via TTY mode */ |
| loop 5b |
| |
| popl %edi |
| |
| addw $2, %bp # (%bp) is the next variable |
| jmp 1b # continue |
| 2: |
| /* print char in AL */ |
| xorw %bx, %bx /* video page 0 */ |
| movb $0x0e, %ah /* print it */ |
| int $0x10 /* via TTY mode */ |
| jmp 1b # continue |
| 1: |
| popaw |
| ret |
| |
| //int13_handler_code_end: |
| |
| //#if 0 |
| // .align 4 |
| //#else |
| // .space (8 - ((int13_handler_code_end - int13_handler) % 8)) % 8 |
| //#endif |
| |
| .align 16 |
| |
| GDT_data: /* used by int15/ah=87h */ |
| |
| /*00*/ .long 0, 0 /* unused descriptor, must be 0 */ |
| /*08*/ .long 0, 0 /* GDT descriptor, must be 0 */ |
| |
| /* source descriptor */ |
| /*10*/ .word 0xffff /* segment limit, 64K */ |
| /*12*/ .byte 0, 0, 0 /* physical address(low 24 bits) */ |
| /*15*/ .byte 0x93 /* access rights, 93h=readable/writable */ |
| /*16*/ .byte 0 /* low 4 bits are high 4 bits of segment limit(here |
| should be 0). high 4 bits are flags, also should |
| be set to 0 for this function call */ |
| /*17*/ .byte 0 /* physical address(high 8 bits) */ |
| |
| /* destination descriptor */ |
| /*18*/ .word 0xffff /* segment limit, 64K */ |
| /*1A*/ .byte 0, 0, 0 /* physical address(low 24 bits) */ |
| /*1D*/ .byte 0x93 /* access rights, 93h=readable/writable */ |
| /*1E*/ .byte 0 /* low 4 bits are high 4 bits of segment limit(here |
| should be 0). high 4 bits are flags, also should |
| be set to 0 for this function call */ |
| /*1F*/ .byte 0 /* physical address(high 8 bits) */ |
| |
| /*20*/ .long 0, 0 /* code segment descriptor, must be 0 */ |
| /*28*/ .long 0, 0 /* stack segment descriptor, must be 0 */ |
| /*30*/ |
| |
| .align 16 |
| |
| MyGDT: .word MyGDTEnd - MyGDT - 1 |
| .long 0 /* Pointer to self */ |
| .word 0 |
| |
| MyRMDS: .long 0x0000ffff /* 64K data segment */ |
| .long 0x00009200 |
| |
| MyPMDS: .long 0x0000ffff /* 4GB data segment */ |
| .long 0x00cf9200 |
| |
| MyGDTEnd: |
| |
| |
| #;******************************************************************* |
| #; ATA Registers Layout |
| #;******************************************************************* |
| #; |
| #; Address Read Write |
| #; --------- --------------------- ----------------------------- |
| #; A N 0 0 0 16-bit data port for both read and write |
| #; ------------------------------------------------------------------ |
| #; A N 0 0 1 ATAPI/ATA Error(Read) ATAPI/ATA Feature(Write) |
| #; ------------------------------------------------------------------ |
| #; A N 0 1 0 ATAPI Interrupt Reason(Read) / ATA Sector Count |
| #; ------------------------------------------------------------------ |
| #; A N 0 1 1 Reserved for SAM TAG Byte / ATA Sector Number |
| #; ------------------------------------------------------------------ |
| #; A N 1 0 0 ATAPI Byte Count LSB(R/W) / ATA Cylinder Low(R/W) |
| #; ------------------------------------------------------------------ |
| #; A N 1 0 1 ATAPI Byte Count MSB(R/W) / ATA Cylinder High(R/W) |
| #; ------------------------------------------------------------------ |
| #; A N 1 1 0 ATAPI Drive Select(R/W) / ATA Drive/Head Select(R/W) |
| #; ------------------------------------------------------------------ |
| #; A N 1 1 1 ATAPI/ATA Status(Read) ATA Command(Write) |
| #; ------------------------------------------------------------------ |
| #; N A 1 1 0 Alternate Status(Read) Device Control(Write) |
| #; ------------------------------------------------------------------ |
| |
| reg_base_addr: |
| .word 0x01f0, 0x03f6 |
| .word 0x0170, 0x0376 |
| .word 0x00f0, 0x02f6 |
| #;.word 0x0070, 0x0276 //port 70 and 71 is for CMOS |
| .word 0x0180, 0x0386 |
| .word 0x6b00, 0x6f00 |
| .word 0x7300, 0x7700 |
| ENTRY(reg_base_addr_append) |
| .word 0, 0, 0, 0 |
| |
| /* EBIOS_disk_address_packet should be at the end of the handler because some |
| * buggy BIOSes could destroy the memory that immediately follows. |
| */ |
| |
| EBIOS_disk_address_packet: |
| .byte 0x10 /* packet size, 16 or more */ |
| .byte 0 /* reserved, must be 0 */ |
| .byte 0 /* number of sectors, must be from 1 to 127 */ |
| .byte 0 /* reserved, must be 0 */ |
| .word 0 /* displacement of memory address */ |
| .word 0 /* segment of memory address */ |
| .long 0 /* 64bit, start logical sector number */ |
| .long 0 |
| |
| /* Don't insert code or data here! Buggy BIOSes could overwrite this area! */ |
| |
| int13_handler_end: |
| |
| // /* confirm the handler routine does not exceed the 0x1000(4KB) size */ |
| // . = . - (int13_handler_end - int13_handler) / 0x1001 |
| |
| |
| #define CDROM_INIT |
| #ifdef CDROM_INIT |
| |
| /* unsigned long |
| * init_atapi() : return number of atapi cdrom devices. |
| */ |
| |
| ENTRY(init_atapi) |
| |
| .code32 |
| |
| push %ebp |
| push %ebx |
| |
| call EXT_C(prot_to_real) /* enter real mode */ |
| |
| .code16 |
| |
| sti /* enable interrupt for ATAPI */ |
| |
| cld |
| |
| /* set CS base to be int13_handler */ |
| #if 0 |
| ljmp $((ABS(int13_handler)) >> 4), $(1f - int13_handler) |
| #else |
| .byte 0xEA |
| .word 1f - int13_handler |
| .word (ABS(int13_handler)) >> 4 |
| #endif |
| 1: |
| pushaw |
| pushw %es |
| pushw %ds |
| |
| pushw %cs |
| popw %ds |
| pushw %cs |
| popw %es |
| xorw %ax, %ax |
| |
| # initialize variables |
| |
| movw $(start_of_atapi_data - int13_handler), %di |
| movw $(end_of_atapi_data - start_of_atapi_data), %cx |
| repz stosb |
| |
| decw atapi_cur_dev - int13_handler # device serial number initialized to 0xFFFF |
| |
| #; begin init_timer |
| |
| call read_bios_time |
| movw %ax, %bx #; store initial timer in BX |
| 1: |
| call read_bios_time |
| cmpw %ax, %bx #; timer just changed? |
| je 1b #; no, continue. |
| |
| /* now AX=BX+1 */ |
| |
| movw %ax, %bx #; store initial timer in BX |
| xorl %ecx, %ecx |
| 1: |
| call read_bios_time |
| cmpw %ax, %bx #; timer changed? |
| addr32 loope 1b |
| |
| negl %ecx |
| |
| /* ECX loops=a tick=1/18.2second=1000/18.2(=54.9) milliseconds */ |
| |
| movl %ecx, %eax |
| xorl %edx, %edx |
| xorl %ecx, %ecx |
| movw $55, %cx # a tick is about 55 milliseconds |
| divl %ecx |
| |
| /* EAX=loops per millisecond */ |
| |
| movl %eax, delay_repeat_num - int13_handler |
| |
| #; end init_timer |
| |
| call init_atapi_cdroms |
| |
| //movw $-1, atapi_cur_dev - int13_handler # device serial number initialized to 0xFFFF |
| |
| popw %ds |
| popw %es |
| |
| movw %sp, %bp |
| movw %cx, 8(%bp) #; 8(%bp) is old BX on stack ! |
| popaw |
| |
| ljmp $0, $ABS(1f) |
| 1: |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movzwl %bx, %eax |
| |
| pop %ebx |
| pop %ebp |
| ret |
| |
| #endif /* CDROM_INIT */ |
| |
| |
| #ifdef CDROM_INIT |
| |
| init_atapi_cdroms: |
| |
| .code16 |
| |
| #; return: CF=0 success, CX=number of cdroms |
| #; CF=1 failed, no cdrom found |
| #; push cs |
| #; pop es |
| |
| call reg_probe #; return cx = number of atapi devices |
| |
| orw %cx, %cx |
| jnz 1f |
| |
| call reg_probe #; return cx = number of atapi devices |
| |
| orw %cx, %cx |
| jnz 1f |
| |
| stc |
| ret |
| |
| 1: |
| #if 1 |
| movw $atapi_dev_base - int13_handler, %si #; array of ATAPI reg pointer and dev |
| movw $atapi_dev_base_bak - int13_handler, %di #; for CDROMs |
| |
| cld |
| |
| pushw %si |
| pushw %di |
| |
| xorw %ax, %ax |
| xorw %bx, %bx |
| 1: |
| call select_atapi_force #;input: ax = device serial number |
| #;return: cf=0 success, cf=1 failed |
| |
| |
| jc 2f |
| |
| call check_atapi_cdrom #;return: cf=0 is cdrom, cf=1 not cdrom |
| |
| jc 2f |
| |
| /* It is CDROM */ |
| |
| incw %bx #;count the cdroms |
| movsw #;store CDROM base pointer |
| movsw #;store CDROM device number |
| subw $4, %si |
| |
| # debug print the reg and dev |
| pushw %bx |
| |
| movw (%si), %bx # BX=reg group pointer |
| |
| pushw 2(%si) # CDROM device number |
| pushw 2(%bx) # base port of control block regs, i.e., device control port |
| pushw (%bx) # base port of command block regs, i.e., data port |
| pushw $cdrom_reg_dev - int13_handler # the format string |
| call realmode_printf |
| addw $8, %sp # adjust the stack pointer |
| |
| popw %bx |
| |
| 2: |
| addw $4, %si |
| incw %ax #; try next ATAPI device |
| loop 1b |
| |
| popw %si #; points to atapi_dev_base_bak, which is for CDROMs |
| popw %di #; points to atapi_dev_base, which is for ATAPIs |
| |
| movw $64, %cx #; overwrite ATAPIs with CDROMs |
| repz movsb |
| |
| movw %bx, atapi_dev_count - int13_handler |
| movw min_cdrom_id - int13_handler, %cx |
| addw %bx, %cx |
| decw %cx |
| movw %cx, max_cdrom_id - int13_handler |
| movw %bx, %cx |
| #endif |
| |
| clc |
| ret |
| |
| cdrom_reg_dev: |
| .ascii "CDROM device found: Data=%X, Ctrl=%X, dev=%X(Note: 0=master, 1=slave)\r\n\0" |
| |
| #endif /* CDROM_INIT */ |
| |
| |
| #ifdef CDROM_INIT |
| |
| /* Called by init_atapi_cdroms |
| * Calls reg_setup_base_addr, reg_reset |
| */ |
| reg_probe: |
| |
| .code16 |
| |
| #; return cx = number of atapi devices |
| pushw %ax |
| pushw %bx |
| pushw %si |
| pushw %di |
| cld |
| movw $0, atapi_dev_count - int13_handler # reset counter |
| movw $reg_base_addr - int13_handler, %bx # BX points to base address array |
| movw $atapi_dev_base - int13_handler, %di # will store base pointers and device numbers |
| 1: |
| call reg_setup_base_addr # setup the base reg addresses(reg_addr) for reg base address BX |
| |
| #; begin reg_probe_exist |
| movb $cmd_DC, %al |
| movw reg_addr - int13_handler + (8 * 2), %dx #; CB_DC |
| outb %al, %dx |
| |
| movw $0, %ax |
| |
| call reg_probe_dev_exist |
| |
| movb %al, reg_dev_info - int13_handler # master device existence |
| movw $1, %ax |
| |
| call reg_probe_dev_exist |
| |
| movb %al, reg_dev_info - int13_handler + 1 # slave device existence |
| |
| #; end reg_probe_exist |
| |
| call reg_reset |
| |
| xor %si, %si # SI=0 for master |
| 2: |
| // movw %si, %ax |
| // call reg_probe_dev_exist |
| |
| cmpb $0, (reg_dev_info - int13_handler)(%si) # check existence |
| // cmpb $0, %al # check existence |
| je 3f # device not exist, so skip |
| |
| /* The device exists, so do a further check for device type. */ |
| |
| movw %si, %ax |
| |
| #; begin reg_check_dev_type |
| |
| #; call after a reset |
| |
| call __reg_select_dev |
| |
| movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register |
| inb %dx, %al |
| movb %al, %ah |
| |
| movw reg_addr - int13_handler + (3 * 2), %dx #; CB_SN = reserved for SAM TAG byte |
| inb %dx, %al |
| |
| cmpw $0x0101, %ax # success? |
| movw $1, %ax # REG_CONFIG_TYPE_UNKN |
| jne 4f # No. The device type is unknown. |
| |
| movw reg_addr - int13_handler + (4 * 2), %dx #; CB_CL = byte count low |
| inb %dx, %al |
| movb %al, %ah |
| |
| movw reg_addr - int13_handler + (5 * 2), %dx #; CB_CH = byte count high |
| inb %dx, %al |
| |
| cmpw $0x14EB, %ax # Is ATAPI? |
| je 5f # yes. |
| |
| testb %al, %al # 0 is for ATA |
| movw $1, %ax # REG_CONFIG_TYPE_UNKN |
| jnz 4f |
| # AX=1 |
| |
| movw reg_addr - int13_handler + (7 * 2), %dx #; CB_STAT |
| inb %dx, %al |
| # AH=0 |
| //if AL==0, then AX=1=REG_CONFIG_TYPE_UNKN, else AX=2=REG_CONFIG_TYPE_ATA |
| testb %al, %al |
| setne %al |
| incw %ax |
| jmp 4f |
| 5: |
| movw $3, %ax # REG_CONFIG_TYPE_ATAPI |
| 4: |
| |
| #; end reg_check_dev_type |
| |
| |
| movb %al, (reg_dev_info - int13_handler)(%si) #; added (%si) recently |
| cmpb $3, %al # Is ATAPI? |
| jne 3f # no, ignore it. |
| |
| #; It is ATAPI, so add it to the list at atapi_dev_base |
| incw atapi_dev_count - int13_handler # count it |
| movw %bx, %ax # store the pointer to the base reg pair |
| stosw |
| movw %si, %ax # store the device number(master/slave) |
| stosw |
| |
| # debug print the reg and dev |
| pushw %si |
| pushw 2(%bx) # base port of control block regs, i.e., device control port |
| pushw (%bx) # base port of command block regs, i.e., data port |
| pushw $atapi_reg_dev - int13_handler # the format string |
| call realmode_printf |
| addw $8, %sp # adjust the stack pointer |
| 3: |
| incw %si # SI=1 for slave |
| cmpw $2, %si |
| jb 2b |
| |
| addw $4, %bx # try next group of base reg |
| #; incw %bx # try next group of base reg |
| #; incw %bx |
| cmpw $0, (%bx) # end? |
| jnz 1b # no, probe next reg group |
| |
| movw atapi_dev_count - int13_handler, %cx |
| movw %cx, %bx |
| decw %bx |
| movw min_cdrom_id - int13_handler, %ax |
| addw %bx, %ax |
| movw %ax, max_cdrom_id - int13_handler |
| popw %di |
| popw %si |
| popw %bx |
| popw %ax |
| ret |
| |
| atapi_reg_dev: |
| .ascii "ATAPI device found: Data=%X, Ctrl=%X, dev=%X(Note: 0=master, 1=slave)\r\n\0" |
| |
| #endif /* CDROM_INIT */ |
| |
| |
| #ifdef CDROM_INIT |
| |
| check_atapi_cdrom: |
| |
| .code16 |
| |
| #;return: cf =0 is cdrom, cf =1 not cdrom |
| pushaw |
| #; push es |
| #; push cs |
| #; pop es |
| #; mov di, atapi_devinfo |
| call inquiry_atapi |
| jc 1f |
| clc |
| |
| #define ATATYPE_CDR 0x4 //#; Write-once device |
| #define ATATYPE_CD 0x5 //#; CD-ROM device |
| |
| cmpb $ATATYPE_CD, %al |
| je 1f |
| cmpb $ATATYPE_CDR, %al |
| je 1f |
| stc |
| 1: |
| #; pop es |
| popaw |
| ret |
| |
| #endif /* CDROM_INIT */ |
| |
| |
| #ifdef CDROM_INIT |
| |
| inquiry_atapi: |
| |
| .code16 |
| |
| #;input: es:di -> atapi_devinfo |
| #;return: cf =0 success, al = device type, |
| #; cf =1 fail |
| #; save si, di, cx |
| call clear_atapi_buffer |
| movb $0x12, atapi_cmd_buffer - int13_handler |
| movb $128, atapi_cmd_buffer - int13_handler + 4 |
| |
| # invoke reg_packet,byte,0,cs, atapi_tmp_buffer, 128 |
| pushw $128 |
| pushw $atapi_tmp_buffer - int13_handler |
| pushw %cs |
| pushw $0 |
| call reg_packet |
| addw $8, %sp |
| |
| orw %ax, %ax |
| jnz 1f |
| |
| movb atapi_tmp_buffer - int13_handler, %al |
| testb $0xe0, %al |
| jnz 1f |
| |
| andb $0x1f, %al |
| |
| #if 0 |
| mov [es:di + struc_atapi_devinfo.dev_type], al |
| |
| mov ah, [atapi_tmp_buffer+7] |
| mov [es:di + struc_atapi_devinfo.dev_flags], ah |
| |
| add di, struc_atapi_devinfo.vender_id |
| mov si, atapi_tmp_buffer + 8 |
| mov cx, 24 |
| cld |
| rep movsb |
| #endif |
| clc |
| ret |
| 1: |
| stc |
| ret |
| |
| #endif /* CDROM_INIT */ |
| |
| |
| #ifdef CDROM_INIT |
| |
| reg_probe_dev_exist: |
| |
| .code16 |
| |
| #; input ax=dev |
| #; return ax = 1: exist # <----- "ax=1" should be "al=1" |
| call __reg_select_dev |
| |
| # outbytes CB_SC,0x55,CB_SN,0xaa,CB_SC,0xaa,CB_SN,0x55,CB_SC,0x55 |
| movb $0x55, %al |
| movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register |
| outb %al, %dx |
| |
| movb $0xAA, %al |
| movw reg_addr - int13_handler + (3 * 2), %dx #; CB_SN = reserved for SAM TAG byte |
| outb %al, %dx |
| |
| movb $0xAA, %al |
| movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register |
| outb %al, %dx |
| |
| movb $0x55, %al |
| movw reg_addr - int13_handler + (3 * 2), %dx #; CB_SN = reserved for SAM TAG byte |
| outb %al, %dx |
| |
| movb $0x55, %al |
| movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register |
| outb %al, %dx |
| |
| movb $0xAA, %al |
| movw reg_addr - int13_handler + (3 * 2), %dx #; CB_SN = reserved for SAM TAG byte |
| outb %al, %dx |
| |
| movw reg_addr - int13_handler + (2 * 2), %dx #; CB_SC = interrupt reason register |
| inb %dx, %al |
| movb %al, %ah |
| |
| movw reg_addr - int13_handler + (3 * 2), %dx #; CB_SN = reserved for SAM TAG byte |
| inb %dx, %al |
| |
| cmpw $0x55AA, %ax |
| sete %al |
| ret |
| |
| #endif /* CDROM_INIT */ |
| |
| .code32 |
| |
| |
| /* |
| * chain_stage1(segment, offset, part_table_addr) |
| * |
| * This starts another stage1 loader, at segment:offset. |
| */ |
| |
| ENTRY(chain_stage1) |
| /* no need to save anything, just use %esp */ |
| |
| /* store %ESI, presuming %ES is 0 */ |
| movl 0xc(%esp), %esi |
| |
| /* store new offset */ |
| movl 0x8(%esp), %eax |
| movl %eax, offset |
| |
| /* store new segment */ |
| movw 0x4(%esp), %ax |
| movw %ax, segment |
| |
| /* set up to pass boot drive */ |
| movb EXT_C(boot_drive), %dl |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| #ifdef ABSOLUTE_WITHOUT_ASTERISK |
| DATA32 ADDR32 ljmp (offset) |
| #else |
| DATA32 ADDR32 ljmp *(offset) |
| #endif |
| .code32 |
| #endif /* STAGE1_5 */ |
| |
| |
| #ifdef STAGE1_5 |
| /* |
| * chain_stage2(segment, offset, second_sector) |
| * |
| * This starts another stage2 loader, at segment:offset. It presumes |
| * that the other one starts with this same "asm.S" file, and passes |
| * parameters by writing the embedded install variables. |
| */ |
| |
| ENTRY(chain_stage2) |
| /* no need to save anything, just use %esp */ |
| |
| /* store new offset */ |
| movl 0x8(%esp), %eax |
| movl %eax, offset |
| movl %eax, %ebx |
| |
| /* store new segment */ |
| movw 0x4(%esp), %ax |
| movw %ax, segment |
| shll $4, %eax |
| |
| /* generate linear address */ |
| addl %eax, %ebx |
| |
| /* set up to pass the partition where stage2 is located in */ |
| movl EXT_C(current_partition), %eax |
| movl %eax, (EXT_C(install_partition)-EXT_C(main))(%ebx) |
| |
| /* set up to pass the drive where stage2 is located in */ |
| movb EXT_C(current_drive), %dl |
| |
| /* set up to pass the second sector of stage2 */ |
| movl 0xc(%esp), %ecx |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| movl %ecx, %ebp |
| |
| #ifdef ABSOLUTE_WITHOUT_ASTERISK |
| DATA32 ADDR32 ljmp (offset) |
| #else |
| DATA32 ADDR32 ljmp *(offset) |
| #endif |
| |
| .code32 |
| #endif /* STAGE1_5 */ |
| |
| /* |
| * These next two routines, "real_to_prot" and "prot_to_real" are structured |
| * in a very specific way. Be very careful when changing them. |
| * |
| * NOTE: Use of either one messes up %eax and %ebp. |
| */ |
| |
| ENTRY(real_to_prot) |
| .code16 |
| |
| #if 0 |
| #ifndef STAGE1_5 |
| /* ensure A20 is on when we come back to protected mode. */ |
| |
| pushal |
| |
| cli /* yes, keep interrupt off when controlling A20 */ |
| |
| movw $0x00ff, %cx # try so many times on failure |
| movw $0x0001, %dx # non-zero means `enable' |
| call enable_disable_a20 |
| //sete %dl # DL=1 means success |
| |
| popal |
| #endif /* ! STAGE1_5 */ |
| #endif |
| |
| cli |
| |
| /* load the GDT register */ |
| DATA32 ADDR32 lgdt gdtdesc |
| |
| /* turn on protected mode */ |
| movl %cr0, %eax |
| orl $CR0_PE_ON, %eax |
| movl %eax, %cr0 |
| |
| /* jump to relocation, flush prefetch queue, and reload %cs */ |
| DATA32 ljmp $PROT_MODE_CSEG, $protcseg |
| |
| /* |
| * The ".code32" directive only works in GAS, the GNU assembler! |
| * This gets out of "16-bit" mode. |
| */ |
| .code32 |
| |
| protcseg: |
| /* reload other segment registers */ |
| movw $PROT_MODE_DSEG, %ax |
| movw %ax, %ds |
| movw %ax, %es |
| movw %ax, %fs |
| movw %ax, %gs |
| movw %ax, %ss |
| |
| #; /* put the return address in a known safe location */ |
| #; movl (%esp), %eax |
| #; movl %eax, STACKOFF |
| |
| #; /* get protected mode stack */ |
| #; movl protstack, %eax |
| #; movl %eax, %esp |
| #; movl %eax, %ebp |
| |
| #; /* get return address onto the right stack */ |
| #; movl STACKOFF, %eax |
| #; movl %eax, (%esp) |
| |
| /* zero %eax */ |
| xorl %eax, %eax |
| |
| /* return on the old (or initialized) stack! */ |
| ret |
| |
| |
| ENTRY(prot_to_real) |
| /* just in case, set GDT */ |
| lgdt gdtdesc |
| |
| #; /* save the protected mode stack */ |
| #; movl %esp, %eax |
| #; movl %eax, protstack |
| |
| #; /* get the return address */ |
| #; movl (%esp), %eax |
| #; movl %eax, STACKOFF |
| |
| #; /* set up new stack */ |
| #; movl $STACKOFF, %eax |
| #; movl %eax, %esp |
| #; movl %eax, %ebp |
| |
| /* set up segment limits */ |
| movw $PSEUDO_RM_DSEG, %ax |
| movw %ax, %ds |
| movw %ax, %es |
| movw %ax, %fs |
| movw %ax, %gs |
| movw %ax, %ss |
| |
| /* this might be an extra step */ |
| ljmp $PSEUDO_RM_CSEG, $tmpcseg /* jump to a 16 bit segment */ |
| |
| tmpcseg: |
| .code16 |
| |
| /* clear the PE bit of CR0 */ |
| movl %cr0, %eax |
| andl $CR0_PE_OFF, %eax |
| movl %eax, %cr0 |
| |
| /* flush prefetch queue, reload %cs */ |
| DATA32 ljmp $0, $realcseg |
| |
| realcseg: |
| /* we are in real mode now |
| * set up the real mode segment registers : DS, SS, ES |
| */ |
| /* zero %eax */ |
| xorl %eax, %eax |
| |
| movw %ax, %ds |
| movw %ax, %es |
| movw %ax, %fs |
| movw %ax, %gs |
| movw %ax, %ss |
| |
| /* restore interrupts */ |
| /* oh, don't enable interrupt when we are controlling gateA20 */ |
| //sti |
| |
| /* return on new stack! */ |
| DATA32 ret |
| |
| .code32 |
| |
| |
| /* |
| * int biosdisk_int13_extensions (int ax, int drive, void *dap) |
| * |
| * Call IBM/MS INT13 Extensions (int 13 %ax=AX) for DRIVE. DAP |
| * is passed for disk address packet. If an error occurs, return |
| * non-zero, otherwise zero. |
| */ |
| |
| ENTRY(biosdisk_int13_extensions) |
| pushl %ebp |
| movl %esp, %ebp |
| |
| #; +16 dap |
| #; +12 drive |
| #; +8 ax |
| #; +4 EIP |
| #; ebp EBP |
| #; -4 ESI |
| #; -8 EBX |
| |
| pushl %esi |
| pushl %ebx |
| pushl %ecx |
| pushl %edx |
| |
| /* compute the address of disk_address_packet */ |
| movl 0x10(%ebp), %eax #; linear address of dap |
| |
| /* if DS can be 0x40, we can avoid AWARD BIOS bug of int13/AX=4B01 */ |
| subl $0x400, %eax |
| |
| shll $1, %eax |
| movw %ax, %si |
| shrw $1, %si #; low 15-bit for offset |
| |
| xorw %ax, %ax |
| shrl $5, %eax #; segment value in AX |
| addw $0x40, %ax |
| movw %ax, %cx /* save the segment to cx */ |
| |
| /* drive */ |
| movb 0xc(%ebp), %dl |
| /* ax */ |
| movw 0x8(%ebp), %bx |
| /* enter real mode */ |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| sti #; cli should also work for biosdisk_int13_extensions |
| |
| movw %bx, %ax |
| movw %cx, %ds |
| |
| /* set additional registers to serve buggy BIOSes. */ |
| pushw %di |
| pushw %bx |
| movw %cx, %es |
| movw %si, %di |
| movw %si, %bx |
| |
| #ifdef STAGE1_5 |
| int $0x13 |
| #else |
| call safe_int13 |
| #endif |
| popw %bx |
| popw %di |
| movb $1, %dl /* set error */ |
| jc 1f |
| movb %ah, %dl /* save return value */ |
| 1: |
| /* clear the data segment */ |
| xorw %ax, %ax |
| movw %ax, %ds |
| movw %ax, %es |
| #ifndef STAGE1_5 |
| |
| /* if it is not read/write operation, we can skip the A20 code. */ |
| |
| andb $0xFE, %bh |
| cmpb $0x42, %bh |
| jne 1f |
| |
| /* ensure A20 is on when we come back to protected mode. */ |
| |
| pushal |
| |
| cli /* yes, keep interrupt off when controlling A20 */ |
| |
| movw $0x00ff, %cx # try so many times on failure |
| movw $0x0001, %dx # non-zero means `enable' |
| call enable_disable_a20 |
| //sete %dl # DL=1 means success |
| |
| popal |
| 1: |
| #endif /* ! STAGE1_5 */ |
| /* back to protected mode */ |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movzbl %dl, %eax /* return value in %eax */ |
| |
| popl %edx |
| popl %ecx |
| popl %ebx |
| popl %esi |
| popl %ebp |
| |
| ret |
| |
| /* |
| * int biosdisk_standard (int ah, int drive, int coff, int hoff, int soff, |
| * int nsec, int segment) |
| * |
| * Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write |
| * NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs, |
| * return non-zero, otherwise zero. |
| */ |
| |
| ENTRY(biosdisk_standard) |
| pushl %ebp |
| movl %esp, %ebp |
| |
| pushl %ebx |
| pushl %edi |
| pushl %esi |
| |
| /* set up CHS information */ |
| movl 0x10(%ebp), %eax |
| movb %al, %ch |
| movb 0x18(%ebp), %al |
| shlb $2, %al |
| shrw $2, %ax |
| movb %al, %cl |
| movb 0x14(%ebp), %dh |
| /* drive */ |
| movb 0xc(%ebp), %dl |
| /* segment */ |
| movw 0x20(%ebp), %bx |
| /* save nsec and ah to %di */ |
| movb 0x8(%ebp), %ah |
| movb 0x1c(%ebp), %al |
| movw %ax, %di |
| /* enter real mode */ |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti #; biosdisk_standard won't require sti |
| sti #; added 2006-11-30 |
| |
| movw %bx, %es |
| movw $3, %si /* attempt at least three times */ |
| |
| 1: |
| pushw %si |
| pushw %di |
| pushw %cx |
| pushw %dx |
| xorw %bx, %bx |
| movw %di, %ax |
| #ifdef STAGE1_5 |
| int $0x13 |
| #else |
| call safe_int13 |
| #endif |
| popw %dx |
| popw %cx |
| popw %di |
| popw %si |
| movb %ah, %bl /* save return value */ |
| jc 3f /* check if successful */ |
| |
| testb %ah, %ah |
| jz 2f |
| 3: |
| movw %di, %ax |
| cmpb $0x04, %ah /* verify sectors? */ |
| je 4f /* yes, do not retry */ |
| /* if fail, reset the disk system */ |
| pushw %si |
| pushw %di |
| pushw %cx |
| pushw %dx |
| xorw %ax, %ax |
| #ifdef STAGE1_5 |
| int $0x13 |
| #else |
| call safe_int13 |
| #endif |
| popw %dx |
| popw %cx |
| popw %di |
| popw %si |
| |
| decw %si |
| jnz 1b /* retry */ |
| 4: |
| movb $1, %bl |
| 2: |
| #ifndef STAGE1_5 |
| /* ensure A20 is on when we come back to protected mode. */ |
| |
| pushal |
| |
| cli /* yes, keep interrupt off when controlling A20 */ |
| |
| movw $0x00ff, %cx # try so many times on failure |
| movw $0x0001, %dx # non-zero means `enable' |
| call enable_disable_a20 |
| //sete %dl # DL=1 means success |
| |
| popal |
| #endif /* ! STAGE1_5 */ |
| /* back to protected mode */ |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movb %bl, %al /* return value in %eax */ |
| |
| popl %esi |
| popl %edi |
| popl %ebx |
| popl %ebp |
| |
| ret |
| |
| |
| /* |
| * int check_int13_extensions (int drive) |
| * |
| * Check if LBA is supported for DRIVE. If it is supported, then return |
| * the major version and API support bits of extensions, otherwise zero. |
| */ |
| |
| ENTRY(check_int13_extensions) |
| pushl %ebp |
| movl %esp, %ebp |
| |
| pushl %ebx |
| |
| /* drive */ |
| movb 0x8(%ebp), %dl |
| /* enter real mode */ |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti #; check_int13_extensions won't require sti |
| sti #; added 2006-11-30 |
| |
| pushw %cx |
| pushw %dx |
| |
| movb $0x41, %ah |
| movw $0x55aa, %bx |
| #ifdef STAGE1_5 |
| int $0x13 |
| #else |
| call safe_int13 |
| #endif |
| |
| /* check the result */ |
| jc 1f |
| cmpw $0xaa55, %bx |
| jne 1f |
| |
| movb %ah, %bl /* save the major version into %bl */ |
| |
| /* check if AH=0x42 is supported if FORCE_LBA is zero */ |
| movb EXT_C(force_lba), %al |
| testb %al, %al |
| #if 0 |
| jnz 2f |
| andw $1, %cx |
| jnz 2f |
| #else |
| setnz %al |
| orb %al, %cl |
| jmp 2f |
| #endif |
| |
| 1: |
| xorw %bx, %bx |
| xorw %cx, %cx |
| 2: |
| roll $16, %ebx #; version number in high word |
| movw %cx, %bx #; API subset support bitmap in low word |
| |
| popw %dx |
| popw %cx |
| |
| /* back to protected mode */ |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| xchgl %eax, %ebx /* return value in %eax */ |
| |
| popl %ebx |
| popl %ebp |
| |
| ret |
| |
| |
| /* |
| * int get_diskinfo_standard (int drive, unsigned long *cylinders, |
| * unsigned long *heads, unsigned long *sectors) |
| * |
| * Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an |
| * error occurs, then return non-zero, otherwise zero. |
| */ |
| |
| ENTRY(get_diskinfo_standard) |
| pushl %ebp |
| movl %esp, %ebp |
| |
| pushl %ebx |
| pushl %edi |
| |
| /* drive */ |
| movb 0x8(%ebp), %dl |
| |
| /* enter real mode */ |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti #; get_diskinfo_standard won't require sti |
| sti #; added 2006-11-30 |
| |
| xorw %cx, %cx |
| movb $0x8, %ah |
| #ifdef STAGE1_5 |
| int $0x13 |
| #else |
| call safe_int13 |
| #endif |
| jnc 3f |
| /* failure */ |
| testb %ah, %ah |
| jz 2f |
| jmp 1f |
| 3: |
| /* check if successful */ |
| testb %ah, %ah |
| jnz 1f /* Error number in AH */ |
| /* bogus BIOSes may not return an error number */ |
| testb $0x3f, %cl /* 0 sectors means no disk */ |
| jnz 1f /* if non-zero, then succeed */ |
| 2: |
| /* XXX 0x60 is one of the unused error numbers */ |
| movb $0x60, %ah |
| 1: |
| movb %ah, %bl /* save return value in %bl */ |
| /* back to protected mode */ |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| testb %bl, %bl /* check failure */ |
| jnz 1f /* failure */ |
| |
| /* restore %ebp */ |
| leal 0x8(%esp), %ebp |
| |
| /* heads */ |
| xorl %eax, %eax |
| movb %dh, %al |
| incl %eax /* number of heads is counted from zero */ |
| movl 0x10(%ebp), %edi |
| //movl %eax, (%edi) |
| stosl |
| |
| /* sectors */ |
| xorl %eax, %eax |
| movb %cl, %al |
| andb $0x3f, %al |
| movl 0x14(%ebp), %edi |
| //movl %eax, (%edi) |
| stosl |
| |
| /* cylinders */ |
| xorl %eax, %eax |
| shrb $6, %cl |
| movb %cl, %ah |
| movb %ch, %al |
| incl %eax /* number of cylinders is counted from zero */ |
| movl 0x0C(%ebp), %edi |
| //movl %eax, (%edi) |
| stosl |
| 1: |
| movzbl %bl, %eax /* return value in %eax */ |
| |
| popl %edi |
| popl %ebx |
| popl %ebp |
| |
| ret |
| |
| |
| #if 0 |
| /* |
| * int get_diskinfo_floppy (int drive, unsigned long *cylinders, |
| * unsigned long *heads, unsigned long *sectors) |
| * |
| * Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an |
| * error occurs, then return non-zero, otherwise zero. |
| */ |
| |
| ENTRY(get_diskinfo_floppy) |
| pushl %ebp |
| movl %esp, %ebp |
| |
| pushl %ebx |
| pushl %esi |
| |
| /* drive */ |
| movb 0x8(%ebp), %dl |
| /* enter real mode */ |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| sti #; added 2006-11-30 |
| |
| /* init probe value */ |
| movl $probe_values-1, %esi |
| 1: |
| xorw %ax, %ax |
| #ifdef STAGE1_5 |
| int $0x13 |
| #else |
| call safe_int13 |
| #endif |
| |
| incw %si |
| movb (%si), %cl |
| cmpb $0, %cl /* probe failed if zero */ |
| je 2f |
| |
| /* perform read */ |
| movw $SCRATCHSEG, %ax |
| movw %ax, %es |
| xorw %bx, %bx |
| movw $0x0201, %ax |
| movb $0, %ch |
| movb $0, %dh |
| #ifdef STAGE1_5 |
| int $0x13 |
| #else |
| call safe_int13 |
| #endif |
| |
| /* FIXME: Read from floppy may fail even if the geometry is correct. |
| So should retry at least three times. */ |
| jc 1b /* next value */ |
| |
| /* succeed */ |
| jmp 2f |
| |
| probe_values: |
| .byte 36, 18, 15, 9, 0 |
| |
| 2: |
| /* back to protected mode */ |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| /* restore %ebp */ |
| leal 0x8(%esp), %ebp |
| |
| /* cylinders */ |
| movl 0xc(%ebp), %eax |
| movl $80, %ebx |
| movl %ebx, (%eax) |
| /* heads */ |
| movl 0x10(%ebp), %eax |
| movl $2, %ebx |
| movl %ebx, (%eax) |
| /* sectors */ |
| movl 0x14(%ebp), %eax |
| movzbl %cl, %ebx |
| movl %ebx, (%eax) |
| |
| /* return value in %eax */ |
| xorl %eax, %eax |
| cmpb $0, %cl |
| jne 3f |
| incl %eax /* %eax = 1 (non-zero) */ |
| 3: |
| popl %esi |
| popl %ebx |
| popl %ebp |
| |
| ret |
| #endif |
| |
| |
| /* Source files are splitted, as they have different copyrights. */ |
| #ifndef STAGE1_5 |
| //# include "setjmp.S" |
| # include "apm.S" |
| #endif /* ! STAGE1_5 */ |
| |
| |
| |
| #ifndef STAGE1_5 |
| /* get_code_end() : return the address of the end of the code |
| * This is here so that it can be replaced by asmstub.c. |
| */ |
| ENTRY(get_code_end) |
| /* will be the end of the bss */ |
| # if defined(HAVE_END_SYMBOL) |
| movl $end, %eax |
| # elif defined(HAVE_USCORE_END_SYMBOL) |
| movl $_end, %eax |
| # endif |
| shrl $2, %eax /* Round up to the next word. */ |
| incl %eax |
| shll $2, %eax |
| ret |
| #endif /* ! STAGE1_5 */ |
| |
| /* |
| * |
| * get_memsize(i) : return the memory size in KB. i == 0 for conventional |
| * memory, i == 1 for extended memory |
| * BIOS call "INT 12H" to get conventional memory size |
| * BIOS call "INT 15H, AH=88H" to get extended memory size |
| * Both have the return value in AX. |
| * |
| */ |
| |
| ENTRY(get_memsize) |
| pushl %ebp |
| pushl %ebx |
| |
| movl 0xc(%esp), %ebx |
| |
| call EXT_C(prot_to_real) /* enter real mode */ |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| cmpb $0x1, %bl |
| //DATA32 je xext |
| je 1f |
| |
| int $0x12 |
| //DATA32 jmp xdone |
| jmp 2f |
| |
| //xext: |
| 1: |
| movb $0x88, %ah |
| int $0x15 |
| |
| //xdone: |
| 2: |
| movw %ax, %bx |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movw %bx, %ax |
| popl %ebx |
| popl %ebp |
| ret |
| |
| |
| #ifndef STAGE1_5 |
| |
| /* |
| * |
| * get_eisamemsize() : return packed EISA memory map, lower 16 bits is |
| * memory between 1M and 16M in 1K parts, upper 16 bits is |
| * memory above 16M in 64K parts. If error, return -1. |
| * BIOS call "INT 15H, AH=E801H" to get EISA memory map, |
| * AX = memory between 1M and 16M in 1K parts. |
| * BX = memory above 16M in 64K parts. |
| * |
| */ |
| |
| ENTRY(get_eisamemsize) |
| pushl %ebp |
| pushl %ebx |
| pushl %ecx |
| pushl %edx |
| |
| call EXT_C(prot_to_real) /* enter real mode */ |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| movw $0xe801, %ax |
| int $0x15 |
| jc 1f |
| testb $0x80, %ah |
| jnz 1f |
| |
| shll $16, %ebx |
| movw %ax, %bx |
| testl %ebx, %ebx |
| jnz 3f |
| movw %cx, %ax |
| movw %dx, %bx |
| shll $16, %ebx |
| movw %ax, %bx |
| 3: |
| jmp 2f |
| 1: |
| movl $0xFFFFFFFF, %ebx |
| 2: |
| xorw %ax, %ax |
| movw %ax, %ds |
| movw %ax, %es |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movl %ebx, %eax |
| |
| popl %edx |
| popl %ecx |
| popl %ebx |
| popl %ebp |
| ret |
| |
| /* |
| * |
| * get_mmap_entry(addr, cont) : address and old continuation value (zero to |
| * start), for the Query System Address Map BIOS call. |
| * |
| * Sets the first 4-byte int value of "addr" to the size returned by |
| * the call. If the call fails, sets it to zero. |
| * |
| * Returns: new (non-zero) continuation value, 0 if done. |
| * |
| * NOTE: Currently hard-coded for a maximum buffer length of 1024. |
| */ |
| |
| ENTRY(get_mmap_entry) |
| pushl %ebp |
| pushl %ebx |
| pushl %edi |
| pushl %esi |
| |
| /* place address (+4) in ES:DI */ |
| movl 0x14(%esp), %eax |
| addl $4, %eax |
| movl %eax, %edi |
| andl $0xf, %edi |
| shrl $4, %eax |
| movl %eax, %esi |
| |
| /* set continuation value */ |
| movl 0x18(%esp), %ebx |
| |
| pushl %ecx /* save ECX */ |
| |
| /* set default maximum buffer size */ |
| movl $0x14, %ecx |
| |
| /* set EDX to 'SMAP' */ |
| movl $0x534d4150, %edx |
| |
| call EXT_C(prot_to_real) /* enter real mode */ |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| movw %si, %es |
| |
| /* set additional registers to serve buggy BIOSes. */ |
| movw %si, %ds |
| movw %di, %si |
| |
| movl $0xe820, %eax |
| int $0x15 |
| |
| //DATA32 jc xnosmap |
| jnc 1f |
| movl $0, %ebx /* set end indicator */ |
| 1: |
| cmpl $0x534d4150, %eax |
| //DATA32 jne xnosmap |
| jne 1f |
| |
| /* 20-byte length is currently standard. So others are considered |
| * invalid. |
| */ |
| |
| cmpl $0x14, %ecx |
| //DATA32 jb xnosmap |
| // |
| //cmpl $0x400, %ecx |
| //DATA32 jg xnosmap |
| //DATA32 jmp xsmap |
| je 2f |
| |
| //xnosmap: |
| 1: |
| movl $0, %ecx |
| //xsmap: |
| 2: |
| |
| xorw %ax, %ax |
| movw %ax, %ds |
| movw %ax, %es |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| popl %eax /* OLD ECX */ |
| |
| /* write length of buffer (zero if error) into "addr" */ |
| movl 0x14(%esp), %edi |
| xchgl %eax, %ecx |
| stosl |
| |
| /* set return value to continuation */ |
| movl %ebx, %eax |
| |
| popl %esi |
| popl %edi |
| popl %ebx |
| popl %ebp |
| ret |
| |
| /* |
| * get_rom_config_table() |
| * |
| * Get the linear address of a ROM configuration table. Return zero, |
| * if fails. |
| */ |
| |
| ENTRY(get_rom_config_table) |
| pushl %ebp |
| pushl %ebx |
| pushl %edx |
| |
| /* zero %ebx for simplicity */ |
| xorl %ebx, %ebx |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| movb $0xc0, %ah |
| int $0x15 |
| |
| jc no_rom_table |
| testb %ah, %ah |
| jnz no_rom_table |
| |
| movw %es, %dx |
| jmp found_rom_table |
| |
| no_rom_table: |
| xorw %dx, %dx |
| xorw %bx, %bx |
| |
| found_rom_table: |
| |
| xorw %ax, %ax |
| movw %ax, %ds |
| movw %ax, %es |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| /* compute the linear address */ |
| xorl %eax, %eax |
| movw %dx, %ax |
| shll $4, %eax |
| addl %ebx, %eax |
| |
| popl %edx |
| popl %ebx |
| popl %ebp |
| ret |
| |
| |
| /* |
| * int get_vbe_controller_info (struct vbe_controller *controller_ptr) |
| * |
| * Get VBE controller information. |
| */ |
| |
| ENTRY(get_vbe_controller_info) |
| pushl %ebp |
| movl %esp, %ebp |
| |
| pushl %edi |
| pushl %ebx |
| pushl %esi |
| |
| /* Convert the linear address to segment:offset */ |
| movl 8(%ebp), %eax |
| movl %eax, %edi |
| andl $0x0000000f, %edi |
| shrl $4, %eax |
| movl %eax, %ebx |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| movw %bx, %es |
| |
| /* set additional registers to serve buggy BIOSes. */ |
| movw %bx, %ds |
| movw %di, %si |
| |
| movw $0x4F00, %ax |
| int $0x10 |
| |
| movw %ax, %bx |
| |
| xorw %ax, %ax |
| movw %ax, %ds |
| movw %ax, %es |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movzwl %bx, %eax |
| |
| popl %esi |
| popl %ebx |
| popl %edi |
| popl %ebp |
| ret |
| |
| |
| /* |
| * int get_vbe_mode_info (int mode_number, struct vbe_mode *mode_ptr) |
| * |
| * Get VBE mode information. |
| */ |
| |
| ENTRY(get_vbe_mode_info) |
| pushl %ebp |
| movl %esp, %ebp |
| |
| pushl %edi |
| pushl %ebx |
| |
| /* Convert the linear address to segment:offset */ |
| movl 0xc(%ebp), %eax |
| movl %eax, %edi |
| andl $0x0000000f, %edi |
| shrl $4, %eax |
| movl %eax, %ebx |
| |
| /* Save the mode number in %cx */ |
| movl 0x8(%ebp), %ecx |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| movw %bx, %es |
| movw $0x4F01, %ax |
| int $0x10 |
| |
| movw %ax, %bx |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movzwl %bx, %eax |
| |
| popl %ebx |
| popl %edi |
| popl %ebp |
| ret |
| |
| |
| /* |
| * int set_vbe_mode (int mode_number) |
| * |
| * Set VBE mode. Don't support user-specified CRTC information. |
| */ |
| |
| ENTRY(set_vbe_mode) |
| pushl %ebp |
| movl %esp, %ebp |
| |
| pushl %ebx |
| |
| /* Save the mode number in %bx */ |
| movl 0x8(%ebp), %ebx |
| /* Clear bit D11 */ |
| andl $0xF7FF, %ebx |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| movw $0x4F02, %ax |
| int $0x10 |
| |
| movw %ax, %bx |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movzwl %bx, %eax |
| |
| popl %ebx |
| popl %ebp |
| ret |
| |
| |
| /* |
| * int gateA20(int linear) |
| * |
| * Gate address-line 20 for high memory. |
| * |
| * This routine is probably overconservative in what it does, but so what? |
| * |
| * It also eats any keystrokes in the keyboard buffer. :-( |
| * |
| * on call: linear=0 for a20 off and 1 for on |
| * |
| * return value: 0 for failure and 1 for success |
| * |
| */ |
| |
| ENTRY(gateA20) |
| pushl %ebp |
| movl 8(%esp), %edx /* the value of `linear' */ |
| |
| #if 1 |
| /* first, check if A20 status is already what we desired. */ |
| |
| /* disable CPU cache for the test to work reliably. */ |
| movl %cr0, %eax |
| pushl %eax /* save old cr0 */ |
| // andl $0x00000011, %eax |
| orl $0x60000000, %eax /* set CD and NW */ |
| movl %eax, %cr0 |
| movl %cr0, %eax |
| testl $0x60000000, %eax /* check if we can use wbinvd. */ |
| jz 1f /* CPU has no wbinvd instruction. */ |
| wbinvd |
| andl $0xDFFFFFFF, %eax /* clear NW */ |
| movl %eax, %cr0 |
| 1: |
| movl 0x00000000, %eax |
| pushl %eax /* save old int0 vector */ |
| |
| cmpl 0x00100000, %eax |
| jne 1f /* A20 is on */ |
| |
| notl 0x00000000 |
| movl 0x00000000, %eax |
| cmpl 0x00100000, %eax |
| notl 0x00000000 /* logical `NOT' won't touch flags */ |
| 1: |
| /* ZF=0(means not equal) for A20 on, ZF=1(means equal) for A20 off. */ |
| sete %al /* save ZF to AL */ |
| testl %edx, %edx |
| sete %ah /* save ZF to AH */ |
| cmpb %al, %ah |
| |
| /* ZF=1(means equal) for desired and we needn't do anything. */ |
| |
| popl %eax /* restore int0 vector */ |
| movl %eax, 0x00000000 |
| popl %eax /* restore cr0 */ |
| movl %eax, %cr0 |
| movl $1, %eax /* return value 1 means success */ |
| |
| jz 1f /* success */ |
| |
| #endif |
| |
| /* failure, then call a real-mode function enable_disable_a20 */ |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| cli /* yes, keep interrupt off when controlling A20 */ |
| |
| movw $0x00ff, %cx # try so many times on failure |
| movb $0x01, %dh # non-zero means `reset keyborad' |
| call enable_disable_a20 |
| sete %dl # DL=1 means success |
| |
| #if 0 /* comment out to avoid hanging ONDA C51G board. */ |
| pushal |
| |
| #if 0 |
| # qemu arrow keys will not work if we turn no NumLock in this way. |
| |
| /* Turn on Number Lock */ |
| orb $0x20, 0x417 |
| #endif |
| |
| /* reset mouse */ |
| movw $0xC201, %ax |
| int $0x15 |
| |
| /* disable mouse */ |
| movw $0xC200, %ax |
| xorw %bx, %bx /* BH=0 means disable */ |
| int $0x15 |
| |
| popal |
| #endif |
| |
| #if 0 |
| movw $0x2400, %ax |
| testw %dx, %dx |
| jz 1f |
| incw %ax |
| 1: stc |
| int $0x15 |
| jnc 2f |
| |
| /* set non-zero if failed */ |
| movb $1, %ah |
| |
| /* save the status */ |
| 2: movb %ah, %dl |
| #endif |
| |
| DATA32 call EXT_C(real_to_prot) |
| |
| .code32 |
| |
| movzbl %dl, %eax |
| |
| 1: |
| popl %ebp |
| ret |
| #if 0 |
| popl %ebp |
| testb %dl, %dl |
| jnz 3f |
| ret |
| |
| 3: /* use keyboard controller */ |
| pushl %eax |
| |
| call gloop1 |
| |
| movb $KC_CMD_WOUT, %al |
| outb $K_CMD |
| |
| gloopint1: |
| inb $K_STATUS |
| andb $K_IBUF_FUL, %al |
| jnz gloopint1 |
| |
| movb $KB_OUTPUT_MASK, %al |
| cmpb $0, 0x8(%esp) |
| jz gdoit |
| |
| orb $KB_A20_ENABLE, %al |
| gdoit: |
| outb $K_RDWR |
| |
| call gloop1 |
| |
| /* output a dummy command (USB keyboard hack) */ |
| movb $0xff, %al |
| outb $K_CMD |
| call gloop1 |
| |
| popl %eax |
| ret |
| |
| gloop1: |
| inb $K_STATUS |
| andb $K_IBUF_FUL, %al |
| jnz gloop1 |
| |
| gloop2: |
| inb $K_STATUS |
| andb $K_OBUF_FUL, %al |
| jz gloop2ret |
| inb $K_RDWR |
| jmp gloop2 |
| |
| gloop2ret: |
| ret |
| |
| #endif |
| |
| .code16 |
| |
| // #include "a20.inc" |
| |
| .code32 |
| |
| ENTRY(patch_code) /* labels start with "pc_" */ |
| .code16 |
| |
| mov %cs, %ax |
| mov %ax, %ds |
| mov %ax, %es |
| mov %ax, %fs |
| mov %ax, %gs |
| ADDR32 movl $0, 0 |
| pc_stop: |
| hlt |
| DATA32 jmp pc_stop |
| ENTRY(patch_code_end) |
| |
| .code32 |
| |
| |
| /* |
| * linux_boot() |
| * |
| * Does some funky things (including on the stack!), then jumps to the |
| * entry point of the Linux setup code. |
| */ |
| |
| VARIABLE(linux_text_len) |
| .long 0 |
| |
| VARIABLE(linux_data_tmp_addr) |
| .long 0 |
| |
| VARIABLE(linux_data_real_addr) |
| .long 0 |
| |
| ENTRY(linux_boot) |
| /* don't worry about saving anything, we're committed at this point */ |
| cld /* forward copying */ |
| |
| /* copy kernel */ |
| movl EXT_C(linux_text_len), %ecx |
| addl $3, %ecx |
| shrl $2, %ecx |
| movl $LINUX_BZIMAGE_ADDR, %esi # 0x100000 |
| movl $LINUX_ZIMAGE_ADDR, %edi # 0x10000 |
| |
| rep movsl |
| |
| ENTRY(big_linux_boot) |
| movl EXT_C(linux_data_real_addr), %ebx |
| |
| /* copy the real mode part */ |
| movl EXT_C(linux_data_tmp_addr), %esi |
| movl %ebx, %edi |
| movl $LINUX_SETUP_MOVE_SIZE, %ecx # 0x9100 |
| cld |
| rep movsb |
| |
| /* change %ebx to the segment address */ |
| shrl $4, %ebx #; CS of LINUX SETUP, high word = 0 |
| movl %ebx, %eax |
| addl $0x20, %eax #; one sector |
| movw %ax, 1f // linux_setup_seg |
| |
| #; /* XXX new stack pointer in safe area for calling functions */ |
| #; movl $0x4000, %esp |
| #; call EXT_C(stop_floppy) |
| |
| /* final setup for linux boot */ |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| /* final setup for linux boot */ |
| cli |
| movw %bx, %ss |
| movw $LINUX_SETUP_STACK, %sp # 0x9000 |
| |
| movw %bx, %ds |
| movw %bx, %es |
| movw %bx, %fs |
| movw %bx, %gs |
| |
| #; /* Reset floppy. Not required. */ |
| #; xorw %ax, %ax |
| #; xorb %dl, %dl |
| #; int $0x13 |
| |
| /* jump to start */ |
| /* ljmp */ |
| .byte 0xea |
| .word 0 |
| 1: //linux_setup_seg: |
| .word 0 |
| |
| |
| .code32 |
| |
| |
| /* |
| * multi_boot(int start, int mb_info) |
| * |
| * This starts a kernel in the manner expected of the multiboot standard. |
| */ |
| |
| ENTRY(multi_boot) |
| /* no need to save anything */ |
| call EXT_C(stop_floppy) |
| |
| movl $0x2BADB002, %eax |
| movl 0x8(%esp), %ebx |
| |
| /* boot kernel here (absolute address call) */ |
| call *0x4(%esp) |
| |
| /* error */ |
| call EXT_C(stop) |
| |
| #endif /* ! STAGE1_5 */ |
| |
| /* |
| * void console_putchar (int c) |
| * |
| * Put the character C on the console. Because GRUB wants to write a |
| * character with an attribute, this implementation is a bit tricky. |
| * If C is a control character (CR, LF, BEL, BS), use INT 10, AH = 0Eh |
| * (TELETYPE OUTPUT). Otherwise, save the original position, put a space, |
| * save the current position, restore the original position, write the |
| * character and the attribute, and restore the current position. |
| * |
| * The reason why this is so complicated is that there is no easy way to |
| * get the height of the screen, and the TELETYPE OUPUT BIOS call doesn't |
| * support setting a background attribute. |
| */ |
| ENTRY(console_putchar) |
| movl 0x4(%esp), %edx |
| pusha |
| #ifdef STAGE1_5 |
| movb $0x07, %bl |
| #else |
| movl EXT_C(console_current_color), %ebx |
| #endif |
| |
| call EXT_C(prot_to_real) |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| movb %dl, %al |
| xorb %bh, %bh |
| |
| #ifndef STAGE1_5 |
| /* use teletype output if control character */ |
| cmpb $0x7, %al |
| je 1f |
| cmpb $0x8, %al |
| je 1f |
| cmpb $0xa, %al |
| je 1f |
| cmpb $0xd, %al |
| je 1f |
| |
| /* save the character and the attribute on the stack */ |
| pushw %ax |
| pushw %bx |
| |
| /* get the current position */ |
| movb $0x3, %ah |
| int $0x10 |
| |
| /* check the column with the width */ |
| cmpb $79, %dl |
| jl 2f |
| |
| /* print CR and LF, if next write will exceed the width */ |
| movw $0x0e0d, %ax |
| int $0x10 |
| movb $0x0a, %al |
| int $0x10 |
| |
| /* get the current position */ |
| movb $0x3, %ah |
| int $0x10 |
| |
| 2: |
| /* restore the character and the attribute */ |
| popw %bx |
| popw %ax |
| |
| /* write the character with the attribute */ |
| movb $0x9, %ah |
| movw $1, %cx |
| int $0x10 |
| |
| /* move the cursor forward */ |
| incb %dl |
| movb $0x2, %ah |
| int $0x10 |
| |
| jmp 3f |
| #endif /* ! STAGE1_5 */ |
| |
| 1: movb $0xe, %ah |
| int $0x10 |
| |
| 3: DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| popa |
| ret |
| |
| |
| #ifndef STAGE1_5 |
| |
| #if 0 |
| /* this table is used in translate_keycode below */ |
| translation_table: |
| .word KEY_LEFT, 2 |
| .word KEY_RIGHT, 6 |
| .word KEY_UP, 16 |
| .word KEY_DOWN, 14 |
| .word KEY_HOME, 1 |
| .word KEY_END, 5 |
| .word KEY_DC, 4 |
| .word KEY_BACKSPACE, 8 |
| .word KEY_PPAGE, 7 |
| .word KEY_NPAGE, 3 |
| .word 0 |
| |
| /* |
| * translate_keycode translates the key code %dx to an ascii code. |
| */ |
| .code16 |
| |
| translate_keycode: |
| pushw %bx |
| pushw %si |
| |
| movw $ABS(translation_table), %si |
| |
| 1: lodsw |
| /* check if this is the end */ |
| testw %ax, %ax |
| jz 2f |
| /* load the ascii code into %ax */ |
| movw %ax, %bx |
| lodsw |
| /* check if this matches the key code */ |
| cmpw %bx, %dx |
| jne 1b |
| /* translate %dx, if successful */ |
| movw %ax, %dx |
| |
| 2: popw %si |
| popw %bx |
| ret |
| #endif |
| |
| .code32 |
| |
| |
| /* |
| * remap_ascii_char remaps the ascii code %dl to another if the code is |
| * contained in ASCII_KEY_MAP. |
| */ |
| .code16 |
| |
| remap_ascii_char: |
| pushw %si |
| |
| movw $ABS(EXT_C(ascii_key_map)), %si |
| 1: |
| lodsw |
| /* check if this is the end */ |
| testw %ax, %ax |
| jz 2f |
| /* check if this matches the ascii code */ |
| cmpb %al, %dl |
| jne 1b |
| /* if so, perform the mapping */ |
| movb %ah, %dl |
| 2: |
| /* restore %si */ |
| popw %si |
| |
| ret |
| |
| .code32 |
| |
| .align 4 |
| ENTRY(ascii_key_map) |
| .space (KEY_MAP_SIZE + 1) * 2 |
| |
| |
| /* |
| * int console_getkey (void) |
| * BIOS call "INT 16H Function 00H" to read character from keyboard |
| * Call with %ah = 0x0 |
| * Return: %ah = keyboard scan code |
| * %al = ASCII character |
| */ |
| |
| ENTRY(console_getkey) |
| push %ebp |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| sti /* getkey needs interrupt on */ |
| |
| #; work around for Apple BIOS getkey bug |
| #; check the keyboard buffer, until there is a keypress. |
| 1: |
| movb $0x01, %ah #; check key |
| int $0x16 |
| jz 1b #; no keypress |
| |
| xorw %ax, %ax |
| int $0x16 |
| |
| movw %ax, %dx /* real_to_prot uses %eax */ |
| //call translate_keycode |
| call remap_ascii_char |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movw %dx, %ax |
| |
| pop %ebp |
| ret |
| |
| |
| /* |
| * int console_checkkey (void) |
| * if there is a character pending, return it; otherwise return -1 |
| * BIOS call "INT 16H Function 01H" to check whether a character is pending |
| * Call with %ah = 0x1 |
| * Return: |
| * If key waiting to be input: |
| * %ah = keyboard scan code |
| * %al = ASCII character |
| * Zero flag = clear |
| * else |
| * Zero flag = set |
| */ |
| ENTRY(console_checkkey) |
| push %ebp |
| xorl %edx, %edx |
| |
| call EXT_C(prot_to_real) /* enter real mode */ |
| |
| .code16 |
| |
| sti /* checkkey needs interrupt on */ |
| |
| movb $0x1, %ah |
| int $0x16 |
| |
| DATA32 jz notpending |
| |
| movw %ax, %dx |
| //call translate_keycode |
| call remap_ascii_char |
| DATA32 jmp pending |
| |
| notpending: |
| movl $0xFFFFFFFF, %edx |
| |
| pending: |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| mov %edx, %eax |
| |
| pop %ebp |
| ret |
| |
| |
| /* |
| * int console_getxy (void) |
| * BIOS call "INT 10H Function 03h" to get cursor position |
| * Call with %ah = 0x03 |
| * %bh = page |
| * Returns %ch = starting scan line |
| * %cl = ending scan line |
| * %dh = row (0 is top) |
| * %dl = column (0 is left) |
| */ |
| |
| |
| ENTRY(console_getxy) |
| push %ebp |
| push %ebx /* save EBX */ |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| xorb %bh, %bh /* set page to 0 */ |
| movb $0x3, %ah |
| int $0x10 /* get cursor position */ |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movb %dl, %ah |
| movb %dh, %al |
| |
| pop %ebx |
| pop %ebp |
| ret |
| |
| |
| /* |
| * void console_gotoxy(int x, int y) |
| * BIOS call "INT 10H Function 02h" to set cursor position |
| * Call with %ah = 0x02 |
| * %bh = page |
| * %dh = row (0 is top) |
| * %dl = column (0 is left) |
| */ |
| |
| |
| ENTRY(console_gotoxy) |
| push %ebp |
| push %ebx /* save EBX */ |
| |
| movb 0xc(%esp), %dl /* %dl = x */ |
| movb 0x10(%esp), %dh /* %dh = y */ |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| xorb %bh, %bh /* set page to 0 */ |
| movb $0x2, %ah |
| int $0x10 /* set cursor position */ |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| pop %ebx |
| pop %ebp |
| ret |
| |
| |
| /* |
| * void console_cls (void) |
| * BIOS call "INT 10H Function 09h" to write character and attribute |
| * Call with %ah = 0x09 |
| * %al = (character) |
| * %bh = (page number) |
| * %bl = (attribute) |
| * %cx = (number of times) |
| */ |
| |
| |
| ENTRY(console_cls) |
| push %ebp |
| push %ebx /* save EBX */ |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| /* move the cursor to the beginning */ |
| movb $0x02, %ah |
| xorb %bh, %bh |
| xorw %dx, %dx |
| int $0x10 |
| |
| /* write spaces to the entire screen */ |
| movw $0x0920, %ax |
| movw $0x07, %bx |
| movw $(80 * 25), %cx |
| int $0x10 |
| |
| /* move back the cursor */ |
| movb $0x02, %ah |
| int $0x10 |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| pop %ebx |
| pop %ebp |
| ret |
| |
| |
| /* |
| * int console_setcursor (int on) |
| * BIOS call "INT 10H Function 01h" to set cursor type |
| * Call with %ah = 0x01 |
| * %ch = cursor starting scanline |
| * %cl = cursor ending scanline |
| */ |
| |
| console_cursor_state: |
| .byte 1 |
| console_cursor_shape: |
| .word 0 |
| |
| ENTRY(console_setcursor) |
| push %ebp |
| push %ebx |
| |
| /* check if the standard cursor shape has already been saved */ |
| movw console_cursor_shape, %ax |
| testw %ax, %ax |
| jne 1f |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| movb $0x03, %ah |
| xorb %bh, %bh |
| int $0x10 |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movw %cx, console_cursor_shape |
| 1: |
| /* set %cx to the designated cursor shape */ |
| movw $0x2000, %cx |
| movl 0xc(%esp), %ebx |
| testl %ebx, %ebx |
| jz 2f |
| movw console_cursor_shape, %cx |
| 2: |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| movb $0x1, %ah |
| int $0x10 |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movzbl console_cursor_state, %eax |
| movb %bl, console_cursor_state |
| |
| pop %ebx |
| pop %ebp |
| ret |
| |
| /* graphics mode functions */ |
| #ifdef SUPPORT_GRAPHICS |
| VARIABLE(cursorX) |
| .word 0 |
| VARIABLE(cursorY) |
| .word 0 |
| VARIABLE(cursorCount) |
| .word 0 |
| VARIABLE(cursorBuf) |
| .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
| |
| |
| /* |
| * int set_videomode(mode) |
| * BIOS call "INT 10H Function 0h" to set video mode |
| * Call with %ah = 0x0 |
| * %al = video mode |
| * Returns old videomode. |
| */ |
| ENTRY(set_videomode) |
| push %ebp |
| push %ebx |
| push %ecx |
| |
| movb 0x10(%esp), %cl |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| xorw %bx, %bx |
| movb $0xf, %ah |
| int $0x10 /* Get Current Video mode */ |
| movb %al, %ch |
| xorb %ah, %ah |
| movb %cl, %al |
| int $0x10 /* Set Video mode */ |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| xorb %ah, %ah |
| movb %ch, %al |
| |
| pop %ecx |
| pop %ebx |
| pop %ebp |
| ret |
| |
| |
| /* |
| * unsigned char * graphics_get_font() |
| * BIOS call "INT 10H Function 11h" to set font |
| * Call with %ah = 0x11 |
| */ |
| ENTRY(graphics_get_font) |
| push %ebp |
| push %ebx |
| push %ecx |
| push %edx |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| movw $0x1130, %ax |
| movb $6, %bh /* font 8x16 */ |
| int $0x10 |
| movw %bp, %dx |
| movw %es, %cx |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| xorl %eax, %eax |
| movw %cx, %ax |
| shll $4, %eax |
| movw %dx, %ax |
| |
| pop %edx |
| pop %ecx |
| pop %ebx |
| pop %ebp |
| ret |
| |
| |
| |
| /* |
| * graphics_set_palette(index, red, green, blue) |
| * BIOS call "INT 10H Function 10h" to set individual dac register |
| * Call with %ah = 0x10 |
| * %bx = register number |
| * %ch = new value for green (0-63) |
| * %cl = new value for blue (0-63) |
| * %dh = new value for red (0-63) |
| */ |
| |
| ENTRY(graphics_set_palette) |
| push %ebp |
| push %eax |
| push %ebx |
| push %ecx |
| push %edx |
| |
| movw $0x3c8, %bx /* address write mode register */ |
| |
| /* wait vertical retrace */ |
| |
| movw $0x3da, %dx |
| l1b: inb %dx, %al /* wait vertical active display */ |
| test $8, %al |
| jnz l1b |
| |
| l2b: inb %dx, %al /* wait vertical retrace */ |
| test $8, %al |
| jnz l2b |
| |
| mov %bx, %dx |
| movb 0x18(%esp), %al /* index */ |
| outb %al, %dx |
| inc %dx |
| |
| movb 0x1c(%esp), %al /* red */ |
| outb %al, %dx |
| |
| movb 0x20(%esp), %al /* green */ |
| outb %al, %dx |
| |
| movb 0x24(%esp), %al /* blue */ |
| outb %al, %dx |
| |
| movw 0x18(%esp), %bx |
| |
| call EXT_C(prot_to_real) |
| |
| .code16 |
| |
| //sti /* it is not bad keeping interrupt off */ |
| |
| movb %bl, %bh |
| movw $0x1000, %ax |
| int $0x10 |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| pop %edx |
| pop %ecx |
| pop %ebx |
| pop %eax |
| pop %ebp |
| ret |
| |
| #endif /* SUPPORT_GRAPHICS */ |
| |
| /* |
| * getrtsecs() |
| * if a seconds value can be read, read it and return it (BCD), |
| * otherwise return 0xFF |
| * BIOS call "INT 1AH Function 02H" to check whether a character is pending |
| * Call with %ah = 0x2 |
| * Return: |
| * If RT Clock can give correct values |
| * %ch = hour (BCD) |
| * %cl = minutes (BCD) |
| * %dh = seconds (BCD) |
| * %dl = daylight savings time (00h std, 01h daylight) |
| * Carry flag = clear |
| * else |
| * Carry flag = set |
| * (this indicates that the clock is updating, or |
| * that it isn't running) |
| */ |
| ENTRY(getrtsecs) |
| push %ebp |
| |
| call EXT_C(prot_to_real) /* enter real mode */ |
| |
| .code16 |
| |
| #;sti /* getrtsecs needs interrupt on */ |
| sti #; added 2006-11-30 |
| |
| #if 0 |
| movb $0x2, %ah |
| |
| /* Ralf Brown's Interrupt List says: |
| * |
| * BUG: Some BIOSes leave CF unchanged if successful, |
| * so CF should be cleared before calling this function |
| */ |
| |
| clc |
| int $0x1a |
| |
| DATA32 jnc gottime |
| movb $0xff, %dh |
| |
| gottime: |
| #else |
| /* The call int1A/ah=02 could fail all the time. |
| * So we should avoid using it. Instead, we use ticks at 0040:006C. |
| * - Tinybit 2007-04-21 |
| */ |
| pushl %ecx |
| movl 0x46C, %eax |
| movl $5, %ecx /* 5 seconds */ |
| mull %ecx |
| xorl %edx, %edx |
| movl $91, %ecx /* 91 ticks = 5 seconds */ |
| divl %ecx |
| xorl %edx, %edx |
| movl $60, %ecx |
| divl %ecx /* EDX=seconds (0 .. 59) */ |
| movb %dl, %dh |
| popl %ecx |
| |
| #endif |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movb %dh, %al |
| |
| pop %ebp |
| ret |
| |
| |
| #if 0 |
| /* This BIOS call should NOT be called since it will clear the byte at 0040:0070. */ |
| |
| /* |
| * currticks() |
| * return the real time in ticks, of which there are about |
| * 18-20 per second |
| */ |
| ENTRY(currticks) |
| pushl %ebp |
| |
| call EXT_C(prot_to_real) /* enter real mode */ |
| |
| .code16 |
| |
| #;sti /* currticks needs interrupt on */ |
| sti #; added 2006-11-30 |
| |
| /* %ax is already zero */ |
| int $0x1a |
| |
| DATA32 call EXT_C(real_to_prot) |
| .code32 |
| |
| movl %ecx, %eax |
| shll $16, %eax |
| movw %dx, %ax |
| |
| popl %ebp |
| ret |
| #endif |
| |
| #endif /* STAGE1_5 */ |
| |
| /* |
| * This is the area for all of the special variables. |
| */ |
| |
| .p2align 2 /* force 4-byte alignment */ |
| |
| protstack: |
| .long PROTSTACKINIT |
| |
| VARIABLE(boot_drive) |
| #ifdef SUPPORT_DISKLESS |
| .long NETWORK_DRIVE |
| #else |
| .long 0 |
| #endif |
| |
| VARIABLE(install_second_sector) |
| .long 0 |
| |
| /* an address can only be long-jumped to if it is in memory, this |
| is used by multiple routines */ |
| offset: |
| .long 0x8000 |
| segment: |
| .word 0 |
| |
| VARIABLE(apm_bios_info) |
| .word 0 /* version */ |
| .word 0 /* cseg */ |
| .long 0 /* offset */ |
| .word 0 /* cseg_16 */ |
| .word 0 /* dseg_16 */ |
| .word 0 /* cseg_len */ |
| .word 0 /* cseg_16_len */ |
| .word 0 /* dseg_16_len */ |
| |
| /* |
| * This is the Global Descriptor Table |
| * |
| * An entry, a "Segment Descriptor", looks like this: |
| * |
| * 31 24 19 16 7 0 |
| * ------------------------------------------------------------ |
| * | | |B| |A| | | |1|0|E|W|A| | |
| * | BASE 31..24 |G|/|0|V| LIMIT |P|DPL| TYPE | BASE 23:16 | |
| * | | |D| |L| 19..16| | |1|1|C|R|A| | |
| * ------------------------------------------------------------ |
| * | | | |
| * | BASE 15..0 | LIMIT 15..0 | |
| * | | | |
| * ------------------------------------------------------------ |
| * |
| * Note the ordering of the data items is reversed from the above |
| * description. |
| */ |
| |
| .p2align 2 /* force 4-byte alignment */ |
| gdt: |
| .word 0, 0 |
| .byte 0, 0, 0, 0 |
| |
| /* code segment */ |
| .word 0xFFFF, 0 |
| .byte 0, 0x9A, 0xCF, 0 |
| |
| /* data segment */ |
| .word 0xFFFF, 0 |
| .byte 0, 0x92, 0xCF, 0 |
| |
| /* 16 bit real mode CS */ |
| .word 0xFFFF, 0 |
| .byte 0, 0x9E, 0, 0 |
| |
| /* 16 bit real mode DS */ |
| .word 0xFFFF, 0 |
| .byte 0, 0x92, 0, 0 |
| |
| |
| /* this is the GDT descriptor */ |
| gdtdesc: |
| .word 0x27 /* limit */ |
| .long gdt /* addr */ |
| |
| .code32 |
| |
| /* this code will be moved to and get executed at HMA_ADDR=0x2B0000 */ |
| |
| /* our gdt starts at HMA_ADDR=0x2B0000 */ |
| |
| |
| |
| ENTRY(HMA_start) |
| |
| /* the first entry of GDT, i.e., the default null entry, |
| * can be any value. it never get used. So we use these |
| * 8 bytes for our jmp and GDT descriptor |
| */ |
| |
| jmp 1f /* two-byte short jmp */ |
| |
| . = HMA_start + 2 |
| |
| /* 6-byte GDT descriptor */ |
| gdtdescHMA: |
| .word 0x27 /* limit */ |
| .long HMA_ADDR /* linear base address */ |
| |
| // this is the default null entry in GDT |
| // |
| // .word 0, 0 |
| // .byte 0, 0, 0, 0 |
| |
| . = HMA_start + 8 |
| |
| /* code segment, although it is no use here for now */ |
| .word 0xFFFF, 0 |
| .byte 0, 0x9A, 0xCF, 0 |
| |
| /* data segment, although it is no use here for now */ |
| .word 0xFFFF, 0 |
| .byte 0, 0x92, 0xCF, 0 |
| |
| // /* 16 bit real mode CS */ |
| // .word 0xFFFF, 0 |
| // .byte 0, 0x9E, 0, 0 |
| // |
| // /* 16 bit real mode DS */ |
| // .word 0xFFFF, 0 |
| // .byte 0, 0x92, 0, 0 |
| // |
| // . = (HMA_start - 0x10) + 0xfff0 |
| |
| #if 0 |
| /* real mode code segment base=0xffff0 */ |
| .word 0xFFFF, 0xfff0 |
| .byte 0x0f, 0x9E, 0, 0 |
| |
| /* real mode data segment base=0xffff0 */ |
| .word 0xFFFF, 0xfff0 |
| .byte 0x0f, 0x92, 0, 0 |
| #else |
| /* 16-bit code segment base=0x2B0000 */ |
| .word 0xFFFF, 0x0000 |
| .byte 0x2B, 0x9E, 0, 0 |
| |
| /* real mode data segment base=0x200 */ |
| .word 0xFFFF, 0x0200 |
| .byte 0x00, 0x92, 0, 0 |
| #endif |
| |
| 1: |
| /* set up to pass boot drive */ |
| movb EXT_C(boot_drive), %dl |
| |
| /* check if the --ebx option is given. */ |
| movl (chain_ebx_set - HMA_start + HMA_ADDR), %eax |
| testl %eax, %eax |
| jz 1f |
| movl (chain_ebx - HMA_start + HMA_ADDR), %ebx |
| 1: |
| |
| /* check if the --edx option is given. */ |
| movl (chain_edx_set - HMA_start + HMA_ADDR), %eax |
| testl %eax, %eax |
| jz 1f |
| movl (chain_edx - HMA_start + HMA_ADDR), %edx |
| 1: |
| |
| /* move new loader from extended memory to conventional memory. |
| * this will overwrite our GRUB code, data and stack, so we should not |
| * use instuctions like push/pop/call/ret, and we should not use |
| * functions like gateA20(). |
| */ |
| |
| // /* clear the memory range 0000:7e00 - 2000:0000 */ |
| // movl $0x00007e00, %edi |
| // movl $((0x00020000 - 0x00007e00) / 4), %ecx |
| // xorl %eax, %eax |
| // cld |
| // repz stosl |
| |
| /* the new loader is currently at 0x200000 */ |
| movl $0x00200000, %esi |
| xorl %eax, %eax |
| xorl %edi, %edi |
| movw (chain_load_segment - HMA_start + HMA_ADDR), %di |
| shll $4, %edi |
| movw (chain_load_offset - HMA_start + HMA_ADDR), %ax |
| addl %eax, %edi |
| //movl $0x00007c00, %edi |
| movl (chain_load_length - HMA_start + HMA_ADDR), %ecx |
| cld |
| repz movsb |
| |
| //#define WRAPCODEBEGIN 0xb0 |
| //#define WRAPCODESIZE 0x50 |
| |
| #if 0 |
| movl (chain_enable_gateA20 - HMA_start + HMA_ADDR), %eax |
| testl %eax, %eax |
| jnz 1f |
| |
| #if 1 |
| /* disable A20 */ |
| #else |
| /* move real mode code(running when gateA20 is disabled) to 0x300 */ |
| |
| movl $(HMA_ADDR + WrapCodeBegin - HMA_start), %esi |
| movl $0x00000300, %edi |
| movl $(WrapCodeEnd - WrapCodeBegin), %ecx |
| repz movsb |
| #endif |
| 1: |
| #endif |
| /* switch to real mode */ |
| |
| /* set new GDT */ |
| lgdt (gdtdescHMA - HMA_start + HMA_ADDR) |
| |
| /* set up segment limits */ |
| movw $PSEUDO_RM_DSEG, %ax |
| movw %ax, %ds |
| movw %ax, %es |
| movw %ax, %fs |
| movw %ax, %gs |
| movw %ax, %ss |
| |
| movl $0x200, %esp /* points to end of interrupt vector table */ |
| /* SS base=0x200, so SS:SP=physical 0x400 */ |
| /* jump to a 16 bit segment, this might be an extra step: |
| * set up CS limit, also clear high word of EIP |
| */ |
| #if 0 |
| ljmp $PSEUDO_RM_CSEG, $(1f - HMA_start + 0x10) |
| #else |
| ljmp $PSEUDO_RM_CSEG, $(1f - HMA_start) |
| #endif |
| 1: |
| .code16 |
| |
| /* clear the PE bit of CR0 */ |
| movl %cr0, %eax |
| andl $CR0_PE_OFF, %eax |
| movl %eax, %cr0 |
| |
| #if 1 |
| /* setup DS, ES, SS, FS and GS before loading CS */ |
| xorl %eax, %eax |
| movw %ax, %ds |
| movw %ax, %es |
| movw %ax, %ss |
| movl $0x400, %esp |
| movw %ax, %fs |
| movw %ax, %gs |
| #endif |
| |
| /* flush prefetch queue, reload %cs */ |
| #if 1 |
| .byte 0xEA /* ljmp 0000:7c00 */ |
| VARIABLE(chain_boot_IP) |
| .word 0x7c00 /* offset */ |
| VARIABLE(chain_boot_CS) |
| .word 0x0000 /* segment */ |
| #else |
| ljmp $0xffff, $(1f - HMA_start + 0x10) |
| 1: |
| /* we are in real mode now |
| * set up the real mode segment registers : DS, SS, ES |
| */ |
| /* zero %eax */ |
| xorl %eax, %eax |
| |
| movw %ax, %ds |
| movw %ax, %es |
| movw %ax, %fs |
| movw %ax, %gs |
| movw %ax, %ss |
| |
| movl %eax, %esp /* clear high word of ESP */ |
| movw $0x400, %sp /* points to end of interrupt vector table */ |
| ///* restore interrupts */ |
| //sti |
| |
| movl %cs:(chain_enable_gateA20 - HMA_start + 0x10), %eax |
| testl %eax, %eax |
| jnz 2f |
| |
| pushal |
| ljmp $0, $0x300 //jmp WrapCodeBegin |
| #endif |
| |
| VARIABLE(chain_load_offset) |
| .word 0x7c00 |
| VARIABLE(chain_load_segment) |
| .word 0x0000 |
| VARIABLE(chain_load_length) |
| .long 0x200 |
| VARIABLE(chain_ebx) |
| .long 0 |
| VARIABLE(chain_ebx_set) |
| .long 0 |
| VARIABLE(chain_edx) |
| .long 0 |
| VARIABLE(chain_edx_set) |
| .long 0 |
| VARIABLE(chain_enable_gateA20) |
| .long 0 |
| |
| //. = HMA_start + WRAPCODEBEGIN //. = HMA_start + 0xb0 |
| |
| /* this final piece of code will also be copied to absolute |
| * address 0x300, occupying part of the real mode IDT table */ |
| #if 0 |
| WrapCodeBegin: |
| call 1f |
| popal |
| 2: |
| .byte 0xEA /* ljmp 0000:7c00 */ |
| VARIABLE(chain_boot_IP) |
| .word 0x7c00 /* offset */ |
| VARIABLE(chain_boot_CS) |
| .word 0x0000 /* segment */ |
| |
| 1: /* routine to disable gateA20 */ |
| /* first, try a BIOS call */ |
| // pushl %ebp |
| // movl 8(%esp), %edx |
| |
| // call EXT_C(prot_to_real) |
| |
| // .code16 |
| // //sti /* it is not bad keeping interrupt off */ |
| movw $0x2400, %ax |
| stc |
| int $0x15 |
| jc 1f |
| testb %ah, %ah |
| jnz 1f |
| ret |
| |
| // jnc 2f |
| |
| /* set non-zero if failed */ |
| // movb $1, %ah |
| |
| /* save the status */ |
| //2: movb %ah, %dl |
| |
| // DATA32 call EXT_C(real_to_prot) |
| // .code32 |
| |
| // popl %ebp |
| // testb %dl, %dl |
| // jnz 3f |
| // ret |
| 1: |
| /* use keyboard controller */ |
| // pushl %eax |
| |
| call 1f//gloop1 |
| |
| movb $KC_CMD_WOUT, %al |
| outb $K_CMD |
| |
| 2://gloopint1: |
| inb $K_STATUS |
| andb $K_IBUF_FUL, %al |
| jnz 2b//gloopint1 |
| |
| movb $KB_OUTPUT_MASK, %al |
| // cmpb $0, 0x8(%esp) |
| // jz gdoit |
| |
| // orb $KB_A20_ENABLE, %al |
| //gdoit: |
| outb $K_RDWR |
| |
| call 1f//gloop1 |
| |
| /* output a dummy command (USB keyboard hack) */ |
| movb $0xff, %al |
| outb $K_CMD |
| call 1f//gloop1 |
| |
| // popl %eax |
| ret |
| |
| 1://gloop1: |
| inb $K_STATUS |
| andb $K_IBUF_FUL, %al |
| jnz 1b//gloop1 |
| |
| 1://gloop2: |
| inb $K_STATUS |
| andb $K_OBUF_FUL, %al |
| jz 1f//gloop2ret |
| inb $K_RDWR |
| jmp 1b//gloop2 |
| |
| 1://gloop2ret: |
| ret |
| |
| WrapCodeEnd: |
| |
| //. = HMA_start + WRAPCODEBEGIN + WRAPCODESIZE |
| /* ensure this not exceed 0xc0 */ |
| . = . - ((WrapCodeEnd - WrapCodeBegin) / (0xc0 + 1)) |
| #endif |
| |
| /* ensure this not exceed 0xfff0, i.e., (64K -16bytes) */ |
| . = . - ((. - HMA_start) / 0xfff1) |
| |
| /* ensure this resides in the first 64KB */ |
| . = . - (ABS(.) / 0x10001) |
| |
| .code32 |
| |
| |