grub-1.96/boot/i386/pc/boot.S

/*这个看起来是AT&T的语法,使用GAS(GNU Assembler->GNU Binutils)*/
/* http://sourceware.org/binutils/docs-2.20/as/index.html */
    
/* -*-Asm-*- */
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 1999,2000,2001,2002,2005,2006,2007  Free Software Foundation, Inc.
 *
 *  GRUB 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB 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 GRUB.  If not, see .
 */

#include
#include
    
/*
 *  defines for the code go here
 */

    /* Absolute addresses
       This makes the assembler generate the address without support
       from the linker. (ELF can't relocate 16-bit addresses!) */
#define ABS(x) (x-_start+0x7c00)

    /* Print message string */
#define MSG(x)    movw $ABS(x), %si; call message

    /* XXX:    binutils-2.9.1.0.x doesn't produce a short opcode for this. */
#define    MOV_MEM_TO_AL(x)    .byte 0xa0;  .word x
    
    .file    "boot.S"

    .text

    /* Tell GAS to generate 16-bit instructions so that this code works
       in real mode. */
    .code16

.globl _start; _start:
    /*
     * _start is loaded at 0x7c00 and is jumped to with CS:IP 0:0x7c00
     */

    /*
     * Beginning of the sector is compatible with the FAT/HPFS BIOS
     * parameter block.
     */

    jmp    after_BPB
    nop    /* do I care about this ??? */

    /*
     * This space is for the BIOS parameter block!!!!  Don't change
     * the first jump, nor start the code anywhere but right after
     * this area.
     */

    . = _start + 4
    /*The special symbol `.' refers to the current address that as is assembling into. Thus, the expression `melvin: .long .' defines melvin to contain its own address. Assigning a value to . is treated the same as a .org directive. Thus, the expression `.=.+4' is the same as saying `.space 4'. */
    
    /* scratch space */
mode:
    .byte    0
disk_address_packet:    
sectors:
    .long    0
heads:
    .long    0
cylinders:
    .word    0
sector_start:
    .byte    0
head_start:
    .byte    0
cylinder_start:
    .word    0
    /* more space... */

    . = _start + GRUB_BOOT_MACHINE_BPB_END
/* #define GRUB_BOOT_MACHINE_BPB_END    0x3e */
    /*
     * End of BIOS parameter block.
     */

boot_version:    
    .byte    GRUB_BOOT_VERSION_MAJOR, GRUB_BOOT_VERSION_MINOR
kernel_address:
    .word    GRUB_BOOT_MACHINE_KERNEL_ADDR
                                             /* #define GRUB_BOOT_MACHINE_KERNEL_SEG    0x800
                                             /* #define GRUB_BOOT_MACHINE_KERNEL_ADDR    (GRUB_BOOT_MACHINE_KERNEL_SEG << 4) */
kernel_segment:
    .word    GRUB_BOOT_MACHINE_KERNEL_SEG
kernel_sector:
    .long    1, 0
boot_drive:    
    .byte 0xff    /* the disk to load kernel from */
            /* 0xff means use the boot drive */

after_BPB:

/* general setup */
    cli        /* we're not safe here! */

        /*
         * This is a workaround for buggy BIOSes which don't pass boot
         * drive correctly. If GRUB is installed into a HDD, check if
         * DL is masked correctly. If not, assume that the BIOS passed
         * a bogus value and set DL to 0x80, since this is the only
         * possible boot drive. If GRUB is installed into a floppy,
         * this does nothing (only jump).
         */
boot_drive_check:
        jmp     1f    /* grub-setup may overwrite this jump */
    /* 1f 是哪?*/
        testb   $0x80, %dl
        jnz     1f
        movb    $0x80, %dl
1:
    
    /*
     * ljmp to the next instruction because some bogus BIOSes
     * jump to 07C0:0000 instead of 0000:7C00.
     */
    ljmp    $0, $ABS(real_start)

real_start:    

    /* set up %ds and %ss as offset from 0 */
    xorw    %ax, %ax
    movw    %ax, %ds
    movw    %ax, %ss

    /* set up the REAL stack */
    movw    $GRUB_BOOT_MACHINE_STACK_SEG, %sp
      /* #define GRUB_BOOT_MACHINE_STACK_SEG    0x2000*/
    sti        /* we're safe again */

    /*
     *  Check if we have a forced disk reference here
     */
    MOV_MEM_TO_AL(ABS(boot_drive))    /* movb    ABS(boot_drive), %al */
    cmpb    $0xff, %al
    je    1f
    movb    %al, %dl
