ASM.S生成的代碼將放置到第三扇區,緊跟Start.S生成的第二扇區代碼。如上一篇代碼中展現的這個代碼將被start.s加載到內存地址的0x8200或者0x2200中執行。
#define ASM_FILE
#include "shared.h"
#ifdef STAGE1_5
# define ABS(x) ((x) - EXT_C(main) + 0x2200)
#else
# define ABS(x) ((x) - EXT_C(main) + 0x8200)
#endif
.file "asm.S"
.text
/* GAS生成16位代碼 */
.code16
#ifndef STAGE1_5
/*
* 在階段2中不要將start.S和剩餘源碼直接鏈接
* so define the start symbols here just to
* force ld quiet. These are not referred anyway.
*/
.globl start, _start
start:
_start:
#endif /* ! STAGE1_5 */
ENTRY(main) //stage2的入口
/*
* 確保"main"加載在0x0:0x8200(stage2)或者加載在0x0:0x2200(stage1.5).
* 如果不是,下面的跳轉指令會跑飛。
*/
ljmp $0, $ABS(codestart)
// /* control byte
// * Currently only bit 0 is used.
// * bit 0 disable the "unconditional command-line entrance" feature
// */
//
// . = EXT_C(main) + 0x5
// .byte 0
/*
* 兼容性版本號
*
* 它們必須在可執行鏡像字節偏移等於6和7的位置中
* 不要移動它!!!
*/
. = EXT_C(main) + 0x6
.byte COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR
/*
* 以上是一塊8字節的特殊區域。
*/
. = EXT_C(main) + 0x8
VARIABLE(install_partition)
.long 0xFFFFFF
/* 因爲是歷史原因這個變量放在這裏,沒有其他用處*/
VARIABLE(saved_entryno)
#if defined(STAGE1_5) /* || ! defined(PRESET_MENU_STRING) */
.long 0
#else
/* Note: GRUB4DOS 使用這個作爲命令行“preset_menu”.
* 用GRUB.EXE的命令行可以嵌入preset_menu.
* 一個新的preset_menu可以覆蓋內置的preset_menu.
* 如果未改變該變量,並且config_file的第一個字節爲0,
* 那麼位於0x0800的新菜單將起作用。
* 如果變量被設置爲0, 或者config_file的第一個字節不爲0
* 那麼將使用內置的preset_menu.
*
* 不要將這個變量的值設置爲非0的其他值.
*/
.long preset_menu
#endif
VARIABLE(stage2_id)
.byte STAGE2_ID
VARIABLE(force_lba)
.byte 0
VARIABLE(version_string)
.string VERSION
VARIABLE(config_file)
#ifndef STAGE1_5
.string "/boot/grub/menu.lst"
#else /* STAGE1_5 */
.long 0xffffffff
.string "/boot/grub/stage2"
#endif /* STAGE1_5 */
/*
* 爲config文件名騰出一些空間
*/
. = EXT_C(main) + 0x6C #; bss開始地址,BSS段通常是指用來存放程序中未初始化的或者初始化爲0的全局變量和靜態變量的一塊內存區域。
#ifndef STAGE1_5
#if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
//.word (__bss_start - main) & 0x0F, (__bss_start - main) >> 4
.long __bss_start
#elif defined(HAVE_USCORE_EDATA_SYMBOL)
//.word (_edata - main) & 0x0F, (_edata - main) >> 4
.long _edata
#elif defined(HAVE_EDATA_SYMBOL)
//.word (edata - main) & 0x0F, (edata - main) >> 4
.long edata
#else
#error no bss starting address
#endif
#endif
. = EXT_C(main) + 0x70
/* 這裏繼續實模式的代碼 */
codestart:
cli /* 禁止軟中斷 */
/* %ds = %ss = %es = 0, esp = 2000h 注意這裏重置了棧*/
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movl $STACKOFF, %esp
#ifndef STAGE1_5
movb 0x0410, %al
/* floppies_orig = *(byte*)0x0410 */
movb %al, ABS(floppies_orig)
movb 0x0475, %al
/* harddrives_orig = *(byte*)0x0475 */
movb %al, ABS(harddrives_orig)
//movb $((int13_handler_end - int13_handler + 0x3ff) / 0x400), ABS(int13_handler)
/* BIOS在程序最開始的位置,即0x00000,用1K的內存空間(0x00000~0x003FF)構建中斷向量表,
並在緊挨他的位置用256字節的內存空間構建BIOS數據區(0x00400~0x004FF),在大約56K的位置(0x0E2CE)
加載了8K左右的與中斷向量表相應的若干中斷服務程序。中斷向量表中有256箇中斷向量,
每個中斷向量佔有4個字節空間,其中2個字節是CS的值,2個是IP的值,
每個中斷向量都指向一個具體的中斷服務程序。*/
movl 0x4C, %eax
/* int13中斷向量表偏移 = 0x13 * 4 = 0x4C;
這裏將這個值保存到int13_offset和(int13_offset + 0x113 - 0x1C)的地方
等同於int13_offset = *(DWORD*)0x4c
*/
movl %eax, ABS(int13_offset)
movl %eax, ABS(int13_offset) + 0x113 - 0x1C
cmpl $0xC0000000, %eax
jnb 1f
cmpl $0x9A000000, %eax
jb 1f
/* 取中斷向量的低22位,該值(其中低16位是offset)必須等於0x100 */
andl $0x3FFFFF, %eax
cmpl $0x100, %eax
jnz 1f
/* 在0x413處存放的是RAM剩餘大小,以KB爲單位 */
movw 0x413, %ax
shlw $6, %ax
cmpw 0x4E, %ax /* (4E = 0x13 * 4 + 2) 中斷向量的高2字節(CS) */
jne 1f
movw %ax, %ds /* DS=old int13 code segment */
movl 0x1C, %eax /* ROM int 13 vector */
cmpl $0x9A000000, %eax
jb 2f
movl 0x0C, %eax /* ROM int 15 vector */
cmpl $0x9A000000, %eax
jb 2f
/* restore old emu data, except the first byte of handler size. */
movw $(0x140 - 1), %cx
movw $1, %si
movw $ABS(int13_handler + 1), %di
cld
repz movsb
///* restore old int13_dup */
//movl (int13_dup - int13_handler), %eax
//movl %eax, %es:ABS(int13_dup)
///* restore old safe_mbr_hook */
//movl (safe_mbr_hook - int13_handler), %eax
//movl %eax, %es:ABS(safe_mbr_hook)
2:
xorw %ax, %ax
movw %ax, %ds /* DS=0 */
1:
/*ROM_int15 默認爲0,不確定會不會在寫入扇區的時候被改寫*/
movl ABS(ROM_int15), %eax
cmpl $0x9A000000, %eax
jnb 1f
/* 變量ROM_int15的值 = INT15在中斷向量表中的偏移 = 0x15*4 = 0x0054*/
movl 0x0054, %eax
movl %eax, ABS(ROM_int15) /* ROM_int15 = *(DWORD*)0x0054 */
1:
/* 檢查BIOS類型 (當前僅檢查Bochs) ,檢查從0xFF00開始的字符是否爲"(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team.\0"*/
movw $0xF000, %ax
movw %ax, %es /* ES=0xF000 */
movw $0xFF00, %di
movw $ABS(bochs_copygrght_string), %si
movw $0x22, %cx
repz cmpsw
setz ABS(bios_id) /* 爲bochs時設置bios_id=1, 否則爲0. */
xorw %ax, %ax
movw %ax, %es /* ES=0 */
#endif /* STAGE1_5 */
#ifndef SUPPORT_DISKLESS
/*
* Save the sector number of the second sector (i.e. this sector)
* in INSTALL_SECOND_SECTOR. See also "stage2/start.S".
*/
ADDR32 movl %ebp, EXT_C(install_second_sector)
#endif
///* set up the real mode/BIOS stack */
//movl $STACKOFF, %ebp
//movl %ebp, %esp
/* for usb keyboard hack here, disable interrupt */
//sti /* we're safe again */
/* 保存DX */
pushw %dx
/*設置ES = DS = AX = 0*/
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
/* 禁用虛擬磁盤服務(clear VDS),配置在內存地址0x47B中。
0xd7 = 1101 0111(b),即置零第3和第5位 */
andb $0xD7, 0x47B
/* 恢復DX */
popw %dx
cli
#ifndef SUPPORT_DISKLESS
/* save boot drive reference */
ADDR32 movb %dl, EXT_C(boot_drive)
movw $ABS(reset_disk_string),%si
call print_message /* will not change DX */
xorw %ax, %ax
/* 重啓磁盤系統 INT 13 (%ah = 0)
INT 13,0 - Reset Disk System
AH = 00
DL = drive number (0=A:, 1=2nd floppy, 80h=drive 0, 81h=drive 1)
on return:
AH = disk operation status (see INT 13,STATUS)
CF = 0 if successful
= 1 if error
- clears reset flag in controller and pulls heads to track 0
- setting the controller reset flag causes the disk to recalibrate
on the next disk operation
- if bit 7 is set, the diskette drive indicated by the lower 7 bits
will reset then the hard disk will follow; return code in AH is
for the drive requested
*/
#ifdef STAGE1_5
int $0x13
#else
/* safe_int13會重置INT0~7的中斷向量表,當程序發生故障時
將調用我們設定的異常處理程序,調用返回前恢復原有的值*/
call safe_int13
#endif
movw $ABS(reset_disk_failure_string),%si
jc 1f
movw $ABS(reset_disk_success_string),%si
1:
call print_message /* 打印Failure或者Success,DX不會發生變化 */
#endif
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
#ifndef STAGE1_5
movl $1, %ebx
/* BIOS的地址0x5FC中保存了DUCE標識 */
cmpl $0x45435544, 0x5FC
jz 1f
/* 檢查用戶是否輸入了'c' */
pushl %ebx
movb $0x01, %ah
/*INT16/1用來查詢鍵盤緩衝區,對鍵盤掃描但不等待,並設置ZF標誌。
若有按鍵操作(即鍵盤緩衝區不空),則ZF=0,AL中存放的是
輸入的ASCII碼,AH中存放輸入字符的擴展碼。若無鍵按下,則
標誌位ZF=1。*/
int $0x16
popl %ebx
jz 1f /* 沒有按鍵 */
orb $0x20, %al
cmpb $0x63, %al
jne 1f /* 按鍵不是'c' */
/* 如果用戶按下了C鍵,則跳過配置文件中後續的啓動 */
movl $0, %ebx
1:
#endif
/* 從實模式切換到保護模式 */
DATA32 call EXT_C(real_to_prot)
/* ".code32" 讓GAS編譯32位代碼. */
.code32
#ifndef STAGE1_5
/* 如果用戶按下了C鍵則EBX=0,此時需要設置use_config_file = 0 */
testl %ebx, %ebx
jnz 1f
movl %ebx, EXT_C(use_config_file)
1:
#endif
/* clean out the bss */
/* 讓%edi等於BSS的開始地址,BSS的介紹在前面 */
#if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
movl $__bss_start, %edi
#elif defined(HAVE_USCORE_EDATA_SYMBOL)
movl $_edata, %edi
#elif defined(HAVE_EDATA_SYMBOL)
movl $edata, %edi
#else
#error no bss starting address
#endif
/* 讓%ecx等於BSS的結束地址 */
#if defined(HAVE_END_SYMBOL)
movl $end, %ecx
#elif defined(HAVE_USCORE_END_SYMBOL)
movl $_end, %ecx
#else
#error no bss ending address
#endif
/* 計算BSS的長度,保存在ECX */
subl %edi, %ecx
/* %al = 0 */
xorb %al, %al
/* set the direction */
cld
/* BSS的內容全部置零 */
rep
stosb
/* 如果config_file == 0(用戶按下了C時)不做任何事情。
否則將EBX設置爲preset_menu。詳見前面preset_menu變量處的介紹。 */
//movb config_file, %al
//testb %al, %al
cmpb %al, config_file /* 注:此時AL == 0 */
jnz 1f
movl saved_entryno, %ebx /* 檢查saved_entryno,這個值默認爲0*/
testl %ebx, %ebx
jz 1f
movl $0x0800, (%ebx) /* 採用位於0x0800處的內置菜單 */
1:
/*
* 調用C代碼入口init_bios_info函數, 它在調用cmain前會做一些初始化工作。
*/
call EXT_C(init_bios_info)
這個過程中調用的其他函數
safe_int13:
.code16
/* 設置我們的故障恢復處理程序(重寫INT 0~7中斷向量表) */
call set_fault_recovery_handler
/* 備份寄存器的值到original_registers. Note: CS=0 */
movw %ds, %cs:ABS(original_registers)
movw %es, %cs:ABS(original_registers) + 4
movw %ss, %cs:ABS(original_registers) + 8
movl %esp, %cs:ABS(original_registers) + 12
movl %eax, %cs:ABS(original_registers) + 16
movl %ebx, %cs:ABS(original_registers) + 20
movl %ecx, %cs:ABS(original_registers) + 24
movl %edx, %cs:ABS(original_registers) + 28
movl %esi, %cs:ABS(original_registers) + 32
movl %edi, %cs:ABS(original_registers) + 36
movl %ebp, %cs:ABS(original_registers) + 40
pushw %bp
pushw %ax
movw %sp, %bp
movw (%bp), %ax
movw %ax, %cs:ABS(original_registers) + 44 #; return IP
popw %ax
popw %bp
int $0x13
/* 從備份中恢復故障恢復程序(中斷向量表INT 0~7)*/
call unset_fault_recovery_handler
ret
/* 替換INT0~INT7
* CPU exceptions 0 - 7
* 0 Divide
* 1 Debug
* 2 NMI
* 3 Break point
* 4 Overflow
* 5 Bound
* 6 Invalid Instruction
* 7 no coprocessor
* 保存INT00到INT07的中斷向量到int_00_07_vectors,INT 0-7的中斷向量保存在地址爲0000 - 0020的32個字節中。
* 並設置新的中斷向量表,將fault_recovery_handler開始地址存放的32個字節複製到0000 - 0020
*/
set_fault_recovery_handler:
.code16
pushfw
pushw %ds
pushw %es
pushaw
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
xorw %si, %si
movw $ABS(int_00_07_vectors), %di
movw $16, %cx
cld
repz movsw /*拷貝從0000:0000開始的32個字節到int_00_07_vectors*/
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
xorw %si, %si
movw $ABS(int_00_07_vectors), %di
pushl %eax
xorw %di, %di
movl $ABS(fault_recovery_handler), %eax /* 0000:fault_recovery_handler */
movw $8, %cx
cld
/*stosl將EAX中的值保存到ES:EDI指向的地址中*/
repz stosl
popl %eax
popaw
popw %es
popw %ds
popfw
ret
/* 恢復中斷向量表的前32個字節,從int_00_07_vectors拷貝32個字節到0000 - 0020 */
unset_fault_recovery_handler:
.code16
pushfw
pushw %ds
pushw %es
pushaw
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
xorw %di, %di
movw $ABS(int_00_07_vectors), %si
movw $16, %cx
cld
repz movsw
popaw
popw %es
popw %ds
popfw
ret