GRUB SOURCE Analysis---1

Source: Internet
Author: User
Grub Source Analysis-1

This chapter begins the analysis of Grub's source code, version 2.02.

When the system starts, the BIOS loads the first sector of the hard drive (assuming boot from the hard disk) into the memory 0x7c00 location, which corresponds to the start function in grub (assuming grub boot), as shown below.

first part of boot start
Grub-core/boot/i386/pc/boot. S

start:jmp local (AFTER_BPB) ... Local (AFTER_BPB): CLI. org grub_boot_machine_drive_check boot_drive_check:jmp 3f testb $0x %DL JZ 2f 3:testb $0x70,%dl JZ 1f 2:movb $0x80,%dl 1:ljmp $, $re Al_start real_start:xorw%ax,%ax movw%ax,%ds movw%ax,%ss movw $GRUB _boot_machine_stack_

    SEG%sp sti Movb boot_drive,%al cmpb $0xff,%al JE 1f movb%al,%dl 1:pushw%dx MSG (notification_string) MOVW $disk _address_packet,%si movb $0x41,%ah MOVW-$0X55AA,%BX int     $0x13 POPW%dx pushw%dx JC Local (Chs_mode) CMPW $0xaa55,%bx jne Local (chs_mode) ANDW $%cx JZ Local (chs_mode) 

First, the interrupt is closed via CLI directives. The. org pseudo-instruction is used to tell the compiler the address of the next instruction, and the Grub_boot_machine_drive_check macro is defined as 0x66, so it means that the next instruction from 0x7c00 to JMP 3f is altogether 102 bytes.
JMP 3f has a total of two bytes and may be overwritten with two NOP instructions when GRUB is installed in the first sector, so check this below.
The DL registers are set by the BIOS as the boot device number, typically the 0x80~0xff,0x80 number corresponding to the first hard drive. If the JMP 3f is rewritten, it must be started from the 0x80 device (the following check is also, are some BIOS version caused, specifically why, the protocol should have written, lazy check), if not satisfied, then directly set to 0x80.
Further down TESTB 0x70 the boot device number for further inspection, limiting it to 0x0~0xf and 0x80~0x8f, and if not within this range, set it to 0x80.

Since entering this code, it is possible to use cs:ip=0x07c0:0x0000, the next long jump instruction to set the value to CS:IP=0X0000:0X7C00, together with the following instructions, the CS, DS and SS-segment registers are placed 0x0000.
Then set the current top of the stack pointer to grub_boot_machine_stack_seg, the macro defined as 0x2000, and then open the interrupt via STI. Now that the stack is built, you can use the push and pop instructions to work on the stack.

The default value of Boot_Drive is 0xFF, if overridden, to force a device boot to replace the previously computed DL with the device number.

The DX registers are then saved to the stack, which is also the device number, because the DL may be interrupted by an int 0x13, so the value is later assigned from the stack. Using MSG (notification_string), the data is printed to the screen with an int 10 interrupt.

Notification_string:    . Asciz "GRUB"
#define MSG (x)  movw $x,%si; Call
1:
    movw    $0x0001,%bx
    movb    $0xe,%ah
    int $0x10 local
(message):
    LODSB
    cmpb    $,%al
    jne 1b
    RET

LODSB the string in the notification_string into the Al register by byte, and prints it to the screen by int10 interrupt.

Then save the Disk_address_packet address to the SI register, which is used to save the parameters for reading the hard drive, and see immediately after.

int 0x13 Interrupt is the interface that the computer provides read and write disk information in real mode, in fact, it calls the BIOS code.
This interrupt is used to check disk expansion mode when the interrupt parameter is ah=0x41 and BX=0X55AA. The hard drive has LBA and CHS two modes, simply stated that the CHS mode supports small hard disk capacity and reads and writes exactly according to the hardware structure of the hard disk, and the LBA mode supports up to terabytes of hard disk capacity, so now most of the LBA mode is used. When the interrupt return value cf=1 indicates that the hard drive does not support LBA mode, jump directly to local (Chs_mode), and continue checking if cf=0 supports LBA. The return value BX has stored the magic number 0xaa55, if not equal, also direct jump to local (Chs_mode). The values in CX store the interface bitmap of the hard disk access, which, when even, represents some APIs that do not support LBA, and jumps to CHS.

Since most hard drives now support LBA mode, the following only looks at local (lba_mode) code.

Boot start Part II
Grub-core/boot/i386/pc/boot. S

Local (Lba_mode):
    xorw    %ax,%ax
    movw%ax    , 4 (%si)
    INCW%ax movb,%al    ,-1 (%si)
    MOVW    %ax, 2 (%si)
    MOVW    $0x0010, (%si)

    MOVL local    (kernel_sector),    %ebx movl%ebx, 8 (%si)
    MOVL local    (Kernel_sector_high),%ebx
    movl%ebx    , (%si)

    MOVW    $GRUB _boot_ MACHINE_BUFFER_SEG, 6 (%si)

    movb    $0x42,%ah
    int $0x13

    JC Local  (chs_mode)

    MOVW    $ Grub_boot_machine_buffer_seg,%BX
    jmp Local (copy_buffer)

