blob: 488b8dc0f31b2e4c5a6ecc43b8dc027fbcb3cf9d [file] [log] [blame] [raw]
/*
* 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)
. = EXT_C(main) + 0x5
/* control byte: pxe, DUCE, tune
* bit 0 = 1: disable pxe
* bit 1 = 1: disable keyboard intervention in boot process
* bit 2 = 1: disable the "unconditional command-line entrance" feature
* bit 3 = 1: disable geometry tune
*/
.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 EXT_C(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
sti /* added 2008-08-04 */
#ifndef STAGE1_5
movb 0x0410, %al
movb %al, ABS(EXT_C(floppies_orig))
movb 0x0475, %al
movb %al, ABS(EXT_C(harddrives_orig))
//movb $((int13_handler_end - int13_handler + 0x3ff) / 0x400), ABS(int13_handler)
movl 0x54, %eax
movl %eax, ABS(EXT_C(ROM_int15))
movl 0x4C, %eax
movl %eax, ABS(ROM_int13)
movl %eax, ABS(ROM_int13_dup)
cmpl $0xC0000000, %eax
jnb 1f
cmpl $0x5A000000, %eax // cmpl $0x9A000000, %eax
jb 1f
andl $0x3FFFFF, %eax
cmpl $0x100, %eax
jnz 1f
movw 0x413, %ax /* Memory size in Kb */
shlw $6, %ax /* Memory size in paragragh */
cmpw 0x4E, %ax /* 0000:004E=current int 13 segment */
jne 1f /* not hooked */
movw %ax, %ds /* DS=current int13 code segment */
/* check our int13 signature "$INT13SFGRUB4DOS" */
cmpl $0x544E4924, 0x103 /* $INT */
jnz 2f
cmpl $0x46533331, 0x107 /* 13SF */
jnz 2f
cmpl $0x42555247, 0x10B /* GRUB */
jnz 2f
cmpl $0x534F4434, 0x10F /* 4DOS */
jnz 2f
movl (ROM_int13 - int13_handler), %eax /* 0x1C=ROM int 13 */
cmpl $0x5A000000, %eax // cmpl $0x9A000000, %eax
jb 2f /* not our handler */
movl (EXT_C(ROM_int15) - int13_handler), %eax /* 0x0C=ROM int 15 */
cmpl $0x5A000000, %eax // cmpl $0x9A000000, %eax
jb 2f /* not our handler */
/* restore old emu data, except the first byte of handler size. */
movw $(0x140 - 1), %cx
movw $1, %si /* DS=current int13 code segment */
movw $ABS(int13_handler + 1), %di /* ES=0 */
repz movsb
/* calculate the new max_cdrom_id. */
movw $2, %si
lodsw /* AL=atapi_dev_count, AH=min_cdrom_id */
addb %ah, %al
decw %ax /* AL=max_cdrom_id */
movb %al, %cs:ABS(max_cdrom_id) /* CS=0 */
/* initialize bios_drive_map with hooked_drive_map */
movw $(DRIVE_MAP_SIZE * DRIVE_MAP_SLOT_SIZE / 2), %cx
movw $ABS(EXT_C(hooked_drive_map)), %si /* CS=0 */
movw $ABS(EXT_C(bios_drive_map)), %di /* ES=0 */
cs repz movsw
//xorw %ax, %ax
//movw %ax, %ds /* DS=0 */
//jmp 3f
2:
xorw %ax, %ax
movw %ax, %ds /* DS=0 */
1:
//movl ABS(EXT_C(ROM_int15)), %eax
//cmpl $0x5A000000, %eax // cmpl $0x9A000000, %eax
//jnb 3f
//movl 0x0054, %eax
//movl %eax, ABS(EXT_C(ROM_int15))
//3:
/* 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(EXT_C(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
/* 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
#if 0 /* commented out 2008-06-11 */
/* clear VDS */
andb $0xD7, 0x47B
#endif
#ifndef STAGE1_5
sti
/* check the DUCE indicator */
movl 0x5FC, %eax
movl %eax, %ecx
testb $0x0C, %al
jz 1f
orb $0x0C, %al
cmpl $0x4543554C, %eax
jne 1f
andb $0x0C, %cl /* bit2=DUCE, bit3=chs-no-tune */
orb %cl, ABS(EXT_C(main)) + 5 /* at 0x8205 */
1:
movw $1, %bx /* BL=1, BH=0 */
testb $0x02, ABS(EXT_C(main)) + 5 /* disable keyboard intervention? */
jnz 4f /* yes */
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. */
testb $0x04, ABS(EXT_C(main)) + 5 /* at 0x8205, bit2=DUCE */
jnz 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
4:
movb %bh, EXT_C(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
testb $0x02, ABS(EXT_C(main)) + 5 /* disable keyboard intervention? */
jnz 4f /* yes */
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
4:
/* 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 EXT_C(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, EXT_C(config_file) /* AL == 0 */
jnz 2f
movl EXT_C(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, EXT_C(preset_menu) /* use new menu at 0x800 */
1:
#endif /* !STAGE1_5 */
/* if force_cdrom_as_boot_device==0, we are running by configfile, so we do not clear bss */
cmpl $0, EXT_C(force_cdrom_as_boot_device)
je 1f
/* 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
1:
/*
* 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)
call EXT_C(cmain)
/*
* 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:
sti /* for hardware interrupt or watchdog */
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)
pushl %ebx
call EXT_C(prot_to_real)
.code16
sti /* for hardware interrupt or watchdog */
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
movw %es, %cx
jmp 2f
1:
xorw %bx, %bx
xorw %cx, %cx
2:
DATA32 call EXT_C(real_to_prot)
.code32
xorl %eax, %eax
movw %cx, %ax
shll $4, %eax
andl $0xFFFF, %ebx
addl %ebx, %eax
jz 3f
movl 0x10(%eax), %ebx
movl %ebx, EXT_C(pxe_entry)
xorl %ecx, %ecx
movl 0x2A(%eax), %ebx
cmpl 0x32(%eax), %ebx
ja 1f
movl 0x32(%eax), %ebx
movw 0x36(%eax), %cx
jmp 2f
1:
movw 0x2E(%eax), %cx
2:
addl %ecx, %ebx
addl $1023, %ebx
shrl $10, %ebx
movw %bx, EXT_C(pxe_freemem)
3:
popl %ebx
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
sti /* for hardware interrupt or watchdog */
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
movzwl %cx, %eax
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
movl 8(%ebp), %edx
movl 12(%ebp), %esi /* num */
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
sti /* for hardware interrupt or watchdog */
movw %si, %cx /* num */
1:
pushw %cx /* blocks to read */
pushw %di /* block size */
pushl %ebx /* pxe_entry */
pushl %edx /* data pointer */
pushw $0x22 // PXENV_TFTP_READ
movw %sp, %bp
lesw 2(%bp), %si /* ES:SI=data pointer */
movw %di, %es:4(%si) /* block size */
lcall *6(%bp)
movw %sp, %bp
lesw 2(%bp), %si /* ES:SI=data pointer */
popw %dx // PXENV_TFTP_READ
popl %edx /* data pointer */
popl %ebx /* pxe_entry */
popw %di /* block size */
popw %cx /* blocks to read */
cld
cmpw $0, %es:(%si)
jnz 2f
movw %es:4(%si), %bp
// cmpw $512, %dx
// jb 2f
// cmpw %di, %dx
// ja 2f
// je 3f
// cmpl EXT_C(filemax), %edx
// jae 3f
// movl %edx, EXT_C(pxe_blksize)
// movl %edx, %edi
//3:
addw %bp, %es:6(%si)
cmpw %di, %bp
jb 2f
loop 1b
2:
//addw $10, %sp
movw %ax, %cx
DATA32 call EXT_C(real_to_prot)
.code32
movzwl %cx, %eax
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)
pushal
call EXT_C(prot_to_real)
.code16
sti #; added 2006-11-30
xorb %dl, %dl
#if defined(STAGE1_5) || 1
int $0x13
#else
call safe_int13
#endif
DATA32 call EXT_C(real_to_prot)
.code32
popal
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
/*
* int tpm_init (void)
* return non-zero for success and zero for failure.
*/
ENTRY(tpm_init)
pushl %ebp
pushl %ebx
pushl %esi
pushl %edi
call EXT_C(prot_to_real) /* enter real mode */
.code16
sti
movw $0xBB00, %ax
int $0x1A
testl %eax, %eax
jnz 1f /* failure */
cmpl $0x41504354, %ebx /* "TCPA" */
jnz 1f /* failure */
cmpw $0x102, %cx /* TCG BIOS version 1.2 */
jb 1f /* failure */
movw %ax, %es /* ES=0 */
movw $0xBB07, %ax /* eax hi word=0 */
movl $0x00000200, %ecx /* buffer size to hash */
movl $0x00000008, %edx /* PCR index for the hashed result */
xorl %esi, %esi /* place 0 into the event field */
movl $0x00007C00, %edi /* ES:DI point to data buffer to hash */
int $0x1A
testl %eax, %eax
1:
setz %dl
DATA32 call EXT_C(real_to_prot)
.code32
movzbl %dl, %eax
popl %edi
popl %esi
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 4(%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, 4(%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
/*
* set_int15_handler(void)
*
* Set up int15_handler.
*/
ENTRY(set_int15_handler)
.code32
pushl %edi
/* save the original int15 handler */
movl $0x54, %edi
#if 0
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)
#else
movl (%edi), %eax
movl %eax, ABS(int15_offset)
/* set the new int15 handler */
movl $ABS(int15_handler), %eax
stosl
#endif
popl %edi
ret
/*
* unset_int15_handler(void)
*
* Restore the original int15 handler
*/
ENTRY(unset_int15_handler)
.code32
pushl %edi
/* check if int15_handler is set */
movl $0x54, %edi
#if 0
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)
#else
movl $ABS(int15_handler), %eax
cmpl %eax, (%edi)
jne 1f
/* restore the original */
movl ABS(int15_offset), %eax
stosl
#endif
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)
.code32
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(EXT_C(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(ROM_int13)
/* 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(EXT_C(hooked_drive_map)), %esi
movl $(DRIVE_MAP_SIZE), %ecx
1:
cmpb $0xff, 1(%esi) /* Is there a mapped memdrive? */
jne 2f /* No. Try next slot */
testb $0x40, 5(%esi) /* Is To_DRIVE a CDROM? */
jz 1f /* No. Memdrive indeed. Hook int15 */
2:
/* 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)
.code32
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 */
/* check our int13 signature "$INT13SFGRUB4DOS" */
cmpl $0x544E4924, 0x103(%edi) /* $INT */
jnz 1f
cmpl $0x46533331, 0x107(%edi) /* 13SF */
jnz 1f
cmpl $0x42555247, 0x10B(%edi) /* GRUB */
jnz 1f
cmpl $0x534F4434, 0x10F(%edi) /* 4DOS */
jnz 1f
//cmpl $0x9A000000, 0x1C(%edi) /* old int 13 */
cmpl $0x5A000000, 0x1C(%edi) /* old int 13 */
jb 1f
//cmpl $0x9A000000, 0x0C(%edi) /* old int 15 */
cmpl $0x5A000000, 0x0C(%edi) /* old int 15 */
jb 1f
movl ABS(ROM_int13), %eax
cmpl 0x1C(%edi), %eax
jnz 1f
movl ABS(EXT_C(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(EXT_C(ROM_int15)), %eax
movl %eax, 0x54
/* restore the original int13 handler */
movl ABS(ROM_int13), %eax
movl %eax, 0x4c
xorl %eax, %eax /* success */
1:
/* return non-zero for failure */
popl %edi
popl %ebp
ret
/*
* Map a drive to another drive or a disk image file.
*/
.code16
.align 4
ENTRY(bios_drive_map)
.space (DRIVE_MAP_SIZE + 1) * DRIVE_MAP_SLOT_SIZE
/* 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)
/* 1-byte space reserved. */
. = int13_handler + 0x2
ENTRY(atapi_dev_count) .byte 0
ENTRY(min_cdrom_id) .byte 0xE0
. = int13_handler + 0x4
/* Signature */
.ascii "G4DS" # Please don't use this signature any longer.
# This field might be used for other purposes.
# Use signature at offset 0x103 instead.
. = int13_handler + 0x08
/* Version number */
.word 1
. = 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
VARIABLE(ROM_int15)
.long 0 /* original int15 vector */
. = int13_handler + 0x10
/* 12-byte space reserved. */
. = int13_handler + 0x1C
ROM_int13: .long 0 /* original int13 vector */
. = 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
ROM_int13_dup: /* Win9x Safe-MBR-Hook structure requires this! */
.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
#if 0
1:
/* backup far return address */
popl %cs:(int13_old_cs_ip - int13_handler)
/* build new stack: flags */
popw %cs:(int13_handler_end - 2 - int13_handler)
/* backup old stack */
movw %sp, %cs:(int13_old_sp - int13_handler)
movw %ss, %cs:(int13_old_ss - int13_handler)
/* build new stack pointer */
movw $(int13_handler_end - 2 - int13_handler), %cs:(int13_new_sp - int13_handler)
movw %cs, %cs:(int13_new_ss - int13_handler)
/* switch to new stack */
lssw %cs:(int13_new_sp - int13_handler), %sp
//pushfw /* flags already on stack */
pushw %cs
call 1f
/* restore old stack */
lssw %cs:(int13_old_sp - int13_handler), %sp
/* transfer control to caller */
ljmp *%cs:(int13_old_cs_ip - int13_handler)
//iret /* never reach here */
.align 4
int13_old_cs_ip: .long 0
int13_old_sp: .word 0
int13_old_ss: .word 0
int13_new_sp: .word 0
int13_new_ss: .word 0
#endif
1:
/* backup far return address */
popl %cs:(int13_old_cs_ip - int13_handler)
/* backup old flags */
popw %cs:(int13_old_flags - int13_handler)
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/* original stack layout for reference */
/* 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 */
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cld
movl %eax, %cs:(int13_old_eax - int13_handler)
movl %ebx, %cs:(int13_old_ebx - int13_handler)
movl %ecx, %cs:(int13_old_ecx - int13_handler)
movl %edx, %cs:(int13_old_edx - int13_handler)
movl %esi, %cs:(int13_old_esi - int13_handler)
movl %edi, %cs:(int13_old_edi - int13_handler)
movl %esp, %cs:(int13_old_esp - int13_handler)
movl %ebp, %cs:(int13_old_ebp - int13_handler)
movw %ds, %cs:(int13_old_ds - int13_handler)
movw %es, %cs:(int13_old_es - int13_handler)
cmpb $0x1a, %ah /* PS/2 low level format ESDI drive!!!! */
je error_01_disable /* disabled in any case */
cmpb $0, %cs:(EXT_C(atapi_dev_count) - int13_handler)
jz 1f /* no cdrom */
cmpb %cs:(EXT_C(min_cdrom_id) - int13_handler), %dl
jb 1f /* not cdrom drive */
cmpb %cs:(max_cdrom_id - int13_handler), %dl
jbe edd30_for_cdrom
1:
/****************************************************************************/
/* find the drive number from the drive map */
movw $(EXT_C(hooked_drive_map) - int13_handler - DRIVE_MAP_SLOT_SIZE), %bp
1:
addw $DRIVE_MAP_SLOT_SIZE, %bp
movw %bp, %cs:(int13_new_bp - int13_handler)
movl %cs:(%bp), %eax /* FROM, TO, Hmax, Smax */
/* check if this is the end */
testl %eax, %eax
jnz 3f /* not end, continue */
movl %cs:8(%bp), %eax /* StartLBA_Lo */
testl %eax, %eax
jnz 3f /* not end, continue */
movl %cs:16(%bp), %eax /* S_count_Lo */
testl %eax, %eax
jz 2f /* map whole drive to itself signals the end */
3:
/* Now this is a valid drive map slot */
cmpb %cs:(%bp), %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 %cs:2(%bp), %ax /* AL=Hmax, AH=Smax */
/* bit 1-5 already cleared for in-situ */
//andb $0xC1, %ah /* clear bit 5 - bit 1 */
testb $0x80, %cs:7(%bp) /* TO_S */
jnz 1f /* in-situ */
/* non-zero StartLBA signals emulation */
cmpl $0, %cs:8(%bp) /* StartLBA_Lo */
jnz drive_emulation
/* StartLBA_Lo == 0 */
/* if FROM and TO are both cdrom, this is a whole drive map. */
testw $0x4000, %cs:4(%bp) /* TO_C bit 14=TO has 2048-byte cdrom sector */
jz 3f /* not cdrom */
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jz 3f /* not cdrom */
movb %cs:1(%bp), %dl /* DL changed(!!) to TO_drive */
cmpb $0, %cs:(EXT_C(atapi_dev_count) - int13_handler)
jz 2f /* no cdrom by our driver, so it is a bios drive map. */
cmpb %cs:(EXT_C(min_cdrom_id) - int13_handler), %dl /* TO_drive */
jb 2f /* not cdrom drive by our driver, so it is a bios drive map. */
cmpb %cs:(max_cdrom_id - int13_handler), %dl /* TO_drive */
ja 2f /* not cdrom drive by our driver, so it is a bios drive map. */
jmp edd30_for_cdrom
3:
/* S_count being not 1 signals emulation */
movl %cs:16(%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 %cs:2(%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, %cs:7(%bp) /* TO_S */
jz 1f /* not in-situ, allow write */
testb $0x40, %cs:7(%bp) /* TO_S. bit 6 here means safe-boot */
jz 1f /* unsafe-boot, allow write */
movw %cs:(int13_old_eax - int13_handler), %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
orl 12(%si), %eax /* LBA_hi */
orl 8(%si), %eax /* LBA_lo */
je readonly_fakewrite /* deny the write and end */
1:
movb %cs:1(%bp), %dl /* Let DL access TO instead of FROM */
2:
/* might map to itself, i.e., actually not mapped */
movl %cs:(int13_old_eax - int13_handler), %eax
movl %cs:(int13_old_ebp - int13_handler), %ebp /* BP changed!! */
call backup_int13
pushw %cs:(int13_old_flags - int13_handler)
popfw
int $0x13
/* save the returned CF flag to int13_old_flags */
jnc 1f
orb $1, %cs:(int13_old_flags - int13_handler)
jmp 2f
1:
andb $0xFE, %cs:(int13_old_flags - int13_handler)
2:
call restore_int13
/* restore BP!! */
movw %cs:(int13_new_bp - int13_handler), %bp
xchgw %ax, %cs:(int13_old_eax - int13_handler)
/* old AX changed!! */
/* check int13/AH=4Bh for cdrom */
testw $0x4000, %cs:4(%bp) /* TO_C bit 14=TO has 2048-byte cdrom sector */
jz 1f /* not cdrom */
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jz 1f /* not cdrom */
cmpb $0x4B, %ah
jne 1f
cmpb $0x01, %al
ja 1f
/* this is int13 function 0x4B00 or 0x4B01 */
/* restore DS:SI, just in case they were changed by buggy int13 */
movw %cs:(int13_old_esi - int13_handler), %si
movw %cs:(int13_old_ds - int13_handler), %ds
cmpb $0x13, (%si) /* packet size */
jne 1f
testb $0x0F, 1(%si) /* boot type(0=no emu) */
jne 1f
movb %cs:1(%bp), %dl /* just in case DL was changed by a buggy int13 */
cmpb %dl, 2(%si)
jne 1f
movb %cs:(%bp), %dl /* restore DL back to the FROM drive */
movb %dl, 2(%si)
jmp 3f /* return */
1:
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
pushw %cs:(int13_old_flags - int13_handler)
popfw
/* check if should restore(reversely map) the drive number */
jc 1f /* restore DL on error */
/* alter MBR after read */
testb $0x80, %cs:7(%bp) /* TO_S */
jz 2f
cmpb $0x02, %ah
je 4f /* alter MBR */
cmpb $0x42, %ah
je 4f /* alter MBR */
2:
cmpb $0x08, %ah /* int13 AH=08h, read drive parameters */
jne 2f
/* DL==number of drives, should not restore */
xorw %ax, %ax
movw %ax, %ds
movb 0x475, %dl /* DL=number of hard drives */
testb $0x80, %cs:(int13_old_edx - int13_handler) /* harddrive? */
jnz 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 */
jmp 3f
2:
cmpb $0x15, %ah /* read drive type */
jne 2f
testb $0x80, %cs:(int13_old_edx - int13_handler) /* harddrive? */
jnz 3f /* yes, do not restore DL */
/* restore DL for floppy int13 AH=15h call */
jmp 1f
4:
call modify_in_situ
movl %cs:(int13_old_ebx - int13_handler), %ebx
movl %cs:(int13_old_ecx - int13_handler), %ecx
movl %cs:(int13_old_edx - int13_handler), %edx
movl %cs:(int13_old_edi - int13_handler), %edi
movw %cs:(int13_old_es - int13_handler), %es
2:
movw %cs:(%bp), %ax /* get the drive mapping */
/* 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 */
1:
movb %cs:(int13_old_edx - int13_handler), %dl /* restore DL back to the FROM drive */
3:
/* return */
/* BX, CX, DX, ES, DI are output registers and should not touch. */
pushw %cs:(int13_old_flags - int13_handler)
popfw
movl %cs:(int13_old_esi - int13_handler), %esi
movw %cs:(int13_old_ds - int13_handler), %ds
movl %cs:(int13_old_eax - int13_handler), %eax
movl %cs:(int13_old_ebp - int13_handler), %ebp
movw %cs:(int13_old_esp - int13_handler), %sp
ljmp *%cs:(int13_old_cs_ip - int13_handler)
.align 4
int13_old_cs_ip: .long 0
int13_old_eax: .long 0
int13_old_ebx: .long 0
int13_old_ecx: .long 0
int13_old_edx: .long 0
int13_old_esi: .long 0
int13_old_edi: .long 0
int13_old_esp: .long 0
int13_old_ebp: .long 0
int13_old_ds: .word 0
int13_old_es: .word 0
int13_old_flags: .word 0
int13_new_bp: .word 0
/****************************************************************************/
restricted_map:
movw %cs:(int13_old_eax - int13_handler), %ax
/* 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 %cs: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 %cs: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 %cs: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 %cs: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 */
movl %cs:(int13_old_eax - int13_handler), %eax
movb $0x01, %ah /* invalid function call */
1:
pushw %cs:(int13_old_flags - int13_handler)
popfw
stc /* error */
jmp 1f
readonly_fakewrite:
testb $0x40, %cs:7(%bp) /* bit 6 of TO_S */
movl %cs:(int13_old_eax - int13_handler), %eax
movb $0x03, %ah /* write protection */
jz 1b /* read only */
xorb %ah, %ah /* fake write */
pushw %cs:(int13_old_flags - int13_handler)
popfw
clc /* write succeeded */
1:
movl %cs:(int13_old_esi - int13_handler), %esi
movw %cs:(int13_old_ds - int13_handler), %ds
movl %cs:(int13_old_ebp - int13_handler), %ebp
movw %cs:(int13_old_esp - int13_handler), %sp
ljmp *%cs:(int13_old_cs_ip - int13_handler)
/****************************************************************************/
drive_emulation:
movw %cs:2(%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 %cs:(int13_old_eax - int13_handler), %ax
testb %ah, %ah /* reset disk system, always succeed */
jnz 1f
/*clc*/ /* CF already cleared by TEST */
jmp int13_return
/****************************************************************************/
1:
cmpb $0x01, %ah /* get status, always succeed */
jnz 1f
xorb %ah, %ah
jmp int13_return
/****************************************************************************/
1:
cmpb $0x04, %ah /* verify sectors, always succeed */
jnz 1f
xorb %ah, %ah
jmp int13_return
/****************************************************************************/
1:
cmpb $0x09, %ah /* INITIALIZE CONTROLLER WITH DRIVE PARAMETERS */
jnz 1f
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jnz error_01_invalid /* CDROM */
xorb %ah, %ah
jmp int13_return
/****************************************************************************/
1:
cmpb $0x0c, %ah /* SEEK TO CYLINDER */
jnz 1f
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jnz error_01_invalid /* CDROM */
xorb %ah, %ah
jmp int13_return
/****************************************************************************/
1:
cmpb $0x0d, %ah /* reset hard disks */
jnz 1f
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jnz error_01_invalid /* CDROM */
xorb %ah, %ah
jmp int13_return
/****************************************************************************/
1:
cmpb $0x10, %ah /* check if drive ready */
jnz 1f
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jnz error_01_invalid /* CDROM */
xorb %ah, %ah
jmp int13_return
/****************************************************************************/
1:
cmpb $0x11, %ah /* recalibrate drive */
jnz 1f
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jnz error_01_invalid /* CDROM */
xorb %ah, %ah
jmp int13_return
/****************************************************************************/
1:
cmpb $0x14, %ah /* CONTROLLER INTERNAL DIAGNOSTIC */
jnz 1f
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jnz error_01_invalid /* CDROM */
xorb %ah, %ah
jmp int13_return
/****************************************************************************/
1:
cmpb $0x05, %ah /* format track */
jnz 1f
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jnz error_01_invalid /* CDROM */
/* CH=cylinder number (bits 8,9 in high bits of CL)
* CL=high bits of cylinder number (bits 7,6)
* DH=head number
* DL=drive number
*/
xorb %ah, %ah /* do nothing but return success */
jmp int13_return
/****************************************************************************/
1:
cmpb $0x08, %ah /* get drive parameters */
jnz 1f
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jnz error_01_invalid /* CDROM */
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 %cs:2(%bp), %ax /* AL=Hmax, AH=Smax */
movb %al, %dh /* max head number */
andb $63, %ah
movb %ah, %cl /* max sector number */
/* 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 %cs:16(%bp), %eax /* S_count_Lo */
cmpl $4, %eax
ja 2f
cmpl $0, %cs:8(%bp) /* StartLBA_Lo */
jnz 2f
/* map whole drive, use TO_C instead. */
movw %cs:4(%bp), %ax /* TO_C */
jmp 3f
2:
decl %eax
//xorl %edx, %edx
divl %ecx /* EAX=max cylinder number */
3:
popl %ecx
popl %edx
movb %al, %ch /* low 8 bits of cylinder */
shlb $6, %ah /* high 2 bits of cylinder */
orb %ah, %cl
xorw %ax, %ax
jmp int13_return
/****************************************************************************/
1:
cmpb $0x15, %ah /* get disk type */
jnz 1f
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jnz error_01_invalid /* CDROM */
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 %cs:16(%bp), %dx /* lo word of S_count_Lo */
movw %cs:18(%bp), %cx /* hi word of S_count_Lo */
clc
jmp int13_return
/****************************************************************************/
1:
cmpb $0x16, %ah
jnz 1f
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jnz error_01_invalid /* CDROM */
xorb %ah, %ah /* AH=0 means disk not changed */
jmp int13_return
/****************************************************************************/
1:
cmpb $0x17, %ah /* set floppy type for format */
jnz 1f
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jnz error_01_invalid /* CDROM */
movb $0x03, %al /* 1.44M drive, 1.44M floppy */
xorb %ah, %ah
jmp int13_return
/****************************************************************************/
1:
cmpb $0x18, %ah /* set media type for format */
jnz 1f
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jnz error_01_invalid /* CDROM */
testb %dl, %dl /* hard drive? */
js error_01_invalid
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
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) */
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jnz error_01_invalid /* CDROM */
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:
testb $0x40, %cs:7(%bp) /* TO_S, bit 6=fake write(safeboot) */
jnz readonly_fakewrite /* fake the write */
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 %cs: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 %cs:2(%bp), %ah /* AH=Hmax */
cmpb %ah, %dh /* DH should not > max head number */
ja error_01_invalid
pushw %cs
popw %ds /* DS=CS */
movw $(EBIOS_disk_address_packet - int13_handler), %si
movw $0x0010, (%si) /* Disk Address Packet length */
movb %al, 2(%si) /* number of sectors to transfer */
movw %bx, 4(%si) /* offset */
movw %es, 6(%si) /* segment */
xorl %eax, %eax /* EAX=0 */
movb %ah, 3(%si) /* sectors_hi_reserved */
movl %eax, 12(%si) /* zero out hi 32 bits */
movb %ch, %al /* cylinder number lo 8 bits */
movb %cl, %ah /* CL holds higher 2 bits */
shrb $6, %ah /* AH lower holds the 2 bits */
/* EAX=cylinder number, <1024 */
pushl %ebx /* save EBX */
xorl %ebx, %ebx /* EBX=0 */
movb %cs:2(%bp), %bl /* BL=Hmax */
incw %bx /* EBX=total heads, <=256 */
pushl %edx
mull %ebx /* EDX=0, EAX=tracks before this cylinder */
popl %edx
xorw %bx, %bx /* EBX=0 */
movb %dh, %bl /* EBX=head number */
addl %ebx, %eax /* EAX=tracks before this head */
movb %cs:3(%bp), %bl /* Max sector number */
andb $63, %bl /* EBX=sectors per track */
pushl %edx
mull %ebx /* EDX=0, EAX=sectors before this head */
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 /* restore EBX */
disk_address_packet_ready:
/* DS=CS */
/* start-sector-number(LBA) in DAP is still for FROM_DRIVE */
/****************************************************************************/
/*
* check if the request exceeds the boundary of the emulated disk.
*
* input: DS:SI
* output: CF=0, success, all sectors transferred
* CF=1, failure, no sectors transferred
* EAX changed
*
*/
cmpl $1, %cs:16(%bp) /* S_count_Lo */
ja 2f
cmpl $0, %cs:8(%bp) /* StartLBA_Lo */
je 4f /* map whole drive, no restrictions */
2:
cmpl $0, 12(%si) /* hi 32-bit of the requested StartLBA */
jnz 3f /* non-zero is considered `too big' */
movl %cs:16(%bp), %eax /* S_count_Lo */
cmpl %eax, 8(%si) /* lo 32-bit of the requested StartLBA */
jnb 3f
subl 8(%si), %eax
pushl %ebx
movzwl 2(%si), %ebx /* requested sectors */
cmpl %ebx, %eax
popl %ebx
// jnb 4f
//3:
// stc
4:
/****************************************************************************/
jc 3f /* no sectors to transfer, fail */
/* adjust start-sector-number(LBA) to access TO_DRIVE */
movl %cs:8(%bp), %eax /* StartLBA_Lo */
addl %eax, 8(%si)
adcl $0, 12(%si)
/* set drive number(TO_DRIVE) and function number(EBIOS) */
movb %cs:1(%bp), %dl /* DL=TO_DRIVE */
movb %cs:(int13_old_eax - int13_handler + 1), %ah
/* 0x02=read, 0x03=write */
orb $0x40, %ah /* 0x42=EXT_read, 0x43=EXT_write */
call real_int13_service
///* restore original start-sector-number(in the DAP) */
//pushfw
//movl %cs:8(%bp), %eax /* StartLBA_Lo */
//subl %eax, 8(%si)
//sbbl $0, 12(%si)
//popfw
jc 3f /* failure */
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jnz 4f /* CDROM */
movw $(EBIOS_disk_address_packet - int13_handler), %si
pushw %cs
popw %ds
call modify_boot_sectors
4:
movb %cs:(int13_old_eax - int13_handler), %al
xorb %ah, %ah
jmp 4f /* success */
3:
movw $0x400, %ax /* no sectors transferred */
/* write back sectors transferred to original DAP for LBA access */
cmpb $0x40, %cs:(int13_old_eax - int13_handler + 1)
jb 4f /* CF=1, function 02 or 03, not an LBA call */
movw %cs:(int13_old_esi - int13_handler), %si
movw %cs:(int13_old_ds - int13_handler), %ds
/* DS:SI -> original DAP */
movb %al, 2(%si)
stc /* failure */
4:
movl %cs:(int13_old_edx - int13_handler), %edx
movl %cs:(int13_old_ebx - int13_handler), %ebx
movl %cs:(int13_old_ecx - int13_handler), %ecx
movl %cs:(int13_old_edi - int13_handler), %edi
movw %cs:(int13_old_es - int13_handler), %es
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
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jz 2f
/* CDROM */
movb $0x03, %ah /* write protection */
stc /* error */
jmp int13_return
2:
/* get old SI, disk address packet */
movw %cs:(int13_old_esi - int13_handler), %si
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
testb %al, %al
jz error_01_invalid
/* copy disk address packet to EBIOS_disk_address_packet */
pushw %es
pushw %cx
pushw %di
movw %cs, %ax
movw %ax, %es /* ES=CS */
movw $(EBIOS_disk_address_packet - int13_handler), %di
movw $8, %cx /* will copy only 16 bytes! */
cld
repz movsw
popw %di
popw %cx
popw %es
/* set DS:SI */
movw %cs, %ax
movw %ax, %ds /* DS=CS */
movw $(EBIOS_disk_address_packet - int13_handler), %si
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jz 2f
shlb $2, 2(%si)
movl 8(%si), %eax
shldl $2, %eax, 12(%si)
shll $2, 8(%si)
2:
jmp disk_address_packet_ready
/****************************************************************************/
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 1f
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jz 2f /* normal disks */
/* CDROM */
/* get old SI, extended drive parameter table */
movw %cs:(int13_old_esi - int13_handler), %si
cmpw $26, (%si)
jb error_01_invalid
movw $26, (%si) /* buffer length */
movw $0x00, 2(%si) # no flags
movw $0x800, 24(%si) # bytes per sect=2048
xorl %eax, %eax
decw %ax
movl %eax, 4(%si) # cylinders=0xFFFF
movb $0, %ah
movl %eax, 8(%si) # heads=0xFF
movb $15, %al
movl %eax, 12(%si) # sectors per track=15
movl %eax, 20(%si) # total sectors hi dword=0
xorw %ax, %ax # CF cleared
decl %eax # EAX=0xFFFFFFFF
movl %eax, 16(%si) # total sectors lo dword
# CF is cleared
xorw %ax, %ax /* success, CF cleared */
jmp int13_return
2:
/* get old SI, extended drive parameter table */
movw %cs:(int13_old_esi - int13_handler), %si
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 %cs:2(%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 %cs:16(%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
/****************************************************************************/
1:
cmpw $0x4B01, %ax /* CDROM GET DISK EMULATION STATUS */
jnz 1f
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jz error_01_invalid /* normal disks have no 0x4B01 function */
/* CDROM */
pushw %es
pushw %di
movw %cs:(int13_old_esi - int13_handler), %di /* old SI */
pushw %ds
popw %es
movw $0x0013, %ax /* packet size=13h, boot type=0 (no-emu) */
cld
stosw
movb %dl, %al /* drive=DL, controller=0 */
stosw
pushl %cs:EXT_C(lba_cd_boot) - int13_handler
popw %ax
stosw
popw %ax
stosw /* LBA for no-emu image */
xorw %ax, %ax
stosw /* device specification */
stosw /* user buffer segment */
stosw /* load segment */
movb $4, %al
stosw /* sector count=4 */
/* CHS makes no sense for no-emu */
popw %di
popw %es
xorb %ah, %ah /* success, CF cleared */
jmp int13_return
/****************************************************************************/
1:
/****************************************************************************/
error_01_invalid:
movb $0x01, %ah /* unsupported function call */
stc /* signal error */
int13_return:
movw %ax, %cs:(int13_old_eax - int13_handler) /* status */
jnc 1f
orb $1, %cs:(int13_old_flags - int13_handler)
jmp 2f
1:
andb $0xFE, %cs:(int13_old_flags - int13_handler)
2:
pushw %cs:(int13_old_flags - int13_handler)
popfw
movl %cs:(int13_old_eax - int13_handler), %eax
movl %cs:(int13_old_esi - int13_handler), %esi
movw %cs:(int13_old_ds - int13_handler), %ds
movl %cs:(int13_old_ebp - int13_handler), %ebp
movw %cs:(int13_old_esp - int13_handler), %sp
ljmp *%cs:(int13_old_cs_ip - int13_handler)
/****************************************************************************/
real_int13_service:
/* AH = 0x42 or 0x43 */
/* save return address to memory */
popw %cs:(int13_ret_IP - int13_handler)
/* save AX to memory */
movw %ax, %cs:(int13_reg_AX - int13_handler)
cmpb $0xff, %dl /* mem drive */
jne normal_disk_drive
testw $0x4000, %cs:4(%bp) /* TO_C bit 14=TO has 2048-byte cdrom sector */
jnz normal_disk_drive
/* handle memdrive */
sti /* for hardware interrupt or watchdog */
cmpb $0x42, %ah
je 3f
cmpb $0x43, %ah
je 3f
stc
jmp *%cs:(int13_ret_IP - int13_handler) //ret
/* 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 */
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:
/* Data transfer: (%eax) -> (%ecx), number of sectors 2(%si) */
movl %eax, %cs:(int13_data_addr_from - int13_handler)
movl %ecx, %cs:(int13_data_addr_to - int13_handler)
cmpl $0, %cs:(EXT_C(memdisk_raw) - int13_handler)
je 4f /* do not use raw mode */
/* raw mode as in memdisk, contributed by Bean */
// pushw %ax
smsw %bx
testb $1, %bl
// popw %ax
jnz 4f /* protected mode */
/* switch to protected mode myself */
/* ebx destroy */
//pushw %ds
//pushw %es
//pushl %eax
//pushl %ecx
//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
movzbl 2(%si), %ebx
shll $9, %ebx
movl %ebx, %edx
addl %eax, %ebx
decl %ebx
orl %eax, %ebx
addl %ecx, %edx
decl %edx
orl %ecx, %edx
orl %ebx, %edx
testl $0x100000, %edx # zero means data inside an even mega
setz %dl # we think of it as if A20 were on
jz 3f # no need to enable A20
pushw %dx
movw $0x00ff, %cx # try so many times on failure
movw $0x0001, %dx # DL=enable A20, DH=debug off
cli /* yes, keep interrupt off when controlling A20 */
call enable_disable_a20
sti
popw %dx
jz 1f /* success */
/* A20 control failed. */
# notify the user
movzbw %cs:(%bp), %ax /* FROM_DRIVE */
pushw %ax
pushw $a20_failure - int13_handler # the format string
call realmode_printf
addw $4, %sp # adjust the stack pointer
//popl %ecx
//popl %eax
//popw %es
//popw %ds
//jmp 4f /* try once more with int15/ah=87h */
stc
jmp *%cs:(int13_ret_IP - int13_handler) //ret
a20_failure:
.ascii "\r\ngrub4dos: A20 failure on memdrive=0x%X.\r\n\0"
1:
/* CF=1 means A20 was originally enabled. */
setc %dl
movb $0, %dh
3:
movw $(EBIOS_disk_address_packet - int13_handler), %si
pushw %cs
popw %ds
//popl %ecx
//popl %eax
movzbl 2(%si), %ecx #; ECX=number of sectors to transfer
movl %cs:(int13_data_addr_to - int13_handler), %edi
movl %cs:(int13_data_addr_from - int13_handler), %esi
#; ESI changed!!
shll $7, %ecx /* 128 dwords per sector */
cli
lgdt %cs:MyGDT - int13_handler
movl %cr0, %eax
andl $0x0000FFFF, %eax
orb $1, %al
movl %eax, %cr0 /* Switch to protected mode */
// jmp 1f
//1:
// jmp 1f
//1:
movw $16, %bx /* Switch to 4G data segment */
movw %bx, %ds
movw %bx, %es
// jmp 1f
//1:
// jmp 1f
//1:
cld
addr32 rep movsl /* ESI, EDI changed! */
andb $0xFE, %al
movl %eax, %cr0 /* Back to real mode */
// jmp 1f
//1:
// jmp 1f
//1:
sti
//popw %es
//popw %ds
cmpl $0, %cs:(EXT_C(a20_keep_on) - int13_handler)
jne 1f /* Keep A20 on. This should hurt nothing. */
/* Disable A20 if necessary ! */
andw %dx, %dx /* 0=orig A20 off, 1=orig A20 on */
jnz 1f
movw $0x0004, %cx # try so many times on failure
//movw $0x0000, %dx # DL=disable A20, DH=debug off
cli /* yes, keep interrupt off when controlling A20 */
call enable_disable_a20
sti
1:
clc
jmp *%cs:(int13_ret_IP - int13_handler) //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 */
/* real mode */
pushfw
lcall %cs:*(EXT_C(ROM_int15) - int13_handler)
jmp 6f
4:
/* protected mode */
int $0x15
6:
jmp *%cs:(int13_ret_IP - int13_handler) //ret
/****************************************************************************/
normal_disk_drive:
/* handle normal disk drive */
/* AH = 0x42 or 0x43 */
pushw %ax
smsw %ax
testb $1, %al
popw %ax
jz 3f // real mode
/* protected mode(Windows 98, EMM386) */
pushw %si /* save SI */
pushl %eax /* save EAX */
pushl %ebx /* save EBX */
/* set SI to the drive map */
movw $(EXT_C(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 %cs:(%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
/* fall through to real mode... */
3:
/* 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 */
testw $0x4000, %cs:4(%bp) /* TO_C bit 14=TO has 2048-byte cdrom sector */
jz 1f
/* LBA supported, CHS not applicable for CDROM */
movw %es, %cs:(int13_cdrom_es - int13_handler)
movw %cx, %cs:(int13_cdrom_cx - int13_handler)
movl %ebx, %cs:(int13_cdrom_ebx - int13_handler)
movl %edi, %cs:(int13_cdrom_edi - int13_handler)
movzbw 2(%si), %cx /* sectors to transfer */
movl 8(%si), %edi /* start sector number(LBA) */
movl 12(%si), %ebx
3:
/* read 1 small sector at LBA=EDI to edd30_disk_buffer */
/* if we have previously read it, we can skip this step now. */
cmpb %dl, %cs:last_read_cd_drive - int13_handler
jne 2f
cmpl %ebx, %cs:last_read_sector - int13_handler + 4
jne 2f
movl %edi, %eax
andb $0xFC, %al
cmpl %eax, %cs:last_read_sector - int13_handler #; // + 4
je 4f
2:
movl (%si), %eax
movl %eax, %cs:(tmp_dap - int13_handler)
movl 4(%si), %eax
movl %eax, %cs:(tmp_dap - int13_handler + 4)
movl 8(%si), %eax
movl %eax, %cs:(tmp_dap - int13_handler + 8)
movl 12(%si), %eax
movl %eax, %cs:(tmp_dap - int13_handler + 12)
movl %edi, 8(%si)
movl %ebx, 12(%si)
shrdl $2, %ebx, 8(%si) /* turn LBA small to LBA big */
shrl $2, 12(%si)
movb $1, 2(%si) /* read 1 big sector */
//pushw %es
movw %bx, %cs:(int13_cdrom_bx - int13_handler)
movw %cs, %ax
movw %ax, %es
movw %ax, 6(%si) /* set the buffer segment */
movw $edd30_disk_buffer - int13_handler, %bx
movw %bx, 4(%si) /* set the buffer offset */
movw %cs:(int13_reg_AX - int13_handler), %ax
/* check if DL is for anyone of (cd?)'s */
cmpb $0, %cs:(EXT_C(atapi_dev_count) - int13_handler)
jz 2f /* no cdX'es */
cmpb %cs:(EXT_C(min_cdrom_id) - int13_handler), %dl
jb 2f /* not cdX */
cmpb %cs:(max_cdrom_id - int13_handler), %dl
ja 2f /* not cdX */
/* read 1 cdrom sector using our builtin cdrom driver. */
movb $1, %cs:(force_int13 - int13_handler)
2:
call int13_with_retry /* read a big 2048-byte sector */
movb $0, %cs:(force_int13 - int13_handler)
movw %cs:(int13_cdrom_bx - int13_handler), %bx
//popw %es
jc 3f /* failed */
movl %cs:(tmp_dap - int13_handler), %eax
movl %eax, (%si)
movl %cs:(tmp_dap - int13_handler + 4), %eax
movl %eax, 4(%si)
movl %cs:(tmp_dap - int13_handler + 8), %eax
movl %eax, 8(%si)
movl %cs:(tmp_dap - int13_handler + 12), %eax
movl %eax, 12(%si)
pushw %di
andw $0xFFFC, %di
movl %edi, %cs:last_read_sector - int13_handler
popw %di
movl %ebx, %cs:last_read_sector - int13_handler + 4
movb %dl, %cs:last_read_cd_drive - int13_handler
4:
pushw %cx
pushw %bx
pushw %di
/* calculate the buffer ES:BX */
//movw %di, %ax
subw 8(%si), %di /* sectors already read */
shlw $5, %di /* paragraghs */
addw 6(%si), %di /* segment */
movw 4(%si), %bx /* offset */
movw %bx, %ax
shrw $4, %ax /* turn to paragraghs */
andw $0x000F, %bx
addw %di, %ax /* segment */
movw %ax, %es
movw %bx, %di
popw %ax /* AX=old DI */
pushw %ax
andb $3, %al
shlw $9, %ax /* offset in edd30_disk_buffer */
addw $edd30_disk_buffer - int13_handler, %ax
xchgw %ax, %si
/* move 512 bytes */
movw $0x100, %cx
cld
cs repz movsw
xchgw %ax, %si
popw %di
popw %bx
incl %edi /* next sector */
jnz 2f
incl %ebx
2:
popw %cx
decw %cx
jnz 3b
// loop 3b
movw %cs:(int13_cdrom_es - int13_handler), %es
movw %cs:(int13_cdrom_cx - int13_handler), %cx
movl %cs:(int13_cdrom_ebx - int13_handler), %ebx
movl %cs:(int13_cdrom_edi - int13_handler), %edi
clc
jmp *%cs:(int13_ret_IP - int13_handler) //ret
3:
movw %cs:(int13_cdrom_es - int13_handler), %es
movw %cs:(int13_cdrom_cx - int13_handler), %cx
movl %cs:(int13_cdrom_ebx - int13_handler), %ebx
movl %cs:(int13_cdrom_edi - int13_handler), %edi
stc
jmp *%cs:(int13_ret_IP - int13_handler) //ret
/* cache 1 big 2048-byte sector at edd30_disk_buffer */
last_read_cd_drive:
.byte 0 #; a value < 0x80 normally invalidate the cache
.align 4
tmp_dap:
.long 0
.long 0
.long 0
.long 0
last_read_sector: /* set it to 0xFFFFFFFFFFFFFFFF to invalidate the cache */
.long 0xFFFFFFFF #; lo dword. for a valid cache, lowest 2 bits=0
.long 0xFFFFFFFF #; hi dword
int13_data_addr_from:
.long 0
int13_data_addr_to:
.long 0
int13_cdrom_edi:
.long 0
int13_cdrom_ebx:
.long 0
int13_cdrom_cx:
.word 0
int13_cdrom_bx:
.word 0
int13_cdrom_es:
.word 0
1:
#;jmp 1f /* just check if CHS translation code works */
testw $0x8000, %cs:4(%bp) /* TO_C bit 15=LBA support */
#if 0
jnz int13_with_retry /* LBA mode */
#else
jz 1f /* CHS only, so skip the LBA access */
movw %es, %cs:(int13_tmp_es_bx - int13_handler + 2)
movw %bx, %cs:(int13_tmp_es_bx - int13_handler)
movw 4(%si), %bx
shrw $4, %bx
addw %bx, 6(%si)
andw $0x000F, 4(%si)
cmpb $0x80, 2(%si)
jb 2f /* 0x7F sectors or less is ok */
ja 3f /* 0x81 sectors or more is bad */
/* now 0x80 sectors requested. */
testw $0x2000, %cs:4(%bp) /* TO_C bit 13=FROM has 2048-byte cdrom sector */
jz 3f /* not CDROM, fail out. */
/* virtual cdrom function call could request 64K data each time */
/* transfer 0x80 sectors = twice 0x40 sectors */
movb $0x40, 2(%si) /* transfer the first 32K */
movw 4(%si), %bx
movw 6(%si), %es
call int13_with_retry
jc 4f
movb $0x40, 2(%si)
addw $0x800, 6(%si) /* step to next 32K of the buffer */
movw 4(%si), %bx
movw 6(%si), %es
pushl %eax
xorl %eax, %eax
movb $0x40, %al
addl %eax, 8(%si) /* adjust start_LBA */
movb $0, %al
adcl %eax, 12(%si)
popl %eax
call int13_with_retry
/* we ignore this step and keep start_LBA in a modified state. This is
* because only modify_boot_sectors need start_LBA and we are accessing
* the virtual cdrom, so we will not call modify_boot_sectors.
*/
//pushfw
//pushl %eax
//xorl %eax, %eax
//movb $0x40, %al
//subl %eax, 8(%si) /* adjust start_LBA */
//movb $0, %al
//sbbl %eax, 12(%si)
//popl %eax
//popfw
4:
movb $0x80, 2(%si) /* restore its original value */
lesw %cs:(int13_tmp_es_bx - int13_handler), %bx
movw %bx, 4(%si) /* restore its original value */
movw %es, 6(%si) /* restore its original value */
jmp *%cs:(int13_ret_IP - int13_handler) //ret
2:
movw 4(%si), %bx
movw 6(%si), %es
call int13_with_retry
lesw %cs:(int13_tmp_es_bx - int13_handler), %bx
movw %bx, 4(%si) /* restore its original value */
movw %es, 6(%si) /* restore its original value */
jnc 4f
testw $0x1000, %cs:4(%bp) /* TO_C bit 12=BIFURCATE */
jz 1f /* not bifurcate, use CHS access once more. */
stc /* bifurcate, error out. */
4:
jmp *%cs:(int13_ret_IP - int13_handler) //ret
3:
movb $1, %ah
stc
jmp *%cs:(int13_ret_IP - int13_handler) //ret
.align 4
int13_tmp_es_bx: .long 0
1:
#endif
/* CHS mode */
/* byte at SI+2: number of sectors to access */
/* 8 bytes at SI+8: StartLBA */
/* CHS mode cannot access large addresses */
cmpl $0, 12(%si)
jnz 1f
pushl %edx
pushl %ebx
movw %cs:4(%bp), %ax /* TO_C */
andw $0x3FF, %ax /* get lo 10 bits */
incw %ax /* cylinders */
pushw %ax
movzbw %cs:6(%bp), %ax /* TO_H */
incw %ax /* heads */
movzbw %cs: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 /* number of sectors to access */
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 */
movzbw 2(%si), %cx /* sectors to transfer */
movl 8(%si), %edi /* start sector number(LBA) */
3:
/* translate LBA to CHS */
//pushw %cx
movw %cx, %cs:(int13_chs_cx - int13_handler)
/* get sectors per cylinder */
//pushw %dx
movw %dx, %cs:(int13_chs_dx - int13_handler)
pushl %edi /* lba */
movzbw %cs:6(%bp), %ax /* TO_H */
incw %ax /* heads */
movzbw %cs: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
movw %cs:(int13_chs_dx - int13_handler), %dx /* DL=drive number */
//popw %dx /* DL=drive number */
//pushw %dx
movb %al, %dh /* DH=head number */
//pushl %edi
movl %edi, %cs:(int13_chs_edi - int13_handler)
//movw %di, %ax
subw 8(%si), %di /* sectors already read */
shlw $5, %di /* paragraghs */
addw 6(%si), %di /* segment */
movw 4(%si), %bx /* offset */
movw %bx, %ax
shrw $4, %ax /* turn to paragraghs */
andw $0x000F, %bx
addw %di, %ax /* segment */
movw %ax, %es
movb %cs:(int13_old_eax - int13_handler + 1), %ah
/* 0x42/0x02=read, 0x43/0x03=write */
andb $0x03, %ah /* AH=0x02 or 0x03 */
movb $0x01, %al /* number of sectors to read/write */
call int13_with_retry
movl %cs:(int13_chs_edi - int13_handler), %edi
movw %cs:(int13_chs_dx - int13_handler), %dx
//popl %edi
//popw %dx
incl %edi /* next sector */
movw %cs:(int13_chs_cx - int13_handler), %cx
//popw %cx
jc 1f
loop 3b
jmp *%cs:(int13_ret_IP - int13_handler) //ret
1:
stc
jmp *%cs:(int13_ret_IP - int13_handler) //ret
.align 2
int13_ret_IP:
.word 0
int13_reg_AX:
.word 0
int13_chs_cx:
.word 0
int13_chs_dx:
.word 0
.align 4
int13_chs_edi:
.long 0
/****************************************************************************/
int13_with_retry:
/* save return address to memory */
popw %cs:(int13_retry_IP - int13_handler)
// /* backup the last DWORD in the buffer */
// pushl %es:0x1FC(%bx)
// popl %cs:(sector_last_dword - int13_handler)
/* retry count should be an odd number initially */
movb $5, %cs:(retry_count - int13_handler)
cmpb $0, %cs:(force_int13 - int13_handler)
jnz 3f /* we should use current int 13 instead of ROM_int13 */
call backup_int13
3:
movb %ah, %cs:(int13_retry_ah - int13_handler)
call int13_simple
jnc 3f /* success */
// /* if the last dword changed, consider it is success */
//
// pushl %eax
// movl %es:0x1FC(%bx), %eax
// cmpl %eax, %cs:(sector_last_dword - int13_handler)
// popl %eax
//
// jne 3f /* success */
decb %cs:(retry_count - int13_handler)
jz 2f /* finally failed */
/* reset disk and try again */
movb $0, %cs:(int13_retry_ah - int13_handler)
call int13_simple
// testb $1, %cs:(int13_old_eax - int13_handler + 1)
// /* 0x42/0x02=read, 0x43/0x03=write */
// jnz 3b /* it is write */
//
// /* touch the last dword of the buffer */
// notl %es:0x1FC(%bx)
// notl %cs:(sector_last_dword - int13_handler)
jmp 3b /* try again, ignoring the reset failure */
2:
stc /* failure */
jmp 2f
3:
clc /* success */
2:
pushfw
cmpb $0, %cs:(force_int13 - int13_handler)
jnz 2f /* we have not touched the int 13 vector */
call restore_int13
2:
popfw
jmp *%cs:(int13_retry_IP - int13_handler) //ret
backup_int13:
/* backup the current int 13 to tmp_int13, then install ROM_int13 */
cli
pushw %ds
pushl %eax
xorw %ax, %ax
movw %ax, %ds
movl 0x4C, %eax /* current int 13 vector */
movl %eax, %cs:(tmp_int13 - int13_handler)
movl %cs:(ROM_int13 - int13_handler), %eax
movl %eax, 0x4C
popl %eax
popw %ds
ret
restore_int13:
/* restore the current int 13 from tmp_int13 */
cli
pushw %ds
pushl %eax
xorw %ax, %ax
movw %ax, %ds
movl %cs:(tmp_int13 - int13_handler), %eax
movl %eax, 0x4C
popl %eax
popw %ds
ret
retry_count:
.byte 5
int13_retry_ah:
.byte 0
force_int13:
.byte 0
.align 2
int13_retry_IP:
.word 0
int13_retry_ds:
.word 0
int13_retry_es:
.word 0
.align 4
int13_retry_eax:
.long 0
int13_retry_ebx:
.long 0
int13_retry_ecx:
.long 0
int13_retry_edx:
.long 0
int13_retry_esi:
.long 0
int13_retry_edi:
.long 0
int13_retry_ebp:
.long 0
tmp_int13:
.long 0
//sector_last_dword:
// .long 0
/****************************************************************************/
int13_simple:
/* input: CF=0 reset disk */
/* save return address to memory */
popw %cs:(int13_simple_IP - int13_handler)
movl %eax, %cs:(int13_retry_eax - int13_handler)
movl %ebx, %cs:(int13_retry_ebx - int13_handler)
movl %ecx, %cs:(int13_retry_ecx - int13_handler)
movl %edx, %cs:(int13_retry_edx - int13_handler)
movl %esi, %cs:(int13_retry_esi - int13_handler)
movl %edi, %cs:(int13_retry_edi - int13_handler)
movl %ebp, %cs:(int13_retry_ebp - int13_handler)
movw %ds, %cs:(int13_retry_ds - int13_handler)
movw %es, %cs:(int13_retry_es - int13_handler)
//sti
cmpb $0, %cs:(force_int13 - int13_handler)
jz 3f
/* read 1 cdrom sector using our builtin cdrom driver. */
/* backup old variables to bak variables */
movl %cs:(int13_old_eax - int13_handler), %eax
movl %eax, %cs:(int13_bak_eax - int13_handler)
movl %cs:(int13_old_ebx - int13_handler), %eax
movl %eax, %cs:(int13_bak_ebx - int13_handler)
movl %cs:(int13_old_ecx - int13_handler), %eax
movl %eax, %cs:(int13_bak_ecx - int13_handler)
movl %cs:(int13_old_edx - int13_handler), %eax
movl %eax, %cs:(int13_bak_edx - int13_handler)
movl %cs:(int13_old_esi - int13_handler), %eax
movl %eax, %cs:(int13_bak_esi - int13_handler)
movl %cs:(int13_old_edi - int13_handler), %eax
movl %eax, %cs:(int13_bak_edi - int13_handler)
movl %cs:(int13_old_ebp - int13_handler), %eax
movl %eax, %cs:(int13_bak_ebp - int13_handler)
movw %cs:(int13_old_ds - int13_handler), %ax
movw %ax, %cs:(int13_bak_ds - int13_handler)
movw %cs:(int13_old_es - int13_handler), %ax
movw %ax, %cs:(int13_bak_es - int13_handler)
movw %cs:(int13_old_flags - int13_handler), %ax
movw %ax, %cs:(int13_bak_flags - int13_handler)
movl %cs:(int13_old_cs_ip - int13_handler), %eax
movl %eax, %cs:(int13_bak_cs_ip - int13_handler)
/* setup old variables for use by edd30_for_cdrom */
/* edd30_for_cdrom far return address */
movw %cs, %cs:(int13_old_cs_ip + 2 - int13_handler)
movw $(2f - int13_handler), %cs:(int13_old_cs_ip - int13_handler)
/* edd30_for_cdrom flags */
pushfw
popw %cs:(int13_old_flags - int13_handler)
movw %cs:(int13_retry_es - int13_handler), %ax
movw %ax, %cs:(int13_old_es - int13_handler)
movw %cs:(int13_retry_ds - int13_handler), %ax
movw %ax, %cs:(int13_old_ds - int13_handler)
movl %cs:(int13_retry_ebp - int13_handler), %eax
movl %eax, %cs:(int13_old_ebp - int13_handler)
movl %cs:(int13_retry_edi - int13_handler), %eax
movl %eax, %cs:(int13_old_edi - int13_handler)
movl %cs:(int13_retry_esi - int13_handler), %eax
movl %eax, %cs:(int13_old_esi - int13_handler)
movl %cs:(int13_retry_edx - int13_handler), %eax
movl %eax, %cs:(int13_old_edx - int13_handler)
movl %cs:(int13_retry_ecx - int13_handler), %eax
movl %eax, %cs:(int13_old_ecx - int13_handler)
movl %cs:(int13_retry_ebx - int13_handler), %eax
movl %eax, %cs:(int13_old_ebx - int13_handler)
movl %cs:(int13_retry_eax - int13_handler), %eax
movl %eax, %cs:(int13_old_eax - int13_handler)
movb %cs:(int13_retry_ah - int13_handler), %ah
jmp edd30_for_cdrom /* will return at 2f */
2:
/* restore old variables from bak variables */
movl %cs:(int13_bak_eax - int13_handler), %eax
movl %eax, %cs:(int13_old_eax - int13_handler)
movl %cs:(int13_bak_ebx - int13_handler), %eax
movl %eax, %cs:(int13_old_ebx - int13_handler)
movl %cs:(int13_bak_ecx - int13_handler), %eax
movl %eax, %cs:(int13_old_ecx - int13_handler)
movl %cs:(int13_bak_edx - int13_handler), %eax
movl %eax, %cs:(int13_old_edx - int13_handler)
movl %cs:(int13_bak_esi - int13_handler), %eax
movl %eax, %cs:(int13_old_esi - int13_handler)
movl %cs:(int13_bak_edi - int13_handler), %eax
movl %eax, %cs:(int13_old_edi - int13_handler)
movl %cs:(int13_bak_ebp - int13_handler), %eax
movl %eax, %cs:(int13_old_ebp - int13_handler)
movw %cs:(int13_bak_ds - int13_handler), %ax
movw %ax, %cs:(int13_old_ds - int13_handler)
movw %cs:(int13_bak_es - int13_handler), %ax
movw %ax, %cs:(int13_old_es - int13_handler)
movw %cs:(int13_bak_flags - int13_handler), %ax
movw %ax, %cs:(int13_old_flags - int13_handler)
movl %cs:(int13_bak_cs_ip - int13_handler), %eax
movl %eax, %cs:(int13_old_cs_ip - int13_handler)
/* eax changed but no problem */
jmp 2f
3:
movb %cs:(int13_retry_ah - int13_handler), %ah
int $0x13
2:
movw %cs:(int13_retry_ds - int13_handler), %ds
movw %cs:(int13_retry_es - int13_handler), %es
movl %cs:(int13_retry_eax - int13_handler), %eax
movl %cs:(int13_retry_ebx - int13_handler), %ebx
movl %cs:(int13_retry_ecx - int13_handler), %ecx
movl %cs:(int13_retry_edx - int13_handler), %edx
movl %cs:(int13_retry_esi - int13_handler), %esi
movl %cs:(int13_retry_edi - int13_handler), %edi
movl %cs:(int13_retry_ebp - int13_handler), %ebp
jmp *%cs:(int13_simple_IP - int13_handler) //ret
.align 2
int13_simple_IP:
.word 0
.align 4
int13_bak_cs_ip: .long 0
int13_bak_eax: .long 0
int13_bak_ebx: .long 0
int13_bak_ecx: .long 0
int13_bak_edx: .long 0
int13_bak_esi: .long 0
int13_bak_edi: .long 0
int13_bak_ebp: .long 0
int13_bak_ds: .word 0
int13_bak_es: .word 0
int13_bak_flags: .word 0
/****************************************************************************/
modify_boot_sectors:
movw %cs:(int13_old_eax - int13_handler), %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? */
testb $0x01, %ah /* is it extended read? */
jne 4f /* no, do nothing. */
cmpl $0, 12(%si) /* read from LBA_high=0? */
jne 4f /* no, do nothing. */
movb $0, %dh /* simulate CHS read of head 0 */
cmpl $0, 8(%si) /* read from LBA_low=0? */
je 3f /* yes, continue. */
movzbl %cs:3(%bp), %eax /* AL=Smax */
andb $63, %al /* EAX=sectors per track */
movb $1, %dh /* simulate CHS read of head 1 */
cmpl %eax, 8(%si) /* read from LBA_low=sectors per track? */
jne 4f /* no, exit. */
3:
movw 4(%si), %bx /* simulate CHS read buffer offset */
movw 6(%si), %es /* simulate CHS read buffer segment */
5:
/* CHS mode read from cylinder 0, sector 1 */
#; testb %dl, %dl /* The TO_DRIVE is hard drive? */
#; jns 4f /* no, do nothing */
cmpw $0xaa55, %es:0x1fe(%bx) /* is it a valid boot sector? */
jne 4f /* no, skip */
movl %cs:16(%bp), %eax /* S_count_Lo */
shrl $1, %eax /* map a whole drive? */
jz 4f /* yes, nothing need to change */
movl %cs:8(%bp), %eax /* StartLBA_Lo */
testl %eax, %eax /* geometry translation only? */
jz 4f /* yes, needn't change */
movl %cs:(%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.
*/
/* first, the MS magic number should be non-zero */
cmpl $0, %es:0x1b8(%bx)
jne 5f
movb %al, %es:0x1b8(%bx) /* let it be the FROM_DRIVE */
5:
shrl $16, %eax /* AL=Hmax, AH=Smax */
andb $63, %ah /* AH=Smax */
// cmpb $1, %ah
// jbe 4f /* do not modify partition table when disable CHS mode */
/* an extended partition table should have an entry with
* StartSector=Smax.
*/
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)
jnz 4f /* not an extended partition table */
5:
/* check if it is a primary partition table */
/* now the StartSector=Smax. if it is a primary partition table entry,
* it must have C/H/S=0/1/1.
*/
movl %es:-8(%bx, %si), %eax
shrl $8, %eax /* hi word:cylinder/sector, lo word:head */
cmpl $0x000101, %eax /* 0x00=C, 0x01=S, 0x01=H */
je 4f /* primary partition table, nothing to do */
/* 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 %cs:2(%bp), %eax /* Hmax, Smax */
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 %cs:3(%bp), %dl /* DL=Smax */
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 */
/* needn't restore registers */
ret
/****************************************************************************/
modify_floppy:
/* AL=FROM_DRIVE is the floppy drive number. */
cmpb $0x00, %dh /* read from head 0? */
jne 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 $0x01, %dh /* read from head 1? */
jne 4b
movl %cs:8(%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 %cs: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:
movb %al, %cs:(int13_in_situ_sectors - int13_handler)
cld
/* if FROM is not harddrive, do nothing. */
testb $0x80, %cs:(%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 %cs:7(%bp), %al /* TO_S */
andb $63, %al
movzbw %al, %dx
mulb %cs:6(%bp) /* TO_H */
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 %cs: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 %cs:(int13_in_situ_sectors - int13_handler), %ecx
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 %cs:8(%bp), %eax /* startLBA_Lo */
ja 4f /* boot sector was not read */
addl %eax, %ecx
cmpl %cs:8(%bp), %ecx
jbe 4f /* boot sector was not read */
movl %cs:8(%bp), %ecx
subl %eax, %ecx /* sectors between ES:BX and boot record */
/* modify hidden sectors of the partition boot record */
cmpb $0x07, %cs:4(%bp) /* NTFS */
je 3f
cmpb $0x0C, %cs:4(%bp) /* FAT32(LBA) */
je 3f
cmpb $0x0E, %cs:4(%bp) /* FAT12/16 */
je 3f
cmpb $0x83, %cs:4(%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 %cs:8(%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
/* first, the MS magic number should be non-zero */
cmpl $0, %es:0x1b8(%bx)
jne 5f
movb %cs:(%bp), %al /* FROM drive */
movb %al, %es:0x1b8(%bx) /* let it be the FROM_DRIVE */
5:
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 %cs:8(%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 %cs:16(%bp), %eax
addl %cs:8(%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 %cs:4(%bp), %al /* TO_C */
stosl /* DI=BX+0x1c6 */
/* modify start sector and sector count */
movl %cs:8(%bp), %eax
stosl /* DI=BX+0x1ca */
movl %cs:16(%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 */
ret
int13_in_situ_sectors:
.byte 0
/****************************************************************************/
lba_to_chs:
/* input:
*
* EAX: LBA
*
* output:
*
* EAX: CHS
*
*
*
*/
pushl %eax /* EAX=end sector number */
movzwl %cs:6(%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 %cs: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
/****************************************************************************/
max_cdrom_id: .byte 0xE0
.align 4
ENTRY(memdisk_raw) .long 1 /* set to 0 if accessing memdrives using int15/AH=87h */
ENTRY(a20_keep_on) .long 1 /* set to 0 if a20 should be auto-off */
ENTRY(lba_cd_boot) .long 0 /* LBA of no-emulation boot image, in 2048-byte sectors */
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:*(EXT_C(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 $(EXT_C(hooked_drive_map) - int13_handler), %si
movw $(DRIVE_MAP_SIZE), %cx
6:
cmpb $0xff, %cs:1(%si)
jne 5f
testb $0x40, %cs:5(%si) /* TO_C bit 14=TO has 2048-byte cdrom sector */
jnz 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:*(EXT_C(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 $(EXT_C(hooked_drive_map) - int13_handler), %si
movw $(DRIVE_MAP_SIZE), %cx
6:
cmpb $0xff, %cs:1(%si)
jne 5f
testb $0x40, %cs:5(%si) /* TO_C bit 14=TO has 2048-byte cdrom sector */
jnz 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:*(EXT_C(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:*(EXT_C(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:*(EXT_C(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=extended 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:*(EXT_C(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:*(EXT_C(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=extended 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:*(EXT_C(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=extended 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:*(EXT_C(ROM_int15) - int13_handler)
/****************************************************************************/
#include "a20.inc"
/****************************************************************************/
/* 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
/****************************************************************************/
__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 %cs:(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
cmpb %cs:(EXT_C(atapi_dev_count) - int13_handler), %al
cmc
jb 2f
movw %ax, %cs:(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 %cs:(%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 %cs: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
addb $0x0A, %bl #; BL= 0x0A or 0x0B
shlb $4, %bl #; BL= 0xA0 or 0xB0
movb %bl, %cs:(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
# get DS for the disk address packet
movw %cs:(int13_old_ds - int13_handler), %ds
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 %cs:(%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 %cs: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, %cs:(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
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
4:
movw _bcnt_ - int13_handler, %ax
cmpw 24(%bp), %ax
jz 3f
orb $FAILBIT3, %bl #; error
3:
#endif //; sane_check
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
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:
#; 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
testb $(CB_STAT_BSY | CB_STAT_DRQ), _status_ - int13_handler
jnz 4f
orb $0x80, 18(%bp) #; NON_DATA
jmp 2f
4:
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
testb $(CB_SC_P_TAG | CB_SC_P_REL | CB_SC_P_CD), _reason_ - int13_handler
jz 4f
orb $FAILBIT4, %bl #; error
4:
testb $CB_SC_P_IO, _reason_ - int13_handler
jz 4f
cmpb $0, 18(%bp)
jz 4f
orb $FAILBIT5, %bl #; error
4:
#endif //; sane_check
#; do the slow data transfer
call sub_atapi_delay
movw _bcnt_ - int13_handler, %ax
orw %ax, %ax
jnz 4f
movb $60, %bh #; error
orb $0x80, 18(%bp) #; NON_DATA
jmp 2f
4:
#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
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
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
2:
#endif //; check_extra_fail
#; outbyte CB_DC,cmd_DC
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, EXT_C(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
/* will read these variables:
* int13_old_eax
* int13_old_ebx
* int13_old_ecx
* int13_old_edx
* int13_old_esi
* int13_old_edi
* int13_old_ebp
* int13_old_ds
* int13_old_es
* int13_old_flags
* int13_old_cs_ip
* will write these variables:
* int13_old_eax
* int13_old_ebx
* int13_old_ecx
* int13_old_flags
*/
sti
cld
pushw %cs
popw %ds
movzbw %dl, %ax
subb %cs:(EXT_C(min_cdrom_id) - int13_handler), %al
call select_atapi #; select_atapi_force
movw %cs:(int13_old_eax - int13_handler), %ax
jnc 1f
movb $0xAA, %ah
jmp 2f //fail_out
1:
cmpb $0, %ah
je 6f //reset
cmpb $1, %ah
je 7f //get_last_stat
cmpb $0x41, %ah
je 8f //install_check
cmpb $0x42, %ah
je 9f //ext_read
cmpb $0x43, %ah
je 10f //ext_write
cmpb $0x44, %ah /* verify sectors */
je 5f //success_out
cmpb $0x47, %ah /* extended seek */
je 5f //success_out
cmpb $0x48, %ah
je 11f //get_drv_param
cmpb $0x4B, %ah
jne 3f //invalid_cmd
//stop_disk_emu:
cmpb $1, %al /* only 0x4B01 supported */
jne 3f //invalid_cmd
movw %si, %di
movw %cs:(int13_old_ds - int13_handler), %es
movw $0x0013, %ax /* packet size=13h, boot type=0 (no-emu) */
cld
stosw
//movb %dl, %al /* drive=DL, controller=0 */
movb %cs:(int13_old_edx - int13_handler), %al /* drive=DL, controller=0 */
stosw
xorw %ax, %ax
stosw
stosw /* LBA for no-emu image=0 */
stosw /* device specification */
stosw /* user buffer segment */
stosw /* load segment */
movb $4, %al
stosw /* sector count=4 */
/* CHS makes no sense for no-emu */
jmp 5f //success_out
11: //get_drv_param:
movw %cs:(int13_old_ds - int13_handler), %ds
cmpw $26, (%si)
jb 3f //invalid_cmd
movw $26, (%si)
// movw $0x74, 2(%si) #; struc_extparam.flags: removable, lock, chg line
movw $0x00, 2(%si) # none, tinybit 2007-11-15
movw $0x800, 24(%si) # bytes per sect=2048
xorl %eax, %eax
decw %ax
movl %eax, 4(%si) # cylinders=0xFFFF
movb $0, %ah
movl %eax, 8(%si) # heads=0xFF
movb $15, %al
movl %eax, 12(%si) # sectors per track=15
movl %eax, 20(%si) # total sectors hi dword=0
xorw %ax, %ax # CF cleared
decl %eax # EAX=0xFFFFFFFF
movl %eax, 16(%si) # total sectors lo dword
# CF is cleared
jmp 5f //success_out
10: //ext_write:
movb $0x03, %ah # error code of write-protection
jmp 2f //fail_out
9: //ext_read:
call edd30_read_cdrom
jnc 5f //success_out
jmp 2f //fail_out
8: //install_check:
cmpw $0x55AA, %bx # added by tinybit 2007-11-15
jne 3f //invalid_cmd
movw $0xAA55, %cs:(int13_old_ebx - int13_handler) #; bx=0xaa55
movb $0x21, %ah # ah=0x21 edd-1.1
movb $0x01, %cs:(int13_old_ecx - int13_handler) # cx=0x01, ext disk access ok -- tinybit 2007-11-15
jmp 4f //success_out_no_ah
7: //get_last_stat:
movb %cs:(int13_last_stat - int13_handler), %ah
jmp 4f //success_out_no_ah
6: //reset:
call reg_reset
movw %cs:(atapi_cur_dev - int13_handler), %ax # device serial number
call select_atapi_force
5: //success_out:
xorb %ah, %ah
4: //success_out_no_ah:
andb $0xFE, %cs:(int13_old_flags - int13_handler)
jmp 1f
3: //invalid_cmd:
movb $0x01, %ah
2: //fail_out:
orb $1, %cs:(int13_old_flags - int13_handler) // set CF=1 for ERROR
1:
/* set error code AH */
movb %ah, %cs:(int13_last_stat - int13_handler)
movb %ah, %cs:(int13_old_eax + 1 - int13_handler)
movl %cs:(int13_old_eax - int13_handler), %eax
movl %cs:(int13_old_ebx - int13_handler), %ebx
movl %cs:(int13_old_ecx - int13_handler), %ecx
movl %cs:(int13_old_edx - int13_handler), %edx
movl %cs:(int13_old_esi - int13_handler), %esi
movl %cs:(int13_old_edi - int13_handler), %edi
movl %cs:(int13_old_ebp - int13_handler), %ebp
movw %cs:(int13_old_ds - int13_handler), %ds
movw %cs:(int13_old_es - int13_handler), %es
/* restore old flags */
pushw %cs:(int13_old_flags - int13_handler)
popfw
/* transfer control to caller */
ljmp *%cs:(int13_old_cs_ip - int13_handler)
#;============================================================================
int13_last_stat: .byte 0 #; the error code in AH
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:
.align 16
edd30_disk_buffer: .space 0x800
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
#;============================================================================
.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! */
// .align 16
// .space 0x1000 /* 4KB stack */
int13_handler_end:
#;============================================================================
#define CDROM_INIT
#ifdef CDROM_INIT
/* unsigned long
* init_atapi() : return number of atapi cdrom devices.
*/
ENTRY(init_atapi)
.code32
pushl %ebp
pushl %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
popl %ebx
popl %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
movb %bl, EXT_C(atapi_dev_count) - int13_handler
movb EXT_C(min_cdrom_id) - int13_handler, %cl
addb %bl, %cl
decw %cx
movb %cl, 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
movb $0, EXT_C(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
incb EXT_C(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
movzbw EXT_C(atapi_dev_count) - int13_handler, %cx
movw %cx, %bx
decw %bx
movb EXT_C(min_cdrom_id) - int13_handler, %al
addb %bl, %al
movb %al, 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 */
sti /* for hardware interrupt or watchdog */
#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 */
sti /* for hardware interrupt or watchdog */
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
cli
/* load the GDT register */
xorw %ax, %ax
movw %ax, %ds
DATA32 ADDR32 lgdt gdtdesc
/* turn on protected mode */
movl %cr0, %eax
andl $0x0000FFFF, %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
andl $0x0000FFFE, %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
#if defined(STAGE1_5) || 1
int $0x13
#else
call safe_int13
#endif
popw %bx
popw %di
#if 1
/* some buggy USB BIOSes fail to clear AH on success */
setc %dl
#else
movb $1, %dl /* set error */
jc 1f
movb %ah, %dl /* save return value */
1:
#endif
/* 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
movw $0x00ff, %cx # try so many times on failure
movw $0x0001, %dx # DL=enable A20, DH=debug off
cli /* yes, keep interrupt off when controlling A20 */
call enable_disable_a20
sti
//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 $5, %si /* attempt at least 5 times */
1:
pushw %si
pushw %di
pushw %cx
pushw %dx
xorw %bx, %bx
movw %di, %ax
#if defined(STAGE1_5) || 1
int $0x13
#else
call safe_int13
#endif
popw %dx
popw %cx
popw %di
popw %si
#if 1
/* some buggy USB BIOSes fail to clear AH on success */
setc %bl
jnc 2f
#else
movb %ah, %bl /* save return value */
jc 3f /* check if successful */
testb %ah, %ah
jz 2f
3:
#endif
movw %di, %ax /* get function number */
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
#if defined(STAGE1_5) || 1
int $0x13
#else
call safe_int13
#endif
popw %dx
popw %cx
popw %di
popw %si
decw %si
jnz 1b /* retry */
4:
movb $1, %bl /* set error */
2:
#ifndef STAGE1_5
/* ensure A20 is on when we come back to protected mode. */
pushal
movw $0x00ff, %cx # try so many times on failure
movw $0x0001, %dx # DL=enable A20, DH=debug off
cli /* yes, keep interrupt off when controlling A20 */
call enable_disable_a20
sti
//sete %dl # DL=1 means success
popal
#endif /* ! STAGE1_5 */
/* back to protected mode */
DATA32 call EXT_C(real_to_prot)
.code32
movzbl %bl, %eax /* 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
#if defined(STAGE1_5) || 1
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
.code32
/*
* int get_diskinfo_standard (int drive, unsigned long *cylinders,
* unsigned long *heads, unsigned long *sectors)
*
* if bit 8-15 of drive(dh) != 0 on call, then geometry_tune will be used.
*
* 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
/* Heads */
movl 0x10(%ebp), %edi
movl (%edi), %edi
movl %edi, ABS(Heads_passed_in)
/* Sectors */
movl 0x14(%ebp), %edi
movl (%edi), %edi
movl %edi, ABS(Sectors_passed_in)
/* drive */
movl 0x8(%ebp), %edx
/* enter real mode */
call EXT_C(prot_to_real)
.code16
sti
testb %dh, %dh
jz 2f
call geometry_tune
movb $1, %bh /* geometry_tune indicator */
jmp 1f
2:
xorw %cx, %cx
movb $0x8, %ah
#if defined(STAGE1_5) || 1
int $0x13
#else
call safe_int13
#endif
movb $0, %bh /* geometry_tune indicator */
jc 2f
/* 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:
/* failure */
/* 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 */
testb %bh, %bh
jnz 1f /* geometry_tune won't touch 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
.code16
geometry_tune:
/* on call:
* CS 0
*
* on return:
* AH=0 success, otherwise failure
* CL max sector number
* DH max head number
* DS changed
* ES changed
* AX changed
* BX changed
* CX changed
*/
movw $63, %cx /* cylinder=0, sector=Max possible */
1:
movw $0x57E0, %ax /* Don't use SCRATCHSEG */
movw %ax, %es
movw %ax, %ds
xorw %bx, %bx
movw $0x201, %ax /* read 1 sector */
movb $0, %dh /* head=0 */
pushaw
movw %bx, %si
movw %bx, %di
#if defined(STAGE1_5) || 1
int $0x13
#else
call safe_int13
#endif
popaw
jnc 1f /* found Max sector */
loop 1b
2:
movb $1, %ah /* failure */
ret
1:
/* CX=Max Sector */
movw %cx, %cs:ABS(Smax_tuned)
//////////////////////////////////////////////////////////////////////////////
/* check if we can read sectors across the track boundary */
movb $0, %cs:ABS(cross_track)
movw $1, %cx /* sector 1, cylinder 0 */
movb $0, %dh /* head 0 */
movb %cs:ABS(Smax_tuned), %al /* sectors to read */
incw %ax /* read 1 more sector */
movb $2, %ah /* READ */
movw $0x5000, %bx
movw %bx, %es
movw %bx, %ds
xorw %bx, %bx
pushaw
movw %bx, %si
movw %bx, %di
#if defined(STAGE1_5) || 1
int $0x13
#else
call safe_int13
#endif
popaw
jc 1f /* cross-track read is not supported */
/* read again normally */
movb %cs:ABS(Smax_tuned), %al /* sectors to read */
movb $2, %ah /* READ */
movw $0x5800, %bx
movw %bx, %es
movw %bx, %ds
xorw %bx, %bx
pushaw
movw %bx, %si
movw %bx, %di
#if defined(STAGE1_5) || 1
int $0x13
#else
call safe_int13
#endif
popaw
jc 2b /* failure */
/* compare the two tracks */
pushw %cx
pushw %si
pushw %di
movw %cs:ABS(Smax_tuned), %cx /* sectors */
shlw $7, %cx /* dwords */
movw $0x5000, %ax
movw %ax, %ds
movw $0x5800, %ax
movw %ax, %es
xorw %si, %si
xorw %di, %di
cld
repz cmpsl
popw %di
popw %si
popw %cx
jne 1f /* cross-track read is not supported */
movb $1, %cs:ABS(cross_track)
1:
//////////////////////////////////////////////////////////////////////////////
movb $0xFF, %dh /* head=Max possible */
1:
movw $0x57E0, %ax /* Don't use SCRATCHSEG */
movw %ax, %es
movw %ax, %ds
xorw %bx, %bx
movw $0x201, %ax /* read 1 sector */
movw $1, %cx /* cylinder=0, sector=1 */
pushaw
movw %bx, %si
movw %bx, %di
#if defined(STAGE1_5) || 1
int $0x13
#else
call safe_int13
#endif
popaw
jnc 1f /* found Max head */
decb %dh
cmpb $0xFF, %dh
jne 1b
movb $1, %ah /* failure */
ret
1:
/* DH=Max head */
movb %dh, %cs:ABS(Hmax_tuned)
//////////////////////////////////////////////////////////////////////////////
/* tune Hmax */
/* First, try the passed-in value */
movb %cs:ABS(Heads_passed_in), %dh
testb %dh, %dh
jz 1f /* the passed-in heads = 0x100 */
call tune_heads
jb 2f /* failure */
ja 4f /* success */
1:
movb $1, %dh /* Hmax: 1 - 255 */
1:
call tune_heads
jb 2f /* failure */
ja 4f /* success */
cmpb %cs:ABS(Hmax_tuned), %dh
ja 2f /* this should not happen */
je 5f
incb %dh /* Next Hmax */
jnz 1b
/* Hmax=0xFF */
4:
/* got Hmax=DH-1 */
decb %dh
movb %dh, %cs:ABS(Hmax_tuned)
5:
/* Hmax is tuned ok. */
//////////////////////////////////////////////////////////////////////////////
/* tune Smax */
/* First, try the passed-in value */
movb %cs:ABS(Sectors_passed_in), %cl
cmpb $0, %cs:ABS(cross_track)
jnz 4f
cmpb %cs:ABS(Smax_tuned), %cl
jnb 1f
cmpb $8, %cl
jb 1f
4:
call tune_sectors
jb 2f /* failure */
ja 4f /* success */
1:
movb $8, %cl /* Smax: 8 - 63 */
1:
call tune_sectors
jb 2f /* failure */
ja 4f /* success */
incw %cx /* Next Smax */
cmpb %cs:ABS(Smax_tuned), %cl
jb 1b
4:
/* got Smax=CL */
movb %cl, %cs:ABS(Smax_tuned)
/* Smax is tuned ok. */
//////////////////////////////////////////////////////////////////////////////
movw %cs:ABS(Smax_tuned), %cx
movb %cs:ABS(Hmax_tuned), %dh
movb $0, %ah /* success */
ret
2:
movb $1, %ah /* failure */
ret
//////////////////////////////////////////////////////////////////////////////
tune_heads:
/* input: DH = MaxHead + 1 */
movb $0, %ch /* cylinder: 0 - 3 */
2:
/* read ending track of this cylinder */
movb $1, %cl /* sector 1=the leading sector */
movb $2, %ah /* READ */
movb %cs:ABS(Smax_tuned), %al /* sectors to read */
movw $0x5000, %bx
movw %bx, %es
movw %bx, %ds
xorw %bx, %bx
pushaw
movw %bx, %si
movw %bx, %di
#if defined(STAGE1_5) || 1
int $0x13
#else
call safe_int13
#endif
popaw
jc 2f /* failure */
/* read beginning track of the next cylinder */
incb %ch /* next cylinder */
pushw %dx /* save DH */
movb $0, %dh
movb $1, %cl /* sector 1=the leading sector */
movb $2, %ah /* READ */
movb %cs:ABS(Smax_tuned), %al /* sectors to read */
movw $0x5800, %bx
movw %bx, %es
movw %bx, %ds
xorw %bx, %bx
pushaw
movw %bx, %si
movw %bx, %di
#if defined(STAGE1_5) || 1
int $0x13
#else
call safe_int13
#endif
popaw
popw %dx /* restore DH */
jc 2f /* failure */
/* compare the two tracks */
pushw %cx
pushw %si
pushw %di
movw %cs:ABS(Smax_tuned), %cx /* sectors */
shlw $7, %cx /* dwords */
movw $0x5000, %ax
movw %ax, %ds
movw $0x5800, %ax
movw %ax, %es
xorw %si, %si
xorw %di, %di
cld
repz cmpsl
popw %di
popw %si
popw %cx
jne 3f /* Next Hmax */
cmpb $4, %ch
jb 2b /* Next cylinder */
/* all passed, DH-1 is the final Hmax */
cmpb $0, %dh
je 2f /* failure */
ret /* Flag: above */
3:
cmpb %dh, %dh /* Flag: equal */
ret
2:
stc /* Flag: below */
ret
//////////////////////////////////////////////////////////////////////////////
tune_sectors:
/* input: CL = MaxSector */
movw $10, %cs:ABS(Smax_count)
movb $0, %ch /* cylinder: 0 - 7 */
movb $0, %dh /* head: 0 - Hmax */
6:
/* read ending sector of this track. */
movw $0x202, %ax /* read 2 sectors */
movw $0x5000, %bx
movw %bx, %es
movw %bx, %ds
xorw %bx, %bx
pushaw
movw %bx, %si
movw %bx, %di
#if defined(STAGE1_5) || 1
int $0x13
#else
call safe_int13
#endif
popaw
jc 2f /* failure */
/* read beginning sector of the next track. */
incb %dh /* next track */
pushw %cx /* save CL */
movb $1, %cl /* sector 1=the leading sector */
movw $0x201, %ax /* read 1 sector */
movw $0x5800, %bx
movw %bx, %es
movw %bx, %ds
xorw %bx, %bx
pushaw
movw %bx, %si
movw %bx, %di
#if defined(STAGE1_5) || 1
int $0x13
#else
call safe_int13
#endif
popaw
popw %cx /* restore CL */
jc 2f /* failure */
/* compare the two sectors */
pushw %cx
pushw %si
pushw %di
movw $0x80, %cx /* 1 sector == 0x80 dwords */
movw $0x5020, %ax
movw %ax, %ds
movw $0x5800, %ax
movw %ax, %es
xorw %si, %si
xorw %di, %di
cld
repz cmpsl
popw %di
popw %si
popw %cx
jne 3f /* Next Smax */
decw %cs:ABS(Smax_count)
jz 6f
cmpb %cs:ABS(Hmax_tuned), %dh
jb 6b /* Next track */
movb $0, %dh
incb %ch
cmpb $7, %ch
jb 6b /* Next track */
6:
/* all passed, CL is the final Smax */
cmpb $1, %cl
jbe 2f /* failure */
ret /* Flag: above */
3:
cmpb %cl, %cl /* Flag: equal */
ret
2:
stc /* Flag: below */
ret
//////////////////////////////////////////////////////////////////////////////
.align 4
Sectors_passed_in:
.long 0
Heads_passed_in:
.long 0
Smax_tuned:
.word 0
Hmax_tuned:
.word 0
Smax_count:
.word 0
cross_track:
.byte 0
//////////////////////////////////////////////////////////////////////////////
.code32
#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 */
sti /* for hardware interrupt or watchdog */
cmpb $0x1, %bl
//DATA32 je xext
je 1f
int $0x12
//DATA32 jmp xdone
jmp 2f
//xext:
1:
movb $0x88, %ah
#ifdef STAGE1_5
int $0x15
#else
pushfw
lcall *ABS(EXT_C(ROM_int15))
#endif
//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 */
sti /* for hardware interrupt or watchdog */
movw $0xe801, %ax
//int $0x15
pushfw
lcall *ABS(EXT_C(ROM_int15))
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 */
sti /* for hardware interrupt or watchdog */
movw %si, %es
/* set additional registers to serve buggy BIOSes. */
movw %si, %ds
movw %di, %si
movl $0xe820, %eax
//int $0x15
pushfw
lcall %cs:*ABS(EXT_C(ROM_int15))
//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 */
sti /* for hardware interrupt or watchdog */
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 */
sti /* for hardware interrupt or watchdog */
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 */
sti /* for hardware interrupt or watchdog */
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 */
sti /* for hardware interrupt or watchdog */
movw $0x4F02, %ax
int $0x10
movw %ax, %bx
DATA32 call EXT_C(real_to_prot)
.code32
movzwl %bx, %eax
popl %ebx
popl %ebp
ret
/*
* grub2_gate_a20(int on)
*
* 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. :-(
*/
ENTRY(grub2_gate_a20)
.code32
//movl %eax, %edx
movl 4(%esp), %edx /* the value of `on' */
gate_a20_test_current_state:
/* first of all, test if already in a good state */
call gate_a20_check_state
cmpb %al, %dl
jnz gate_a20_try_bios
ret
gate_a20_try_bios:
/* second, try a BIOS call */
pushl %ebp
call EXT_C(prot_to_real)
.code16
sti /* for hardware interrupt or watchdog */
movw $0x2400, %ax
testb %dl, %dl
jz 1f
incw %ax
1: int $0x15
DATA32 call EXT_C(real_to_prot)
.code32
popl %ebp
call gate_a20_check_state
cmpb %al, %dl
jnz gate_a20_try_keyboard_controller
ret
gate_a20_flush_keyboard_buffer:
inb $0x64
andb $0x02, %al
jnz gate_a20_flush_keyboard_buffer
2:
inb $0x64
andb $0x01, %al
jz 3f
inb $0x60
jmp 2b
3:
ret
gate_a20_try_keyboard_controller:
/* third, try the keyboard controller */
call gate_a20_flush_keyboard_buffer
movb $0xd1, %al
outb $0x64
4:
inb $0x64
andb $0x02, %al
jnz 4b
movb $0xdd, %al
testb %dl, %dl
jz 5f
orb $0x02, %al
5: outb $0x60
call gate_a20_flush_keyboard_buffer
/* output a dummy command (USB keyboard hack) */
movb $0xff, %al
outb $0x64
call gate_a20_flush_keyboard_buffer
call gate_a20_check_state
cmpb %al, %dl
jnz gate_a20_try_system_control_port_a
ret
gate_a20_try_system_control_port_a:
/* fourth, try the system control port A */
inb $0x92
andb $(~0x03), %al
testb %dl, %dl
jz 6f
orb $0x02, %al
6: outb $0x92
/* When turning off Gate A20, do not check the state strictly,
because a failure is not fatal usually, and Gate A20 is always
on some modern machines. */
testb %dl, %dl
jz 7f
call gate_a20_check_state
cmpb %al, %dl
/* everything failed, so restart from the beginning */
jnz gate_a20_try_bios
7: ret
gate_a20_check_state:
/* iterate the checking for a while */
movl $100, %ecx
1:
call 3f
cmpb %al, %dl
jz 2f
loop 1b
2:
ret
3:
pushl %ebx
pushl %ecx
xorl %eax, %eax
/* compare the byte at 0x8000 with that at 0x108000 */
movl $0x8000/*GRUB_BOOT_MACHINE_KERNEL_ADDR*/, %ebx
pushl %ebx # EBX=0x8000
/* save the original byte in CL */
movb (%ebx), %cl # CL=byte at 0x8000
/* store the value at 0x108000 in AL */
addl $0x100000, %ebx # EBX=0x108000
movb (%ebx), %al # AL=byte at 0x108000
/* try to set one less value at 0x8000 */
popl %ebx # EBX=0x8000
movb %al, %ch # CH=AL
decb %ch # CH=AL-1
movb %ch, (%ebx) # byte at 0x8000=CH
/* serialize */
outb %al, $0x80
outb %al, $0x80
/* obtain the value at 0x108000 in CH */
pushl %ebx # EBX=0x8000
addl $0x100000, %ebx # EBX=0x108000
movb (%ebx), %ch # CH=byte at 0x108000
/* this result is 1 if A20 is on or 0 if it is off */
subb %ch, %al # AL-=CH
xorb $1, %al
/* restore the original */
popl %ebx # EBX=0x8000
movb %cl, (%ebx) # restore it
popl %ecx
popl %ebx
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)
.code32
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
sti /* for hardware interrupt or watchdog */
movw $0x00ff, %cx # try so many times on failure
movb $0x01, %dh # with a20 debug on
cli /* yes, keep interrupt off when controlling A20 */
call enable_disable_a20
sti /* for hardware interrupt or watchdog */
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 on 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
DATA32 call EXT_C(real_to_prot)
.code32
movzbl %dl, %eax
1:
popl %ebp
ret
.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 */
sti /* for hardware interrupt or watchdog */
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 */
sti /* for hardware interrupt or watchdog */
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 */
sti /* for hardware interrupt or watchdog */
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 */
sti /* for hardware interrupt or watchdog */
/* 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 */
sti /* for hardware interrupt or watchdog */
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 */
sti /* for hardware interrupt or watchdog */
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 */
sti /* for hardware interrupt or watchdog */
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 */
sti /* for hardware interrupt or watchdog */
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 */
sti /* for hardware interrupt or watchdog */
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
/*
* void get_datetime(unsigned long *date, unsigned long *time);
*/
ENTRY(get_datetime)
pushl %ebp
call EXT_C(prot_to_real)
.code16
sti /* for hardware interrupt or watchdog */
movb $2, %ah
clc
int $0x1a
jc 2f
pushw %cx
pushw %dx
movb $4, %ah
clc
int $0x1a
jc 3f
pushw %cx
pushw %dx
popl %edx
popl %ecx
jmp 1f
3:
popl %eax
2:
xorl %ecx, %ecx
xorl %edx, %edx
1:
DATA32 call EXT_C(real_to_prot)
.code32
movl %esp, %ebp
movl 8(%ebp), %eax
movl %edx, (%eax)
movl 12(%ebp), %eax
movl %ecx, (%eax)
popl %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.
*/
.align 4
#;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
#if 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 */
#endif
/*
* 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.
*/
.align 16
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.
*/
. = EXT_C(HMA_start) + 0 /* GDT entry: default null */
jmp 1f /* two-byte short jmp */
. = EXT_C(HMA_start) + 2
/* 6-byte GDT descriptor */
gdtdescHMA:
.word 0x27 /* limit */
.long HMA_ADDR /* linear base address */
. = EXT_C(HMA_start) + 8 /* GDT entry: unused */
/* code segment, although it is no use here for now */
.word 0xFFFF, 0
.byte 0, 0x9A, 0xCF, 0
. = EXT_C(HMA_start) + 0x10 /* GDT entry: unused */
/* data segment, although it is no use here for now */
.word 0xFFFF, 0
.byte 0, 0x92, 0xCF, 0
. = EXT_C(HMA_start) + 0x18 /* GDT entry: 16-bit code */
/* 16-bit code segment base=0x2B0000 */
.word 0xFFFF, 0x0000
.byte 0x2B, 0x9E, 0, 0
. = EXT_C(HMA_start) + 0x20 /* GDT entry: 16-bit data */
/* real mode data segment base=0x200 */
.word 0xFFFF, 0x0200
.byte 0x00, 0x92, 0, 0
1:
/* set up to pass boot drive */
movb EXT_C(boot_drive), %dl
/* check if the --ebx option is given. */
movl (EXT_C(chain_ebx_set) - EXT_C(HMA_start) + HMA_ADDR), %eax
testl %eax, %eax
jz 1f
movl (EXT_C(chain_ebx) - EXT_C(HMA_start) + HMA_ADDR), %ebx
1:
/* check if the --edx option is given. */
movl (EXT_C(chain_edx_set) - EXT_C(HMA_start) + HMA_ADDR), %eax
testl %eax, %eax
jz 1f
movl (EXT_C(chain_edx) - EXT_C(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().
*/
/* the new loader is currently at 0x200000 */
movl $0x00200000, %esi
xorl %eax, %eax
xorl %edi, %edi
movw (EXT_C(chain_load_segment) - EXT_C(HMA_start) + HMA_ADDR), %di
shll $4, %edi
movw (EXT_C(chain_load_offset) - EXT_C(HMA_start) + HMA_ADDR), %ax
addl %eax, %edi
//movl $0x00007c00, %edi
movl (EXT_C(chain_load_length) - EXT_C(HMA_start) + HMA_ADDR), %ecx
cld
repz movsb
/* switch to real mode */
/* set new GDT */
lgdt (gdtdescHMA - EXT_C(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
*/
ljmp $PSEUDO_RM_CSEG, $(1f - EXT_C(HMA_start))
1:
.code16
/* clear the PE bit of CR0 */
movl %cr0, %eax
//andl $CR0_PE_OFF, %eax
andl $0x0000FFFE, %eax
movl %eax, %cr0
/* 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
/* flush prefetch queue, reload %cs */
.byte 0xEA /* ljmp 0000:7c00 */
VARIABLE(chain_boot_IP)
.word 0x7c00 /* offset */
VARIABLE(chain_boot_CS)
.word 0x0000 /* segment */
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
/* max length of code is 1 sector */
. = . - (. - EXT_C(HMA_start))/0x201
/* ensure this resides in the first 64KB */
. = . - (ABS(.) / 0x10001)
.code32