/* -*-Asm-*- */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2005,2006,2007,2008,2009 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 <http://www.gnu.org/licenses/>.
*/
#include <grub/symbol.h>
#include <grub/machine/boot.h>
/*
* defines for the code go here
*/
/* Print message string */
#define MSG(x) movw $x, %si; call LOCAL(message)
#define ERR(x) movw $x, %si; jmp LOCAL(error_message)
.macro floppy
part_start:
LOCAL(probe_values):
.byte 36, 18, 15, 9, 0
LOCAL(floppy_probe):
pushw %dx
/*
* Perform floppy probe.
*/
#ifdef __APPLE__
LOCAL(probe_values_minus_one) = LOCAL(probe_values) - 1
movw MACRO_DOLLAR(LOCAL(probe_values_minus_one)), %si
#else
movw MACRO_DOLLAR(LOCAL(probe_values)) - 1, %si
#endif
LOCAL(probe_loop):
/* reset floppy controller INT 13h AH=0 */
xorw %ax, %ax
int MACRO_DOLLAR(0x13)
incw %si
movb (%si), %cl
/* if number of sectors is 0, display error and die */
testb %cl, %cl
jnz 1f
/*
* Floppy disk probe failure.
*/
MSG(fd_probe_error_string)
jmp LOCAL(general_error)
/* "Floppy" */
fd_probe_error_string: .asciz "Floppy"
1:
/* perform read */
movw MACRO_DOLLAR(GRUB_BOOT_MACHINE_BUFFER_SEG), %bx
movw %bx, %es
xorw %bx, %bx
movw MACRO_DOLLAR(0x201), %ax
movb MACRO_DOLLAR(0), %ch
movb MACRO_DOLLAR(0), %dh
int MACRO_DOLLAR(0x13)
/* if error, jump to "LOCAL(probe_loop)" */
jc LOCAL(probe_loop)
/* %cl is already the correct value! */
movb MACRO_DOLLAR(1), %dh
movb MACRO_DOLLAR(79), %ch
jmp LOCAL(final_init)
.endm
.macro scratch
/* 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... */
.endm
.file "boot.S"
.text
/* Tell GAS to generate 16-bit instructions so that this code works
in real mode. */
.code16
.globl _start, start;
_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 LOCAL(after_BPB)
nop /* do I care about this ??? */
#ifdef HYBRID_BOOT
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
jmp LOCAL(after_BPB)
#else
/*
* This space is for the BIOS parameter block!!!! Don't change
* the first jump, nor start the code anywhere but right after
* this area.
*/
.org GRUB_BOOT_MACHINE_BPB_START
.org 4
#endif
#ifdef HYBRID_BOOT
floppy
#else
scratch
#endif
.org GRUB_BOOT_MACHINE_BPB_END
/* The offset of the end of BPB (BIOS Parameter Block). */
//#define GRUB_BOOT_MACHINE_BPB_END 0x5a
/*
* End of BIOS parameter block.
*/
LOCAL(kernel_address):
//内核的运行地址
.word GRUB_BOOT_MACHINE_KERNEL_ADDR
//0x8000
#ifndef HYBRID_BOOT
//error
.org GRUB_BOOT_MACHINE_KERNEL_SECTOR
LOCAL(kernel_sector):
.long 1
LOCAL(kernel_sector_high):
.long 0
#endif
#ifdef HYBRID_BOOT
//error
#endif
.org GRUB_BOOT_MACHINE_BOOT_DRIVE
//0x64
boot_drive:
.byte 0xff /* the disk to load kernel from */
/* 0xff means use the boot drive */
LOCAL(after_BPB):
/* general setup */
cli /* we're not safe here! */
//1 byte
/*
* 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).
*/
.org GRUB_BOOT_MACHINE_DRIVE_CHECK
boot_drive_check:
//等待键盘输入字符'0'
//sti
//movw $0x1234, %ax
//call print_start
//movb $0x00, %ah
//movb $0xff, %al
//int $0x16
//cmpb $0x0d, %al //2 byte
//jne boot_drive_check //2 byte
//nop
//nop
//jmp boot_drive_check
//jmp 3f /* grub-setup may overwrite this jump */
// testb $0x80, %dl //3 byte
// jz 2f //2 byte
3:
/* Ignore %dl different from 0-0x0f and 0x80-0x8f. */
//testb $0x70, %dl //3 byte
//jz 1f //2 byte
//movw %dx, %bx
//AX,CX,DX,BX,SP,BP,SI,DI依次
2:
//movb $0x80, %dl //2 byte
1:
//movw %i4p, %bx
/*
* ljmp to the next instruction because some bogus BIOSes
* jump to 07C0:0000 instead of 0000:7C00.
*/
//ljmp $0, $real_start //5 byte
//切换cs寄存器的值
//jmp real_start
nop
nop
//此处改成短跳转也可以 但是不可以没有这条指令
//因为grub-install 程序会在此位置 90 90 填充2条nop指令
//如果没有 后面的指令会前移到此位置 也就是会把后续指令nop
//直接2 nop占位也可以
real_start:
/* set up %ds and %ss as offset from 0 */
//movw $0x66BB, %bx
xorw %ax, %ax //2 byte
movw %ax, %ds //2 byte
movw %ax, %ss //2 byte
//set up the REAL stack
//movw $GRUB_BOOT_MACHINE_STACK_SEG, %sp //3 byte
movw $0x2000, %sp
sti /* we're safe again */ //1 byte
wait_user_input:
//movw %dx, %ax
//call print_start
//movw %bx, %ax
//call print_start
//movw %si, %ax
//call print_start
//movw %di, %ax
//call print_start
//movw %bp, %ax
//call print_start
//movw %sp, %ax
//call print_start
//movb $0x00, %ah
//movb $0xff, %al
//int $0x16
//cmpb $0x0d, %al //2 byte
//jne wait_user_input //2 byte
/*
* Check if we have a forced disk reference here
*/
movb boot_drive, %al //3 byte
cmpb $0xff, %al //2 byte
je 1f //2 byte
movb %al, %dl //2 byte
1:
/* save drive reference first thing! */
pushw %dx //1 byte
2:
/* print a notification message on the screen */
//MSG(notification_string)
//6 byte
//BE 80 7D mov si, 7D80h 3 byte
//E8 17 01 call sub_7DAA ; Call Procedure 3byte
//jmp 2b //2字节
/* set %si to the disk address packet */
movw $disk_address_packet, %si //3 byte
/* check if LBA is supported */
movb $0x41, %ah //2 byte
movw $0x55aa, %bx //3 byte
int $0x13 //2字节
//DISK - Check for INT 13h Extensions
//BX = 55AAh, DL = drive number
//Return: CF set if not supported
//AH = extensions version
//BX = AA55h
//CX = Interface support bit map
/*
* %dl may have been clobbered by INT 13, AH=41H.
* This happens, for example, with AST BIOS 1.04.
*/
popw %dx //1 byte
//pushw %dx //1 byte
/* use CHS if fails */
jc LOCAL(chs_mode) //2 byte
cmpw $0xaa55, %bx //4 byte
jne LOCAL(chs_mode) //2 byte
andw $1, %cx //3 byte
jz LOCAL(chs_mode) //2 byte
LOCAL(lba_mode):
//会进入这里
//MSG(hd_probe_error_string) //6 byte
//jmp LOCAL(lba_mode) //3 byte
//MSG(general_error_string)
xorw %ax, %ax //2 byte
movw %ax, 4(%si) //3 byte
incw %ax //1 byte
/* set the mode to non-zero */
movb %al, -1(%si) //3 byte
/* the blocks */
movw %ax, 2(%si) //3 byte
/* the size and the reserved byte */
movw $0x0010, (%si) //4 byte
/* the absolute address */
movl LOCAL(kernel_sector), %ebx //5 byte
movl %ebx, 8(%si) //4 byte
movl LOCAL(kernel_sector_high), %ebx //5 byte
movl %ebx, 12(%si) //4 byte
/* the segment of buffer address */
movw $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si) //5 byte
/*
* 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 //2 byte
int $0x13 //2 byte
// 这次 INT 0x13 调用将完成 :
// 通过扩展读 , 从磁盘读取由 kernel_sector 处8字节长度确定的某一扇区的内容到内存地址 0x70000(0x7000:0x0000) 处
/* LBA read is not supported, so fallback to CHS. */
jc LOCAL(chs_mode) //2 byte
movw $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx //3 byte
jmp LOCAL(copy_buffer) //2 byte
/
//老硬盘的处理 没有走这条路 代码全部注释掉
LOCAL(chs_mode):
/*
* Determine the hard disk geometry from the BIOS!
* We do this first, so that LS-120 IDE floppies work correctly.
*/
//nop
MSG(read_error_string) //6 byte
jmp LOCAL(chs_mode) //3 byte
//MSG(general_error_string)
//movb $8, %ah //2 byte
//int $0x13 //2 byte
//jnc LOCAL(final_init) //2 byte
//popw %dx //1 byte
/*
* The call failed, so maybe use the floppy probe instead.
*/
//testb %dl, %dl //2 byte
//jnb LOCAL(floppy_probe) //4 byte
/* Nope, we definitely have a hard disk, and we're screwed. */
//ERR(hd_probe_error_string) //6 byte
LOCAL(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 LOCAL(kernel_sector_high), %eax
//orl %eax, %eax
//jnz LOCAL(geometry_error)
/* load logical sector start (bottom half) */
//movl LOCAL(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 LOCAL(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 LOCAL(read_error)
//movw %es, %bx
/
//拷贝程序
LOCAL(copy_buffer):
/*
* We need to save %cx and %si because the startup code in
* kernel uses them without initializing them.
*/
//这里需要保存 cx 和 si 的值
//因为内核 startup 里面的代码未经初始化就使用了它( 其实是 diskboot.S 代码 )
pusha
pushw %ds
// 设置循环次数和源/目的串段寄存器和偏移指针
// 设置完毕之后 , 寄存器值 :
// CX = 0x100 ; ES:SI=0x8000:0x0000
// LBA读 : DS:SI = 0x7000:0x0000
// CHS读 : DS:SI = ?
movw $0x100, %cx
movw %bx, %ds
xorw %si, %si
movw $GRUB_BOOT_MACHINE_KERNEL_ADDR, %di
movw %si, %es
cld
//DF = 0 (方向标志)
rep
movsw
// 操作串由 DS:SI 和 ES:DI 指定
// 将 DS:SI 指向的字传送到 ES:DI 指向的内存单元
// DF = 0 , 因此 SI += 2 ; DI += 2
popw %ds
popa
//movw %si, %ax
//call print_start
//call LOCAL(wait_user_input)
/* boot kernel */
jmp *(LOCAL(kernel_address))
// 0x8000 处存放了 diskboot.S 生成的机器码
// 至于 0x8000 处的 512 字节代码完成什么功能 , 见 diskboot.S 注释
//boot.S干的活:
//1.磁盘自检查 确定为LBA模式 软盘和老式硬盘代码没有用到
//2.加载磁盘(diskboot.S)的第1扇区数据到0x7000:0x0000地址 一般boot.S放0扇区,即mbr扇区 接下来就是diskboot.S(1-60几扇区)
//3.加载内存0x7000:0x0000的1扇区内容到内存0x8000:0x0000,然后跳过去执行 内存数据搬运
//END OF MAIN LOOP
//出错处理部分
/*
* BIOS Geometry translation error (past the end of the disk geometry!).
*/
LOCAL(geometry_error):
ERR(geometry_error_string)
/*
* Read error on the disk.
*/
LOCAL(read_error):
movw $read_error_string, %si
LOCAL(error_message):
call LOCAL(message)
LOCAL(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
LOCAL(stop):
jmp LOCAL(stop)
//完犊子了
notification_string: .asciz "GRUB "
geometry_error_string: .asciz "Ggxm"
hd_probe_error_string: .asciz "Hard Disk"
read_error_string: .asciz "Read"
general_error_string: .asciz " Error\r\n"
//终端输出字符串的功能函数
/*
* 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 */
LOCAL(message):
lodsb
cmpb $0, %al
jne 1b /* if not end of string, jmp to display */
ret
print_start:
pusha
pushw %ds
//初始化寄存器
//movw $0x12ab, %ax //将需要转换的值存入eax
mov $0, %di
movw $0x000f, %dx
loop_ascii:
movw %ax, %bx
imul $4, %di, %cx
and %dx, %bx
ror %cl, %bx
cmpb $10, %bl
jnc max_num_is_9
addb $0x30, %bl
jmp modify_index
max_num_is_9:
addb $0x37, %bl
modify_index:
rol $4, %dx
mov $5, %cx
sub %di, %cx
movb %bl, debug_out_buf( , %ecx, 1 )
//AT&T汇编以及Intel汇编说明
//3>变址寻址
// AT&T: _variable(%eax)
// Intel: [eax + _variable]
// AT&T: _array(, %eax,4)
// Intel: [eax*4+_array]
// AT&T: _array(%ebx, %eax,8)
// Intel: [ebx + eax*8 + _array]
cmp $3, %di
jnc print
inc %di
jmp loop_ascii
print:
movw $debug_out_buf, %si;
call LOCAL(message)
//jmp .
popw %ds
popa
ret
//.section .bss
//.lcomm
debug_out_buf:
.byte '0'
.byte 'x'
.byte '1'
.byte '2'
.byte '3'
.byte '4'
.byte '\r'
.byte '\n'
.long 0
LOCAL(wait_user_input):
pusha
pushw %ds
1:
movb $0x00, %ah
movb $0xff, %al
int $0x16
cmpb $0x0d, %al //2 byte
jne 1b //2 byte
popw %ds
popa
ret
//从0x1b0开始 由于有org指令 所以代码不能越过这个位置
/*
* Windows NT breaks compatibility by embedding a magic
* number here.
*/
#ifdef HYBRID_BOOT
.org 0x1b0
LOCAL(kernel_sector):
//diskboot.S文件的扇区低地址 4字节 lba_mode
.long 1
LOCAL(kernel_sector_high):
//diskboot.S文件的扇区高地址 4字节 lba_mode
.long 0
#endif
.org GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC
//0x1b8 windows系统使用的魔幻数 6字节
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?
*/
.org GRUB_BOOT_MACHINE_PART_START
//Boot.h (include\grub\i386\pc):#define GRUB_BOOT_MACHINE_PART_START 0x1be
//分区表的开始位置 4个分区项 每项16字节
#ifndef HYBRID_BOOT
floppy
#else
scratch
#endif
.org GRUB_BOOT_MACHINE_PART_END
//#define GRUB_BOOT_MACHINE_PART_END 0x1fe
//the last 2 bytes in the sector 0 contain the signature
.word GRUB_BOOT_MACHINE_SIGNATURE
//#define GRUB_BOOT_MACHINE_SIGNATURE 0xaa55
//在0扇区的最后2字节是签名魔幻数 55 aa