(chs_mode):
    ...

Local (Final_init): ...
    

This part mainly reads the hard disk, first sets the parameter which reads the hard disk to disk_address_packet the address, also namely SI registers pointed address.

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:

First two bytes of head are cleared 0, in fact the address is 4 (%si). Then set mode to 1, the corresponding address is-1 (%si), which indicates the LBA mode, if 0, corresponds to CHS mode.
Further down the sectors two-byte 2 (%SI) is set to 0x0001, representing the number of sectors transferred, the lower two bytes (%si) set to $0x0010, where the high byte 0x00 is the default, and the low byte 0x10 represents the size of the data block.
Next, set 8 (%si) and (%si) from the cylinders address, which together determine the starting sector of the read, which defaults to 0x1, or reads the second sector.
Then set the grub_boot_machine_buffer_seg to heads in high two bytes, which indicates the destination address of the transmission, and the default value is 0x7000.

#define GRUB_BOOT_MACHINE_BUFFER_SEG    0x7000

Then an int 0x13 interrupt is performed, and the parameter 0x42 indicates that the data is read from the hard disk through the LBA mode. If you return a flag bit cf=1, you do not support LBA read, and then jump to CHS mode local (Chs_mode).

If the read succeeds, save the front cache address grub_boot_machine_buffer_seg to BX, and jump to jmp local (copy_buffer) to continue execution.

Boot Start Part III
Grub-core/boot/i386/pc/boot. S

Local (Copy_buffer):
    pusha
    pushw   %ds

    movw $0x100    ,%cx    movw,%bx%ds, XORW, %si
    MOVW    $GRUB _boot_machine_kernel_addr,%di
    movw%si    ,%es

    cld

    Rep MOVSW POPW    %ds
    Popa

    jmp * (local (kernel_address))

First through the Pusha instructions to the AX, CX, DX, BX, SP, BP, Si and di registers, PUSHW press into the DS segment registers in order to restore.
Then cycle the number 256 to CX.
Then set the DS segment register to point to the front cache address grub_boot_machine_buffer_seg, and then empty the SI registers.
Next, set the destination address grub_boot_machine_kernel_addr to the DI register, and the macro is defined as

#define GRUB_BOOT_MACHINE_KERNEL_ADDR   (grub_boot_machine_kernel_seg << 4)

GRUB_BOOT_MACHINE_KERNEL_SEG's final macro is defined as

#define GRUB_BOOT_I386_PC_KERNEL_SEG    0x800

Therefore the destination address grub_boot_machine_kernel_addr to 0x8000.
This code is the 0x7000:0x0000 address of the code to carry 256 words (512 bytes) to the 0x0000:0x8000 address.

The final jump to local (kernel_address) to continue to execute, in fact, is grub_boot_machine_kernel_addr, that is, 0x8000.

The code for the second sector is in the grub source diskboot. s in, see below.

Diskboot start the first part
Grub-core/boot/i386/pc/diskboot. S

_start:pushw%dx pushw%si MSG (notification_string) POPW%si MOVW $LOCAL (firstlist),%di movl (%di),%EBP Local (bootloop): CMPW $, 8 (%di) JE Local (bootit) local (Setu    P_sectors): CMPB $1 (%si) JE Local (chs_mode) MOVL (%di),%EBX MOVL 4 (%di),%ecx xorl %eax,%eax movb $0x7f,%al CMPW%ax, 8 (%DI) JG 1f MOVW 8 (%di),%ax 1:SUBW%ax, 8 (%d i) Addl%eax, (%DI) ADCL $, 4 (%DI) MOVW $0x0010, (%si) MOVW%ax, 2 (%si) MOVL%ebx, 8 (%si) movl%ecx,%si movw $GRUB _boot_machine_buffer_seg, 6 (%si) PUSHW%ax MOVW $4 (%si  ) Movb $0x42,%ah int $0x13 JC Local (read_error) MOVW $GRUB _boot_machine_buffer_seg,%BX jmp Local (Copy_buffer) 

First, the DX registers are put into the stack, which, as you know before, holds the boot device number at this time. As soon as MSG printing is to use SI registers, then save the SI registers, corresponding to the address of the disk_address_packet at the front.
Then print the notification_string string to the screen.

Notification_string:    . Asciz "Loading"

Local (Firstlist) is the starting address of the parameter that is about to be read from the hard drive, assigned to the DI and EBP registers, respectively.

. org 0x200-grub_boot_machine_list_size local
(firstlist):
blocklist_default_start:
    . Long 2, 0
Blocklist_default_len:
    . Word 0
blocklist_default_seg:
    . Word (grub_boot_machine_kernel_seg + 0x20)

0X200 is 512, the end of the second sector data. The grub_boot_machine_list_size is 12, which indicates the number of bytes in the first list reading parameter, the maximum of 15 first list items (14 valid), which can be seen from above. Therefore, the address at local (firstlist) is 0x1f4, which corresponds to the last list item.