1:
    /* save drive reference first thing! */
    pushw    %dx

    /* print a notification message on the screen */
    MSG(notification_string)
                       /* notification_string:    .string "GRUB " */
    /* set %si to the disk address packet */
    movw    $ABS(disk_address_packet), %si
    
    /* do not probe LBA if the drive is a floppy */
    testb    $GRUB_BOOT_MACHINE_BIOS_HD_FLAG, %dl
    /* #define GRUB_BOOT_MACHINE_BIOS_HD_FLAG    0x80 */
    jz    chs_mode
            
    /* check if LBA is supported */
    movb    $0x41, %ah
    movw    $0x55aa, %bx
    int    $0x13

    /*
     *  %dl may have been clobbered by INT 13, AH=41H.
     *  This happens, for example, with AST BIOS 1.04.
     */
    popw    %dx
    pushw    %dx

    /* use CHS if fails */
    jc    chs_mode
    cmpw    $0xaa55, %bx
    jne    chs_mode

    andw    $1, %cx
    jz    chs_mode
    
lba_mode:
    xorw    %ax, %ax
    movw    %ax, 4(%si)

    incw    %ax    
    /* set the mode to non-zero */
    movb    %al, -1(%si)
    
    /* the blocks */
    movw    %ax, 2(%si)

    /* the size and the reserved byte */
    movw    $0x0010, (%si)

    /* the absolute address */
    movl    ABS(kernel_sector), %ebx
    movl    %ebx, 8(%si)
    movl    ABS(kernel_sector + 4), %ebx
    movl    %ebx, 12(%si)

    /* the segment of buffer address */
    movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)
                /* #define GRUB_BOOT_MACHINE_BUFFER_SEG    0x7000 */
/*
 * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
 *    Call with    %ah = 0x42
 *            %dl = drive number
 *            %ds:%si = segment:offset of disk address packet
 *    Return:
 *            %al = 0x0 on success; err code on failure
 */

    movb    $0x42, %ah
    int    $0x13

    /* LBA read is not supported, so fallback to CHS.  */
    jc    chs_mode

    movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
    jmp    copy_buffer
        
chs_mode:    
    /*
     *  Determine the hard disk geometry from the BIOS!
     *  We do this first, so that LS-120 IDE floppies work correctly.
     */
    movb    $8, %ah
    int    $0x13
    jnc    final_init

    /*
     *  The call failed, so maybe use the floppy probe instead.
     */
    testb    $GRUB_BOOT_MACHINE_BIOS_HD_FLAG, %dl
    jz    floppy_probe

    /* Nope, we definitely have a hard disk, and we're screwed. */
    jmp    hd_probe_error

final_init:
    /* set the mode to zero */
    movzbl    %dh, %eax
    movb    %ah, -1(%si)
    
    /* save number of heads */
    incw    %ax
    movl    %eax, 4(%si)

    movzbw    %cl, %dx
    shlw    $2, %dx
    movb    %ch, %al
    movb    %dh, %ah

    /* save number of cylinders */
    incw    %ax
    movw    %ax, 8(%si)

    movzbw    %dl, %ax
    shrb    $2, %al

    /* save number of sectors */
    movl    %eax, (%si)

setup_sectors:
    /* load logical sector start (top half) */
    movl    ABS(kernel_sector + 4), %eax
    orl    %eax, %eax
    jnz    geometry_error
    
    /* load logical sector start (bottom half) */
    movl    ABS(kernel_sector), %eax

    /* zero %edx */
    xorl    %edx, %edx

    /* divide by number of sectors */
    divl    (%si)

    /* save sector start */
    movb    %dl, %cl

    xorw    %dx, %dx    /* zero %edx */
    divl    4(%si)        /* divide by number of heads */

    /* do we need too many cylinders? */
    cmpw    8(%si), %ax
    jge    geometry_error

    /* normalize sector start (1-based) */
    incb    %cl

    /* low bits of cylinder start */
    movb    %al, %ch

    /* high bits of cylinder start */
    xorb    %al, %al
    shrw    $2, %ax
    orb    %al, %cl

    /* save head start */
    movb    %dl, %al

    /* restore %dl */
    popw    %dx

    /* head start */
    movb    %al, %dh

