/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define ASM_FILE
#include <shared.h>
#ifndef STAGE1_5
#include <stage2_size.h>
#endif
/*
* defines for the code go here
*/
/* 獲取絕對地址,如果採用1.5階段的方案,則開始地址(物理地址)從0x2000開始,
否則從0x8000開始。*/
#ifdef STAGE1_5
# define ABS(x) (x-_start+0x2000)
#else
# define ABS(x) (x-_start+0x8000)
#endif /* STAGE1_5 */
/* 打印消息 */
#define MSG(x) movw $ABS(x), %si; call message
.file "start.S"
.text
/* 讓GAS生成16位的指令以便在實模式下工作 */
.code16
.globl start, _start
start:
_start:
/*
* 在上一個階段中,這裏的代碼已經被加載到0x8000並且也跳轉到了CS:IP 0:0x8000這個
* 地址,見stage1.S中copybuffer部分。
*/
/*
* 這裏繼續使用stage1的棧,並假設一些寄存器已經設置好了正確的值。
* 詳見 stage1.S的說明。
*/
/* 首先保存驅動器引用,在硬盤啓動時dl值通常是0x80/0x81等,
* 分別表示第一硬盤第二硬盤等等*/
pushw %dx
/* 打印信息:"Loading stage2"/"Loading stage1.5" */
pushw %si
MSG(notification_string)
popw %si
/* 讀取後續引導扇區的參數,放在這個扇區的最後8個字節,
這裏讓di指向這個結構的起始位置 。結構及默認值如下,
注意括號內是stage2的默認值,grub的install命令會設置合適的值。
blocklist{
long StartSect = 2
word blocklist_default_len = 0(STAGE2_SIZE + 511) >> 9)
word blocklist_default_seg = 0x220(0x820)
}*/
movw $ABS(firstlist - BOOTSEC_LISTSIZE), %di
/* 保存開始扇區序號(默認是2)到 %ebp */
movl (%di), %ebp
/* 下面是循環讀取引導代碼 */
bootloop:
/* 檢查扇區數 */
cmpw $0, 4(%di)
/* 如果讀取扇區完成,則跳轉執行下一步的引導*/
je bootit
setup_sectors:
/* 檢查模式,這裏SI指向的是硬盤參數塊,si-1指向的是硬盤模式,見stage1.S中的設置*/
cmpb $0, -1(%si)
/* 0 - CHS / 1 - LBA */
je chs_mode
lba_mode:
/* 起始扇區 => EBX */
movl (%di), %ebx
/* 讀取扇區數上限是0x7f(Phoenix EDD的原因),與實際需要拷貝的數量相比取其中小的值 */
xorl %eax, %eax
movb $0x7f, %al
cmpw %ax, 4(%di)
jg 1f
movw 4(%di), %ax
1:
/* 前移下次讀取位置,即從需要讀取的總扇區數中減去即將讀取的扇區數並將增加起始扇區數 */
subw %ax, 4(%di)
addl %eax, (%di)
/* 填充disk address packet,INT 0x13/42h的參數之一 */
/* 結構大小和保留字節,注意SI也是從Stage1.S中設定好的,指向一個緩衝區
struct disk_address_packet{
+00 byte PacketSize = 10h //此結構大小
+01 byte Reserve = 0
+02 word BlockCount = 1 //傳輸數據塊數
+04 word BufferOff = 0 // 緩存地址
+06 word BufferSeg = 0x0700 //即0700:0000
+08 qword Start = 1 //起始扇區的LBA號
}*/
movw $0x0010, (%si)
/* 需要讀取的扇區數 */
movw %ax, 2(%si)
/* 起始扇區的LBA號(低32位) */
movl %ebx, 8(%si)
/* 讀緩衝區的節地址,默認0x7000 */
movw $BUFFERSEG, 6(%si)
/* 保存%ax以防止污染 */
pushw %ax
/* %eax = 0 */
xorl %eax, %eax
/* 讀緩衝區的偏移=0,默認讀緩衝區爲0x7000 : 0000 */
movw %ax, 4(%si)
/*起始扇區的LBA號 (高32位) */
movl %eax, 12(%si)
/*
* 調用BIOS例程"INT 0x13 Function 0x42" 從硬盤中加載扇區到內存
* Call with %ah = 0x42
* %dl = 驅動器號(對於硬盤是0x80/81....)
* %ds:%si = segment:offset 上面的disk_address_packet
* Return:
* %al = 0x0 on success; err code on failure
*/
movb $0x42, %ah
int $0x13
jc read_error
/* 如果讀取成功則保存讀緩衝區的節到bx,然後跳轉到copy_buffer */
movw $BUFFERSEG, %bx
jmp copy_buffer
/* 下面是CHS模式下的拷貝扇區 */
chs_mode:
/* 讀取的起始扇區 => EAX*/
movl (%di), %eax
/* %edx=0 */
xorl %edx, %edx
/* si指向的是硬盤參數塊(CHS),它在Stage1.S中讀取並賦予了SI指向的地址
CHS模式下的硬盤地址參數臨時緩衝區
struct disk_address_packet{
long sectors //總扇區數
long heads //總磁頭數
word cylinders // 總柱面數
byte sector_start
byte head_start
word cylinder_start
}
下面這段的作用是根據硬盤參數快算出INT 0x13 Function 0x2的各項參數
*/
divl (%si)
/* 餘數即爲開始扇區 */
movb %dl, 10(%si)
xorl %edx, %edx /* zero %edx */
divl 4(%si) /* 結果再除以磁頭數 */
/* 餘數即爲開始磁頭 */
movb %dl, 11(%si)
/* 商即爲開始柱面 */
movw %ax, 12(%si)
/* 檢測柱面數是否溢出 */
cmpw 8(%si), %ax
jge geometry_error
/* 確認讀取的最大扇區數 */
movw (%si), %ax /* 每個磁道的扇區數 */
/* 起始扇區減去每個磁道的扇區數 */
subb 10(%si), %al
/* 和我們想要讀取的扇區數量比較*/
cmpw %ax, 4(%di)
/* which is greater? */
jg 2f
/* if less than, set to total */
movw 4(%di), %ax
2:
/* 前移讀取位置,即從需要讀取的總扇區數中減去即將讀取的扇區數並將增加起始扇區數 */
subw %ax, 4(%di)
addl %eax, (%di)
/*
* 下面是組織BIOS調用的參數以及調用0x13/0x2。
* 調用BIOS "INT 0x13/0x2"從磁盤中讀取一個或者多個扇區到內存中
* 參數 %ah = 0x2
* %al = 讀取扇區數
* %ch = 所在柱面
* %cl = 開始扇區 (6-7位是所在柱面的高兩位)
* %dh = 磁頭
* %dl = drive (0x80是硬盤, 0x0是軟盤)
* %es:%bx = 緩衝區的“節:偏移”
* Return:
* %al = 0x0 on success; err code on failure
*/
/* get high bits of cylinder */
movb 13(%si), %dl
shlb $6, %dl /* shift left by 6 bits */
movb 10(%si), %cl /* get sector */
incb %cl /* normalize sector (sectors go
from 1-N, not 0-(N-1) ) */
orb %dl, %cl /* composite together */
movb 12(%si), %ch /* sector+hcyl in cl, cylinder in ch */
/* restore %dx */
popw %dx
pushw %dx
/* head number */
movb 11(%si), %dh
pushw %ax /* save %ax from destruction! */
movw $BUFFERSEG, %bx
movw %bx, %es /* load %es segment with disk buffer */
xorw %bx, %bx /* %bx = 0, put it at 0 in the segment */
movb $0x2, %ah /* function 2 */
int $0x13
/* 發生讀取錯誤則打印"Read"*/
jc read_error
/* save source segment */
movw %es, %bx
copy_buffer:
/* 前面已經將指定數量的扇區讀取到內存緩衝區
下面是要拷貝到約定的位置,然後我們將調轉到那裏指向下一步驟
默認情況下階段1.5這個節的值是0x200 : 0000,階段2的值是0x820 : 0000
*/
movw 6(%di), %es /* load destination segment */
/* restore %ax */
popw %ax
/* determine the next possible destination address (presuming
512 byte sectors!) */
shlw $5, %ax /* shift %ax five bits to the left */
addw %ax, 6(%di) /* add the corrected value to the destination
address for next time */
/* save addressing regs */
pusha
pushw %ds
/* get the copy length */
shlw $4, %ax
movw %ax, %cx
xorw %di, %di /* zero offset of destination addresses */
xorw %si, %si /* zero offset of source addresses */
movw %bx, %ds /* restore the source segment */
cld /* sets the copy direction to forward */
/* 執行拷貝操作 */
rep /* sets a repeat */
movsb /* this runs the actual copy */
/* restore addressing regs and print a dot with correct DS
(MSG modifies SI, which is saved, and unused AX and BX) */
popw %ds
MSG(notification_step)
popa
/* 如果扇區並沒有全部讀取完畢,則需要繼續循環讀取。*/
cmpw $0, 4(%di)
jne setup_sectors
/* update position to load from */
subw $BOOTSEC_LISTSIZE, %di
/* jump to bootloop */
jmp bootloop
/* END OF MAIN LOOP */
bootit:
/* 打印"\r\n" */
MSG(notification_done)
popw %dx /* this makes sure %dl is our "boot" drive */
#ifdef STAGE1_5
ljmp $0, $0x2200 // 已讀取到0x0220 : 0000
#else /* ! STAGE1_5 */
ljmp $0, $0x8200 // 已讀取到0x0820 : 0000
#endif /* ! STAGE1_5 */
/*下面這些代碼用於錯誤打印*/
/*
* BIOS Geometry translation error (past the end of the disk geometry!).
*/
geometry_error:
MSG(geometry_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 */
stop: jmp stop
#ifdef STAGE1_5
notification_string: .string "Loading stage1.5"
#else
notification_string: .string "Loading stage2"
#endif
notification_step: .string "."
notification_done: .string "\r\n"
geometry_error_string: .string "Geom"
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 */
incw %si
message:
movb (%si), %al
cmpb $0, %al
jne 1b /* if not end of string, jmp to display */
ret
lastlist:
/*
* This area is an empty space between the main body of code below which
* grows up (fixed after compilation, but between releases it may change
* in size easily), and the lists of sectors to read, which grows down
* from a fixed top location.
*/
.word 0
.word 0
. = _start + 0x200 - BOOTSEC_LISTSIZE
/* fill the first data listing with the default */
/*是最後的8字節,用於存放一個blocklist
blocklist{
long StartSect = 2
word blocklist_default_len = 0(STAGE2_SIZE + 511) >> 9)
word blocklist_default_seg = 0x220(0x820)
}
*/
blocklist_default_start:
/* 這是需要讀取的開始扇區,MBR是扇區0本代碼是扇區1所以後續代碼要放在扇區2上 */
.long 2
blocklist_default_len:
/* 這裏填充需要讀取的扇區數量 */
#ifdef STAGE1_5
.word 0 /* 這裏由install命令填充 */
#else
.word (STAGE2_SIZE + 511) >> 9 /*STAGE2_SIZE默認等於6*/
#endif
blocklist_default_seg:
#ifdef STAGE1_5
.word 0x220
#else
.word 0x820 /* 開始位置的節寄存器的值 */
#endif
firstlist: /* this label has to be after the list data!!! */