It then enters the outer loop local (Bootloop), and the outer loop loops through a single list item at a time.
8 (%DI) corresponds to the above Blocklist_default_len, which is the number of sectors that will be read, and the value 0 is rewritten as the correct number when the program is generated. Because the subsequent loop decrements the value, if the value equals 0, all the data for the hard disk is moved to memory, and then jumps to local (bootit).

Then enter the inner loop, the int 0x13 interrupt reads the hard drive to read only 0x7f sectors at a time, so it needs to be read circularly.
First from boot. s mode address ( -1 (%SI)) to obtain the hard drive pattern, if 0, expressed as CHS mode, jump to local (chs_mode), otherwise continue execution.

Save the Blocklist_default_start 4 bytes in the EBX and ECX registers. Represents the logical starting address of a sector in the hard drive LBA mode, with the default value 0x2, that is, the 3rd sector. The AL register is then assigned a value of 0x7f, representing the maximum number of sectors per copy, 8 (%DI) indicates how many of the remaining sectors are not copied, and if 8 (%DI) is less than AL, then the last copy will be made, and 8 (%DI) is assigned to AL.

To Mark 1, first subtract the remaining sectors by 8 (%DI) by subtracting the current number of sectors that are about to be read AL, starting sector number (%DI) plus%eax, if there is a carry, you need to add to 4 (%DI), the three instructions are for the next copy of the preparation.

Next, we will set the various parameters of the hard drive read, similar to the previous boot. s code analysis, (%SI) point to Disk_address_packet, set sectors to 0x007f0010 (default), 0x10 is the size of the block, 0x00 is the default value, 0x007f is the number of sectors in this transmission, 8 (%SI) and (%si The initial sector number of the transmission is saved, and the starting value is 0x2;
6 (%SI) Saves the cached address, the default value is Grub_boot_machine_buffer_seg, also known as 0x7000, and BOOT. The cache address in S, and finally the number of sectors to be read AX into the stack, will be 4 (%si) clear 0. then perform the int 0x13 interrupt, the parameter AH is 0x42, and perform the read.

If CF is set to 1, an error occurs and jumps to local (read_error).
If the data is successfully read, then the buffer address grub_boot_machine_buffer_seg into the BX register, jump to jmp local (copy_buffer).

diskboot Start Part II
Grub-core/boot/i386/pc/diskboot. S

Local (Copy_buffer):
    movw    (%di),%es
    popw%ax    shlw    $,%ax addw
    %ax    , A (%DI)

    Pusha
    pushw   %ds

    shlw    $,%ax
    movw%ax    ,%cx

    -xorw%di    ,%di
    xorw    %si,%si
    movw    %bx,%ds

    CLD rep movsw popw%ds
    MSG (notification_ Step)
    Popa

    CMPW    $8 (%di)
    jne Local (setup_sectors)

    SUBW    $GRUB _boot_machine_list_ SIZE,%di

    jmp Local (bootloop)

(%DI) corresponding to the blocklist_default_seg, stored grub_boot_machine_kernel_seg + 0x20 Destination address, also that the destination address corresponding to the segment register value of 0x820, into the ES registers.
The stack is then restored to the AX register, saving the number of sectors that were read this time. The SHLW directive shifts the AX register to the left 5 digits to the segment address, which equals the ax left 9 digits to the final destination address, the number of sectors of this copy is also converted to the total number of bytes (2 of 9 to 512 bytes, corresponding to a sector), added to ten (%di), representing the next copy of the destination address.
The corresponding registers are then put into the stack for recovery.
Then ax left 3 digits, the front has moved 5 bits left, add together a total left 8 digits, indicating that the number of copies of this copy (1 words equals 2 bytes), into the CX registers.

The Di and SI registers are then cleared 0. BX Storage Source Address grub_boot_machine_buffer_seg, or 0x7000, into the DS segment registers, CLD instructions set the copy direction, and then cycle until the copy completed.

After the copy is complete, restore the register and print Notification_step to the screen.

Notification_step:  . Asciz "."

Then check 8 (%DI), that is, whether the data is still not copied in the first list item that is read, and if there are any remaining data, skip back to the local (setup_sectors) loop and read the 0x7f sector again.

If 8 (%di) is 0, indicating that there is no readable data in the first list item, the DI register is subtracted from the grub_boot_machine_list_size to the starting address of the next first list entry, and then to the local ( Bootloop) The outer loop continues to execute.

Diskboot Start Part III
Grub-core/boot/i386/pc/diskboot. S

Local (bootit):
    MSG (notification_done)
    popw    %dx
    ljmp    $ (grub_boot_machine_kernel_addr + 0x200

To get here, it means that all the data has been copied, printing Notification_done first.

Notification_done:  . Asciz "\ r \ n"

The

Then recovers the DX registers from the stack and stores the boot device number.
The last long jump to grub_boot_machine_kernel_addr + 0x200 execution, which starts from 0x0820:0x0000. The Startup_raw is saved at the
0x8200 start address. S, the next chapter continues to analyze.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.