/*
 * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
 *    Call with    %ah = 0x2
 *            %al = number of sectors
 *            %ch = cylinder
 *            %cl = sector (bits 6-7 are high bits of "cylinder")
 *            %dh = head
 *            %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
 *            %es:%bx = segment:offset of buffer
 *    Return:
 *            %al = 0x0 on success; err code on failure
 */

    movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
    movw    %bx, %es    /* load %es segment with disk buffer */

    xorw    %bx, %bx    /* %bx = 0, put it at 0 in the segment */
    movw    $0x0201, %ax    /* function 2 */
    int    $0x13

    jc    read_error

    movw    %es, %bx
    
copy_buffer:
    movw    ABS(kernel_segment), %es

    /*
     * We need to save %cx and %si because the startup code in
     * kernel uses them without initializing them.
     */
    pusha
    pushw    %ds
    
    movw    $0x100, %cx
    movw    %bx, %ds
    xorw    %si, %si
    xorw    %di, %di
    
    cld
    
    rep
    movsw

    popw    %ds
    popa

    /* boot kernel */
    jmp    *(kernel_address)

/* END OF MAIN LOOP */

/*
 * BIOS Geometry translation error (past the end of the disk geometry!).
 */
geometry_error:
    MSG(geometry_error_string)
    jmp    general_error

/*
 * Disk probe failure.
 */
hd_probe_error:
    MSG(hd_probe_error_string)
    jmp    general_error

/*
 * Read error on the disk.
 */
read_error:
    MSG(read_error_string)

general_error:
    MSG(general_error_string)

/* go here when you need to stop the machine hard after an error condition */
        /* tell the BIOS a boot failure, which may result in no effect */
        int    $0x18
stop:    jmp    stop

notification_string:    .string "GRUB "
geometry_error_string:    .string "Geom"
hd_probe_error_string:    .string "Hard Disk"
read_error_string:    .string "Read"
general_error_string:    .string " Error"

/*
 * message: write the string pointed to by %si
 *
 *   WARNING: trashes %si, %ax, and %bx
 */

    /*
     * Use BIOS "int 10H Function 0Eh" to write character in teletype mode
     *    %ah = 0xe    %al = character
     *    %bh = page    %bl = foreground color (graphics modes)
     */
1:
    movw    $0x0001, %bx
    movb    $0xe, %ah
    int    $0x10        /* display a byte */
message:
    lodsb
    cmpb    $0, %al
    jne    1b    /* if not end of string, jmp to display */
    ret

    /*
     *  Windows NT breaks compatibility by embedding a magic
     *  number here.
     */

    . = _start + GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC
    /* #define GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC    0x1b8 */
nt_magic:    
    .long 0
    .word 0

    /*
     *  This is where an MBR would go if on a hard disk.  The code
     *  here isn't even referenced unless we're on a floppy.  Kinda
     *  sneaky, huh?
     */

part_start:    
    . = _start + GRUB_BOOT_MACHINE_PART_START

probe_values:
    .byte    36, 18, 15, 9, 0

floppy_probe:
/*
 *  Perform floppy probe.
 */

    movw    $ABS(probe_values-1), %si

probe_loop:
    /* reset floppy controller INT 13h AH=0 */
    xorw    %ax, %ax
    int    $0x13

    incw    %si
    movb    (%si), %cl

    /* if number of sectors is 0, display error and die */
    cmpb    $0, %cl
    jne    1f

/*
 * Floppy disk probe failure.
 */
    MSG(fd_probe_error_string)
    jmp    general_error

fd_probe_error_string:    .string "Floppy"

1:
    /* perform read */
    movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
    movw    $0x201, %ax
    movb    $0, %ch
    movb    $0, %dh
    int    $0x13

    /* if error, jump to "probe_loop" */
    jc    probe_loop

    /* %cl is already the correct value! */
    movb    $1, %dh
    movb    $79, %ch

    jmp    final_init

    . = _start + GRUB_BOOT_MACHINE_PART_END

/* the last 2 bytes in the sector 0 contain the signature */
    .word    GRUB_BOOT_MACHINE_SIGNATURE

你可能感兴趣的:(Linux_grub)