| /* |
| * grldrstart.S -- Startup code for GRLDR |
| * Copyright (C) 2004-2006 Tinybit(tinybit@tom.com) |
| * |
| * 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. |
| */ |
| |
| /* |
| * This program is used to generate the GRLDR file. |
| * |
| * Use the following shell command to generate the GRLDR file: |
| * |
| * cat grldrstart pre_stage2 > grldr |
| * |
| */ |
| |
| #define ASM_FILE |
| #include <shared.h> |
| #define ASM_FILE |
| |
| #ifndef STAGE1_5 |
| #include <stage2_size.h> |
| #else |
| #error cannot compile with STAGE1_5 |
| #endif |
| |
| #ifdef GRLDR_MBR |
| .file "mbrstart.S" |
| #elif defined(GRLDR_INSTALL) |
| .file "bootlacestart.S" |
| #else |
| .file "grldrstart.S" |
| #endif |
| |
| #ifdef GRLDR_INSTALL |
| //.data |
| #else |
| .text |
| |
| .globl start, _start |
| |
| start: |
| _start: |
| #endif |
| |
| _start1: |
| |
| /* Tell GAS to generate 16-bit real mode instructions */ |
| |
| .code16 |
| |
| . = _start1 + 0x00 |
| |
| /* 1 byte at offset 0x00 will be overwritten for the EBIOS indicator |
| * later. This is safe because the jmp instruction only get executed |
| * once. The write happens after the jmp instruction have got |
| * executed. |
| * |
| * The value written would be 0x42 for EBIOS present(LBA) and 0x02 |
| * for non-present(CHS). |
| * |
| */ |
| |
| /* No cli, we use stack! BIOS or caller usually sets SS:SP=0000:0400 */ |
| |
| jmp 1f /* FAT32/NTFS routine comes to offset 0 */ |
| |
| . = _start1 + 0x02 |
| |
| .byte 0x80 /* bit0=1: disable GRLDR search on floppy */ |
| /* bit1=1: disable the boot of the previous MBR with |
| * invalid partition table */ |
| /* bit2=1: disable the feature of unconditional |
| * entrance to the command-line */ |
| /* bit7=1: disable the boot of the previous MBR prior |
| to the search for GRLDR */ |
| |
| /* GRLDR.MBR uses offset 0x03 to indicate a timer counter. */ |
| |
| /* 0xff indicates waiting forever, |
| * other value specifies the time in seconds to wait */ |
| |
| . = _start1 + 0x03 |
| |
| .byte 5 |
| |
| /* a key press to wait. if AX returned from int16 equals this word, |
| * the desired action will occur. */ |
| |
| . = _start1 + 0x04 |
| |
| .word 0x3920 /* the space bar */ |
| |
| . = _start1 + 0x06 |
| |
| .byte 0xff /* preferred boot drive number, 0xff for no-drive(i.e., drive not defined) */ |
| .byte 0xff /* preferred partition number, 0xff for whole drive(a floppy that has no partition table) */ |
| |
| . = _start1 + 8 |
| |
| #if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL)) |
| |
| /* filled in by mkisofs using the -boot-info-table option */ |
| |
| #;bi_pvd: .long 0xDEADBEEF /* LBA of primary volume descript */ |
| #;bi_file: .long 0xDEADBEEF /* LBA of boot file */ |
| #;bi_length: .long 0xDEADBEEF /* Length of boot file */ |
| #;bi_csum: .long 0xDEADBEEF /* Checksum of boot file */ |
| #;bi_reserved: .space (10*4) /* Reserved */ |
| |
| . = _start1 + 0x40 |
| |
| #endif /* ! defined(GRLDR_MBR) && (! defined(GRLDR_INSTALL)) */ |
| |
| 1: |
| call 1f |
| |
| #if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL)) |
| |
| . = _start1 + 0x43 |
| |
| #else |
| |
| . = _start1 + 0x0b |
| |
| #endif /* ! defined(GRLDR_MBR) && (! defined(GRLDR_INSTALL)) */ |
| |
| 1: |
| popw %bx /* Instruction Pointer of 1b */ |
| |
| #if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL)) |
| |
| subw $(1b - _start1), %bx /* CS:BX=_start1 */ |
| |
| #endif /* ! defined(GRLDR_MBR) && (! defined(GRLDR_INSTALL)) */ |
| |
| shrw $4, %bx |
| movw %cs, %ax |
| addw %ax, %bx /* BX:0000=_start1 */ |
| |
| #if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL)) |
| |
| /* we are booted from BOOT.INI, or whole GRLDR image already loaded */ |
| |
| pushw %bx /* BX:0000=_start1 */ |
| addw $((grldr_signature - _start1 + 4 + STAGE2_SIZE - 4) >> 4), %bx |
| movw %bx, %ds |
| |
| cmpl $0xCE1A02B0, ((STAGE2_SIZE - 4) & 0x0F) |
| popw %ds /* DS:0000=_start1 */ |
| je grldr_real_start /* whole image loaded. boot it! */ |
| |
| /* bad! we might be loaded by a buggy BIOS with a no-emulation-mode |
| * bootable CD. The buggy BIOS might load only 1 CD-ROM sector(2048 |
| * bytes) of our grldr image. So we need this check. |
| */ |
| |
| /* Our cdrom_check code begins at 0x1BE and overlaps the partition |
| * table. Just in case someone replace it with a partition table and |
| * use this sector as an MBR, we do this additional test for safety. |
| */ |
| testb $0x7F, cdrom_check - _start1 |
| jz 1f |
| call cdrom_check |
| 1: |
| /* DS:0000=_start1 */ |
| |
| /* Let CS:0000=_start1 */ |
| pushw %ds |
| |
| #;pushw $(1f - _start1) |
| .byte 0x6A, (1f - _start1) |
| |
| lret |
| . = . - (. - _start1) / 0x80 |
| 1: |
| #else |
| /* BX:0000=_start1 */ |
| |
| movw %bx, %ds |
| |
| /* Let CS:0000=_start1 */ |
| pushw %bx |
| |
| #;pushw $(1f - _start1) |
| .byte 0x6A, (1f - _start1) |
| |
| lret |
| . = . - (. - _start1) / 0x80 |
| 1: |
| testb $0x04, 0x02 |
| jz 1f |
| |
| /* set the DUCE indicator */ |
| xorw %ax, %ax |
| movw %ax, %es |
| movw $0x5FC, %di |
| movl $0x45435544, %eax |
| stosl |
| 1: |
| #endif |
| |
| /* CS:0000=DS:0000=_start1 */ |
| |
| /* we are loaded by BIOS or another boot loader */ |
| |
| #define GRLDR_CS 0x2000 /* grldr code segment */ |
| /* hope this segment never be used by all */ |
| /* subsequent partition boot records */ |
| // cli |
| pushw $GRLDR_CS |
| popw %ss |
| movw $0x9000, %sp /* SS:SP=0x9d000, keep away from EBDA data */ |
| // sti |
| |
| /* Extended BIOS Data Area should not take up space below 0x9d000 */ |
| |
| /* |
| * 0x07c00-0x07dff This sector. Another boot loader load us here |
| * 0x0d000-0x14dff partition/floppy boot track(bootsector,etc) |
| * 0x94000-0x9bdff master boot track(MBR,etc,usually 63 sectors) |
| * 0x9be00-0x9c3ff 3 sectors for temp extended partition entries |
| * 0x9c400-0x9cfff 6 sectors for stack |
| */ |
| |
| #define FS_BOOT 0xd00 /* segment of partition boot track */ |
| |
| xorw %cx, %cx |
| pushw %cx /* CX=0 */ |
| movw $0x0080, %dx |
| pushw %dx |
| //movb $0x80, %dl |
| movb $8, %ah /* read drive parameters changes DX,ES,DI */ |
| stc |
| int $0x13 |
| popw %dx |
| popw %ax /* AX=0 */ |
| |
| pushw %ss /* SS=0x9400 */ |
| popw %es /* ES=0x9400 */ |
| |
| #; jc 2f |
| jc Error1 |
| |
| andb $63, %cl /* AL=sectors per track, CF cleared */ |
| |
| #; jnz 1f |
| stc |
| jz Error1 |
| #;2: |
| #; /* drive 0x80 not present. check hard drives in BIOS DATA AREA. */ |
| #; //pushw %cs /* DS=CS */ |
| #; //xorw %ax, %ax |
| #; movw %ax, %ds /* DS=0 */ |
| #; cmpb 0x475, %al |
| #; //popw %ds /* DS=CS */ |
| #; stc |
| #; je Error1 /* no hard drive, continue */ |
| #; movw $(buggy_bios_string - _start1), %si |
| #; jmp Error2 /* there is a hard drive, fatal. Halt! */ |
| #; //stc |
| #; //jz Error1 /* invalid value, try floppy (fd0) */ |
| #;1: |
| xchgw %ax, %cx /* this moves CL to AL, and CX=0 */ |
| movb $0x02, %ah |
| movw %ax, %bp /* save AX to BP: read 1 track */ |
| xorw %bx, %bx /* ES already has a known value of 0x9400 */ |
| incw %cx |
| pushw %dx |
| stc |
| int $0x13 /* read master boot track to ES:0000 */ |
| popw %dx |
| jc Error1 |
| negb %ah /* set CF=1 if non-zero */ |
| Error1: |
| pushw %cs /* DS=0 */ |
| popw %ds /* DS=CS */ |
| pushfw /* CF=1 on error */ |
| |
| /* CS=DS=old segment. ES=SS=new segment. */ |
| |
| /* Move the code and error messages from DS:0000 to 9400:0000, do not |
| * touch the partition table |
| */ |
| xorw %si, %si |
| xorw %di, %di |
| movw $223, %cx /* 223 words = 446 bytes = 0x1be bytes */ |
| cld |
| repz movsw /* SI=DI=0x1be, CX=0 */ |
| |
| movw $(grldr_signature - _start1), %bx |
| |
| /* if the boot loader has loaded more than one sector, we use them */ |
| movl $0xAA555247, %eax /* "GR" 0x55 0xAA */ |
| #if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL)) |
| cmpl %eax, (%bx) /* DS=old segment! */ |
| jne 1f |
| |
| /* The MOVE_HELPER code is in the old segment! */ |
| |
| call move_helper /* SI=0x1be, CX=0 */ |
| 1: |
| #endif |
| |
| /* Jump to new segment! */ |
| #if 1 |
| ljmp $GRLDR_CS, $(1f - _start1) |
| #else |
| pushw %ss /* 0x9400 */ |
| |
| //pushw $(1f - _start1) |
| .byte 0x6A, (1f - _start1) |
| |
| lret |
| . = . - (. - _start1) / 0x80 |
| #endif |
| 1: |
| |
| /* We are at the new segment. CS=ES=SS=new segment. */ |
| |
| /* But DS is still old segment. */ |
| |
| pushw %ss |
| popw %ds |
| |
| /* CS=DS=ES=SS=new segment. */ |
| |
| //movw $0x01be, %si |
| |
| /* check the existence of helper */ |
| cmpl %eax, (%bx) |
| |
| jne Error_or_prev_MBR /* Missing helper */ |
| |
| popfw /* CF=1 on error */ |
| jc try_floppy /* harddisk (hd0) failed, try floppy (fd0) */ |
| 1: |
| pushw %cs |
| popw %ds |
| lodsw |
| movb %ah, %dh /* head number */ |
| lodsw |
| movw %ax, %cx /* sector and cylinder number */ |
| andb $63, %al |
| //stc |
| jz helper_call_c |
| |
| /* use BP to calculate the sectors to read within 1 track */ |
| subw %bp, %ax |
| decw %ax /* decb %al */ |
| negb %al /* AL=sectors upto the end of the track */ |
| 7: |
| movw $3, %di /* retry 3 times on read failure */ |
| 2: |
| movb $2, %ah |
| pushw $FS_BOOT |
| popw %es /* ES=FS_BOOT */ |
| xorw %bx, %bx |
| //pushw %ax |
| pushaw |
| int $0x13 /* read partition boot track to FS_BOOT:0000 */ |
| popaw |
| //popw %ax |
| jnc helper_call |
| //pushw %ax |
| pushaw |
| xorw %ax, %ax |
| int $0x13 |
| popaw |
| //popw %ax |
| decw %di |
| jnz 2b |
| |
| helper_call_c: |
| |
| stc |
| |
| helper_call: |
| /* find GRLDR in this partition |
| * before the call: |
| * CF=1 : indicates an invalid or corrupt entry |
| * CF=0 : indicates a valid entry |
| * |
| * on return: |
| * CF=1 : means "below", try next entry |
| * CF=0,ZF=1 : means "equal", helper did nothing, so we need |
| * a further try to boot via NT bootsector |
| * CF=0,ZF=0 : means "above", helper succeeded, boot it now |
| */ |
| call helper_start /* change to jmp 6f if helper not present */ |
| ja filesystem_boot /* helper succeeded, directly boot it */ |
| 6: |
| |
| add_sub_si: |
| |
| /* extended partition check routine will adjust this to |
| * |
| * 0x83, 0xEE, 0x04 for "subw $4, %si" |
| * |
| * or |
| * |
| * 0x83, 0xC6, 0xFC for "addw $-4, %si" |
| * |
| * so that SI keeps the value 0x1fe. |
| */ |
| addw $12, %si /* 0x83, 0xC6, 0x0C */ |
| |
| . = add_sub_si + 3 |
| |
| /* extended partition check routine will adjust the word 0x1fe at |
| * (add_sub_si + 5). The value 0x1ff or greater indicates there are |
| * entries need to be treated. The value 0x1fe indicates no entries |
| * left, and the floppy should be checked. |
| */ |
| |
| cmpw $0x01fe, %si /* 0x81, 0xFE, 0xfe, 0x01 */ |
| /* All entries checked done? */ |
| jb 1b /* No, check the next entry */ |
| ja 5f /* floppy already checked. Fail and hang */ |
| |
| try_floppy: |
| |
| movw $0x31b2, %si /* a value big enough */ |
| movb $0x08, %ah /* read drive parameters changes DX,ES,DI */ |
| cwd /* DL=0 for floppy */ |
| pushw %dx /* DX=0 */ |
| int $0x13 |
| popw %ax /* AX=0 */ |
| jc 5f /* floppy failure, issue "Error" and hang */ |
| cwd /* DX=0 */ |
| xchgw %ax, %cx /* this moves CL to AL, and CX=0 */ |
| andb $63, %al /* AL=sectors per track */ |
| jz 5f /* invalid value. floppy failure. hangs */ |
| //movw $1, %cx |
| incw %cx |
| jmp 7b |
| |
| 5: |
| Error_or_prev_MBR: |
| |
| /* GRLDR not found, print "Error" or launch previous MBR */ |
| movw $(message_string - _start1), %si |
| Error2: |
| call print_message /* CS:SI points to message string */ |
| 3: jmp 3b |
| |
| filesystem_boot: |
| /* The partition boot record successfully modified, just boot it */ |
| |
| /* |
| * The boot might fail, but we want to take back the control. |
| * So we save the registers now. |
| */ |
| pushw %ds |
| pushw %es |
| pushal |
| |
| /* DS=CS=GRLDR_CS, ES=FS_BOOT */ |
| |
| /* save GRLDR_CS */ |
| |
| movw %es, %bx # save old ES to BX |
| |
| cli |
| lgdt gdt - _start1 |
| movl %cr0, %eax |
| orb $1, %al |
| movl %eax, %cr0 |
| |
| movw $8, %si |
| movw %si, %es |
| |
| xorl %esi, %esi |
| xorl %edi, %edi |
| movl $(0x9000 / 4), %ecx |
| |
| cld |
| repz movsl |
| |
| movw $16, %si |
| movw %si, %es |
| |
| andb $0xfe, %al |
| movl %eax, %cr0 |
| |
| movw %bx, %es # restore ES from BX |
| |
| /* move FS_BOOT:0000 to 0:7c00 */ |
| movw $0x0200, %cx /* move 2 sectors, the old FS_BOOT:0000 will |
| * keep untouched. */ |
| xorw %si, %si |
| pushw %si /* SI=0, for the segment of 0000:7c00 */ |
| movw $0x7c00, %di |
| pushw %di /* DI=0x7c00, for the offset of 0000:7c00 */ |
| pushw %es /* ES=FS_BOOT */ |
| popw %ds /* DS=FS_BOOT */ |
| pushw %si /* SI=0 */ |
| popw %es /* ES=0 */ |
| cld |
| repz movsw |
| |
| movw $0x7e10, %di |
| movw $(restore_GRLDR_CS - _start1), %si |
| movw $((gdt_end - restore_GRLDR_CS) / 4), %cx |
| cld |
| repz cs movsl /* CS segment override prefix(=0x2E) */ |
| |
| pushw %es /* ES=0 */ |
| popw %ds /* DS=0 */ |
| sti |
| lret //ljmp $0, $0x7c00 |
| |
| try_next_partition: |
| |
| cli |
| movw $GRLDR_CS, %ax |
| movw %ax, %ss |
| movw $(0x9000-36), %sp |
| sti |
| |
| /* restore the registers and continue */ |
| popal |
| popw %es |
| popw %ds |
| jmp add_sub_si |
| |
| /* prints string CS:SI (modifies AX BX SI) */ |
| 3: |
| //xorw %bx, %bx /* video page 0 */ |
| movb $0x0e, %ah /* print char in AL */ |
| int $0x10 /* via TTY mode */ |
| |
| print_message: |
| |
| lodsb %cs:(%si), %al /* get token */ |
| cmpb $0, %al /* end of string? */ |
| jne 3b |
| ret |
| |
| message_string: |
| |
| #if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL)) |
| .ascii "\r\nMissing helper.\0" |
| #else |
| .ascii "\r\nMissing MBR-helper.\0" |
| #endif |
| |
| #;buggy_bios_string: |
| #; |
| #; .ascii "\r\nBuggy BIOS!\0" |
| |
| /* Make sure the above code does not occupy the partition table */ |
| |
| #if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL)) |
| /* offset value here must be less than or equal to 0x1be */ |
| . = . - ((. - _start1) / 0x1bf) |
| #else |
| /* offset value here must be less than or equal to 0x1b8 */ |
| . = . - ((. - _start1) / 0x1b9) |
| #endif |
| |
| /* The following code may occupy the same area as the partition table */ |
| |
| #if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL)) |
| |
| /* we are not booted from MBR. So we can reuse the area of partition |
| * table for our code. |
| */ |
| |
| . = _start1 + 0x1be |
| |
| cdrom_check: |
| |
| /* DS points to the sector start, but CS does not. */ |
| |
| /* BX segment points to near the end of GRLDR image. */ |
| |
| popw %ax /* old return IP */ |
| |
| /* set BX as the new safe stack. */ |
| movw %bx, %ss |
| movw $0xFFF0, %sp |
| |
| pushw %ax /* old return IP */ |
| |
| /* check if DL is no-emulation-mode bootable CDROM. */ |
| pushw %ds |
| |
| cmpb $0x81, %dl |
| jb 1f /* not a valid no-emulation-mode cdrom drive number */ |
| |
| cmpw $0xAA55, 0x7FE /* 2048 bytes loaded? */ |
| jne 1f |
| |
| cmpw $0xAA55, 0x5FE /* 2048 bytes loaded? */ |
| jne 1f |
| |
| // movw 0x7FE, %ax |
| // xchgb %ah, %al |
| // orw 0x5FE, %ax |
| // inc %ax |
| // jne 1f |
| // /* check cdrom using int13/AX=4B01h */ |
| // |
| // /* AX=0 */ |
| // movw $0x0180, %si |
| // movw %si, %di |
| // movw $0x40, %cx |
| // pushw %cx |
| // popw %ds |
| // pushw %ds |
| // popw %es |
| // repz stosw |
| |
| movw $0x0180, %si |
| movw $0x4B01, %ax |
| pushw $0x0040 |
| //.byte 0x6A, 0x40 |
| popw %ds |
| pushw %ds |
| popw %es |
| movb $0x13, (%si) |
| int $0x13 |
| jc 2f /* not in emulation mode */ |
| testb $0x0F, 1(%si) /* boot media type is No Emulation? */ |
| jnz 2f /* no, it simulates floppy or hard disk. */ |
| cmpb %dl, 2(%si) /* drive number */ |
| // jnz 2f /* invalid drive */ |
| |
| jz cdrom_helper /* OK! it is no-emulation-mode cdrom drive. */ |
| |
| // movl 4(%si), %eax /* LBA of GRLDR */ |
| |
| // jmp cdrom_helper |
| |
| // /* load the first sector of GRLDR */ |
| // movw $0x4201, %ax |
| // movw $0x01A0, %si |
| // int $0x13 |
| 2: |
| // /* check once more using int13/AH=48h */ |
| // movb $0x48, %ah |
| // movw $0x0180, %si |
| // movl $0x1E, (%si) |
| // int $0x13 |
| // jc 1f |
| // cmpw $0x800, 0x18(%si) |
| // jne 1f |
| // jmp cdrom_helper |
| 1: |
| popw %ds |
| ret |
| |
| |
| #endif /* ! defined(GRLDR_MBR) && (! defined(GRLDR_INSTALL)) */ |
| |
| . = _start1 + 0x1fe /* boot signature */ |
| |
| /* partition entries in the extended partitions will overwrite code here upto |
| * 0x3fd. |
| * |
| * the extended partition entries will occupy a temp area at 0x9be00-0x9c3ff |
| */ |
| |
| #if (defined(GRLDR_MBR)) || (defined(GRLDR_INSTALL)) |
| .word 0xaa55 |
| #endif |
| |
| . = _start1 + 0x200 |
| |
| /* if it is in the Master Boot Track, the second sector can be used to backup |
| * the previously working MBR, typically, the MS MBR. if the backup copy of |
| * the MBR cannot boot(because, e.g., it depends on another sector of code |
| * that does not exist for now), then please do not set the ending signature |
| * to 0xAA55, that is to say, if the signature is already 0xAA55, you should |
| * change it to another value(for example, 0x0000). |
| */ |
| |
| #if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL)) |
| #if 0 |
| print_cl: |
| pushaw |
| |
| movw %cx, %ax |
| movb $16, %cl |
| divb %cl # quo=AL, rem=AH |
| orw $0x3030, %ax |
| |
| cmpb $0x39, %ah |
| jbe 1f |
| addb $7, %ah |
| 1: |
| cmpb $0x39, %al |
| jbe 1f |
| addb $7, %al |
| 1: |
| movb %ah, %cl |
| |
| xorw %bx, %bx |
| |
| movb $0x0e, %ah |
| int $0x10 |
| |
| movb $0x0e, %ah |
| movb %cl, %al |
| int $0x10 |
| |
| movw $0x0e20, %ax |
| int $0x10 |
| |
| popaw |
| ret |
| #else |
| #if 0 |
| .word 5, 0x47, 0x52, 0x4c, 0x44, 0x52, 4, 0x24 |
| .word 0x49, 0x33, 0x30, 0xe000, 0, 0x3000, 0, 0 |
| #else |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| #endif |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| #endif |
| . = _start1 + 0x256 /* cmdcons comes here */ |
| |
| #if 0 |
| jmp 1f |
| #else |
| .byte 0x90, 0x90 |
| #endif |
| |
| . = _start1 + 0x258 |
| |
| .byte 0x90, 0x90 |
| |
| . = _start1 + 0x25a |
| |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| .byte 0x90, 0x90 |
| |
| . = _start1 + 0x26a |
| 1: |
| //movw %cs, %ax |
| //movw %ax, %ds |
| //jmp single_boot_sector |
| movb $0x80, %dl |
| jmp _start1 |
| |
| cdrom_helper: |
| |
| /* IP and old_DS is on the stack. DS=40h */ |
| |
| // jz 2f |
| // |
| // /* check once more using int13/AH=48h */ |
| // movb $0x48, %ah |
| // movw $0x0180, %si |
| // movl $0x1E, (%si) |
| // int $0x13 |
| // jc 1f |
| // cmpw $0x800, 0x18(%si) |
| // je 3f |
| //1: |
| // popw %ds |
| // ret |
| // |
| // |
| //2: |
| // /* EAX is LBA. if EAX==0, LBA is unknown. */ |
| // |
| // /* load sector */ |
| // /* check if sector is the start sector */ |
| // /* if yes, goto 3f */ |
| // |
| // /* for sector=0x10 to 0x1000 */ |
| // /* load sector */ |
| // /* check if sector is the start sector */ |
| // /* if yes, goto 3f */ |
| // /* next */ |
| // popw %ds |
| // ret |
| //3: |
| // movb $0x00, %ah |
| // //movw $0x55AA, %bx |
| // int $0x13 |
| // //jc 4f |
| // movb %ah, %cl |
| // call print_cl |
| // //jmp grldr_real_start |
| |
| /* load all sectors (except the first sector) */ |
| movl 4(%si), %eax /* LBA of GRLDR */ |
| // movb %al, %cl |
| // call print_cl |
| // movb %ah, %cl |
| // call print_cl |
| // movl $0x1B, %eax |
| popw %bx /* BX = old_DS = load_segment */ |
| pushw %bx |
| // movw $0x780, %bx |
| movw %bx, %es |
| movw $((grldr_signature - _start1 + 4 + STAGE2_SIZE - 1) / 2048), %cx /* sectors to load */ |
| 1: |
| incl %eax /* next sector */ |
| addw $0x0080, %bx /* buffer segment */ |
| |
| pushw %cx |
| movw $6, %cx /* retry counter */ |
| 2: |
| |
| movw $0x1A0, %si |
| |
| /* disk address packet */ |
| movl $0x00010010, (%si) /* load 1 sector each time. */ |
| movw $0, 4(%si) /* buffer offset=0 */ |
| movw %bx, 6(%si) /* buffer segment */ |
| movl %eax, 8(%si) /* LBA lo 32 bits */ |
| movl $0, 12(%si) /* LBA hi 32 bits */ |
| |
| pushal |
| movb $0x42, %ah |
| int $0x13 |
| xchgw %ax, %bx |
| lahf /* Load Flags into AH */ |
| // movb %bh, %cl |
| // call print_cl |
| testb %bh, %bh /* error code */ |
| setnz %bh |
| orb %bh, %ah |
| sahf /* Store AH into flags SF ZF xx AF xx PF xx CF */ |
| popal |
| jnc 3f |
| loop 2b |
| 3: |
| popw %cx |
| jc 4f /* disk I/O error */ |
| loop 1b |
| |
| /* loading is completed. BX=segment of the last sector. */ |
| |
| decw %bx |
| movw %bx, %ds |
| |
| /* check the ending signature */ |
| cmpl $0xCE1A02B0, ((grldr_signature - _start1 + 4 + STAGE2_SIZE - 1) % 2048) + 13 |
| je grldr_real_start /* yes. boot it! */ |
| |
| /* it is not our grldr image, return and use MBR-helper. */ |
| |
| 4: |
| //jmp grldr_real_start |
| popw %ds |
| ret |
| |
| grldr_real_start: |
| |
| #; FAT_12_16 no longer be used. So comment out. |
| #;je 1f /* jc 1f */ |
| #;//ZF=0 /* CF cleared, so we are coming from FAT_12_16 */ |
| #;popw %dx /* discard the cluster number */ |
| #;popw %dx /* this is our boot_drive/boot_partition */ |
| #;1: |
| |
| #; The partition number for no-emulation-mode bootable CDROM will be |
| #; set to 0xFF later(in common.c). So comment out. |
| #;cli |
| #;movw %cs, %ax |
| #;cmpw $0x1000, %ax |
| #;jne 1f |
| #; |
| #;/* CS=0x1000, may be booted from ext2 or no-emulation-mode CDROM */ |
| #; |
| #;cmpw $0x1000, %di |
| #;jne 2f |
| #;cmpw $0x7c00, %bp |
| #;jne 2f |
| #;movw %es, %ax |
| #;cmpw $0x1000, %ax |
| #;jbe 2f |
| #;cmpw $0x7c00, %si |
| #;jbe 2f |
| #;movl %edx, %eax |
| #;shrl $16, %eax |
| #;jnz 2f |
| #;jecxz 1f // booted from ext2 partition |
| #;2: |
| #;// booted from no-emulation-mode bootable CDROM |
| #;movb $0xff, %dh // partition 0xff means whole drive(for CDROM) |
| #; #; if needed, 0xfe can be used as an indicator |
| #; #; here for the bootable CDROM and changed to |
| #; #; 0xff later. |
| #;1: |
| #; |
| #;//if not booted from CDROM, don't touch the boot partition number(dh) |
| |
| cli |
| xorw %ax, %ax |
| movw %ax, %ss |
| movw $0x0400, %sp /* tmp use real-mode IDT as stack */ |
| movw %cs, %bp /* save CS to BP */ |
| call 1f |
| 1: |
| popw %bx /* BX=Instruction Pointer of 1b */ |
| subw $(1b - _start1), %bx |
| movw %bx, %cx |
| shrw $4, %bx |
| addw %bp, %bx |
| pushw %bx /* new CS */ |
| andw $0x000f, %cx |
| addw $(1f - _start1), %cx |
| pushw %cx /* new IP */ |
| lret |
| 1: |
| pushw %cs |
| popw %ds |
| |
| /* CS=DS=BX, CS:0000 = _start1 */ |
| |
| addw $((pre_stage2_start - _start1) >> 4), %bx |
| |
| /* BX:0000 = pre_stage2_start */ |
| |
| cmpw $0x820, %bx |
| jb 2f |
| |
| movw $((0x8200 - (pre_stage2_start - _start1) - 0x400) >> 4), %cx |
| |
| /* Now CS(=DS) >= CX+0x40 */ |
| |
| movw %cx, %es |
| xorw %di, %di |
| xorw %si, %si |
| |
| ///////////////////////////////////////////////////////////// |
| // |
| // CS |
| // DS 0x820 BX |
| // _start1---------------pre_stage2_start |
| // CX+0x40---------------0x820 |
| // CX |
| // ES |
| // |
| ///////////////////////////////////////////////////////////// |
| |
| movw $0x200, %cx /* move 2 sectors */ |
| cld |
| repz movsw |
| |
| pushw %es /* ES:0000 = _start */ |
| pushw $(1f - _start) |
| lret /* CS=ES, CS:0000 = _start1 */ |
| 1: |
| |
| /* move BX:0000 to 0820:0000 upward since BX >= 0x820 */ |
| |
| cld |
| |
| movw %bx, %ds |
| movw $0x820, %bx |
| movw %bx, %es |
| |
| xorw %si, %si |
| xorw %di, %di |
| |
| movw $6, %bx /* 64K pages: 0x20000 - 0x7ffff */ |
| 1: |
| movw $0x8000, %cx |
| repz movsw |
| movw %ds, %ax |
| addw $0x1000, %ax |
| movw %ax, %ds |
| movw %es, %ax |
| addw $0x1000, %ax |
| movw %ax, %es |
| decw %bx |
| jnz 1b |
| |
| jmp 3f |
| 2: |
| |
| /* move BX:0000 to 0820:0000 downward since BX < 0x820 */ |
| |
| std |
| |
| addw $0x7000, %bx |
| movw %bx, %ds |
| movw $0x7820, %bx |
| movw %bx, %es |
| |
| movw $0xfffe, %si |
| movw %si, %di |
| |
| movw $8, %bx /* 64K pages: 0x08200 - 0x881ff */ |
| 1: |
| movw $0x8000, %cx |
| repz movsw |
| movw %ds, %ax |
| subw $0x1000, %ax |
| movw %ax, %ds |
| movw %es, %ax |
| subw $0x1000, %ax |
| movw %ax, %es |
| decw %bx |
| jnz 1b |
| |
| cld |
| |
| 3: |
| |
| /* put the config file name */ |
| xorw %ax, %ax |
| movw %ax, %es |
| movw %ax, %ds |
| |
| xorl %ebp, %ebp |
| |
| movb %dh, 0x820A /* this is the boot partition number */ |
| |
| #; clear saved_entryno so that force_cdrom_as_boot_device be cleared |
| #; later in common.c |
| |
| movl %ebp, 0x820C /* EBP=0, clear saved_entryno */ |
| |
| movw $0x0010, %cx /* set max length of grub version string */ |
| movw $0x8212, %di /* version string */ |
| cld |
| /* AL is already 0. Locate the end of version string */ |
| repnz scasb /* find the location of the default config file name */ |
| |
| jcxz 1f /* failed, will not use the default config file name */ |
| |
| movw $0x4e, %cx /* max length of config file name */ |
| |
| movw %cs, %si /* CS:0000 = _start1 */ |
| shlw $4, %si /* 0000:SI = _start1 */ |
| |
| addw $(default_config_file - _start1), %si |
| |
| //movw $(default_config_file + 0x8200 - pre_stage2_start), %si |
| cld |
| repz movsb /* move file name to the config-file field of stage2 */ |
| 1: |
| |
| movw $0x0003, %ax /* set display mode: 80*25 color text */ |
| int $0x10 |
| |
| xorw %bx, %bx |
| movw $(launch_pre_stage2 - _start1), %si |
| call print_message /* CS:SI points to message string */ |
| |
| xorw %ax, %ax |
| movw %ax, %ss |
| movw $0x2000, %sp |
| |
| sti |
| |
| ljmp $0, $0x8200 |
| |
| launch_pre_stage2: |
| .ascii "\r\n\r\nBooting GRLDR...\r\n" |
| |
| .byte 0 /* mark the end of ascii zero string */ |
| |
| default_config_file: |
| //#ifndef PRESET_MENU_STRING |
| .ascii "/menu.lst" |
| //#else |
| // .ascii "[default menu is disabled]" |
| //#endif |
| |
| .byte 0 /* mark the end of ascii zero string */ |
| #endif /* ! defined(GRLDR_MBR) && (! defined(GRLDR_INSTALL)) */ |
| |
| . = _start1 + 0x400 |
| |
| /* |
| * The following is based on FreeDOS, modified heavily by Tinybit in Feb, 2004 |
| * |
| * Merges LBA and CHS boot sectors to ONE FAT32 boot sector! |
| * |
| * Memory layout for GRLDR FAT32 single stage boot process: |
| * |
| * ... |
| * |-------| 1FE0:7E00 |
| * |BOOTSEC| (GRUB does not use this relocation area) |
| * |RELOC. | (overwritten by kernel loaded) |
| * |-------| 1FE0:7C00 |
| * ... |
| * |-------| |
| * |KERNEL | (overwrites bootsec reloc.) |
| * |LOADED | (holds 1 sector directory buffer before kernel load) |
| * |-------| 2000:0000 |
| * ... |
| * |-------| 0000:7E00 |
| * |BOOTSEC| GRUB always run inside this sector, |
| * |ORIGIN | no relocation. |
| * |-------| 0000:7C00 |
| * ... |
| * |-------| 0060:0200 |
| * | FAT | (only 1 sector buffered) |
| * |-------| 0060:0000 |
| * ... |
| * |
| */ |
| |
| /* |
| ; This is an LBA-enabled FreeDOS FAT32 boot sector (single sector!). |
| ; You can use and copy source code and binaries under the terms of the |
| ; GNU Public License (GPL), version 2 or newer. See www.gnu.org for more. |
| |
| ; Based on earlier work by FreeDOS kernel hackers, modified heavily by |
| ; Eric Auer and Jon Gentle in 7 / 2003. |
| ; |
| ; Features: Uses LBA and calculates all variables from BPB/EBPB data, |
| ; thus making partition move / resize / image-restore easier. FreeDOS |
| ; can boot from FAT32 partitions which start > 8 GB boundary with this |
| ; boot sector. Disk geometry knowledge is not needed for booting. |
| ; |
| ; Windows uses 2-3 sectors for booting (sector stage, statistics sector, |
| ; filesystem stage). Only using 1 sector for FreeDOS makes multi-booting |
| ; of FreeDOS and Windows on the same filesystem easier. |
| ; |
| ; Requirements: LBA BIOS and 386 or better CPU. Use the older CHS-only |
| ; boot sector if you want FAT32 on really old PCs (problems: you cannot |
| ; boot from > 8 GB boundary, cannot move / resize / ... without applying |
| ; SYS again if you use the CHS-only FAT32 boot sector). |
| ; |
| ; FAT12 / FAT16 hints: Use the older CHS-only boot sector unless you |
| ; have to boot from > 8 GB. The LBA-and-CHS FAT12 / FAT16 boot sector |
| ; needs applying SYS again after move / resize / ... a variant of that |
| ; boot sector without CHS support but with better move / resize / ... |
| ; support would be good for use on LBA harddisks. |
| |
| |
| ; Memory layout for the FreeDOS FAT32 single stage boot process: |
| |
| ; ... |
| ; |-------| 1FE0:7E00 |
| ; |BOOTSEC| |
| ; |RELOC. | |
| ; |-------| 1FE0:7C00 |
| ; ... |
| ; |-------| 2000:0200 |
| ; | FAT | (only 1 sector buffered) |
| ; |-------| 2000:0000 |
| ; ... |
| ; |-------| 0000:7E00 |
| ; |BOOTSEC| overwritten by the kernel, so the |
| ; |ORIGIN | bootsector relocates itself up... |
| ; |-------| 0000:7C00 |
| ; ... |
| ; |-------| |
| ; |KERNEL | maximum size 134k (overwrites bootsec origin) |
| ; |LOADED | (holds 1 sector directory buffer before kernel load) |
| ; |-------| 0060:0000 |
| ; ... |
| */ |
| |
| #define BOOTGRUB /* undef this if compiled for loading FreeDOS */ |
| //#undef BOOTGRUB |
| |
| #ifdef BOOTGRUB |
| #define LOADSEG 0x2000 |
| #define FATSEG 0x0060 |
| #else |
| #define LOADSEG 0x0060 |
| #define FATSEG 0x2000 |
| #endif |
| |
| Entry_32: |
| jmp 1f |
| |
| . = Entry_32 + 0x02 |
| |
| /* The default mode is CHS. This is for maximum compatiblity with |
| * small-sized disks, e.g., floppies. |
| * |
| * Valid values are 0x90 for CHS mode, or 0x0e for LBA mode. |
| * |
| * If the BIOS int13 supports LBA, this byte can be safely set to 0x0e. |
| * |
| * Some USB BIOSes might have bugs when using CHS mode, so the format |
| * program should set this byte to 0x0e. It seems that (generally) all |
| * USB BIOSes have LBA support. |
| * |
| * If the format program does not know whether the BIOS has LBA |
| * support, it may operate this way: |
| * |
| * if (partition_start + total_sectors_in_partition) exceeds the CHS |
| * addressing ability(especially when it is greater than 1024*256*63), |
| * the caller should set this byte to 0x0e, otherwise, set to 0x90. |
| */ |
| |
| .byte 0x90 /* for CHS. Another possible value is 0x0e for LBA */ |
| |
| |
| . = Entry_32 + 0x03 |
| |
| #ifdef BOOTGRUB |
| .ascii "GRLDR " /* OEM name string (of OS which formatted the disk). */ |
| #endif |
| |
| . = Entry_32 + 0x0b |
| |
| .word 0x200 /* bytes per sector. Must be 512 */ |
| |
| . = Entry_32 + 0x0d |
| |
| /* Sectors per cluster. Valid values are 1, 2, 4, 8, 16, 32, 64 and 128. |
| * But a cluster size larger than 32K should not occur. |
| */ |
| |
| .byte 1 /* sectors per cluster */ |
| |
| . = Entry_32 + 0x0e |
| |
| /* Reserved sectors(number of sectors before the first FAT, |
| * including the boot sector), usually 1. |
| */ |
| |
| .word 1 /* reserved sectors */ |
| |
| . = Entry_32 + 0x10 |
| |
| /* Number of FATs(nearly always 2). */ |
| |
| .byte 2 /* number of FATs */ |
| |
| . = Entry_32 + 0x11 |
| |
| /* (Maximum number of root directory entries)Must be 0. */ |
| |
| .word 0 /* Max dir entries for FAT12/FAT16 */ |
| |
| . = Entry_32 + 0x13 |
| |
| /* (Total number of sectors for small disks only)Must be 0. */ |
| |
| .word 0 /* total sectors for FAT12/FAT16 */ |
| |
| . = Entry_32 + 0x15 |
| |
| /* Media descriptor byte, pretty meaningless now. */ |
| |
| .byte 0xf8 /* media descriptor */ |
| |
| . = Entry_32 + 0x16 |
| |
| /* (Sectors per FAT)Must be 0. */ |
| |
| .word 0 /* sectors per FAT for FAT12/FAT16 */ |
| |
| . = Entry_32 + 0x18 |
| |
| .word 18 /* sectors per track */ |
| |
| . = Entry_32 + 0x1a |
| |
| .word 2 /* number of heads */ |
| |
| . = Entry_32 + 0x1c |
| |
| /* Number of hidden sectors (those preceding the boot sector). |
| * Also referred to as the starting sector of the partition. |
| * For floppies, it should be 0. |
| */ |
| |
| .long 0 /* hidden sectors */ |
| |
| . = Entry_32 + 0x20 |
| |
| /* Total number of sectors in the filesystem. */ |
| |
| .long 0 /* total sectors for FAT32 */ |
| |
| . = Entry_32 + 0x24 |
| |
| /* FAT32 sectors per FAT. */ |
| |
| .long 0 |
| |
| . = Entry_32 + 0x28 |
| |
| /* If bit 7 is clear then all FATs are updated, otherwise bits 0-3 |
| * give the current active FAT, all other bits are reserved. |
| * This word is not used by grldr boot code. |
| */ |
| |
| .word 0 |
| |
| . = Entry_32 + 0x2a |
| |
| /* High byte is major revision number, low byte is minor revision |
| * number, currently both are 0. |
| * This word is not used by grldr boot code. |
| */ |
| |
| .word 0 |
| |
| . = Entry_32 + 0x2c |
| |
| /* Root directory starting cluster. */ |
| |
| .long 0 |
| |
| . = Entry_32 + 0x30 |
| |
| /* File system information sector number. |
| * This word is not used by grldr boot code. |
| */ |
| |
| .word 0 |
| |
| . = Entry_32 + 0x32 |
| |
| /* If non-zero this gives the sector which holds a copy of the |
| * boot record, usually 6. |
| * This word is not used by grldr boot code. |
| */ |
| |
| .word 6 |
| |
| . = Entry_32 + 0x34 |
| |
| /* Reserved, 12 bytes, set to 0. */ |
| |
| .long 0 |
| .long 0 |
| .long 0 |
| |
| . = Entry_32 + 0x40 |
| |
| /* drive number of the boot device. |
| * This byte is ignored for read. The program will write DL onto |
| * this byte. The caller should set drive number in DL. |
| * We assume all BIOSes pass correct drive number in DL. |
| * That is to say, buggy BIOSes are not supported!! |
| */ |
| |
| .byte 0 |
| |
| . = Entry_32 + 0x41 |
| |
| /* partition number of this filesystem in the boot drive. |
| * This byte is ignored for read. The boot code will write partition |
| * number onto this byte. See Entry + 0x5d below. |
| */ |
| |
| .byte 0 |
| |
| . = Entry_32 + 0x42 |
| |
| /* Signature (must be 28h or 29h to be recognised by NT). */ |
| |
| .byte 0x29 /* extended boot signature for FAT12/FAT16 */ |
| |
| . = Entry_32 + 0x43 |
| |
| .long 0x0AC4AF63 /* volume serial number */ |
| |
| . = Entry_32 + 0x47 |
| |
| .ascii "NO NAME " /* volume label, 11 bytes. */ |
| |
| . = Entry_32 + 0x52 |
| |
| .ascii "FAT32 " /* filesystem ID, 8 bytes. */ |
| |
| /* |
| ; bp is initialized to 7c00h |
| ; %define bsOemName bp+0x03 ; OEM label (8) |
| %define bsBytesPerSec bp+0x0b ; bytes/sector (dw) |
| %define bsSecPerClust bp+0x0d ; sectors/allocation unit (db) |
| %define bsResSectors bp+0x0e ; # reserved sectors (dw) |
| %define bsFATs bp+0x10 ; # of fats (db) |
| ; %define bsRootDirEnts bp+0x11 ; # of root dir entries (dw, 0 for FAT32) |
| ; (FAT32 has root dir in a cluster chain) |
| ; %define bsSectors bp+0x13 ; # sectors total in image (dw, 0 for FAT32) |
| ; (if 0 use nSectorHuge even if FAT16) |
| ; %define bsMedia bp+0x15 ; media descriptor: fd=2side9sec, etc... (db) |
| ; %define sectPerFat bp+0x16 ; # sectors in a fat (dw, 0 for FAT32) |
| ; (FAT32 always uses xsectPerFat) |
| %define sectPerTrack bp+0x18 ; # sectors/track |
| ; %define nHeads bp+0x1a ; # heads (dw) |
| %define nHidden bp+0x1c ; # hidden sectors (dd) |
| ; %define nSectorHuge bp+0x20 ; # sectors if > 65536 (dd) |
| %define xsectPerFat bp+0x24 ; Sectors/Fat (dd) |
| ; +0x28 dw flags (for fat mirroring) |
| ; +0x2a dw filesystem version (usually 0) |
| %define xrootClst bp+0x2c ; Starting cluster of root directory (dd) |
| ; +0x30 dw -1 or sector number of fs.-info sector |
| ; +0x32 dw -1 or sector number of boot sector backup |
| ; (+0x34 .. +0x3f reserved) |
| %define drive bp+0x40 ; Drive number |
| bp+0x41 ; partition number for GRLDR |
| |
| %define fat_sector bp+0x44 ; last accessed FAT sector (dd) |
| ; (overwriting unused bytes) |
| %define fat_start bp+0x48 ; first FAT sector (dd) |
| ; (overwriting unused bytes) |
| %define data_start bp+0x4c ; first data sector (dd) |
| ; (overwriting unused bytes) |
| |
| */ |
| /* not used: [0x42] = byte 0x29 (ext boot param flag) |
| * [0x43] = dword serial |
| * [0x47] = label (padded with 00, 11 bytes) |
| * [0x52] = "FAT32",32,32,32 (not used by Windows) |
| * ([0x5a] is where FreeDOS parts start) |
| */ |
| |
| . = Entry_32 + 0x5a |
| 1: |
| cli |
| cld |
| |
| #ifdef BOOTGRUB |
| |
| . = Entry_32 + 0x5c |
| |
| /* the byte at offset 0x5d stores the real partition number for read. |
| * the format program or the caller should set it to a correct value. |
| * For floppies, it should be 0xff, which stands for whole drive. |
| */ |
| |
| movb $0xff, %dh /* boot partition number */ |
| |
| #endif |
| |
| xorw %ax, %ax |
| movw %ax, %ds |
| movw $0x7c00, %bp |
| |
| #ifdef BOOTGRUB |
| movw %ax, %es |
| #else |
| movw $0x1fe0, %ax |
| movw %ax, %es |
| movw %bp, %si /* move from 0000:7c00 */ |
| movw %bp, %di /* move to 1fe0:7c00 */ |
| movw $0x0100, %cx /* one sector to move */ |
| repz movsw |
| ljmp $0x1fe0, $(1f - Entry_32 + 0x7c00) |
| 1: |
| movw %ax, %ds |
| #endif |
| movw %ax, %ss /* stack and BP-relative moves up, too */ |
| leaw -0x20(%bp), %sp |
| sti |
| movw %dx, 0x40(%bp) /* BIOS passes drive number in DL */ |
| |
| movb $0x41, %ah |
| movw $0x55AA, %bx |
| int $0x13 |
| jc 1f /* No EBIOS */ |
| cmpw $0xAA55, %bx |
| jne 1f /* No EBIOS */ |
| testb $1, %cl |
| jz 1f /* No EBIOS */ |
| /* EBIOS supported */ |
| movb $0x42, (ebios_32 - 1 - Entry_32 + 0x7c00) |
| 1: |
| |
| /* figure out where FAT and DATA area starts |
| * (modifies EAX EDX, sets fat_start and data_start variables) |
| */ |
| xorl %eax, %eax |
| movl %eax, 0x44(%bp) /* init buffer status */ |
| |
| /* first, find fat_start */ |
| movw 0x0e(%bp), %ax /* reserved sectors */ |
| addl 0x1c(%bp), %eax /* hidden sectors */ |
| movl %eax, 0x48(%bp) /* first FAT sector */ |
| movl %eax, 0x4c(%bp) /* first data sector, initial value */ |
| |
| /* next, find data_start */ |
| movl 0x10(%bp), %eax /* number of fats, no movzbl needed: the |
| 2 words after 0x10(%bp) are 0 for fat32 */ |
| mull 0x24(%bp) /* sectors per fat (EDX=0) */ |
| addl %eax, 0x4c(%bp) /* first DATA sector */ |
| |
| /* Searches for the file in the root directory. |
| * Returns: EAX = first cluster of file |
| */ |
| |
| movl 0x2c(%bp), %eax /* root dir cluster */ |
| |
| 1: |
| pushl %eax /* save cluster */ |
| call cluster_to_lba_32 |
| /* EDX is sectors per cluster, EAX is sector number */ |
| movw $(msg_BootError_32 - Entry_32 + 0x7c00), %si |
| jc boot_error_32 /* EOC encountered */ |
| |
| 2: |
| lesw (loadseg_off_32 - Entry_32)(%bp), %bx /* load to loadseg:0 */ |
| call readDisk_32 |
| |
| xorw %di, %di |
| |
| /* Search for kernel file name, and find start cluster */ |
| 3: |
| movw $11, %cx |
| movw $(filename_32 - Entry_32 + 0x7c00), %si |
| repz cmpsb |
| jz 1f /* note that di now is at dirent+11 */ |
| |
| addw $0x20, %di |
| andw $-0x20, %di /* 0xffe0 */ |
| cmp 0x0b(%bp), %di /* bytes per sector */ |
| jnz 3b /* next directory entry */ |
| |
| decw %dx /* initially DX holds sectors per cluster */ |
| jnz 2b /* loop over sectors in cluster */ |
| |
| popl %eax /* restore current cluster */ |
| call next_cluster_32 |
| jmp 1b /* read next cluster */ |
| |
| loadseg_off_32: |
| .word 0 |
| .word LOADSEG |
| |
| 1: |
| /* kernel directory entry is found */ |
| pushw %es:(0x14-11)(%di) /* get cluster number HI */ |
| pushw %es:(0x1a-11)(%di) /* get cluster number LO */ |
| popl %eax /* convert to 32bit */ |
| |
| xorw %bx, %bx /* read kernel at ES:BX=LOADSEG:0 */ |
| |
| /* read kernel */ |
| |
| 2: |
| pushl %eax |
| call cluster_to_lba_32 |
| /* EDX is sectors per cluster, EAX is sector number */ |
| jnc 1f |
| |
| /* EOC encountered - done */ |
| #ifdef BOOTGRUB |
| movw 0x40(%bp), %dx /* boot_drive and boot_partition */ |
| #else |
| movb 0x40(%bp), %bl /* FreeDOS kernel uses BL, not DL, for drive */ |
| #endif |
| ljmp *(loadseg_off_32 - Entry_32)(%bp) |
| |
| 1: |
| call readDisk_32 |
| decw %dx /* initially DX holds sectors per cluster */ |
| jnz 1b /* loop over sectors in cluster */ |
| |
| popl %eax |
| call next_cluster_32 |
| jmp 2b |
| |
| /* given a cluster number, find the number of the next cluster in |
| * the FAT chain. Needs fat_start. |
| * input: EAX - cluster |
| * EDX = 0 |
| * output: EAX - next cluster |
| * EDX = undefined |
| */ |
| |
| next_cluster_32: |
| pushw %es |
| /* pushw %di */ |
| pushw %bx /* hi word of EBX never used */ |
| |
| #if 1 |
| /* xorl %edx, %edx */ |
| shll $2, %eax /* 32bit FAT */ |
| movzwl 0x0b(%bp), %ebx /* bytes per sector */ |
| divl %ebx /* residue is in EDX */ |
| /* movw %dx, %di */ |
| #else |
| shll $2, %eax /* 32bit FAT */ |
| ;xchgw %ax, %di /* movw %ax, %di */ |
| movw %ax, %di |
| ;shlw $2, %di /* 32bit FAT */ |
| |
| pushw %cx |
| movw 0x0b(%bp), %bx /* bytes per sector */ |
| bsfw %bx, %cx |
| ;decw %cx |
| ;decw %cx |
| decw %bx |
| andw %bx, %di /* mask to sector size */ |
| shrl %cl, %eax |
| popw %cx |
| #endif |
| addl 0x48(%bp), %eax /* add the first FAT sector number. |
| EAX is absolute sector number now */ |
| movw $FATSEG, %bx |
| movw %bx, %es |
| xorw %bx, %bx |
| |
| cmpl 0x44(%bp), %eax /* is it the last accessed and already buffered |
| FAT sector? */ |
| jz 1f |
| movl %eax, 0x44(%bp) /* mark sector EAX as buffered */ |
| call readDisk_32 /* read sector EAX to buffer */ |
| 1: |
| #if 1 |
| //.byte 0x67, 0x26, 0x80, 0x62, 0x03, 0x0f |
| addr32 andb $0x0f, %es:3(%edx) /* mask out top 4 bits */ |
| |
| //.byte 0x67, 0x66, 0x26, 0x8b, 0x02 |
| addr32 movl %es:(%edx), %eax /* read next cluster number */ |
| #else |
| andb $0x0f, %es:3(%di) /* mask out top 4 bits */ |
| movl %es:(%di), %eax /* read next cluster number */ |
| #endif |
| popw %bx |
| /* popw %di */ |
| popw %es |
| ret |
| |
| /* Convert cluster number to the absolute sector number |
| * ... or return carry if EndOfChain! Needs data_start. |
| * input: EAX - target cluster |
| * output: EAX - absolute sector |
| * EDX - [bsSectPerClust] (byte) |
| * carry clear |
| * (if carry set, EAX/EDX unchanged, end of chain) |
| */ |
| |
| cluster_to_lba_32: |
| cmpl $0x0ffffff8, %eax /* check End Of Chain */ |
| cmc |
| jb 1f /* carry is stored if EOC */ |
| |
| /* sector = (cluster-2) * clustersize + data_start */ |
| decl %eax |
| decl %eax |
| |
| movzbl 0x0d(%bp), %edx /* sectors per cluster */ |
| pushw %dx /* only DX would change */ |
| mull %edx /* EDX = 0 */ |
| popw %dx |
| addl 0x4c(%bp), %eax /* data_start */ |
| /* here, carry is cleared (unless parameters are wrong) */ |
| 1: |
| ret |
| |
| /* Read a sector from disk, using LBA or CHS |
| * input: EAX - 32-bit DOS sector number |
| * ES:BX - destination buffer |
| * (will be filled with 1 sector of data) |
| * output: ES:BX points one byte after the last byte read. |
| * EAX - next sector |
| */ |
| |
| readDisk_32: |
| pushal |
| xorl %edx, %edx /* EDX:EAX = LBA */ |
| pushl %edx /* hi 32bit of sector number */ |
| pushl %eax /* lo 32bit of sector number */ |
| pushw %es /* buffer segment */ |
| pushw %bx /* buffer offset */ |
| pushw $1 /* 1 sector to read */ |
| pushw $16 /* size of this parameter block */ |
| |
| xorl %ecx, %ecx |
| pushl 0x18(%bp) /* lo:sectors per track, hi:number of heads */ |
| popw %cx /* ECX = sectors per track */ |
| divl %ecx /* residue is in EDX */ |
| /* quotient is in EAX */ |
| incw %dx /* sector number in DL */ |
| popw %cx /* ECX = number of heads */ |
| pushw %dx /* push sector number into stack */ |
| xorw %dx, %dx /* EDX:EAX = cylinder * TotalHeads + head */ |
| divl %ecx /* residue is in EDX, head number */ |
| /* quotient is in EAX, cylinder number */ |
| xchgb %dl, %dh /* head number should be in DH */ |
| /* DL = 0 */ |
| popw %cx /* pop sector number from stack */ |
| xchgb %al, %ch /* lo 8bit cylinder should be in CH */ |
| /* AL = 0 */ |
| shlb $6, %ah /* hi 2bit cylinder ... */ |
| orb %ah, %cl /* ... should be in CL */ |
| |
| movw $0x201, %ax /* read 1 sector */ |
| ebios_32: /* ebios_32 - 1 points to 0x02 that can be changed to 0x42 */ |
| |
| // cmpb $0x0e, 2(%bp) /* force LBA? */ |
| // jnz 1f /* no, continue */ |
| // movb $0x42, %ah /* yes, use extended disk read */ |
| //1: |
| movw %sp, %si /* DS:SI points to disk address packet */ |
| movb 0x40(%bp), %dl /* hard disk drive number */ |
| int $0x13 |
| popaw /* remove parameter block from stack */ |
| popal |
| jc disk_error_32 /* disk read error, jc 1f if caller handles */ |
| incl %eax /* next sector */ |
| addw 0x0b(%bp), %bx /* bytes per sector */ |
| jnc 1f /* 64K bound check */ |
| pushw %dx |
| movw %es, %dx |
| addb $0x10, %dh /* add 1000h to ES */ |
| /* here, carry is cleared */ |
| movw %dx, %es |
| popw %dx |
| 1: |
| /* carry stored on disk read error */ |
| ret |
| |
| . = . - (. - readDisk_32)/91 |
| |
| msg_DiskReadError_32: |
| |
| .ascii "disk error\0" |
| |
| msg_BootError_32: |
| |
| .ascii "No " |
| |
| filename_32: |
| |
| #ifdef BOOTGRUB |
| .ascii "GRLDR \0" |
| #else |
| .ascii "KERNEL SYS\0" |
| #endif |
| |
| . = Entry_32 + 0x1ee |
| |
| disk_error_32: |
| |
| movw $(msg_DiskReadError_32 - Entry_32 + 0x7c00), %si |
| |
| boot_error_32: |
| |
| /* prints string DS:SI (modifies AX BX SI) */ |
| |
| //print_32: |
| 1: |
| 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 */ |
| |
| /* The caller will change this to |
| * ljmp $0x9400, $(try_next_partition - _start1) |
| */ |
| |
| 1: jmp 1b |
| |
| . = Entry_32 + 0x1fc |
| |
| .word 0, 0xAA55 /* Win9x uses all 4 bytes as magic value here */ |
| |
| . = Entry_32 + 0x200 |
| |
| . = _start1 + 0x600 |
| |
| //.arch i8086, nojumps |
| .arch i186, nojumps |
| /* |
| * The following is based on FreeDOS, modified heavily by Tinybit in Feb, 2004 |
| * |
| * Merges FAT12 and FAT16 boot sectors to ONE FAT boot sector! |
| * |
| * Memory layout for GRLDR FAT single stage boot process: |
| * |
| * +--------+ |
| * | | |
| * |GRLDR | also used as max 128k FAT buffer |
| * |LOADED | before GRLDR loading starts |
| * |--------| 2000:0000 |
| * | | |
| * |--------| 0000:7E00 |
| * |BOOTSECT| |
| * |ORIGIN | |
| * |--------| 0000:7C00 |
| * | | |
| * |--------| 0000:3000 |
| * |CLUSTER | |
| * |LIST | |
| * |--------| 0000:2000 |
| * | | |
| * +--------+ |
| */ |
| |
| /* |
| ; |
| ; File: |
| ; boot.asm |
| ; Description: |
| ; DOS-C boot |
| ; |
| ; Copyright (c) 1997; |
| ; Svante Frey |
| ; All Rights Reserved |
| ; |
| ; This file is part of DOS-C. |
| ; |
| ; DOS-C 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, or (at your option) any later version. |
| ; |
| ; DOS-C 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 DOS-C; see the file COPYING. If not, |
| ; write to the Free Software Foundation, 675 Mass Ave, |
| ; Cambridge, MA 02139, USA. |
| ; |
| ; |
| ; +--------+ 1FE0:7E00 |
| ; |BOOT SEC| |
| ; |RELOCATE| |
| ; |--------| 1FE0:7C00 |
| ; | | |
| ; |--------| 1FE0:3000 |
| ; | CLUSTER| |
| ; | LIST | |
| ; |--------| 1FE0:2000 |
| ; | | |
| ; |--------| 0000:7E00 |
| ; |BOOT SEC| overwritten by max 128k FAT buffer |
| ; |ORIGIN | and later by max 134k loaded kernel |
| ; |--------| 0000:7C00 |
| ; | | |
| ; |--------| |
| ; |KERNEL | also used as max 128k FAT buffer |
| ; |LOADED | before kernel loading starts |
| ; |--------| 0060:0000 |
| ; | | |
| ; +--------+ |
| */ |
| |
| #ifdef BOOTGRUB |
| #define LOADSEG_12_16 0x2000 |
| #define FATBUF 0x2000 /* offset of temp buffer for FAT chain */ |
| #else |
| #define LOADSEG_12_16 0x0060 |
| #define FATBUF 0x2000 /* offset of temp buffer for FAT chain */ |
| #endif |
| |
| Entry_12_16: |
| jmp 1f |
| |
| . = Entry_12_16 + 0x02 |
| |
| /* The default mode is CHS. This is for maximum compatiblity with |
| * small-sized disks, e.g., floppies. |
| * |
| * Valid values are 0x90 for CHS mode, or 0x0e for LBA mode. |
| * |
| * If the BIOS int13 supports LBA, this byte can be safely set to 0x0e. |
| * |
| * Some USB BIOSes might have bugs when using CHS mode, so the format |
| * program should set this byte to 0x0e. It seems that (generally) all |
| * USB BIOSes have LBA support. |
| * |
| * If the format program does not know whether the BIOS has LBA |
| * support, it may operate this way: |
| * |
| * if (partition_start + total_sectors_in_partition) exceeds the CHS |
| * addressing ability(especially when it is greater than 1024*256*63), |
| * the caller should set this byte to 0x0e, otherwise, set to 0x90. |
| */ |
| |
| .byte 0x90 /* for CHS. Another possible value is 0x0e for LBA */ |
| |
| |
| . = Entry_12_16 + 0x03 |
| |
| #ifdef BOOTGRUB |
| .ascii "GRLDR " |
| #endif |
| |
| . = Entry_12_16 + 0x0b |
| |
| .word 0x200 /* bytes per sector */ |
| |
| . = Entry_12_16 + 0x0d |
| |
| .byte 1 /* sectors per cluster */ |
| |
| . = Entry_12_16 + 0x0e |
| |
| .word 1 /* reserved sectors */ |
| |
| . = Entry_12_16 + 0x10 |
| |
| .byte 2 /* number of FATs */ |
| |
| . = Entry_12_16 + 0x11 |
| |
| .word 224 /* Max dir entries */ |
| |
| . = Entry_12_16 + 0x13 |
| |
| .word 2880 /* total sectors in the filesystem */ |
| |
| . = Entry_12_16 + 0x15 |
| |
| .byte 0xf0 /* media descriptor */ |
| |
| . = Entry_12_16 + 0x16 |
| |
| .word 9 /* sectors per FAT */ |
| |
| . = Entry_12_16 + 0x18 |
| |
| .word 18 /* sectors per track */ |
| |
| . = Entry_12_16 + 0x1a |
| |
| .word 2 /* number of heads */ |
| |
| . = Entry_12_16 + 0x1c |
| |
| .long 0 /* hidden sectors */ |
| |
| . = Entry_12_16 + 0x20 |
| |
| .long 0 /* total sectors for large partitions */ |
| |
| . = Entry_12_16 + 0x24 |
| |
| /* drive number of the boot device. |
| * This byte is ignored for read. The program will write DL onto |
| * this byte. The caller should set drive number in DL. |
| * We assume all BIOSes pass correct drive number in DL. |
| * That is to say, buggy BIOSes are not supported!! |
| */ |
| |
| .byte 0 |
| |
| . = Entry_12_16 + 0x25 |
| |
| /* partition number of this filesystem in the boot drive. |
| * This byte is ignored for read. The boot code will write partition |
| * number onto this byte. See Entry_12_16 + 0x41 below. |
| */ |
| |
| .byte 0 |
| |
| . = Entry_12_16 + 0x26 |
| |
| .byte 0x29 /* extended boot signature */ |
| |
| . = Entry_12_16 + 0x27 |
| |
| .long 0x0AC4AF63 /* volume serial number */ |
| |
| . = Entry_12_16 + 0x2b |
| |
| .ascii "NO NAME " /* volume label */ |
| |
| . = Entry_12_16 + 0x36 |
| |
| .ascii "FAT12 " /* filesystem ID */ |
| |
| /* |
| ; bp is initialized to 7c00h |
| %define bsOemName bp+0x03 ; OEM label |
| %define bsBytesPerSec bp+0x0b ; bytes/sector |
| %define bsSecPerClust bp+0x0d ; sectors/allocation unit |
| %define bsResSectors bp+0x0e ; # reserved sectors |
| %define bsFATs bp+0x10 ; # of fats |
| %define bsRootDirEnts bp+0x11 ; # of root dir entries |
| %define bsSectors bp+0x13 ; # sectors total in image |
| %define bsMedia bp+0x15 ; media descrip: fd=2side9sec, etc... |
| %define sectPerFat bp+0x16 ; # sectors in a fat |
| %define sectPerTrack bp+0x18 ; # sectors/track |
| %define nHeads bp+0x1a ; # heads |
| %define nHidden bp+0x1c ; # hidden sectors |
| %define nSectorHuge bp+0x20 ; # sectors if > 65536 |
| %define drive bp+0x24 ; drive number |
| bp+0x25 ; partition number for GRLDR |
| %define extBoot bp+0x26 ; extended boot signature |
| %define volid bp+0x27 |
| %define vollabel bp+0x2b |
| %define filesys bp+0x36 |
| |
| %define RootDirSecs bp+0x26 ; # of sectors root dir uses |
| ; (overwriting unused bytes) |
| %define fat_start bp+0x28 ; first FAT sector |
| ; (overwriting unused bytes) |
| %define root_dir_start bp+0x2c ; first root directory sector |
| ; (overwriting unused bytes) |
| %define data_start bp+0x30 ; first data sector |
| ; (overwriting unused bytes) |
| %define data_clusters bp+0x34 ; # of clusters in data area |
| ; (overwriting unused bytes) |
| bp+0x36 ; bytes per FAT( > 0x1800 means FAT16) |
| ; (overwriting unused bytes) |
| */ |
| /* not used: [0x26] = byte 0x29 (ext boot param flag) |
| * [0x27] = dword serial |
| * [0x2b] = label (padded with 00, 11 bytes) |
| * [0x36] = "FAT12" or "FAT16",32,32,32 (not used by Windows) |
| * ([0x3e] is where FreeDOS parts start) |
| */ |
| |
| . = Entry_12_16 + 0x3e |
| 1: |
| cli |
| cld |
| |
| #ifdef BOOTGRUB |
| |
| . = Entry_12_16 + 0x40 |
| |
| /* the byte at offset 0x41 stores the real partition number for read. |
| * the format program or the caller should set it to a correct value. |
| * For floppies, it should be 0xff, which stands for whole drive. |
| */ |
| |
| movb $0xff, %dh /* boot partition number */ |
| |
| #endif |
| |
| xorw %ax, %ax |
| movw %ax, %ds |
| movw $0x7c00, %bp |
| |
| #ifdef BOOTGRUB |
| movw %ax, %es |
| movw %ax, %ss /* stack and BP-relative moves up, too */ |
| leaw -0x20(%bp), %sp |
| sti |
| movw %dx, 0x24(%bp) /* BIOS passes drive number in DL */ |
| /* AX=0 */ |
| // xchgw %ax, %dx /* let DX = 0 */ |
| // xorw %cx, %cx /* CX = 0 */ |
| #else |
| movw %bp, %si /* move from 0000:7c00 */ |
| movw %bp, %di /* move to 1fe0:7c00 */ |
| movb %dl, 0x24(%si) /* BIOS passes drive number in DL */ |
| // xchgw %ax, %dx /* let DX = 0 */ |
| movw $0x1fe0, %ax |
| movw %ax, %es |
| movw $0x0100, %cx /* one sector to move */ |
| repz movsw |
| /* CX = 0 */ |
| ljmp $0x1fe0, $(1f - Entry_12_16 + 0x7c00) |
| 1: |
| movw %ax, %ds |
| movw %ax, %ss /* stack and BP-relative moves up, too */ |
| leaw -0x20(%bp), %sp |
| sti |
| /* AX=0x1fe0 */ |
| #endif |
| |
| movb $0x41, %ah |
| movw $0x55AA, %bx |
| int $0x13 |
| jc 1f /* No EBIOS */ |
| cmpw $0xAA55, %bx |
| jne 1f /* No EBIOS */ |
| testb $1, %cl |
| jz 1f /* No EBIOS */ |
| /* EBIOS supported */ |
| movb $0x42, (ebios_12_16 - 1 - Entry_12_16 + 0x7c00) |
| 1: |
| // xorw %cx, %cx |
| xorw %ax, %ax |
| |
| /* GET DRIVE PARMS: Calculate start of some disk areas */ |
| |
| movw 0x1c(%bp), %si /* number of hidden sectors(lo) */ |
| movw 0x1e(%bp), %di /* number of hidden sectors(hi) */ |
| addw 0x0e(%bp), %si /* number of reserved sectors */ |
| adcw %ax, %di /* DI:SI = first FAT sector */ |
| /* AX = 0 */ |
| |
| movw %si, 0x28(%bp) /* FAT start sector(lo) */ |
| movw %di, 0x2a(%bp) /* FAT start sector(hi) */ |
| |
| //xchgw %ax, %dx /* let AX = 0 */ |
| movb 0x10(%bp), %al /* number of FATs */ |
| /* cbw */ |
| mulw 0x16(%bp) /* sectors per FAT */ |
| /* DX:AX = total number of FAT sectors */ |
| /* DX = 0 since no too many FAT sectors */ |
| addw %ax, %si |
| adcw %dx, %di /* DI:SI = root directory start sector */ |
| movw %si, 0x2c(%bp) /* root directory starting sector(lo) */ |
| movw %di, 0x2e(%bp) /* root directory starting sector(hi) */ |
| |
| /* Calculate how many sectors the root directory occupies */ |
| |
| movw 0x0b(%bp), %bx /* bytes per sector */ |
| movb $5, %cl /* divide BX by 32 */ |
| shrw %cl, %bx /* BX = directory entries per sector */ |
| |
| movw 0x11(%bp), %ax /* max number of root dir entries */ |
| /* xorw %dx, %dx */ /* assuming DX = 0 */ |
| divw %bx /* AX = sectors per root directory */ |
| /* DX = 0 since normally no residue */ |
| |
| movw %ax, 0x26(%bp) /* number of sectors the root dir occupies */ |
| |
| addw %ax, %si /* DI:SI = first data sector */ |
| adcw %dx, %di /* assuming DX = 0 */ |
| |
| movw %si, 0x30(%bp) /* data starting sector(lo) */ |
| movw %di, 0x32(%bp) /* data starting sector(hi) */ |
| #ifdef USE_TOTAL_CLUSTERS |
| movw 0x13(%bp), %cx /* total sectors(small) */ |
| jcxz 1f |
| movw %cx, 0x20(%bp) /* total sectors(large)(lo) */ |
| movw %dx, 0x22(%bp) /* total sectors(large)(hi), assuming DX = 0 */ |
| 1: |
| movw 0x20(%bp), %ax /* total sectors(large) */ |
| movw 0x22(%bp), %bx |
| addw 0x1c(%bp), %ax /* number of hidden sectors */ |
| adcw 0x1e(%bp), %bx |
| subw %si, %ax /* data starting sector */ |
| sbbw %di, %bx /* BX:AX = total sectors in the data area */ |
| movb 0x0d(%bp), %dl /* sectors per cluster(DH=0) */ |
| xchgw %bx, %dx /* DX:AX = total sectors in the data area */ |
| /* BX = sectors per cluster */ |
| divw %bx /* AX = total clusters in the data area */ |
| movw %ax, 0x34(%bp) /* total clusters in the data area */ |
| #else |
| movw $0xffff, 0x36(%bp) |
| movw 0x16(%bp), %ax /* sectors per FAT */ |
| mulw 0x0b(%bp) /* bytes per sector */ |
| jc 1f |
| movw %ax, 0x36(%bp) |
| 1: |
| #endif |
| /* Searches for the file in the root directory |
| * |
| * Returns: |
| * AX = first cluster of file |
| */ |
| |
| /* First, read the whole root directory into the temporary buffer */ |
| |
| movw 0x2c(%bp), %ax /* root directory starting sector(lo) */ |
| movw 0x2e(%bp), %dx /* root directory starting sector(hi) */ |
| movw 0x26(%bp), %di /* number of sectors the root dir occupies */ |
| lesw (loadseg_off_12_16 - Entry_12_16)(%bp), %bx |
| /* ES:BX = loadseg:0 */ |
| call readDisk_12_16 |
| |
| lesw (loadseg_off_12_16 - Entry_12_16)(%bp), %di |
| /* ES:DI = loadseg:0 */ |
| |
| |
| /* Search for kernel file name, and find start cluster */ |
| |
| 1: |
| movw $11, %cx |
| movw $(filename_12_16 - Entry_12_16 + 0x7c00), %si |
| pushw %di |
| repz cmpsb |
| popw %di |
| movw %es:0x1a(%di), %ax /* get cluster number from dir entry */ |
| jz 1f |
| |
| addw $0x20, %di /* go to next directory entry */ |
| cmpb %ch, %es:(%di) /* if the first byte of the name is 0, */ |
| /* there is no more files in the directory */ |
| /* assuming CH = 0 */ |
| jnz 1b |
| movw $(msg_BootError_12_16 - Entry_12_16 + 0x7c00), %si |
| jmp boot_error_12_16 /* fail if not found */ |
| |
| loadseg_off_12_16: .word 0 |
| loadseg_seg_12_16: .word LOADSEG_12_16 |
| |
| 1: |
| pushw %ax /* store first cluster number */ |
| /* CX = 0 */ |
| |
| |
| /* Reads the FAT chain and stores it in a temporary buffer in the first |
| * 64KB. The FAT chain is stored an array of 16-bit cluster numbers, |
| * ending with 0. |
| * |
| * The file must fit in conventional memory, so it can't be larger than |
| * 640KB. The sector size must be at least 512 bytes, so the FAT chain |
| * can't be larger than around 3KB. |
| * |
| * Call with: AX = first cluster in chain |
| */ |
| |
| /* Load the complete FAT into memory. The FAT can't be larger |
| * than 128 kb, so it should fit in the temporary buffer. |
| */ |
| |
| lesw (loadseg_off_12_16 - Entry_12_16)(%bp), %bx |
| /* ES:BX = loadseg:0 */ |
| movw 0x16(%bp), %di /* sectors per FAT */ |
| movw 0x28(%bp), %ax /* FAT start sector(lo) */ |
| movw 0x2a(%bp), %dx /* FAT start sector(hi) */ |
| call readDisk_12_16 |
| popw %ax /* restore first cluster number */ |
| |
| /* Set ES:DI to the temporary storage for the FAT chain */ |
| pushw %ds |
| popw %es |
| movw (loadseg_seg_12_16 - Entry_12_16)(%bp), %ds |
| movw $FATBUF, %di |
| |
| 2: |
| stosw /* store cluster number */ |
| movw %ax, %si /* SI = cluster number */ |
| addw %si, %si /* multiply cluster number by two */ |
| movw (loadseg_seg_12_16 - Entry_12_16)(%bp), %dx |
| /* segment for FAT16 */ |
| jnc 1f |
| addb $0x10, %dh /* overflow. Add 0x1000 to segment value */ |
| 1: |
| |
| #ifdef USE_TOTAL_CLUSTERS |
| cmpw $0x0ff7, 0x34(%bp) /* total clusters in the data area */ |
| #else |
| cmpw $0x1801, 0x36(%bp) /* bytes per FAT */ |
| #endif |
| jnb 3f |
| |
| /* This is a FAT12 disk */ |
| |
| addw %ax, %si /* multiply cluster number by 3 ... */ |
| shrw $1, %si /* ... and divide by 2 */ |
| lodsw |
| |
| /* If the cluster number was even, the cluster value is now in |
| * bits 0-11 of AX. If the cluster number was odd, the cluster |
| * value is in bits 4-15, and must be shifted right 4 bits. If |
| * the number was odd, CF was set in the last shift instruction. |
| */ |
| |
| jnc 1f |
| movb $4, %cl |
| shrw %cl, %ax |
| 1: |
| andb $0x0f, %ah /* mask off the highest 4 bits */ |
| cmpw $0x0ff7, %ax /* check for EOF */ |
| jmp 4f |
| |
| 3: |
| /* This is a FAT16 disk. The maximal size of a 16bit FAT |
| * is 128KB, so it may not fit within a single 64KB segment |
| */ |
| |
| movw %dx, %ds /* DS:SI points to next cluster */ |
| lodsw /* AX = next cluster */ |
| |
| cmpw $0xfff7, %ax /* check for EOF */ |
| 4: |
| jbe 2b /* continue if not EOF */ |
| |
| /* Mark end of FAT chain with 0, so we have a single |
| * EOF marker for both FAT12 and FAT16 systems. |
| */ |
| |
| xorw %ax, %ax |
| stosw |
| |
| pushw %cs |
| popw %ds |
| |
| /* Loads the file into memory, one cluster at a time */ |
| |
| lesw (loadseg_off_12_16 - Entry_12_16)(%bp), %bx |
| /* ES:BX = loadseg:0 */ |
| movw $FATBUF, %si /* set DS:SI to the FAT chain */ |
| |
| 2: |
| lodsw /* AX = next cluster to read */ |
| orw %ax, %ax |
| jnz 1f |
| |
| /* EOC encountered - done */ |
| #ifdef BOOTGRUB |
| movw 0x24(%bp), %dx /* boot_drive and boot_partition */ |
| #else |
| movb 0x24(%bp), %bl /* FreeDOS kernel uses BL, not DL, for drive */ |
| #endif |
| ljmp *(loadseg_off_12_16 - Entry_12_16)(%bp) /* boot it! */ |
| |
| 1: |
| decw %ax /* cluster numbers start with 2 */ |
| decw %ax |
| |
| movw 0x0d(%bp), %di /* sectors per cluster */ |
| andw $0xff, %di /* DI = sectors per cluster */ |
| mulw %di |
| addw 0x30(%bp), %ax /* data starting sector(lo) */ |
| adcw 0x32(%bp), %dx /* data starting sector(hi) */ |
| /* DX:AX = first sector to read */ |
| call readDisk_12_16 |
| jmp 2b /* read next cluster */ |
| |
| /* Reads a number of sectors into memory. |
| * |
| * Call with: DX:AX = 32-bit DOS sector number |
| * DI = number of sectors to read |
| * ES:BX = destination buffer |
| * |
| * Returns: CF set on error |
| * ES:BX points one byte after the last byte read. |
| * DX:AX = next sector number after read |
| */ |
| |
| readDisk_12_16: |
| 2: |
| pushaw |
| xorw %cx, %cx |
| pushw %cx |
| pushw %cx |
| pushw %dx |
| pushw %ax |
| pushw %es /* buffer segment */ |
| pushw %bx /* buffer offset */ |
| incw %cx |
| pushw %cx /* 1 sector to read */ |
| movb $16, %cl |
| pushw %cx /* size of this parameter block */ |
| |
| xchgw %ax, %cx /* save AX to CX */ |
| |
| /* |
| * translate sector number to BIOS parameters |
| * |
| * LBA = sector-1 offset in track |
| * + head * sectPerTrack offset in cylinder |
| * + cyl * sectPerTrack * nHeads offset in platter |
| * |
| */ |
| pushw %bx |
| movw 0x18(%bp), %ax /* sectors per track */ |
| movw %ax, %bx |
| mulb 0x1a(%bp) /* nHeads, but maybe a word value 0x100 */ |
| jnz 1f |
| movb %bl, %ah /* nHeads=0x100, so AX=sectPerTrack*0x100 */ |
| 1: |
| xchgw %ax, %cx /* restore AX from CX, and save AX to CX */ |
| /* DX:AX = LBA, CX = nHeads * sectPerTrack <= 256*63 */ |
| divw %cx /* AX = cyl, DX = sector-1 + head * sectPerTrack */ |
| xchgw %ax, %dx /* DX = cyl, AX = sector-1 + head * sectPerTrack */ |
| divb %bl /* sectors per track */ |
| /* DX = cyl, AL = head, AH = sector-1 */ |
| #if 1 |
| xchgb %al, %ah /* DX = cyl, AH = head, AL = sector-1 */ |
| incw %ax /* DX = cyl, AH = head, AL = sector */ |
| xchgw %ax, %dx /* AX = cyl, DH = head, DL = sector */ |
| xchgw %ax, %cx /* CX = cyl, DH = head, DL = sector */ |
| xchgb %cl, %ch /* set cyl number low 8 bits in CH */ |
| rorb $1, %cl /* move cyl high bits into bits 7-6 */ |
| rorb $1, %cl /* (assumes top = 0) */ |
| orb %dl, %cl /* merge sector into cylinder */ |
| #else |
| movw %dx, %cx /* CX = cyl, AL = head, AH = sector-1 */ |
| |
| /* |
| * the following manipulations are necessary in order to properly place |
| * parameters into registers. |
| * CH = cylinder number low 8 bits |
| * CL<7-6> = cylinder high two bits |
| * CL<5-0> = sector |
| */ |
| movb %al, %dh /* save head into DH for BIOS */ |
| xchgb %cl, %ch /* set cyl number low 8 bits in CH */ |
| rorb $1, %cl /* move cyl high bits into bits 7-6 */ |
| rorb $1, %cl /* (assumes top = 0) */ |
| incb %ah /* AH = sector number */ |
| orb %ah, %cl /* merge sector into cylinder */ |
| #endif |
| popw %bx |
| |
| movw $0x0201, %ax /* read 1 sector */ |
| ebios_12_16: /* ebios_12_16 - 1 points to 0x02 that can be changed to 0x42 */ |
| |
| // cmpb $0x0e, 2(%bp) /* force LBA? */ |
| // jnz 1f /* no, continue */ |
| // movb $0x42, %ah /* yes, use extended disk read */ |
| //1: |
| movw %sp, %si /* DS:SI points to disk address packet */ |
| movb 0x24(%bp), %dl /* drive number */ |
| int $0x13 |
| popaw /* remove parameter block from stack */ |
| popaw |
| jc disk_error_12_16 /* disk read error, jc 1f if caller handles */ |
| incw %ax /* next sector */ |
| jnz 1f |
| incw %dx |
| 1: |
| addw 0x0b(%bp), %bx /* bytes per sector */ |
| jnc 1f /* 64K bound check */ |
| pushw %dx |
| movw %es, %dx |
| addb $0x10, %dh /* add 1000h to ES */ |
| /* here, carry is cleared */ |
| movw %dx, %es |
| popw %dx |
| 1: |
| decw %di |
| jnz 2b |
| |
| /* carry stored on disk read error */ |
| ret |
| |
| . = . - (. - readDisk_12_16)/99 |
| |
| msg_DiskReadError_12_16: |
| |
| .ascii "disk error\0" |
| |
| msg_BootError_12_16: |
| |
| .ascii "No " |
| |
| filename_12_16: |
| |
| #ifdef BOOTGRUB |
| .ascii "GRLDR \0" |
| #else |
| .ascii "KERNEL SYS\0" |
| #endif |
| |
| . = Entry_12_16 + 0x1ee |
| |
| disk_error_12_16: |
| |
| movw $(msg_DiskReadError_12_16 - Entry_12_16 + 0x7c00), %si |
| |
| boot_error_12_16: |
| |
| /* prints string DS:SI (modifies AX BX SI) */ |
| |
| //print_12_16: |
| 1: |
| 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 */ |
| |
| /* The caller will change this to |
| * ljmp $0x9400, $(try_next_partition - _start1) |
| */ |
| |
| 1: jmp 1b |
| |
| . = Entry_12_16 + 0x1fc |
| |
| .word 0, 0xAA55 /* Win9x uses all 4 bytes as magic value here */ |
| |
| . = Entry_12_16 + 0x200 |
| |
| . = _start1 + 0x800 |
| |
| |
| |
| |
| .arch i486, nojumps |
| |
| /* |
| #; Ext2 boot sector for GRLDR |
| */ |
| |
| |
| #define DEBUG call debug_print |
| #undef DEBUG |
| |
| //. = _start1 + 0x800 |
| |
| Entry_ext2: |
| |
| jmp 1f |
| |
| . = Entry_ext2 + 0x02 |
| |
| /* The default mode is CHS. This is for maximum compatiblity with |
| * small-sized disks, e.g., floppies. |
| * |
| * Valid values are 0x02 for CHS mode, or 0x42 for LBA mode. |
| * |
| * If the BIOS int13 supports LBA, this byte can be safely set to 0x42. |
| * |
| * Some USB BIOSes might have bugs when using CHS mode, so the format |
| * program should set this byte to 0x42. It seems that (generally) all |
| * USB BIOSes have LBA support. |
| * |
| * If the format program does not know whether the BIOS has LBA |
| * support, it may operate this way: |
| * |
| * if (partition_start + total_sectors_in_partition) exceeds the CHS |
| * addressing ability(especially when it is greater than 1024*256*63), |
| * the caller should set this byte to 0x42, otherwise, set to 0x02. |
| */ |
| |
| .byte 0x02 /* for CHS. Another possible value is 0x42 for LBA */ |
| |
| . = Entry_ext2 + 0x03 |
| |
| .ascii "ext2 grldr" |
| |
| . = Entry_ext2 + 0x0d |
| |
| /* sectors per block. Valid values are 2, 4, 8, 16, 32. */ |
| |
| .byte 2 |
| |
| . = Entry_ext2 + 0x0e |
| |
| /* bytes per block. |
| * Valid values are 0x400, 0x800, 0x1000, 0x2000, 0x4000. |
| */ |
| |
| .word 1024 /* bytes per block, at most 16K */ |
| |
| . = Entry_ext2 + 0x10 |
| |
| /* pointers in pointers-per-block blocks, that is, number of blocks |
| * covered by a double-indirect block. |
| * Valid values are 0x10000, 0x40000, 0x100000, 0x400000, 0x1000000. |
| */ |
| |
| .long 0x10000 /* number of blocks covered by double-indirect block */ |
| /* low word=0 */ |
| |
| . = Entry_ext2 + 0x14 |
| |
| /* pointers per block, that is, number of blocks covered by an indirect |
| * block. Valid values are 0x100, 0x200, 0x400, 0x800, 0x1000. |
| */ |
| |
| .long 0x100 /* high word=0, low byte=0 */ |
| |
| . = Entry_ext2 + 0x18 |
| |
| /* this is default for 1.44M floppy, the caller should set it to |
| * a correct value */ |
| |
| .word 18 /* sectors per track */ |
| |
| . = Entry_ext2 + 0x1a |
| |
| /* this is default for 1.44M floppy, the caller should set it to |
| * a correct value */ |
| |
| .word 2 /* number of heads */ |
| |
| . = Entry_ext2 + 0x1c |
| |
| /* this is default for 1.44M floppy, the caller should set it to |
| * a correct value */ |
| |
| .long 0 /* hidden sectors */ |
| |
| . = Entry_ext2 + 0x20 |
| |
| /* total sectors in the filesystem(or in the partition). |
| * This value is informative. The code does not use it. |
| */ |
| |
| /* this is default for 1.44M floppy, the caller should set it to |
| * a correct value */ |
| |
| .long 2880 |
| |
| . = Entry_ext2 + 0x24 |
| |
| /* This byte is ignored for read. The program will write DL onto |
| * this byte. The caller should set drive number in DL. |
| * We assume all BIOSes pass correct drive number in DL. |
| * That is to say, buggy BIOSes are not supported!! |
| */ |
| |
| .byte 0 /* drive number */ |
| |
| . = Entry_ext2 + 0x25 |
| |
| /* this is default for floppies, the caller should set it to |
| * a correct value for hard-drive partitions */ |
| |
| .byte 0xff /* partition number, 0xff for whole drive */ |
| |
| . = Entry_ext2 + 0x26 |
| |
| .word 0 /* reserved for future use */ |
| |
| . = Entry_ext2 + 0x28 |
| |
| /* this is default for 1.44M floppy, the caller should set it to |
| * a correct value */ |
| |
| .long 2048 /* s_inodes_per_group */ |
| |
| . = Entry_ext2 + 0x2c |
| |
| /* block number for group descriptors = s_first_data_block + 1. |
| * Valid values are 2 for 1024-byte blocks, and 1 for otherwise. |
| */ |
| |
| /* this is default for 1.44M floppy, the caller should set it to |
| * a correct value */ |
| |
| .long 2 /* block number for group descriptors */ |
| |
| . = Entry_ext2 + 0x30 |
| 1: |
| cld |
| |
| xorw %ax, %ax /* CF=0, ZF=1 */ |
| movw %ax, %ss /* constant SS=0 */ |
| movw $0x7c00, %sp |
| |
| movw %sp, %bp /* constant BP=0x7c00 */ |
| movw %ax, %ds /* constant DS=0 */ |
| |
| pushw %ax /* 0x0000 at 0000:7bfe */ |
| movw $0x1000, %bx |
| pushw %bx /* 0x1000 at 0000:7bfc */ |
| pushw %ax /* 0x0000 at 0000:7bfa */ |
| /* SP=0x7bfa */ |
| |
| /* the 6 bytes in the stack are used by read_block(): |
| * 0000 ---- -2(%bp) |
| * 1000 ---- -4(%bp) |
| * 0000 ---- -6(%bp) |
| * Don't touch them! |
| */ |
| |
| movb %dl, 0x24(%bp) /* BIOS passes drive number in DL */ |
| |
| movb $0x41, %ah |
| movw $0x55AA, %bx |
| int $0x13 |
| #if 0 |
| jnc 1f |
| /* No EBIOS */ |
| movb $0x02, (ebios_ext2 - 1 - Entry_ext2 + 0x7c00) |
| #else |
| jc 1f #; No EBIOS |
| |
| //testb $1, %cl |
| //jz 1f #; No EBIOS |
| #if 0 |
| /* gcc-4.0.1 does not generate 2-byte code. */ |
| rcrb $1, %cl #; also can be rorb $1, %cl |
| #else |
| .byte 0xD0, 0xD9 #; ror cl: D0 C9 |
| #endif |
| jnc 1f #; No EBIOS |
| |
| movb $0x42, (ebios_ext2 - 1 - Entry_ext2 + 0x7c00) |
| #endif |
| 1: |
| xorl %eax, %eax /* CF=0, ZF=1 */ |
| |
| #if 0 |
| /* the INC touches ZF flag, so use MOV instead */ |
| |
| incw %ax |
| incw %ax /* EAX=2=inode number for root dir */ |
| #else |
| |
| /* MOV keeps all flags untouched, so it is better than INC */ |
| |
| movb $2, %al /* EAX=2=inode number for root dir */ |
| #endif |
| |
| /* CF=0, ZF=1 because MOV and PUSH do not touch Flags */ |
| |
| /* read root dir to 0000:1000, and grldr to 1000:0000 */ |
| |
| 4: |
| /* EAX holds the inode number: for root dir or grldr */ |
| |
| /* These 3 PUSHes is intended to place 1000:0000 onto the stack for |
| * grldr. For root dir, the stack is not used since CF is cleared. |
| * Although there is no corresponding POPs, this is safe enough |
| * because the program comes here only twice: the first is for |
| * the root dir, and the second is for grldr. |
| * |
| * For root dir, CF=0 and ZF=1. For grldr, CF=1. |
| */ |
| |
| pushw %di /* 0x1000, see "jz 4b" below. */ |
| pushw %ss /* 0x0000 */ |
| pushfw |
| |
| /* SP=0x7bf4 for root dir, or 0x7bee for grldr */ |
| |
| decl %eax /* EAX=(inode - 1) */ |
| |
| /* inode numbers are far less than 0x7fffffff, so it is safe to |
| * initialise EDX with CDQ */ |
| |
| cdq /* let EDX=0 */ |
| |
| divl 0x28(%bp) /* s_inodes_per_group */ |
| /* EAX=group number */ |
| pushl %edx /* EDX=inode number in the group */ |
| |
| /* group numbers are far less than 0x7fffffff, so it is safe to |
| * initialise EDX with CDQ */ |
| |
| cdq /* let EDX=0 */ |
| shll $5, %eax /* EAX=relative displacement of the group descriptor */ |
| divl 0x0e(%bp) /* bytes per block */ |
| /* EAX=relative block number for the group descriptor */ |
| /* DX=displacement in the block */ |
| /* EDX high=0 */ |
| |
| pushw %dx /* we don't care about EDX high word, because it is 0 */ |
| |
| addl 0x2c(%bp), %eax /* EAX=absolute block number for the group descriptor */ |
| /* CF=0, ZF=0 */ |
| |
| call read_block /* 0000:1000 points to the block data containing the group descriptor */ |
| /* ES changed and > 0, BX=0x1000 */ |
| /* ECX=EDX=0 */ |
| /* CF=0, ZF=0 */ |
| |
| popw %si /* DS:[BX+SI] points to the group descriptor */ |
| /* DS:[BX+SI+8] points to the starting block number of the group inode table */ |
| |
| popl %eax /* inode number in the group */ |
| shll $7, %eax /* inode struct size = 0x80 */ |
| /* EAX=relative displacement of the inode struct */ |
| /* EDX=0 */ |
| |
| divl 0x0e(%bp) /* bytes per block */ |
| /* EAX=relative block number for the inode struct */ |
| pushw %dx /* DX=displacement of the inode struct in the block */ |
| /* EDX high=0 */ |
| |
| addl 8(%bx, %si), %eax /* EAX=absolute block number for the inode struct */ |
| /* CF=0, ZF=0 */ |
| |
| call read_block /* 0000:1000 points to the block data containing the inode struct */ |
| /* ES changed and > 0, BX=0x1000 */ |
| /* ECX=EDX=0 */ |
| /* CF=0, ZF=0 */ |
| |
| popw %si /* DS:[BX+SI] points to the inode struct */ |
| |
| addw %bx, %si /* DS:SI points to the inode struct */ |
| |
| /* Move the inode struct to a known safe area(0000:0fa8 - 0000:0fff), |
| * that is, 0x58 bytes immediately before 0000:1000. We care about only |
| * the beginning 0x58 bytes of the 0x80-byte inode struct, the last |
| * 0x28 bytes are ignored. The area from 0xfa8+0x28 to 0xfa8+0x57 |
| * stores 12 direct block pointers. |
| * |
| * |
| * At address Initial value Stores what? |
| * ========== ============= ====================================== |
| * 0xfa8+0x04 (const) the size of the file in bytes |
| * |
| * 0xfa8+0x08 total blocks blocks left to read |
| * |
| * 0xfa8+0x0c 0 serial number of the block to read |
| * |
| */ |
| |
| pushw %ss |
| popw %es /* ES=0 */ |
| |
| leaw -0x58(%bx), %di /* BX=0x1000, so DI=0x0fa8 */ |
| //movw $0x0fa8, %di |
| movb $0x2c, %cl /* 0x2c words = 0x58 bytes */ |
| |
| repz movsw /* now ECX=0, BX=0x1000=DI */ |
| |
| movl %ecx, (0x0c - 0x58)(%di) /* block serial number of the file */ |
| /* ECX=0 means first block */ |
| /* DI=0x1000 */ |
| |
| movl (0x04 - 0x58)(%di), %eax /* i_size, the file size */ |
| decl %eax |
| |
| divl 0x0e(%bp) /* bytes per block */ |
| /* EDX=various */ |
| incl %eax |
| movl %eax, (0x08 - 0x58)(%di) /* total blocks for file data */ |
| |
| /* |
| * 0000:1000 trebly indirect block |
| * 0000:8000 indirect block |
| * 0000:c000 double indirect block |
| * 1000:0000 the file data |
| */ |
| |
| /* now DS:SI points to indirect block number */ |
| |
| lodsl /* indirect block number */ |
| testl %eax, %eax |
| jz 1f |
| |
| //pushw %ss |
| //popw %es /* ES=0 */ |
| movb $0x80, %bh /* ES:BX=0000:8000 */ |
| #if 0 |
| stc |
| call read_block |
| #else |
| call read_block_c |
| #endif |
| /* ES changed and > 0, BX=0x8000 */ |
| /* ECX=EDX=0 */ |
| /* ZF=0, CF=0 */ |
| |
| /* now DS:SI points to double indirect block number */ |
| |
| lodsl /* double indirect block number */ |
| testl %eax, %eax |
| jz 1f |
| |
| #if 0 |
| pushw %ss |
| popw %es /* ES=0 */ |
| movb $0xc0, %bh /* ES:BX=0000:c000 */ |
| stc |
| call read_block |
| #else |
| movb $0xc0, %bh /* ES:BX=0000:c000 */ |
| call read_block_c |
| #endif |
| /* ES changed and > 0, BX=0xc000 */ |
| /* ECX=EDX=0 */ |
| /* ZF=0, CF=0 */ |
| |
| /* now DS:SI points to trebly indirect block number */ |
| |
| lodsl /* trebly indirect block number */ |
| testl %eax, %eax /* CF=0, TEST always clears CF */ |
| jz 1f |
| /* ZF=0 */ |
| //pushw %ss |
| //popw %es /* ES=0 */ |
| //movb $0x10, %bh /* ES:BX=0000:1000 */ |
| //stc |
| call read_block /* 0000:1000 points to the block data */ |
| /* ES changed and > 0, BX=0x1000 */ |
| /* ECX=EDX=0 */ |
| /* ZF=0, CF=0 */ |
| |
| /* the block at 0000:1000, which contains the indirect block numbers, |
| * is just overwritten by the trebly indirect block */ |
| |
| 1: |
| /* get absolute block number by block serial number */ |
| |
| movl (0x0c - 0x58)(%di), %ebx /* block serial number of the file */ |
| subl $12, %ebx |
| jc 3f /* direct block: block serial number < 12 */ |
| |
| pushw %bx |
| subl 0x14(%bp), %ebx |
| popw %ax |
| jnc 2f |
| |
| /* indirect block: 12 <= block serial number < 12 + 0x14(%bp) */ |
| |
| //addw 0x14(%bp), %bx |
| addb $(0x70 / 4), %ah |
| //xchgw %ax, %bx |
| jmp 8f |
| |
| 2: |
| pushl %ebx |
| subl 0x10(%bp), %ebx |
| jc 7f /* EBX on the stack is < 0x10(%bp). double indirect block: |
| * 12 + 0x14(%bp) <= block serial number < 12 + 0x14(%bp) + 0x10(%bp) |
| */ |
| |
| /* trebly indirect block: block serial number >= 12 + 0x14(%bp) + 0x10(%bp) */ |
| |
| popl %eax /* discard the stack */ |
| xchgl %eax, %ebx /* move EBX to EAX */ |
| /* EDX=0 */ |
| divl 0x10(%bp) |
| /* EAX=indirect block number, < 0x14(%bp) */ |
| /* EDX=block number, < 0x10(%bp) */ |
| |
| pushl %edx /* EDX < 0x10(%bp) */ |
| testl %edx, %edx |
| jnz 7f |
| |
| /* EDX=0, so we need to load the double indirect block */ |
| |
| shlw $2, %ax |
| xchgw %ax, %bx |
| |
| /* get the double indirect block number from the trebly indirect |
| * block data */ |
| |
| movl (%bx, %di), %eax |
| |
| //6: |
| movw $0xc000, %bx /* ES:BX=0000:c000 */ |
| |
| //pushw %ss |
| //popw %es /* ES=0 */ |
| //stc |
| call read_block_c /* 0000:c000 points to the block data */ |
| /* ES changed and > 0, BX=0xc000 */ |
| /* ECX=EDX=0 */ |
| /* CF=0, ZF=0 */ |
| 7: |
| popl %eax /* EAX < 0x10(%bp) */ |
| cdq /* let EDX=0 (notice the above jc 7f and jnz 7f) */ |
| divl 0x14(%bp) |
| /* EAX=indirect block number, < 0x14(%bp) */ |
| /* EDX=block number, < 0x14(%bp) */ |
| |
| pushw %dx /* EDX < 0x14(%bp) */ |
| testw %dx, %dx |
| jnz 7f |
| |
| /* if DX=0, we need to load the indirect block */ |
| |
| //addb $(0xb0 / 4), %ah |
| shlw $2, %ax |
| xchgw %ax, %bx |
| |
| /* get the indirect block number from the double indirect block data */ |
| |
| movl 0xb000(%bx, %di), %eax |
| //movl (%bx, %di), %eax |
| //5: |
| movw $0x8000, %bx /* ES:BX=0000:8000 */ |
| |
| //pushw %ss |
| //popw %es /* ES=0 */ |
| //stc |
| call read_block_c /* 0000:8000 points to the block data */ |
| /* ES changed and > 0, BX=0x8000 */ |
| /* ECX=EDX=0 */ |
| /* CF=0, ZF=0 */ |
| 7: |
| popw %ax /* AX < 0x14(%bp) */ |
| 8: |
| xchgw %ax, %bx |
| 3: |
| shlw $2, %bx |
| movl (%bx, %di), %eax |
| |
| /* got it! EAX=absolute block number */ |
| |
| /* read block data to 1000:0000. For root dir, read each block to |
| * 1000:0000(overwrite the previous read). For grldr, read blocks |
| * one by one to the area starting at 1000:0000. |
| */ |
| |
| popfw |
| popw %bx |
| popw %es |
| pushfw |
| |
| /* CF=0 and ZF=1 for reading root dir, CF=1 for reading grldr */ |
| |
| call read_block /* 1000:0000 points to the block data */ |
| /* ES changed and > 0x1000, BX=0 */ |
| /* ECX=EDX=0 */ |
| /* CF=0, ZF=0 */ |
| |
| popfw |
| pushw %es |
| pushw %bx |
| pushfw |
| |
| jc 3f /* CF=1, we are reading grldr */ |
| |
| /* We have just read a block of the root dir to 1000:0000. |
| * So we check all dir entries in the block to see if anyone |
| * matches grldr. |
| */ |
| |
| xorw %si, %si |
| pushw %ss |
| popw %es /* ES=0 */ |
| |
| 2: |
| pushw %ds /* DS=0 */ |
| movw %di, %ds /* DS=0x1000 */ |
| movw $(filename_ext2 - Entry_ext2 + 0x7c00), %di |
| |
| pushw %si |
| lodsl /* This is possible inode number for grldr */ |
| pushl %eax /* This is possible inode number for grldr */ |
| lodsw |
| xchgw %ax, %dx /* rec_len */ |
| lodsw /* AL=name_len, should be 5 for grldr */ |
| /* AH=file_type(1 for regular file) */ |
| cmpw $0x0105, %ax |
| jnz 5f |
| movb %al, %cl /* CH is already 0 */ |
| repz cmpsb |
| 5: |
| popl %eax /* This is possible inode number for grldr */ |
| popw %si |
| |
| /* DS=0x1000, EAX=inode number */ |
| |
| movw %ds, %di /* DI=0x1000 */ |
| popw %ds /* DS=0 */ |
| |
| stc /* indicates the new inode is for grldr */ |
| |
| jz 4b /* grldr is found with EAX=inode number */ |
| |
| addw %dx, %si |
| cmpw 0x0e(%bp), %si /* bytes per block */ |
| jb 2b |
| |
| /* file not found in this block, continue */ |
| |
| /* We are lucky that CF=0, which indicates we are dealing with |
| * the root dir. |
| */ |
| |
| 3: |
| |
| /* CF=1 for grldr, CF=0 for root dir. */ |
| |
| incl (0x0c - 0x58)(%di) |
| decl (0x08 - 0x58)(%di) |
| jnz 1b |
| |
| #if 0 |
| /* The above 2 instructions INC and DEC do not touch CF, so we |
| * can omit this POP-PUSH pair. |
| */ |
| |
| popfw |
| pushfw |
| #endif |
| |
| movw $(msg_No_grldr_ext2 - Entry_ext2 + 0x7c00), %si |
| |
| jnc boot_error_ext2 /* grldr not found in the root dir */ |
| |
| /* All grldr blocks have been loaded to memory starting at 1000:0000, |
| * Before the boot, we pass boot_drive and boot_partition to grldr. |
| */ |
| |
| /* ES>0x1000, BX=0, ECX=EDX=0, DI=0x1000, SS=0, SI>0x7c00, DS=0 |
| * BP=0x7c00, SP<=0x7c00 |
| */ |
| |
| movw 0x24(%bp), %dx |
| |
| /* boot it now! */ |
| |
| pushw %di /* 0x1000 */ |
| pushw %ss /* 0x0000 */ |
| lret |
| |
| read_block_c: |
| |
| pushw %ss |
| popw %es /* ES=0 */ |
| stc |
| |
| /* read_block - read a block |
| * input: CF - indicator for overlap or consecution |
| * EAX = block number |
| * ES:BX - buffer |
| * |
| * output: if CF is cleared on input, ES:BX is initialized to 0000:1000 |
| * ES:BX - buffer filled with data |
| * ES, EAX - Changed |
| * ECX = 0 |
| * EDX = 0 |
| * ZF = 0 |
| * CF = 0 |
| */ |
| |
| read_block: |
| |
| jc 1f |
| |
| .byte 0xC4, 0x5E, 0xFC /* lesw -4(%bp), %bx */ |
| /* ES:BX=0000:1000 */ |
| jnz 1f |
| |
| //at this time, the gcc cannot generate 3 byte code |
| .byte 0xC4, 0x5E, 0xFA /* lesw -6(%bp), %bx */ |
| /* ES:BX=1000:0000 */ |
| //. = . - (. - read_block) / 6 |
| 1: |
| movzbl 0x0d(%bp), %ecx /* CX=sectors per block */ |
| /* ECX high=0 */ |
| . = . - (. - 1b) / 6 |
| mull %ecx /* EAX=relative sector number */ |
| /* EDX=0 */ |
| . = . - (. - 1b) / 9 |
| addl 0x1c(%bp), %eax /* EAX=absolute sector number */ |
| |
| #if 1 |
| /* pass through, saving 4 bytes(call and ret) */ |
| #else |
| call readDisk_ext2 |
| ret |
| #endif |
| |
| /* Read sectors from disk, using LBA or CHS |
| * input: EAX = 32-bit LBA sector number |
| * CX = number of sectors to read |
| * ECX high word = 0 |
| * ES:BX = destination buffer |
| * |
| * output: No return on error |
| * BX not changed |
| * ES = ES + 0x20 * CX |
| * EAX = EAX + CX |
| * ZF = 0 |
| * CF = 0 |
| */ |
| |
| readDisk_ext2: |
| 2: |
| pushal |
| //xorl %edx, %edx /* EDX:EAX = LBA */ |
| pushl %edx /* hi 32bit of sector number */ |
| pushl %eax /* lo 32bit of sector number */ |
| pushw %es /* buffer segment */ |
| pushw %bx /* buffer offset */ |
| pushw $1 /* 1 sector to read */ |
| pushw $16 /* size of this parameter block */ |
| |
| //xorl %ecx, %ecx |
| pushl 0x18(%bp) /* lo:sectors per track, hi:number of heads */ |
| popw %cx /* ECX = sectors per track */ |
| divl %ecx /* residue is in EDX */ |
| /* quotient is in EAX */ |
| /* EDX high=0, DH=0 */ |
| incw %dx /* DL=sector number */ |
| popw %cx /* ECX = number of heads */ |
| pushw %dx /* push sector number into stack */ |
| xorw %dx, %dx /* EDX:EAX = cylinder * TotalHeads + head */ |
| divl %ecx /* residue is in EDX, head number */ |
| /* quotient is in EAX, cylinder number */ |
| /* EDX high=0, EAX high=0 */ |
| |
| |
| xchgb %dl, %dh /* head number should be in DH */ |
| /* DL = 0 */ |
| popw %cx /* pop sector number from stack */ |
| xchgb %al, %ch /* lo 8bit cylinder should be in CH */ |
| /* AL = 0 */ |
| shlb $6, %ah /* hi 2bit cylinder ... */ |
| orb %ah, %cl /* ... should be in CL */ |
| |
| incw %ax /* AL=1, read 1 sector */ |
| |
| /* Instead of 0x0e, the LBA indicator at 2(%bp) is |
| * |
| * 0x42 for LBA |
| * |
| * and |
| * |
| * 0x02 for CHS |
| */ |
| #if 0 |
| movb $0x42, %ah |
| /* ebios_ext2 - 1 points to 0x42 that can be changed to 0x02 */ |
| #else |
| movb $0x02, %ah |
| /* ebios_ext2 - 1 points to 0x02 that can be changed to 0x42 */ |
| #endif |
| ebios_ext2: |
| |
| //andb 2(%bp), %ah |
| |
| movw %sp, %si /* DS:SI points to disk address packet */ |
| movb 0x24(%bp), %dl /* drive number */ |
| int $0x13 |
| jc disk_error_ext2 |
| movw %es, %ax |
| addw $0x20, %ax /* here, carry is cleared */ |
| movw %ax, %es |
| popaw /* remove parameter block from stack */ |
| popal |
| incl %eax /* next sector, here ZF=0 */ |
| loop 2b |
| ret |
| |
| //. = . - (. - readDisk_ext2)/74 |
| |
| msg_DiskReadError_ext2: |
| |
| .ascii "disk error\0" |
| |
| msg_No_grldr_ext2: |
| |
| .ascii "No " |
| |
| filename_ext2: |
| .ascii "grldr\0" |
| |
| . = Entry_ext2 + 0x1f0 |
| |
| disk_error_ext2: |
| |
| movw $(msg_DiskReadError_ext2 - Entry_ext2 + 0x7c00), %si |
| |
| boot_error_ext2: |
| |
| /* prints string DS:SI (modifies AX BX SI) */ |
| |
| //print_ext2: |
| 1: |
| 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 */ |
| #if 1 |
| |
| /* The caller will change this to |
| * ljmp $0x9400, $(try_next_partition - _start1) |
| */ |
| |
| 1: jmp 1b |
| |
| #else |
| /* boot failed, try to hand over the control to supervisor */ |
| ldsw (1f + 3 - Entry_ext2)(%bp), %si |
| lodsl |
| cmpl $0x9400b8fa, %eax |
| 1: jnz 1b /* no supervisor, hang up. */ |
| ljmp $0x9400, $(try_next_partition - _start1) |
| |
| //. = . - (. - disk_error_ext2) / 30 |
| #endif |
| |
| . = Entry_ext2 + 0x1fe |
| |
| .word 0xAA55 |
| |
| . = _start1 + 0xA00 |
| |
| .arch i586, jumps |
| |
| #ifdef DEBUG |
| |
| . = Entry_ext2 + 0x201 |
| |
| debug_print: |
| |
| pushfl |
| pushal |
| movl %eax, %ebp |
| call 2f |
| #if 0 |
| popal |
| pushal |
| movl %ebx, %ebp |
| call 2f |
| popal |
| pushal |
| movl %ecx, %ebp |
| call 2f |
| popal |
| pushal |
| movl %edx, %ebp |
| call 2f |
| popal |
| pushal |
| movl %esi, %ebp |
| call 2f |
| popal |
| pushal |
| movl %edi, %ebp |
| call 2f |
| popal |
| popfl |
| pushfl |
| pushal |
| pushfl |
| popl %ebp /* flags */ |
| call 2f |
| movw %ds, %bp |
| shll $16, %ebp |
| movw %es, %bp |
| call 2f |
| movw $0x0e0d, %ax /* print CR */ |
| int $0x10 /* via TTY mode */ |
| movw $0x0e0a, %ax /* print LF */ |
| int $0x10 /* via TTY mode */ |
| #endif |
| popal |
| popfl |
| ret |
| 2: |
| movw $7, %cx |
| 1: |
| xorw %bx, %bx /* video page 0 */ |
| movl %ebp, %eax |
| shrl %cl, %eax |
| shrl %cl, %eax |
| shrl %cl, %eax |
| shrl %cl, %eax |
| andb $0x0f, %al |
| addb $0x30, %al |
| movb $0x0e, %ah /* print char in AL */ |
| int $0x10 /* via TTY mode */ |
| |
| decw %cx |
| testw %cx, %cx |
| jns 1b |
| |
| movw $0x0e20, %ax /* print space */ |
| int $0x10 /* via TTY mode */ |
| ret |
| #endif |
| |
| #if 1 |
| /* restore GRLDR_CS */ |
| |
| /* this code is executed at 0000:7e10 */ |
| |
| /* CS=code */ |
| |
| .align 16 |
| |
| restore_GRLDR_CS: |
| 2: |
| call 1f |
| 1: |
| popw %bx # instruction pointer of 1b |
| movw %cs, %ax |
| shrw $4, %bx |
| addw %ax, %bx # BX=segment value of this code |
| pushw %bx |
| pushw $(1f - 2b) |
| lret |
| 1: |
| /* modify gdt base */ |
| xorl %eax, %eax |
| movw %bx, %ax |
| shll $4, %eax |
| addl $(gdt -2b), %eax |
| movl %eax, %cs:(gdt - 2b + 2) |
| |
| movw $GRLDR_CS, %bx |
| movw %bx, %es |
| movw %ds, %bx # save old DS to BX |
| |
| cli |
| lgdt %cs:(gdt - 2b) |
| movl %cr0, %eax |
| orb $1, %al |
| movl %eax, %cr0 |
| |
| movw $8, %si |
| movw %si, %ds |
| |
| xorl %esi, %esi |
| xorl %edi, %edi |
| movl $(0x9000 / 4), %ecx |
| |
| cld |
| repz movsl |
| |
| movw $16, %si |
| movw %si, %ds |
| |
| andb $0xfe, %al |
| movl %eax, %cr0 |
| |
| movw %bx, %ds # restore DS from BX |
| |
| ljmp $GRLDR_CS, $(try_next_partition - _start1) |
| |
| #endif |
| |
| # Descriptor tables |
| # |
| # NOTE: The intel manual says gdt should be sixteen bytes aligned for |
| # efficiency reasons. However, there are machines which are known not |
| # to boot with misaligned GDTs, so alter this at your peril! If you alter |
| # GDT_ENTRY_BOOT_CS (in asm/segment.h) remember to leave at least two |
| # empty GDT entries (one for NULL and one reserved). |
| # |
| # NOTE: On some CPUs, the GDT must be 8 byte aligned. This is |
| # true for the Voyager Quad CPU card which will not boot without |
| # This directive. 16 byte aligment is recommended by intel. |
| # |
| .align 16 |
| gdt: |
| /* this is the default null entry in GDT */ |
| .word gdt_end - gdt - 1 # gdt limit |
| .long (GRLDR_CS * 16 + gdt - _start1) # linear address of gdt |
| .word 0 # pad 2 bytes |
| |
| /* real mode data segment base=0x200000 */ |
| .word 0xFFFF, 0 |
| .byte 0x20, 0x92, 0, 0 |
| |
| /* real mode data segment base=0 */ |
| .word 0xFFFF, 0 |
| .byte 0, 0x92, 0, 0 |
| |
| gdt_end: |
| |
| helper_start: |
| |
| /* helper function begins here |
| * before the call: |
| * CF=1 : indicates an invalid or corrupt entry |
| * CF=0 : indicates a valid entry |
| * |
| * on return: |
| * CF=1 : means "below", try next entry |
| * CF=0,ZF=1 : means "equal", helper did nothing, so we need |
| * a further try to boot via NT bootsector |
| * CF=0,ZF=0 : means "above", helper succeeded, boot it now |
| */ |
| |
| sti |
| |
| /* DS=SS=0x9400 */ |
| pushw %cs |
| popw %ds |
| |
| pushw $FS_BOOT |
| popw %es |
| |
| /* ES=FS_BOOT */ |
| |
| /* Format of partition information blocks. |
| * |
| * Offset Length in bytes Field |
| * 00h 1 Set to 80h if this partition is active. |
| * 01h 1 Partition's starting head. |
| * 02h 2 Partition's starting sector and track. |
| * 04h(SI) 1 Partition's ID number. |
| * 05h 1 Partition's ending head. |
| * 06h 2 Partition's ending sector and track. |
| * 08h 4 Starting LBA. |
| * 0Ch 4 Partition's length in sectors. |
| */ |
| |
| pushw %ds /* DS=0x9400 */ |
| pushw %es /* ES=FS_BOOT */ |
| pushal |
| pushfw |
| |
| //pushw %si |
| //stc |
| //jc invalid_or_null /* invalid or null entry */ |
| #if 0 |
| /* backup 63 sectors at FS_BOOT:0 to 63 sectors at FS_BOOT:8000 |
| * this piece of code is no longer useful. |
| */ |
| pushw %es |
| popw %ds |
| xorw %si, %si |
| movw $0x8000, %di |
| movw $0x3f00, %cx |
| cld |
| repz movsw |
| #endif |
| |
| testb $0x80, %cs:0x02 /* boot previous MBR first? */ |
| jnz 2f /* no, continue to find GRLDR */ |
| /* yes, call the routine for booting the previous MBR. |
| * it will not return on success. |
| * on failure, it will return here |
| * |
| * before we call the routine, we will check if the user want to |
| * skip this step and continue to find the GRLDR |
| */ |
| movw $(press_space_bar_string - _start1), %si |
| cmpw $0x3920, %cs:0x04 |
| je 1f |
| movw $(press_hot_key_string - _start1), %si |
| 1: |
| call print_message /* CS:SI points to message string */ |
| movw $(press_any_key_string - _start1), %si |
| call print_message /* CS:SI points to message string */ |
| call sleep_5_seconds |
| jc 1f /* desired hot-key pressed */ |
| call boot_prev_mbr //Error_modify |
| 1: |
| orb $0x80, %cs:0x02 |
| 2: |
| popfw |
| popal |
| popw %es |
| popw %ds |
| |
| pushw %ds /* DS=0x9400 */ |
| pushw %es /* ES=FS_BOOT */ |
| pushal |
| pushfw |
| |
| //cmpb $0x0e, 0x00 /* EBIOS previously checked OK? */ |
| //jbe 1f /* yes, skip the check */ |
| movb $0x02, 0x00 /* initialise this byte to 0x02 */ |
| movb $0x41, %ah /* EBIOS check existence */ |
| movw $0x55aa, %bx |
| int $0x13 |
| jc 1f /* No EBIOS */ |
| cmpw $0xaa55, %bx |
| jnz 1f /* No EBIOS */ |
| testb $1, %cl |
| jz 1f /* No EBIOS */ |
| movb $0x42, 0x00 /* LBA supported, save 0x42 to 9400:0000 */ |
| 1: |
| popfw |
| popal |
| popw %es |
| popw %ds |
| |
| pushw %ds /* DS=0x9400 */ |
| pushw %es /* ES=FS_BOOT */ |
| pushal |
| pushfw |
| |
| pushaw |
| cmpw $0x1c2, %si |
| jne 1f |
| /* initialize partition number and partition entries end */ |
| movw $0xffff, 0x1bc /* hd partition number */ |
| movw $0x01fe, 0x1ba /* partition entries end */ |
| 1: |
| pushw %dx |
| testb %dl, %dl |
| jns 1f /* floppy, use normal CHS mode */ |
| cmpw $0x1f2, %si /* is it a primary partition? */ |
| ja 2f /* no, it is an extended partition */ |
| movl 4(%si), %eax |
| movl %eax, 8(%si) /* parent part_start saved here */ |
| xorl %eax, %eax |
| movl %eax, 4(%si) /* current part_start(0) saved here */ |
| 2: |
| //movl -4(%si), %eax |
| //cmpl $0xfffffe00, %eax /* check the starting CHS */ |
| //jb 1f /* use normal CHS mode */ |
| |
| /* get CHS total number of sectors */ |
| pushw %es |
| pushw %ds |
| movb $8, %ah /* read drive parameters changes DX,ES,DI */ |
| //movb $0x80, %dl /* BIOS drive number is in DL */ |
| int $0x13 |
| popw %ds |
| popw %es |
| jc 3f |
| testb $63, %cl |
| jnz 2f |
| 3: |
| /* failed to get drive parameters, use maximum value */ |
| #if 0 |
| popw %dx |
| pushw %dx |
| cmpb $0x80, %dl |
| jne 3f |
| pushw %ds |
| xorw %ax, %ax |
| movw %ax, %ds |
| cmpb $0, 0x475 |
| popw %ds |
| je 3f |
| |
| 3: |
| #endif |
| movw $0xffff, %cx |
| movw %cx, %dx |
| 2: |
| //xorl %eax, %eax |
| movzbl %dh, %eax |
| incw %ax |
| movzbl %cl, %edx |
| andb $63, %dl |
| mulw %dx /* DX=0, AX=product */ |
| shrb $6, %cl |
| xchgb %cl, %dh |
| xchgb %ch, %dl |
| incw %dx /* DX=total cylinders */ |
| mull %edx /* EDX=0, EAX=product */ |
| |
| /* check the partition's starting LBA */ |
| movl 4(%si), %ebx |
| addl 8(%si), %ebx /* EBX=start_LBA */ |
| |
| testl %ebx, %ebx |
| je 1f |
| |
| ///* we always use LBA mode */ |
| ////cmpl %eax, %ebx |
| ////jb 1f /* use normal CHS mode */ |
| cmpb $0x42, 0x00 /* EBIOS present? */ |
| jne 1f /* no, skip the LBA mode int13 call */ |
| |
| /* load partition boot track to FS_BOOT using LBA mode */ |
| popw %ax /* AX=orig DX which holds drive number DL */ |
| pushw %ax |
| pushl %edx /* EDX=0, higher 4 bytes of starting LBA */ |
| pushl %ebx /* lower 4 bytes of starting LBA */ |
| pushw %es /* ES=FS_BOOT */ |
| pushw %dx /* DX=0, ES:0 is the buffer */ |
| //pushl $0x003f0010 /* transfer 63 sectors */ |
| pushw $0x3f /* transfer 63 sectors */ |
| pushw $0x10 /* size of disk address packet */ |
| xchgw %ax, %dx /* restore drive number DL from AL */ |
| movb $0x42, %ah /* extended read */ |
| movw %sp, %si /* DS:SI points to disk address packet */ |
| int $0x13 /* ignore the read failure */ |
| popaw /* adjust the stack */ |
| jc 1f |
| popw %dx |
| popaw |
| |
| //popw %ax /* discard flags in the stack */ |
| popfw |
| clc |
| |
| pushfw /* push new flags with CF=0 */ |
| pushaw |
| pushw %dx |
| 1: |
| popw %dx |
| popaw |
| |
| popfw |
| popal |
| popw %es |
| popw %ds |
| |
| pushw %ds /* DS=0x9400 */ |
| pushw %es /* ES=FS_BOOT */ |
| pushal |
| pushfw |
| |
| pushw %si |
| |
| pushfw |
| pushw %es |
| //--------------------------------------------------------- |
| /* print "Try (hd0,n): " or "Try (fd0): "*/ |
| pushw %ds |
| popw %es /* ES=DS=CS=0x9400 */ |
| |
| cld /* for stosb */ |
| xorw %ax, %ax |
| testb %dl, %dl |
| jns 1f /* floppy */ |
| /* hard drive */ |
| #if 0 |
| movw %si, %ax |
| subw $0x1c2, %ax |
| shrw $4, %ax |
| cmpw $0x1fe, %si /* is in MBR? */ |
| jb 1f /* yes */ |
| /* no, it is an entry in an extended partition */ |
| movb $0xFC, (add_sub_si + 2 - _start1) /* addw $-4, %si */ |
| incw 0x1bc /* logical partition number */ |
| movb 0x1bc, %al |
| #else |
| incw 0x1bc /* logical partition number */ |
| movw 0x1bc, %ax |
| cmpb $4, %al |
| jb 1f |
| movb $0xFC, (add_sub_si + 2 - _start1) /* addw $-4, %si */ |
| #endif |
| 1: |
| /* AL=partition number, AH=0 */ |
| pushw %ax |
| |
| movw $(partition_message - _start1 + 7), %di /* drive type */ |
| movb %dl, %al |
| shrb $7, %al /* drive type: floppy=0, harddrive=1 */ |
| shlb $1, %al |
| addw $0x6466, %ax /* "fd" or "hd" */ |
| stosw |
| movb %dl, %al |
| andb $0x7f, %al /* drive number */ |
| aam /* convert binary to decimal, AH=high, AL=low */ |
| testb %ah, %ah |
| jz 1f |
| addb $0x30, %ah |
| movb %ah, (%di) |
| incw %di |
| 1: |
| addb $0x30, %al |
| stosb |
| |
| popw %ax |
| |
| testb %dl, %dl |
| jns 2f /* floppy */ |
| /* this is a hard drive, the partition number is in AL */ |
| movb $0x2c, (%di) /* "," */ |
| incw %di |
| aam /* convert binary to decimal, AH=high, AL=low */ |
| testb %ah, %ah |
| jz 1f |
| addb $0x30, %ah |
| movb %ah, (%di) |
| incw %di |
| 1: |
| addb $0x30, %al |
| stosb |
| 2: |
| movl $0x00203a29, (%di) /* "): \0" */ |
| |
| movw $(partition_message - _start1), %si |
| call print_message /* CS:SI points to message string */ |
| //--------------------------------------------------------- |
| popw %es |
| popfw |
| //stc |
| jc invalid_or_null /* invalid or null entry */ |
| |
| xorw %si, %si |
| pushw %es |
| popw %ds |
| |
| /* DS=ES=FS_BOOT */ |
| |
| /* First, check for ext2 filesystem */ |
| |
| cmpw $0xEF53, 0x438 /* Magic signature */ |
| jnz 1f |
| xorl %eax, %eax |
| cmpl %eax, 0x400 /* s_inodes_count */ |
| jz 1f |
| cmpl %eax, 0x404 /* s_blocks_count */ |
| jz 1f |
| cmpw %ax, 0x458 /* s_inode_size, usually 0x80 */ |
| jz 1f |
| cmpl %eax, 0x420 /* s_blocks_per_group */ |
| jz 1f |
| cmpl %eax, 0x428 /* s_inodes_per_group */ |
| jz 1f |
| movl 0x414, %eax /* s_first_data_block */ |
| movw %ax, %bx /* BX=1 for 1K block, 0 otherwise */ |
| shrl $1, %eax /* must be 0 */ |
| jnz 1f |
| movl 0x418, %ecx /* s_log_block_size */ |
| cmpl $4, %ecx /* max size of block is 16K */ |
| ja 1f |
| negw %cx /* CF clear for 1K block, set otherwise */ |
| adcw %ax, %bx |
| decw %bx |
| jnz 1f |
| |
| /* BX = 0 */ |
| |
| /* super block is sane */ |
| |
| //pushw %cs |
| //popw %ds |
| ///* DS=SS=0x9400 */ |
| ///* ES=FS_BOOT */ |
| cld |
| movw $0x800, %si |
| xorw %di, %di |
| movw $0x0200, %cx /* yes, we need 2 sectors if enable debug */ |
| |
| repz cs movsw /* CS segment override prefix(=0x2E) */ |
| |
| /* modify the boot partition number */ |
| |
| /* the boot partition number is at offset 0x25 for ext2 */ |
| |
| testb %dl, %dl |
| jns 3f /* no modification for floppy */ |
| movw $0x25, %di |
| movw %cs:0x1bc, %ax /* partition number */ |
| stosb |
| 3: |
| /* fix for ext2 partition: hidden_sectors, offset 0x1c */ |
| popw %si /* DI points to old entry in MBR */ |
| pushw %si |
| xorl %eax, %eax /* let hidden_sectors=0 for floppy */ |
| testb %dl, %dl |
| jns 3f /* floppy */ |
| movl %cs:4(%si), %eax |
| addl %cs:8(%si), %eax |
| 3: |
| movl %eax, %es:0x1c(%bx) /* adjust hidden_sectors for EXT2 */ |
| |
| /* fix for ext2 partition: EBIOS indicator, offset 0x02 */ |
| |
| movb %cs:0x00(%bx), %al |
| movb %al, %es:0x02(%bx) |
| |
| /* fix for ext2 partition: sectors per block, offset 0x0d */ |
| /* fix for ext2 partition: bytes per block, offset 0x0e */ |
| /* fix for ext2 partition: dwords per block(dpb), offset 0x14 */ |
| /* fix for ext2 partition: square of dpb, offset 0x10 */ |
| |
| movb %es:0x418, %cl /* s_log_block_size */ |
| //incw %cx |
| movl $2, %eax |
| shlw %cl, %ax |
| movb %al, %es:0x0d(%bx) |
| shlw $9, %ax /* block size is word wide */ |
| movw %ax, %es:0x0e(%bx) |
| shrw $2, %ax |
| movl %eax, %es:0x14(%bx) |
| addb $8, %cl |
| shll %cl, %eax |
| movl %eax, %es:0x10(%bx) |
| |
| |
| /* fix for ext2 partition: sectors per track, offset 0x18 */ |
| /* fix for ext2 partition: number of heads, offset 0x1a */ |
| #if 1 |
| pushw %ds |
| pushw %es |
| pushw %bx |
| pushw %dx |
| movb $8, %ah /* read drive parameters changes DX,ES,DI,BX */ |
| movb $0x80, %dl /* BIOS drive number is in DL */ |
| int $0x13 |
| movw %dx, %ax |
| popw %dx |
| popw %bx |
| popw %es |
| popw %ds |
| jc 3f |
| andb $63, %cl |
| jz 3f |
| movb %cl, %es:0x18(%bx) |
| shrw $8, %ax |
| incw %ax |
| movw %ax, %es:0x1a(%bx) |
| 3: |
| #else |
| testb %dl, %dl |
| jns 3f /* floppy */ |
| popw %di /* DI points to old entry in MBR */ |
| pushw %di |
| movw %cs:1(%di), %ax |
| andb $63, %ah |
| movb %ah, %es:0x18 |
| xorb %ah, %ah |
| incw %ax |
| movw %ax, %es:0x1a |
| 3: |
| #endif |
| |
| /* fix for ext2 partition: s_inodes_per_group, offset 0x28 */ |
| movl %es:0x428, %eax /* s_inodes_per_group */ |
| movl %eax, %es:0x28(%bx) |
| |
| /* fix for ext2 partition: block number for group descriptors, offset 0x2c */ |
| /* At which block the group descriptors begin? */ |
| movl %es:0x414, %eax /* s_first_data_block */ |
| incw %ax |
| movl %eax, %es:0x2c(%bx) |
| |
| /* fix for ext2 partition: on error go back to supervisor, offset 0x01fc */ |
| movw $0x01fc, %si |
| movw %si, %di |
| lodsw |
| cmpw $0xFEEB, %ax /* EB FE is jmp back to itself(infinite loop) */ |
| jnz 3f |
| decw %ax /* AL=0xEA, ljmp */ |
| stosb |
| //movw $(try_next_partition - _start1), %ax |
| movw $0x7e10, %ax |
| stosw |
| //movw %cs, %ax /* AX=0x9400 */ |
| xorw %ax, %ax |
| stosw /* the last byte 0x00 is in the next sector! */ |
| // addw $0x0f, %di |
| // movw $(restore_GRLDR_CS - _start1), %si |
| // movw $((gdt_end - restore_GRLDR_CS) / 4), %cx |
| // .byte 0x2e /* %cs: prefix */ |
| // repz movsl |
| 3: |
| |
| movw $(EXT2_message - _start1), %si |
| call print_message /* CS:SI points to message string */ |
| |
| clc |
| jmp move_entries_and_return |
| |
| 1: |
| #; It is not EXT2. Check for FAT12/16/32/NTFS. |
| |
| /* DS=ES=FS_BOOT */ |
| |
| cmpw $0x200, 0x0b(%si) /* bytes per sector */ |
| jne 1f /* not a normal BPB */ |
| movb 0x0d(%si), %al /* sectors per cluster */ |
| testb %al, %al |
| jz 1f /* invalid if = 0 */ |
| movb %al, %cl |
| movw $128, %ax |
| divb %cl /* quo=AL, rem=AH */ |
| testb %ah, %ah |
| jnz 1f /* invalid if not 2^n */ |
| movw 0x18(%si), %ax /* sectors per track */ |
| testw %ax, %ax |
| jz 1f /* invalid if = 0 */ |
| cmpw $63, %ax |
| ja 1f /* invalid if > 63 */ |
| movw 0x1a(%si), %ax /* number of heads */ |
| decw %ax /* Max head number, should be a byte */ |
| testb %ah, %ah /* should be 0 */ |
| jnz 1f /* invalid if number of heads > 256 */ |
| cmpb $0xf0, 0x15(%si) /* media descriptor */ |
| jb 1f |
| |
| cmpb $0x42, %cs:0x00 /* EBIOS present? */ |
| jne 3f |
| //movb $0x41, %ah /* EBIOS check existence */ |
| //movw $0x55aa, %bx |
| //int $0x13 |
| //jc 3f /* No EBIOS */ |
| //cmpw $0xaa55, %bx |
| //jnz 3f /* No EBIOS */ |
| //testb $1, %cl |
| //jz 3f /* No EBIOS */ |
| movb $0x0e, 0x02(%si) /* force LBA */ |
| 3: |
| cld |
| movw $0x0600, %bx /* FAT12/FAT16 */ |
| movw $0x003c, %cx /* FAT12/FAT16 */ |
| |
| movb 0x10(%si), %al /* number of FATs(NTFS:0, FAT:1,2) */ |
| cmpb $2, %al |
| ja 1f /* abnormal FAT */ |
| movw 0x11(%si), %ax /* max root entries */ |
| testw %ax, %ax |
| jnz 2f /* FAT12/FAT16 */ |
| |
| /* FAT32 or NTFS */ |
| movw 0x13(%si), %ax /* total sectors(small) */ |
| testw %ax, %ax |
| jnz 1f /* invalid FAT32 BPB */ |
| movw 0x16(%si), %ax /* sectors per FAT(small) */ |
| testw %ax, %ax |
| jnz 1f /* invalid FAT32 BPB */ |
| movb 0x10(%si), %al /* number of FATs(NTFS:0, FAT:1,2) */ |
| testb %al, %al |
| jz 8f |
| |
| /* FAT32 */ |
| movl 0x20(%si), %eax /* FAT32 total sectors */ |
| testl %eax, %eax |
| jz 1f |
| movl 0x24(%si), %eax /* FAT32 sectors per FAT */ |
| testl %eax, %eax |
| jz 1f |
| movw $0x0400, %bx /* FAT32 */ |
| movw $0x0058, %cx /* FAT32 */ |
| movw $(FAT32_message - _start1), %si |
| jmp 7f |
| 8: |
| /* NTFS */ |
| movl 0x20(%si), %eax /* FAT32 total sectors */ |
| testl %eax, %eax |
| jnz 1f |
| //movw 0x11(%si), %ax /* max root entries */ |
| //testw %ax, %ax |
| //jnz 1f |
| movw 0x0e(%si), %ax /* reserved sectors */ |
| testw %ax, %ax |
| jnz 1f |
| |
| /* BUG fix for extended NTFS partition */ |
| popw %si /* SI points to old entry in MBR */ |
| pushw %si |
| xorl %eax, %eax /* let hidden_sectors=0 for floppy */ |
| testb %dl, %dl |
| jns 3f /* floppy */ |
| movl %cs:4(%si), %eax |
| addl %cs:8(%si), %eax |
| 3: |
| movl %eax, 0x1c /* adjust hidden_sectors for NTFS */ |
| |
| movb %dl, 0x24 /* adjust drive number for NTFS */ |
| /* modify the boot partition number */ |
| movb $0xB6, %al /* 0xB6="MOV DH,imm8" */ |
| movb %cs:0x1bc, %ah |
| testb %dl, %dl |
| js 3f |
| movb $0xff, %ah /* partition number for floppy is whole drive */ |
| 3: |
| |
| /* before the call: |
| * AH= partition number |
| * AL= 0xB6 ; 0xB6 is opcode of "MOV DH,imm8" |
| * DL= drive number |
| * |
| * on return: CF=0 if there is NTFS boot record; |
| * CF=1 otherwise. |
| * CF of flags_orig on the stack will set if CF=1 |
| */ |
| |
| call modify_NTFS_boot_record |
| //jnc move_entries_and_return |
| //movw $(NTFS5_message - _start1), %si |
| ////jmp 4f |
| //call print_message /* CS:SI points to message string */ |
| //stc |
| jmp move_entries_and_return |
| |
| 2: |
| /* FAT12/FAT16 */ |
| movb 0x10(%si), %al /* number of FATs(NTFS:0, FAT:1,2) */ |
| testb %al, %al |
| jz 1f |
| movw 0x16(%si), %ax /* sectors per FAT(small) */ |
| testw %ax, %ax |
| jz 1f |
| movw $(FAT16_message - _start1), %si |
| cmpw $12, %ax |
| ja 7f |
| movw $(FAT12_message - _start1), %si |
| 7: |
| /* BUG fix for extended FAT12/16/32 partition */ |
| popw %di /* DI points to old entry in MBR */ |
| pushw %di |
| xorl %eax, %eax /* let hidden_sectors=0 for floppy */ |
| testb %dl, %dl |
| jns 3f /* floppy */ |
| movl %cs:4(%di), %eax |
| addl %cs:8(%di), %eax |
| 3: |
| movl %eax, 0x1c /* adjust hidden_sectors for FAT */ |
| |
| call print_message /* CS:SI points to message string */ |
| pushw %cs |
| popw %ds |
| /* DS=SS=0x9400 */ |
| /* ES=FS_BOOT */ |
| movw %bx, %si |
| xorw %di, %di |
| lodsw |
| stosw |
| addw %cx, %si |
| addw %cx, %di |
| movw $0x0200, %cx |
| subw %di, %cx |
| repz movsb |
| /* modify the boot partition number */ |
| movb %es:1, %al |
| addb $5, %al /* AL is less than 0x80 */ |
| cbw /* AH=0 */ |
| xchgw %ax, %di /* move AX to DI */ |
| movb $0xff, %al /* partition=whole drive for floppy */ |
| testb %dl, %dl |
| jns 3f /* no modification for floppy */ |
| movb 0x1bc, %al /* partition number */ |
| 3: |
| stosb |
| |
| /* fix for FAT12/16/32 partition: on error go back to supervisor, offset 0x01fa */ |
| //pushw %es |
| //popw %ds |
| movw $0x01fa, %di |
| //movw %di, %si |
| //lodsw |
| movw %es:(%di), %ax |
| cmpw $0xFEEB, %ax /* EB FE is jmp back to itself(infinite loop) */ |
| jnz 3f |
| decw %ax /* AL=0xEA, ljmp */ |
| stosb |
| //movw $(try_next_partition - _start1), %ax |
| movw $0x7e10, %ax |
| stosw |
| //movw %cs, %ax /* AX=0x9400 */ |
| xorw %ax, %ax |
| stosw /* DI=0x01ff */ |
| 3: |
| |
| clc |
| jmp move_entries_and_return |
| 1: |
| #; It is not FAT12/16/32/NTFS. Check for extended partition. |
| |
| /* DS=ES=FS_BOOT */ |
| |
| pushw %cs |
| popw %es |
| |
| /* ES=SS=0x9400 */ |
| /* DS=FS_BOOT */ |
| |
| popw %si |
| pushw %si |
| cmpb $0x05, %es:(%si) /* extended */ |
| je 1f |
| cmpb $0x0f, %es:(%si) /* Win95 extended (LBA) */ |
| je 1f |
| cmpb $0x15, %es:(%si) /* hidden extended */ |
| je 1f |
| cmpb $0x1f, %es:(%si) /* hidden win95 extended (LBA) */ |
| je 1f |
| cmpb $0x85, %es:(%si) /* Linux extended */ |
| je 1f |
| movw $(non_MS_message - _start1), %si |
| 4: |
| call print_message /* CS:SI points to message string */ |
| stc |
| jmp move_entries_and_return |
| 1: |
| /* extended partition entry */ |
| cmpw $0x1fe, %si |
| jb 1f |
| decw %es:0x1bc /* count the partitions in extended zone */ |
| 1: |
| movw $(extended_message - _start1), %si |
| call print_message /* CS:SI points to message string */ |
| movw $0x1be, %si |
| movw $4, %cx |
| 5: |
| //xorl %eax, %eax |
| //cmpl %eax, (%si) |
| //jnz 2f |
| movl (%si), %eax |
| cmpw 2(%si), %ax /* Is EAX high word equal to AX? */ |
| jnz 2f |
| cmpb %al, %ah /* Is AL=AH? */ |
| jnz 2f |
| |
| /* now all 4 bytes in EAX are equal to each other. */ |
| cmpl %eax, 4(%si) |
| jnz 2f |
| cmpl %eax, 8(%si) |
| jnz 2f |
| cmpl %eax, 12(%si) |
| jz 3f /* entry with 16 dups of a byte means empty entry */ |
| 2: |
| movb (%si), %al |
| shlb $1, %al |
| jnz 1f |
| //jnz 3f /* invalid entry is treated as empty entry */ |
| movb 2(%si), %al |
| and $63, %al /* starting sector number */ |
| jz 1f |
| //jz 3f /* invalid entry is treated as empty entry */ |
| movb 6(%si), %al |
| and $63, %al /* ending sector number */ |
| jz 1f |
| //jz 3f /* invalid entry is treated as empty entry */ |
| movl 8(%si), %eax /* starting LBA */ |
| testl %eax, %eax |
| jz 1f |
| //jz 3f /* invalid entry is treated as empty entry */ |
| movl 12(%si), %eax /* total number of sectors in partition */ |
| testl %eax, %eax |
| jz 1f |
| 3: |
| addw $16, %si |
| loop 5b |
| cmpw $0xaa55, (%si) |
| jnz 1f |
| |
| movw $0x1be, %si |
| movw $4, %cx |
| popw %bx /* the old SI points to extended partition ID in MBR */ |
| pushw %bx |
| 5: |
| #if 1 |
| //xorl %eax, %eax |
| //cmpl %eax, (%si) |
| //jnz 2f |
| movl (%si), %eax |
| cmpw 2(%si), %ax /* Is EAX high word equal to AX? */ |
| jnz 2f |
| cmpb %al, %ah /* Is AL=AH? */ |
| jnz 2f |
| |
| /* now all 4 bytes in EAX are equal to each other. */ |
| cmpl %eax, 4(%si) |
| jnz 2f |
| cmpl %eax, 8(%si) |
| jnz 2f |
| cmpl %eax, 12(%si) |
| jz 3f /* entry with 16 dups of a byte means empty entry */ |
| 2: |
| /* now it is an acceptable entry */ |
| movw %es:0x1ba, %di /* partition entries end */ |
| /* ensure our stack not to be overwritten by the partition entries */ |
| cmpw $0x83f0, %di |
| ja 3f /* try next */ |
| /* ensure our code not to be overwritten by the partition entries */ |
| cmpw $0x3fe, %di |
| jne 6f |
| /* more entries stores at 0x9be00-0x9c3ff */ |
| movw $0x7e00, %di |
| movw %di, %es:0x1ba |
| 6: |
| addw $16, %es:0x1ba /* increment partition entries end */ |
| |
| lodsl |
| stosl |
| lodsl |
| stosl |
| |
| xchgw %ax, %dx /* save AL(the partition ID)to DL */ |
| |
| lodsl |
| xchgl %eax, %edx /* restore AL from DL(the partition ID) |
| * and save EAX to EDX */ |
| cmpb $0x05, %al |
| je 6f |
| cmpb $0x0f, %al |
| je 6f |
| cmpb $0x15, %al |
| je 6f |
| cmpb $0x1f, %al |
| je 6f |
| cmpb $0x85, %al |
| je 6f |
| /* normal partition, copied to 0x941fe-0x943fb */ |
| addl %es:4(%bx), %edx /* current partition start */ |
| 6: |
| /* extended partition, copied to 0x941fe-0x943fb */ |
| xchgl %eax, %edx /* restore or update EAX from EDX */ |
| stosl |
| lodsl /* adjust SI only */ |
| movl %es:8(%bx), %eax /* parent partition start ... */ |
| stosl /* ... stored here */ |
| jmp 2f |
| 3: |
| addw $16, %si |
| #endif |
| //. = 5b + 0x7c |
| 2: |
| loop 5b |
| |
| /* extended partition is not a normal one, so set carry to try next */ |
| stc |
| jmp move_entries_and_return |
| |
| invalid_or_null: |
| 1: |
| movw $(invalid_message - _start1), %si |
| call print_message /* CS:SI points to message string */ |
| stc |
| |
| move_entries_and_return: |
| popw %si |
| pushfw |
| pushw %cs |
| popw %ds |
| pushw %cs |
| popw %es |
| pushw %si |
| cmpw $0x202, %si |
| jne 1f |
| /* move entries backward 1 entry */ |
| movw $0x1fe, %di |
| movw $0x20e, %si |
| movw $0xf8, %cx /* 0x1f0 bytes = 0xf8 words */ |
| cld /* move upward */ |
| repz movsw |
| movw $0x3ee, %di |
| movw $0x7e00, %si |
| movw $0x8, %cx /* 0x10 bytes = 0x8 words */ |
| cld /* move upward */ |
| repz movsw |
| movw $0x7e00, %di |
| movw $0x7e10, %si |
| movw $0x2f8, %cx /* 0x5f0 bytes = 0x2f8 words */ |
| cld /* move upward */ |
| repz movsw |
| cmpw $0x7e10, 0x1ba |
| jne 2f |
| movw $0x40e, 0x1ba |
| 2: |
| subw $0x10, 0x1ba |
| |
| 1: |
| popw %si |
| movw $0x1ff, (add_sub_si + 5 - _start1) |
| cmpw $0x1fe, 0x1ba |
| jne 1f |
| decw (add_sub_si + 5 - _start1) |
| cmpw $0x31b2, %si /* floppy? */ |
| je 1f /* yes */ |
| cmpw $0x1f2, %si |
| ja 2f /* logical partition */ |
| jb 1f /* primary partition 0, 1, 2 */ |
| /* primary partition 3 */ |
| cmpw $0x0003, 0x1bc /* are there any logical partitions? */ |
| ja 1f /* yes */ |
| 2: |
| inc_hard_drive: |
| |
| /* all partitions on the drive have been checked, try next drive. |
| * |
| * the current stack is: |
| * |
| * SP + 38 : DS |
| * SP + 36 : ES |
| * SP + 32 : EAX |
| * SP + 28 : ECX |
| * SP + 24 : EDX |
| * SP + 20 : EBX |
| * SP + 16 : ESP_temp |
| * SP + 12 : EBP |
| * SP + 8 : ESI |
| * SP + 4 : EDI |
| * SP + 2 : flags_orig |
| * SP : flags |
| * |
| */ |
| |
| /* get total hard drives */ |
| xorw %ax, %ax |
| movw %ax, %ds |
| movb 0x475, %dh |
| pushw %cs |
| popw %ds |
| // cmpb $16, %dh |
| // jnb 2f |
| // movb $16, %dh |
| //2: |
| orb $0x80, %dh /* CF=0, DH=Max harddrive number + 1 */ |
| //xchgw %ax, %cx /* CL=Max harddrive number + 1, CH=0 */ |
| movw %sp, %bp |
| movb 24(%bp), %dl /* BIOS drive number is in DL */ |
| 2: |
| jnc 3f |
| call print_message /* CS:SI points to message string */ |
| movw $(drive_number_string - _start1), %si |
| movb %dl, %al |
| andb $0x7f, %al |
| aam /* AH=high decimal, AL=low decimal */ |
| addw $0x3030, %ax |
| xchgb %al, %ah |
| movw %ax, 9(%si) |
| call print_message /* CS:SI points to message string */ |
| 3: |
| incw %dx |
| cmpb %dh, %dl |
| jnb 2f /* all drives checked, try floppy finally */ |
| |
| pushw %bx |
| pushw %dx |
| pushw %es |
| movb $8, %ah /* read drive parameters changes DX, ES, DI */ |
| int $0x13 |
| popw %es |
| jc 3f /* try next hard drive */ |
| //xchgw %ax, %cx /* this moves CL to AL */ |
| andb $63, %cl /* CL=sectors per track, CF cleared */ |
| stc |
| jz 3f /* try next hard drive */ |
| popw %dx /* get DL */ |
| popw %bx |
| movb %dl, %ch /* DL saved at BP high byte in the stack */ |
| pushw %cx /* push new BX onto stack */ |
| pushw %dx |
| //movb $0x02, %ah |
| //movw %ax, %si /* save AX to SI: read 1 track */ |
| movw $0x201, %ax /* read 1 sector */ |
| movw $0x7e00, %bx /* read MBR to 9400:7e00 */ |
| movw $1, %cx |
| //popw %dx |
| //pushw %dx |
| xorb %dh, %dh |
| stc |
| int $0x13 |
| sti |
| 3: |
| popw %dx |
| popw %bx /* BL=sectors per track, BH=DL */ |
| |
| //movw %si, %bx /* BL=sectors per track */ |
| |
| movw $(Error_while_reading_string - _start1), %si |
| jc 2b /* read failure, try next hard drive */ |
| |
| /* on seccessful return, should be: ah=0 for OK, al=1 for 1 sector */ |
| //decw %ax /* some BIOSes return incorrect AL */ |
| testb %ah, %ah |
| stc |
| jnz 2b |
| |
| /* The new partition table might be empty or invalid. |
| * Move the new partition table onto the old one while checking |
| */ |
| |
| //movb %dl, %bh /* DL saved at BP high byte in the stack */ |
| |
| movw $0x7fbe, %si |
| movw $0x01be, %di |
| |
| 3: |
| cmpw $0x1fe, %di |
| jnb 3f |
| |
| xorl %ecx, %ecx |
| |
| lodsl |
| stosl |
| orl %eax, %ecx |
| lodsl |
| stosl |
| orl %eax, %ecx |
| lodsl |
| stosl |
| orl %eax, %ecx |
| lodsl |
| stosl |
| orl %eax, %ecx |
| jecxz 3b /* null entry, check next */ |
| |
| //lodsw |
| //stosw |
| movb -16(%si), %al |
| shlb $1, %al |
| stc |
| xchgw %ax, %si /* save SI to AX */ |
| movw $(partition_boot_indicator_string - _start1), %si |
| jnz 2b |
| xchgw %ax, %si /* restore SI from AX */ |
| //lodsw |
| //stosw |
| movb -14(%si), %al |
| andb $63, %al |
| stc |
| xchgw %ax, %si /* save SI to AX */ |
| movw $(partition_sectors_per_track_string - _start1), %si |
| jz 2b |
| xchgw %ax, %si /* restore SI from AX */ |
| //lodsw |
| //stosw |
| //lodsw |
| //stosw |
| movb -10(%si), %al |
| andb $63, %al |
| stc |
| xchgw %ax, %si /* save SI to AX */ |
| movw $(partition_sectors_per_track_string - _start1), %si |
| jz 2b |
| xchgw %ax, %si /* restore SI from AX */ |
| //lodsl |
| //stosl |
| movl -8(%si), %eax |
| testl %eax, %eax |
| stc |
| xchgw %ax, %si /* save SI to AX */ |
| movw $(partition_start_sector_string - _start1), %si |
| jz 2b |
| xchgw %ax, %si /* restore SI from AX */ |
| |
| //lodsl |
| //stosl |
| movl -4(%si), %eax |
| testl %eax, %eax |
| stc |
| xchgw %ax, %si /* save SI to AX */ |
| movw $(partition_end_sector_string - _start1), %si |
| jz 2b |
| xchgw %ax, %si /* restore SI from AX */ |
| |
| jmp 3b |
| 3: |
| cmpw $0xAA55, (%si) |
| stc |
| xchgw %ax, %si /* save SI to AX */ |
| movw $(no_boot_signature_string - _start1), %si |
| jnz 2b |
| xchgw %ax, %si /* restore SI from AX */ |
| //lodsw |
| //stosw /* store boot signature */ |
| |
| /* Now the partition table is OK */ |
| |
| movw %bx, 12(%bp) /* adjust BP in the stack */ |
| |
| movw $0x1b2, 8(%bp) /* adjust SI in the stack */ |
| |
| /* temp change the code: call self_modify_once |
| * |
| * "call self_modify_once" at add_sub_si is: |
| * |
| * .byte 0xE8 |
| * .word (self_modify_once - add_sub_si - 3) |
| * |
| */ |
| movb $0xE8, (add_sub_si - _start1) |
| movw $(self_modify_once - add_sub_si - 3), (add_sub_si + 1 - _start1) |
| |
| /* initialize partition number and partition entries end */ |
| movw $0xffff, 0x1bc /* hd partition number */ |
| movw $0x01fe, 0x1ba /* partition entries end */ |
| |
| jmp 1f |
| 2: |
| /* get here if all drives have been checked */ |
| #if 0 |
| movw $0x202, 8(%bp) /* adjust SI in the stack */ |
| |
| /* restore the original code: addw $-4, %si */ |
| movw $0xC683, (add_sub_si - _start1) /* 0x83, 0xC6 */ |
| movb $0xFC, (add_sub_si + 2 - _start1) /* 0xFC */ |
| #endif |
| //-------------------------------------------------------------------- |
| /* change the code: jmp Error_modify |
| * |
| * "jmp Error_modify" at Error_or_prev_MBR: |
| * |
| * .byte 0xE9 |
| * .word (Error_modify - Error_or_prev_MBR - 3) |
| * |
| */ |
| movb $0xE9, (Error_or_prev_MBR - _start1) |
| movw $(Error_modify - Error_or_prev_MBR - 3), (Error_or_prev_MBR + 1 - _start1) |
| //-------------------------------------------------------------------- |
| |
| //-------------------------------------------------------------------- |
| /* floppy search disabled ? */ |
| #if 0 |
| testb $1, 0x02 /* test bit0 of the third byte */ |
| jz 1f /* zero means floppy search enabled */ |
| /* 0x1fd or below means disable floppy search */ |
| decw (add_sub_si + 5 - _start1) |
| #else |
| movb 0x02, %al |
| andb $0x01, %al |
| subb %al, (add_sub_si + 5 - _start1) |
| #endif |
| //-------------------------------------------------------------------- |
| |
| 1: |
| #if 0 |
| popfw |
| lahf /* Load Flags into AH Register. */ |
| /* AH = SF:ZF:xx:AF:xx:PF:xx:CF */ |
| /* CF will be moved to ZF */ |
| movb %ah, %al |
| andb $1, %al /* CF=0 */ |
| shlb $6, %al /* move CF to ZF */ |
| popfw |
| lahf /* Load Flags into AH Register. */ |
| /* AH = SF:ZF:xx:AF:xx:PF:xx:CF */ |
| andb $0xbf, %ah /* 0xbf= binary 1011 1111. It clears ZF */ |
| orb %al, %ah |
| #else |
| popw %ax /* AX=Flags */ |
| popfw /* Flags_orig */ |
| lahf /* Load Flags_orig into AH Register. */ |
| /* AH = SF:ZF:xx:AF:xx:PF:xx:CF */ |
| shlb $2, %ah |
| rorw $2, %ax /* move CF of Flags to ZF of Flags_orig */ |
| #endif |
| |
| sahf /* update flags */ |
| /* current CF is the CF of Flags_orig */ |
| /* current ZF is the CF of Flags */ |
| jc 1f /* CF=1 means failed in loading bootsector */ |
| popal /* get drive number DL */ |
| pushal |
| pushfw |
| cmpb $0xff, %cs:0x06 |
| jz 2f |
| movb %cs:0x1bc, %dh |
| testb %dl, %dl |
| js 3f |
| movb $0xff, %dh /* partition # for floppy is "whole drive" */ |
| 3: |
| cmpw %cs:0x06, %dx |
| jz 2f |
| popfw |
| stc |
| pushfw |
| 2: |
| popfw |
| 1: |
| popal |
| popw %es |
| popw %ds |
| ret |
| |
| self_modify_once: |
| /* when we get here, SI should be 0x1b2, and BP high holds DL */ |
| addw $12, %si /* 0x83, 0xC6, 0x0C */ |
| movw %bp, %ax |
| movb %ah, %dl |
| |
| /* note: DS=0x9400 */ |
| |
| /* restore the original code: addw $12, %si */ |
| movw $0xC683, (add_sub_si - _start1) /* 0x83, 0xC6 */ |
| movb $0x0C, (add_sub_si + 2 - _start1) /* 0x0C */ |
| ret |
| |
| Error_modify: |
| cmpb $0xff, %cs:0x06 /* preferred drive? */ |
| jz 1f /* not active. Turn to the final step. */ |
| |
| /* preferred drive is already handled, so de-activate it now. */ |
| movb $0xff, %cs:0x06 |
| |
| /* we will do the second pass, from drive 0x80. */ |
| movb $0x7f, %dl /* this will become 0x80 after inc. */ |
| |
| /* pass "error" to PUSHF, simulating a load failure, in order |
| * to try the first entry after return from the helper function. |
| */ |
| |
| stc |
| |
| pushw $(helper_call + 3 - _start1) /* return address */ |
| pushw %cs /* 0x9400, it is for DS. */ |
| pushw $FS_BOOT /* 0x0d00, it is for ES. */ |
| pushal |
| //pushl %eax |
| //pushl %ecx |
| //pushl %edx |
| //pushl %ebx |
| //pushl %esp |
| //pushl %ebp |
| //pushl %esi |
| //pushl %edi |
| pushfw /* CF=1 */ |
| pushfw |
| |
| pushw %cs |
| popw %es /* ES=0x9400 */ |
| |
| /* redo from start: DL will be 0x80 after inc. */ |
| jmp inc_hard_drive |
| 1: |
| boot_prev_mbr: |
| |
| /* prepare to boot the previous MBR */ |
| |
| /* at this moment DS=0x9400, ES=$FS_BOOT or ES=0x9400 */ |
| xorw %ax, %ax |
| //pushw %ax /* AX=0, for the segment of 0000:7c00 */ |
| movw %ax, %es /* ES=0x0000 */ |
| movw %ax, %ds /* DS=0x0000 */ |
| pushw %ds |
| pushw %es |
| movw $0x0202, %ax /* read 2 sectors ... */ |
| movw $0x7A00, %bx /* ... to 0000:7A00 */ |
| //pushw %bx /* BX=0x7c00, for the offset of 0000:7c00 */ |
| movw $0x0001, %cx /* from the first sector ... */ |
| movw $0x0080, %dx /* ... of the first hard drive */ |
| stc |
| int $0x13 |
| sti |
| popw %es |
| popw %ds |
| jc 1f |
| testb %ah, %ah |
| jnz 1f |
| cmpw $0xAA55, 0x7dfe |
| jne 1f |
| cmpw $0xAA55, 0x7bfe |
| jne 1f |
| |
| /* has a valid partition table ? */ |
| movw $0x7dbe, %si |
| 3: |
| cmpw $0x7dfe, %si |
| jnb 3f /* partition table is OK */ |
| movw $4, %cx |
| |
| movw %si, %di |
| 2: |
| lodsl |
| negl %eax |
| jc 2f |
| loop 2b |
| /* empty entry, check next */ |
| jmp 3b |
| 2: |
| /* non-empty entry */ |
| movw %di, %si |
| |
| lodsw |
| shlb $1, %al |
| jnz 2f |
| lodsw |
| andb $63, %al |
| jz 2f |
| lodsw |
| lodsw |
| andb $63, %al |
| jz 2f |
| lodsl |
| negl %eax |
| jnc 2f |
| lodsl |
| negl %eax |
| jc 3b |
| 2: |
| stc /* invalid partition table */ |
| 3: |
| pushfw |
| |
| /* disable the boot of non-MBR bootsector ? */ |
| testb $2, %cs:0x02 /* test bit1 of the third byte */ |
| jz 2f /* zero means non-MBR enabled */ |
| popfw |
| jc 1f /* invalid partition table, print "Error" */ |
| |
| /* the partition table is valid */ |
| pushfw |
| |
| 2: |
| /* the check passed, and the boot is permitted */ |
| popfw |
| |
| jc 2f /* invalid partition table */ |
| |
| /* use partition table in MBR instead */ |
| |
| /* copy 72 bytes at 0000:7bb8 to 0000:7db8 */ |
| |
| movw $0x7bb8, %si |
| movw $0x7db8, %di |
| movw $36, %cx |
| cld |
| repz movsw |
| |
| 2: |
| testb $0x80, %cs:0x02 /* test bit 7 of the third byte */ |
| jz 2f /* zero means boot prev-MBR first */ |
| |
| movw $(Cannot_find_GRLDR_string - _start1), %si |
| call print_message /* CS:SI points to message string */ |
| movw $(press_space_bar_string - _start1), %si |
| cmpw $0x3920, %cs:0x04 |
| je 3f |
| movw $(press_hot_key_string - _start1), %si |
| 3: |
| call print_message /* CS:SI points to message string */ |
| movw $(prev_MBR_string - _start1), %si |
| call print_message /* CS:SI points to message string */ |
| 3: |
| call sleep_5_seconds |
| /* if hot-key is pressed, wait forever until another key is pressed. */ |
| movb $0xff, %cs:0x03 |
| jc 3b /* desired hot-key is pressed */ |
| 2: |
| /* boot the previous MBR */ |
| |
| /* clear the DUCE indicator */ |
| movl $0, 0x5FC /* DS=ES=0 */ |
| |
| //movb $0x80, %dl |
| ljmp $0, $0x7c00 |
| 1: |
| /* no previous MBR, print "Error" */ |
| ///* Note the 0000:7C00 is on the stack */ |
| //popw %ax /* AX=0x0000 */ |
| //popw %ax /* AX=0x7C00 */ |
| |
| testb $0x80, %cs:0x02 /* are we called prior to the GRLDR search? */ |
| jnz 1f /* no, it is a failure at last */ |
| /* yes, so return to the caller */ |
| movw $(continue_string - _start1), %si |
| call print_message /* CS:SI points to message string */ |
| call sleep_5_seconds |
| ret |
| 1: |
| movw $(message_string_helper - _start1), %si |
| call print_message /* CS:SI points to message string */ |
| 1: jmp 1b /* hang */ |
| |
| sleep_5_seconds: |
| /* sleep 5 seconds */ |
| |
| /* sleep forever if %cs:0x03 is 0xff */ |
| |
| /* calculate the timeout ticks */ |
| |
| pushw %ds |
| pushl %edx |
| |
| movl $0xffffffff, %edx |
| movzbl %cs:0x03, %eax |
| cmpb $0xff, %al |
| je 1f |
| movl $18, %edx /* 18.2 ticks per second. We simply use 18. */ |
| mulw %dx /* EDX=0, EAX=ticks */ |
| xchgw %ax, %dx /* EAX=0, EDX=ticks */ |
| 1: |
| xorw %ax, %ax |
| movw %ax, %ds |
| movl 0x46c, %eax /* initial tick */ |
| movl %eax, %ecx /* ECX=initial tick */ |
| testl %edx, %edx |
| js 1f |
| addl %edx, %eax /* EAX=timeout tick */ |
| jmp 3f |
| 1: |
| movl %edx, %eax /* EAX=0xffffffff */ |
| 3: |
| movl 0x46c, %ebx /* EBX=current tick */ |
| cmpl %ecx, %ebx |
| jnb 2f |
| |
| /* current tick is less than initial tick, this means the ticks have |
| * overflowed to the next day, and EBX is rather small. */ |
| xorl %ecx, %ecx |
| movl %edx, %eax |
| 2: |
| /* check if there is any key press. */ |
| pushw %ax |
| movb $1, %ah |
| int $0x16 |
| jnz 1f |
| |
| /* no, there is no key press. */ |
| |
| popw %ax |
| cmpl %eax, %ebx /* timeout? */ |
| jbe 3b /* no, continue to wait */ |
| |
| /* timeout reached, CF=0, no key pressed. */ |
| popl %edx |
| popw %ds |
| ret |
| 1: |
| /* yes, there is a key press. */ |
| #if 0 |
| /* clear the keyboard buffer */ |
| movb $1, %ah |
| int $0x16 |
| jz 1f /* no keys, end */ |
| movb $0, %ah |
| int $0x16 /* discard the key */ |
| jmp 1b |
| 1: |
| #endif |
| |
| /* check if it is the desired key. */ |
| |
| xorw %cs:0x04, %ax /* CF=0 */ |
| jne 1f /* not desired, return CF=0 */ |
| |
| /* remove the desired key from the keyboard buffer. */ |
| |
| movb $0, %ah |
| int $0x16 /* discard the key */ |
| |
| stc /* CF=1, the desired key pressed */ |
| 1: |
| popw %ax |
| popl %edx |
| popw %ds |
| ret |
| |
| modify_NTFS_boot_record: |
| |
| /* before the call: |
| * AH= partition number |
| * AL= 0xB6 ; 0xB6 is opcode of "MOV DH,imm8" |
| * DL= drive number |
| * |
| * on return: CF=0 if there is NTFS boot record; |
| * CF=1 otherwise. |
| * CF of flags_orig on the stack will set if CF=1 |
| */ |
| |
| /* |
| * |
| * the current stack is: |
| * |
| * SP + 40 : DS |
| * SP + 38 : ES |
| * SP + 34 : EAX |
| * SP + 30 : ECX |
| * SP + 26 : EDX |
| * SP + 22 : EBX |
| * SP + 18 : ESP_temp |
| * SP + 14 : EBP |
| * SP + 10 : ESI |
| * SP + 6 : EDI |
| * SP + 4 : flags_orig |
| * SP + 2 : SI ; SI points to old entry in MBR |
| * SP : return_IP |
| * |
| */ |
| |
| /* DS=ES=FS_BOOT */ |
| |
| /* change NTLDR to GRLDR */ |
| |
| /* check GR or NT or anything else */ |
| |
| pushw %ax |
| |
| movw $0x200, %si |
| lodsw |
| cmpw $5, %ax |
| jne 1f /* failure */ |
| lodsw |
| testb %ah, %ah /* high byte of unicode ASCII should be 0 */ |
| jne 1f /* failure */ |
| |
| /* 'N' should be a capital letter */ |
| |
| cmpb $0x41, %al /* Less than 'A' */ |
| jb 1f /* failure */ |
| cmpb $0x5A, %al /* Greater than 'Z'*/ |
| ja 1f /* failure */ |
| |
| xchgw %ax, %cx /* save AX to CX. CL='N' */ |
| |
| lodsw |
| testb %ah, %ah /* high byte of unicode ASCII should be 0 */ |
| jne 1f /* failure */ |
| |
| /* 'T' should be a capital letter */ |
| |
| cmpb $0x41, %al /* Less than 'A' */ |
| jb 1f /* failure */ |
| cmpb $0x5A, %al /* Greater than 'Z'*/ |
| ja 1f /* failure */ |
| |
| movb %al, %ch /* save AL to CH. CH='T' */ |
| |
| lodsw |
| cmpw $0x4C, %ax /* 'L' */ |
| jne 1f /* failure */ |
| lodsw |
| cmpw $0x44, %ax /* 'D' */ |
| jne 1f /* failure */ |
| lodsw |
| cmpw $0x52, %ax /* 'R' */ |
| jne 1f /* failure */ |
| lodsw |
| cmpw $0x04, %ax /* length of "$I30" */ |
| jne 1f /* failure */ |
| lodsw |
| cmpw $0x24, %ax /* '$' */ |
| jne 1f /* failure */ |
| lodsw |
| cmpw $0x49, %ax /* 'I' */ |
| jne 1f /* failure */ |
| lodsw |
| cmpw $0x33, %ax /* '3' */ |
| jne 1f /* failure */ |
| lodsw |
| cmpw $0x30, %ax /* '0' */ |
| jne 1f /* failure */ |
| |
| |
| /* assume it is NT bootsector. first, find "NTLDR". CX holds "NT" */ |
| movw $0x0100, %di |
| movb %cl, %al /* AL="N" */ |
| movb $1, %ah /* AH=Carry for SAHF below */ |
| movl $0x52444c00, %ebx /* "LDR" */ |
| movb %ch, %bl /* 'T' */ |
| movw $0x00fa, %cx |
| |
| /* now AL holds 'N' and BL holds 'T' */ |
| |
| //cld /* already upward */ |
| 3: |
| repnz scasb /* find "N" */ |
| jcxz 4f /* gets the end, exit */ |
| cmpl %ebx, (%di) /* is it "NTLDR"? */ |
| jnz 3b /* no, continue to find */ |
| |
| /* "NTLDR" is found, so we believe it is NT boot sector. */ |
| |
| movw $0x5247, -1(%di) /* change "NT" to "GR" */ |
| |
| /* CF=0 for now */ |
| |
| lahf /* Load Flags into AH */ |
| /* AH = SF:ZF:xx:AF:xx:PF:xx:CF */ |
| /* AH = binary xxxxxxx0 */ |
| jmp 3b |
| 4: |
| sahf /* Store AH into flags SF ZF xx AF xx PF xx CF */ |
| |
| /* CF=0 means "NTLDR" is found, CF=1 means "NTLDR" is not found. */ |
| |
| jc 1f /* failure */ |
| |
| movl $0x00520047, 0x202 /* change to "G R L D R" */ |
| |
| /* check NT 4.0 */ |
| |
| movw $0x406, %si |
| movl (%si), %ebx /* NT 4.0 */ |
| cmpl $0x03E8B800, %ebx /* MOV AX, 03E8 */ |
| jnz 3f |
| |
| movl 0x84, %ebx |
| cmpl $0x680007E8, %ebx /* call 008e; push (0D00) */ |
| jnz 3f |
| |
| // movw 0x154, %bx /* CR LF at end of "A disk read error occurred." */ |
| // cmpw $0x0A0D, %bx /* CR LF */ |
| // jnz 3f |
| // movw 0x180, %bx /* CR LF at end of "A kernel file is missing from the disk." */ |
| // cmpw $0x0A0D, %bx /* CR LF */ |
| // jnz 3f |
| // movw 0x1A8, %bx /* CR LF at end of "A kernel file is too discontiguous." */ |
| // cmpw $0x0A0D, %bx /* CR LF */ |
| // jnz 3f |
| // movw 0x1F8, %bx /* CR LF at end of "NTLDR is compressed." */ |
| // cmpw $0x0A0D, %bx /* CR LF */ |
| // jnz 3f |
| |
| movl 0xE8, %ebx |
| cmpl $0x13CD80B2, %ebx /* "B2 80"="mov DL, 80", "CD 13"="int 13" */ |
| jnz 3f |
| |
| popw %ax |
| movw %ax, 4(%si) |
| |
| movl $0x68909090, %ebx /* nop;nop;nop;push (0D00) */ |
| movl %ebx, 0x84 |
| |
| // /* change CRLF in NTFS error messages to spaces */ |
| // movw $0x2020, %bx /* change CRLF to 2 spaces */ |
| // movw %bx, 0x154 |
| // movw %bx, 0x180 |
| // movw %bx, 0x1A8 |
| // movw %bx, 0x1F8 |
| |
| movb %dl, 0xE9 /* modify drive number */ |
| |
| /* modify NTFS boot record */ |
| movb $0xea, %al /* ljmp, hand over the control to supervisor */ |
| movb %al, 0x122 |
| //movw $(try_next_partition - _start1), %ax /* offset for ljmp */ |
| movw $0x7e10, %ax /* offset for ljmp */ |
| movw %ax, 0x123 |
| //movw %cs, %ax /* AX=0x9400, segment for ljmp */ |
| xorw %ax, %ax |
| movw %ax, 0x125 |
| |
| movw $(NTFS4_message - _start1), %si |
| call print_message /* CS:SI points to message string */ |
| clc |
| ret |
| 3: |
| /* check NT 5.0 */ |
| |
| movw $0x44b, %si |
| movl (%si), %ebx /* NT 5.0 */ |
| cmpl $0x03E8B800, %ebx /* MOV AX, 03E8 */ |
| jz 2f |
| |
| movw $0x479, %si |
| movl (%si), %ebx /* NT 5.1 SP2 */ |
| cmpl $0x03E8B800, %ebx /* MOV AX, 03E8 */ |
| jnz 1f |
| 2: |
| movl 0x71, %ebx |
| cmpl $0x680053E8, %ebx /* call 00C7; push (0D00) */ |
| jnz 1f |
| |
| //movw 0x183, %bx /* CR LF at begin of "A disk read error occurred." */ |
| movb 0x1F8, %bl |
| movb $1, %bh |
| movw (%bx), %bx |
| cmpw $0x0A0D, %bx /* CR LF */ |
| jnz 1f |
| //movw 0x1A0, %bx /* CR LF at begin of "NTLDR is missing." */ |
| movb 0x1F9, %bl |
| movb $1, %bh |
| movw (%bx), %bx |
| cmpw $0x0A0D, %bx /* CR LF */ |
| jnz 1f |
| //movw 0x1B3, %bx /* CR LF at begin of "NTLDR is compressed." */ |
| movb 0x1FA, %bl |
| movb $1, %bh |
| movw (%bx), %bx |
| cmpw $0x0A0D, %bx /* CR LF */ |
| jnz 1f |
| |
| popw %ax |
| movw %ax, 4(%si) |
| |
| movl $0x68909090, %ebx /* nop;nop;nop;push (0D00) */ |
| movl %ebx, 0x71 |
| |
| /* change CRLF in NTFS error messages to spaces */ |
| movw $0x2020, %ax |
| movb 0x1F8, %bl |
| movb $1, %bh |
| movw %ax, (%bx) // 0x183 |
| movb 0x1F9, %bl |
| movb $1, %bh |
| movw %ax, (%bx) // 0x1A0 |
| movb 0x1FA, %bl |
| movb $1, %bh |
| movw %ax, (%bx) // 0x1B3 |
| |
| /* modify NTFS boot record */ |
| movb $0xEA, %al /* ljmp, hand over the control to supervisor */ |
| movb %al, 0x167 |
| //movw $(try_next_partition - _start1), %ax /* offset for ljmp */ |
| movw $0x7e10, %ax /* offset for ljmp */ |
| movw %ax, 0x168 |
| //movw %cs, %ax /* AX=0x9400, segment for ljmp */ |
| xorw %ax, %ax |
| movw %ax, 0x16A |
| |
| cmpw $0x44b, %si |
| jne 2f |
| movw $(NTFS5_message - _start1), %si |
| jmp 3f |
| 2: |
| movw $(NTFS5p_message - _start1), %si |
| 3: |
| call print_message /* CS:SI points to message string */ |
| clc |
| ret |
| 1: |
| /* NTFS boot record not found. */ |
| |
| movw $(NTFS_no_boot_record_message - _start1), %si |
| call print_message /* CS:SI points to message string */ |
| |
| popw %ax |
| popl %eax /* return_IP and SI */ |
| popfw |
| stc |
| pushfw |
| pushl %eax /* return_IP and SI */ |
| ret |
| |
| #if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL)) |
| move_helper: |
| |
| /* called only once and only when the boot loader loaded this code */ |
| pushw %si |
| pushw %bx |
| pushl %eax |
| |
| movw $0x0003, %ax /* set display mode: 80*25 color text */ |
| int $0x10 |
| |
| movw $0x200, %si |
| movw %si, %di |
| movw $0xf00, %cx |
| cld |
| repz movsw |
| |
| popl %eax |
| popw %bx |
| popw %si |
| ret |
| #endif |
| |
| press_space_bar_string: |
| .ascii "\r\nPress space bar\0" |
| |
| press_hot_key_string: |
| .ascii "\r\nPress your hot-key\0" |
| |
| press_any_key_string: |
| .ascii " to start GRUB, any other key to boot previous MBR ...\0" |
| |
| continue_string: |
| .ascii "\r\nInvalid previous MBR. Press any key to start GRUB ...\0" |
| |
| Cannot_find_GRLDR_string: |
| .ascii "\r\nCannot find GRLDR.\0" |
| |
| prev_MBR_string: |
| .ascii " to hold the screen, any other key to boot previous MBR ...\0" |
| |
| Error_while_reading_string: |
| .ascii "\r\nError while reading MBR of \0" |
| |
| drive_number_string: |
| .ascii "drive (hd0 ) \0" |
| |
| partition_boot_indicator_string: |
| .ascii "\r\nInvalid boot indicator in partition table of \0" |
| |
| partition_sectors_per_track_string: |
| .ascii "\r\nInvalid sectors_per_track in partition table of \0" |
| |
| partition_start_sector_string: |
| .ascii "\r\nInvalid start_sector in partition table of \0" |
| |
| partition_end_sector_string: |
| .ascii "\r\nInvalid end_sector in partition table of \0" |
| |
| no_boot_signature_string: |
| .ascii "\r\nNo boot signature in partition table of \0" |
| |
| message_string_helper: |
| .ascii "\r\nError: Cannot find GRLDR in all devices. Press Ctrl+Alt+Del to restart.\0" |
| |
| partition_message: |
| .ascii "\r\nTry (hd0,0 ) : \0" |
| |
| EXT2_message: |
| .ascii "EXT2: \0" |
| NTFS4_message: |
| .ascii "NTFS4: \0" |
| NTFS5_message: |
| .ascii "NTFS5: \0" |
| NTFS5p_message: |
| .ascii "NTFS5p: \0" |
| FAT32_message: |
| .ascii "FAT32: \0" |
| FAT16_message: |
| .ascii "FAT16: \0" |
| FAT12_message: |
| .ascii "FAT12: \0" |
| non_MS_message: |
| .ascii "non-MS: skip \0" |
| extended_message: |
| .ascii "Extended: \0" |
| invalid_message: |
| .ascii "invalid or null \0" |
| NTFS_no_boot_record_message: |
| .ascii "This partition is NTFS but with unknown boot record. Please\r\ninstall Microsoft NTFS boot sectors to this partition correctly, or create an\r\nFAT12/16/32 partition and place the same copy of GRLDR and MENU.LST there.\0" |
| |
| #if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL)) |
| . = _start1 + 0x1ffc |
| #else |
| . = . + (0x3fc - ((. - _start1) % 0x200)) % 0x200 |
| #endif |
| |
| grldr_signature: |
| .byte 0x47, 0x52, 0x55, 0xaa /* signature for helper */ |
| |
| .align 0x200 |
| |
| #if (! defined(GRLDR_MBR)) && (! defined(GRLDR_INSTALL)) |
| |
| /* pre_stage2 start at 0x2000 for grldr */ |
| |
| . = _start1 + 0x2000 |
| |
| #endif |
| |
| pre_stage2_start: |
| |
| |