顯示具有 GRUB Programming tracing 標籤的文章。 顯示所有文章
顯示具有 GRUB Programming tracing 標籤的文章。 顯示所有文章

2011年10月6日 星期四

如何使用Bochs來Debug GRUB2 & Kernel

参考以下文章:
http://www.linuxsir.org/bbs/thread356982.html
http://man.he.net/man8/grub-setup
http://blog.csdn.net/zhouyelihua/article/details/6683437
http://www.gnu.org/software/grub/manual/grub.html#Installing-GRUB-using-grub_002dinstall
http://read.pudn.com/downloads153/ebook/675818/linux-0.11-040304/bochsrc-hd-new.bxrc__.htm

由於bochs內建調試功能, 且支援gdb, 用它調試內核會很方便.
假設你已經build好grub,這裡我舉的例子是使用grub1.99,若還沒有Build,現在正是時候:
1 ./configure CFLAGS=-g
2 make
3 sudo make install 
在ubuntu下預設是安裝到usr/local/lib/grub/i386-pc,別擔心會影響到您的開機MBR,因為make install只是將一些GRUB的各個executable Binary及mod模組存放到指定的prefix,除非你執行grub-install和update-grub 等指令才有可能去動到MBR。
以下指令請使用Root身份執行
1.1構建磁碟影像檔dd if=/dev/zero of=hd0.img count=$((63*16*100))
用這個命令可以構建一個50MB左右的磁片鏡像, 輸出結果如下:
100800+0 records in
100800+0 records out
51609600 bytes (52 MB) copied, 0.734578 s, 70.3 MB/s
注意count必須為63*16的倍數, 否則bochs識別硬碟會有問題.
1.2 掛載磁碟影像losetup /dev/loop0 hd0.img
這個命令可以將檔綁定到一個loop設備. 如果/dev/loop0不存在, 可以嘗試 modprobe loop.
然後進行設備初始化:cfdisk -s 63 -h 16 /dev/loop0
畫面如下橘色文字:
                         cfdisk (util-linux-ng 2.17.2)

                               硬碟: /dev/loop0
                      大小: 51609600 位元組(B), 51 MB
           磁頭: 16   每一磁軌的磁區數: 63   磁柱數: 100

    分割區名稱  屬性旗標   分割區型態 檔案系統型態     標記名稱       大小 (MB)
 ------------------------------------------------------------------------------
                            主要/邏輯 剩餘空間                            51.61

     [  說明  ]    [  新增  ]    [  列印  ]    [  離開  ]    [  單位  ]
     [  寫入  ]

                    由剩餘空間切一個新的分割區

只創建一個主要磁碟分割就可以,並設定 [使可開機] , 寫入後, 用命令fdisk檢查結果。
指令:fdisk -lu /dev/loop0 結果畫面如下:當然Linux版本不同,畫面難免大同小異
磁碟 /dev/loop0: 51 MB,51609600 位元組
16 磁頭,63 磁區/磁軌,100 磁柱,總計 100800 磁區
單位 = 磁區 之於 1 * 512 = 512 位元組
磁區大小 (邏輯/實體):512 位元組 / 512 位元組
I/O 大小 (最小/最佳化):512 位元組 / 512 位元組
磁碟識別碼:0x00000000

所用裝置 開機      開始         結束      區塊   識別號  系統
/dev/loop0p1   *          63      100799       50368+  83  Linux
接著將分區1掛載到/dev/loop1:losetup /dev/loop1 hd0.img -o $((63*512))
格式化/dev/loop1ext3格式.:mkfs.ext3 /dev/loop1
mnt下創建img目錄, 做以後維護用:mkdir -p /mnt/img
loop1掛載到/mnt/img:mount /dev/loop1 /mnt/img/
安裝引導程式. 因為我狂熱傾向於模組化架構, 所以選擇GRUB2.
mkdir /mnt/img/boot
cp -r /usr/lib/grub/i386-pc/ /mnt/img/boot/grub
這個/usr/lib/grub/i386-pc是你的GRUB2安裝路徑,像我的1.98及1.99版在ubuntu下是安裝在以下路徑:/usr/local/lib/grub/i386-pc
接著生成一個core.img, biosdisk模組負責讀取磁片, part_msdos模組負責處理MBR,ext2模組負責讀取ext3分區:
cd /mnt/img/boot/grub/
grub-mkimage -o core.img biosdisk part_msdos ext2   #1.98版使用這個指令
grub-mkimage -O i386-pc -o core.img biosdisk part_msdos ext2  #1.99版使用這個指令
注意,這個core.img還不是個可開機用的影像檔。它並未包含boot.img及diskboot.img。
接著安裝grub2(hd0), 根目錄在(hd0,1)
echo "(hd0)   /dev/loop0" > ./device.map
grub-setup -m ./device.map -d /mnt/img/boot/grub/ -r '(hd0,1)' '(hd0)'
其實上面這2行也可以用下面一行取代
grub-setup -d /mnt/img/boot/grub/ --force /dev/loop0
檢查一下/dev/loop0安裝成果:
hexdump -C /dev/loop0 | less
00000180  7d e8 2e 00 cd 18 eb fe  47 52 55 42 20 00 47 65  |}.......GRUB .Ge|
00000190  6f 6d 00 48 61 72 64 20  44 69 73 6b 00 52 65 61  |om.Hard Disk.Rea|
清理一下
離開終端,重新開一個終端,否則umount會失敗,若你不是以root執行的話
umount /mnt/img
losetup -d /dev/loop1
losetup -d /dev/loop0
2.1建立.bochsrc檔如下:說明一下,在linux下預設是讀取.bochsrc這個檔名,所以你若創建一個叫bochs.bxrc或其他檔名,則等一下開啟bochs時,指令如下:
bochs -q -f bochs.bxrc,否則只須下bochs -q
###############################################################
# bochsrc.bxrc file for kenos disk image.
###############################################################
megs: 32
romimage: file=/usr/share/bochs/BIOS-bochs-legacy, address=0xf0000
vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest
# hard disk
ata0-master: type=disk, mode=flat, path="/home/benson/working_benson/DBG_grub/hd0.img", cylinders=100, heads=16, spt=63
#ata0-slave: type=cdrom, path="/home/marc/setups/slax-6.0.9.iso", status=inserted
# choose the boot disk.
boot: c
# where do we send log messages?
log: bochsout.txt
# disable the mouse, since DLX is text only
mouse: enabled=0
keyboard_mapping: enabled=1, map=/usr/share/bochs/keymaps/x11-pc-us.map
#keyboard_mapping: enabled=1, map=$BXSHARE/keymaps/x11-pc-fr.map
#keyboard_mapping: enabled=1, map=$BXSHARE/keymaps/x11-pc-de.map
#keyboard_mapping: enabled=1, map=$BXSHARE/keymaps/x11-pc-es.map
#cpu: count=1, ips=10000000
# Make the PIT emulated by bochs tick at the correct interval. Note that
# enabling this feature sacrifices reproducibility, which may be a problem
# at times when tracking a specific bug.
#clock: sync=realtime, time0=utc
# For debugging with gdb.
# Keep this line when using bochs-gdb.
# Comment it out when using bochs-dbg
# See http://bochs.sourceforge.net/doc/docbook/user/bochsrc.html#BOCHSOPT-GDBSTUB
#gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0
使用bochs開啟hd0.img
bochs -f bochsrc.bxrc 雖然會顯示unknown filesystem
















解決辦法:
grub rescue>set prefix=(hd0,1)/boot/grub
grub rescue>root=(hd0,1)
grub rescue>insmod normal
grub rescue>normal
這樣就可以回到grub> 或選單狀態了。
此時也可以輸入help及其他command line指令了。
接著我們來試試如何使用gdb來debug GRUB2:
先在bochsrc.bxrc最後面加入幾行來使gdb connect to Bochs:

# Attach ne2000 to PCI bus, so that GRUB Legacy detects it automatically

i440fxsupport: enabled=1, slot1=ne2k
# Enable the ne2000 NIC and the builtin TFTP server
ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=vnet, ethdev="/"
# Enable the GDB remote stub
gdbstub: enabled=1

現在先開啟另一個終端輸入gdb,並重新啟動bochs -qf bochsrc.bxrc指令
gdb會出現如下畫面:藍色字為輸入指令
GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x0000fff0 in ?? ()
(gdb)file kernel.exec
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from /home/benson/kernel.exec...done.

(gdb) b
No default breakpoint address now.
(gdb) b main
Breakpoint 1 at 0xc4fd: file kern/main.c, line 175.
(gdb) c
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
0x0000e05c in ?? ()
(gdb) c
Continuing.
Breakpoint 1, grub_main () at kern/main.c:175
warning: Source file is more recent than executable.
175
(gdb) list
170 void
171 grub_main (void)
172 {
173  /* First of all, initialize the machine.  */
174  grub_machine_init ();
175
176  /* Hello.  */
177  grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
178  grub_printf ("Welcome to GRUB!Benson Debug Here!\n\n");
179  grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
(gdb) 
執行過程如下圖所示:














這張圖是修改過source code,所以下一張圖會多出Benson Debug Here!字樣。















若是你的gdb無法connect到bochs,可能你須要重新build一次Bochs,動作如下:
註:--enable-gdb-stub和--enable-debugger是互斥的
1 ./configure --enable-gdb-stub
2 make
3 sudo make install
update.....

2010年10月25日 星期一

GRUB FOR DOS 的總體框架

文章出處:http://www.linuxeden.com/blog/?uid-19847-action-viewspace-itemid-5490
GRUB FOR DOS 的總體框架
bendany 建議我寫一寫 GRUB4DOS 的大致結構,以便準備投入開發 GRUB4DOS 的朋友們能夠對此有一個快速的瞭解。我試試吧,就寫了這篇短文。文章有什麼不妥的,或者需要補充的,希望大家提出來,我再加以完善。幾年前寫過兩三篇 GRUB4DOS 的技術文章,雖然某些內容有些陳舊,但似乎也還可以用。有需要的朋友,可以在各大搜索引擎中搜索grub for dos 或者 grub.exe 或者 grldr 等字眼,應該能夠找到那些文章。這次不重複那些文章的內容了,主要面向準備繼續開發 Grub4dos 的朋友們。
[@more@]
  GRUB for DOS 是 GNU GRUB 的功能擴展,是以 GNU GRUB 為基礎的。有關 GNU GRUB 的技術資訊,可以由 Linux 下的 info grub 命令獲得。這裏主要說說 GRUB for DOS 的特有資訊,並且只是粗略的、輪廓性的介紹。詳細的資訊還是要通過源代碼才能得到。在關鍵的地方,源代碼中都有比較詳細的注釋。
1。啟動過程,啟動方式
  GNU GRUB 的啟動過程大致是這樣的:512 位元組的 stage1,放在 MBR 或者軟碟第一磁區,它從 BIOS 那裏獲得控制,然後它負責查找 stage1.5 或者 stage2。其實,stage1 在被放置到 MBR 或者軟碟第一磁區的時候,已經把 stage1.5 或者 stage2 的物理磁區位置記錄到 stage1 的某個區域中了,這通常稱為硬編碼。這些都不重要,重要的是,stage1 和 stage1.5 都不是 GRUB 的主體,它們都是為了把 stage2 裝入到記憶體中,除此之外,它們沒有別的用處。
  stage2 又分兩部分:第一部分是 512 位元組的開頭,它像 stage1 一樣,裏面含有 stage2 的物理磁區位置列表,整個磁區沒有別的用處,僅僅只是為了尋找 stage2 的後續部分(也就是從第二磁區開始一直到檔結尾)。這第二部分才是 GRUB 的主程序體,它叫做 pre_stage2。也就是說,stage2 = 512 位元組的頭部 + pre_stage2
stage1 和 stage2 中都含有磁區絕對定位資訊,這些資訊,是在將 GNU GRUB 安裝到 MBR 或者軟碟第一磁區時,由 setup 命令計算之後寫入的。
  說了這麼多,其實只是為了說明一點:pre_stage2 才是對我們的 GRUB FOR DOS 有用的。所以,你在程式中會看到 pre_stage2_start 這個標號。
  GRUB FOR DOS 主要有兩大類啟動方式。一類是從作業系統中啟動(grub.exe),一類是由 BIOS 引導啟動(GRLDR)。
  GRLDR 也可以由 NTLDR 啟動,不過這其實也類似於從 BIOS 啟動,因為由 NTLDR 所賦予的啟動環境,沒有破壞 BIOS 中斷向量。
  GRUB.EXE 可以從 DOS/Windows9x 下運行,也可以經由 kexec 而從 Linux 下運行,或者也可以經由 LILO 或者 syslinux 這類引導器而啟動運行。這又分為兩種情況:在 DOS/Windows9x 下,GRUB.EXE 是被作為 DOS 的可執行檔或者可以作為 CONFIG.SYS 裏的設備驅動檔而運行。在其他情況下,GRUB.EXE 是作為 LINUX 內核格式而被啟動運行的。
2。檔結構
  GRLDR 的檔結構相對來說比較單一,源代碼文件是 grldrstart.S。它的第一磁區,是啟動代碼,如果這是在 MBR 上,它負責裝入位於硬碟第一磁軌上的其餘磁區。如果是經由 NTLDR 啟動,那麼 NTLDR 會裝入 GRLDR 的開頭 16 個磁區。這些磁區中的代碼,負責查找各個硬碟各個分區根目錄下的 grldr 檔,並裝載整個 grldr 檔到記憶體中。緊接著 GRLDR 開頭的 16 磁區,就是 pre_stage2 了。GRLDR 開頭的 16 磁區代碼,負責把 pre_stage2 放到 0000:8200 處,並把控制交給 pre_stage2。在 GNU GRUB 中,0000:8000 處放置的是 stage2,由於我們不用 stage2 的第一磁區,所以,我們直接把 pre_stage2 放置在 0000:8200 了。
  bootlace.com 可以用來將 grldr 的啟動代碼寫入到 MBR 或者軟碟的第一磁區上。其實,寫入到 MBR 上的,不完全就是 GRLDR 開頭的 16 磁區,而是 GRLDR.MBR 檔的內容(只有第一磁區的若干個控制位元組會被改動,以及硬碟分區表會被加入到第一磁區,而第二磁區可以放置硬碟上原來的 MBR 作為備份,其餘磁區都原封不動地被拷貝到第一磁軌的相應磁區上)。bootlace.com 是雙重可執行檔格式:它可以在 DOS 下運行(作為 .com 可執行檔),也可以在 Linux 下運行(作為 ELF 可執行檔)。GRLDR.MBR 檔將來可以增大到 63 個磁區那麼長,這是一個磁軌長度的最大上限。但是 GRLDR 檔的開頭部分卻只能有 16 個磁區那麼長,因為 NTLDR 只能裝入這麼長,它不能為我們直接裝入全部的 GRLDR 檔。GRLDR.MBR 的源代碼文件是 mbrstart.S。bootlace.com 的源代碼文件是 bootlacestart.S。
  GRUB.EXE 是三重檔格式。它的結構比較複雜。首次看源代碼的時候,可以先忽略它作為 DOS 設備驅動程式檔格式的那些代碼,因為那可能比較難以理解。而作為 DOS 的 EXE 格式以及作為 LINUX 的 bzImage 格式,都是比較容易理解的,因為這方面的資料很容易找到。源代碼文件是 dosstart.S。像 GRLDR 的情況那樣,GRUB.EXE 也是由開頭的啟動代碼以及 pre_stage2 這兩部分構成的。
  pre_stage2 的源代碼文件是 asm.S,它會調用其他 C 語言程式檔。這些 C 檔和 asm.S 一起編譯生成 pre_stage2 檔。builtins.c 檔含有各種命令的代碼,包括 map 命令。如果要為 GRUB 增添拷貝檔或者創建檔的命令,則需要修改 fsys_*.c 檔,它們是各種檔系統的驅動程式檔。
3。記憶體的使用
在物理位址 0x800 到 0x17FF 這 4K,放置嵌入到 GRUB.EXE 命令行的那些命令。只有從 CONFIG.SYS 中用 device=grub.exe --config-file="GRUB_COMMANDS" 的方式,才可以嵌入接近 4K 的 GRUB 命令,其他方式都受具體環境的限制。比如,DOS 命令行不超過 127 個字元。GRUB.EXE 作為 Linux 內核格式,它也只能接受最多 512 位元組的命令行。
  物理位址 2M 處放置備份的 640K 的 DOS 常規記憶體,以便 quit 命令可以恢復 DOS 的現場。

從啟動到run grub

文章出處:http://bbs.tongji.net/thread-258437-1-1.html
從啟動到run grub
下面借用了 劉 吟(劉老大) OS啟動的第一步的Ppt
Os 啟動第一步
1.Bios
Cpu 的初始化
► 主機加電後,啟動時鐘發生器,在匯流排上產生POWERGOOD信號,CPU收到RESET信號,進入初始化過程
CPU轉入8086實模式。
DS = ES = FS = GS = SS = 0
CS = 0xFFFF
IP = 0XFFF0
進入BIOS加電自檢過程(Power On Self Test)
BIOS初始化
關中斷,進行所有的POST檢測
將中斷向量表的起始位址設為0x0000H
0x0000H~0x03FFH中存放了256個中斷
建立了實模式下的中斷向量表
BIOS的啟動程式調用INT 19h中斷
將控制權轉移給Boot Loader
► INT 19h中斷的功能
INT 19h按照BIOS中的啟動設備順序查詢每個啟動設備在軟碟的啟動磁區或者硬碟的MBR中有Boot Loader,那麼這個磁區的最後兩個位元組必然為0xAA55。BIOS將這個磁區(512個位元組)讀入記憶體的0000:7C00開始的位置,然後跳轉到記憶體0000:7C00的地方開始執行。如果在所有的啟動設備上都找不到Boot Loader,那麼就調用INT 18h,將控制權交給BIOS ROM Basic,鎖定機器,並且在螢幕上顯示NO BOOT DEVICE AVAILABLE
► 結論
Boot Loader應當存放在啟動設備的第一個磁區中,對於硬碟是MBR,對於軟碟是啟動磁區安裝了Boot Loader的啟動磁區的最後兩個位元組必須為0xAA55 BIOS在POST過程結束以後,調用INT 19h中斷,將Boot Loader讀入記憶體0000:7C00處。然後釋放控制權,跳轉到0000:7C00開始執行Boot Loader的代碼。
2.最簡單的Boot Loader:FDISK /MBR
硬碟的分區表結構
a) 第一個磁區為MBR
b) 一個硬碟上最多有4個主分區
c) 第一個主分區一般從cylinder 0, head 1, sector 1開始
d) cylinder 0, head 0, sector 2~n一般來說保留不用
e) 其餘的主分區一般從cylinder x, head 0, sector 1開始

Master Boot Record
f) 位於硬碟的cylinder 0, head 0, sector 1的位置
g) 大小為512位元組
h) 其中存放了4個主分區的入口,每個入口占16個位元組(這就是為什麼一個磁片最多只能有4個主分區的原因)
i) 最後兩位元為啟動標誌,如果MBR中有Boot Loader的話,則為0xAA55
j) 留給Boot Loader的空間為512-16x4-2=446位元組
MBR中的Boot Loader的功能
k) 初始化,將自身搬移到0000:0600的位置
l) 在主分區入口表中尋找活動的分區
m) 調用INT 13h AH=02將活動的分區的啟動磁區讀入記憶體0000:7C00處
n) 跳轉到0000:7C00處執行活動分區的啟動磁區中的代碼
初始化,將自身搬移到0000:0600的位置
0000:7C00 CLI 關中斷
0000:7C01 XOR AX,AX
0000:7C03 MOV SS,AX 將堆疊段(SS)設為0
0000:7C05 MOV SP,7C00 將棧頂指針(SP)設置為7C00
0000:7C08 MOV SI,SP 將SI也設為7C00
0000:7C0A PUSH AX
0000:7C0B POP ES 將ES設為0000
0000:7C0C PUSH AX
0000:7C0D POP DS 將DS設為0000
0000:7C0E STI 開中斷
0000:7C0F CLD
0000:7C10 MOV DI,0600 將DI設為0600
0000:7C13 MOV CX,0100 準備移動256個字(512位元組)
0000:7C16 REPNZ
0000:7C17 MOVSW 將MBR從0000:7c00移動到0000:0600
0000:7C18 JMP 0000:061D 開始搜索主分區入口表
► 在主分區入口表中尋找活動的分區

0000:061D MOV SI,07BE SI指向分區表入口(在總共512byte的主引導記錄中,MBR的引導程式占了其中的前446個位元組(偏移0H~偏移1BDH),隨後的64個位元組(偏移1BEH~偏移1FDH)為DPT(Disk PartitionTable,硬碟分區表),最後的兩個位元組“55 AA”(偏移1FEH~偏移1FFH)是分區有效結束標誌)
0000:0620 MOV BL,04 一共有4個表項
0000:0622 CMP BYTE PTR [SI],80 是否為活動分區
0000:0625 JZ FOUND_ACTIVE 找到了一個活動表項
0000:0627 CMP BYTE PTR [SI],00 是否為非活動分區
0000:062A JNZ NOT_ACTIVE 不可識別的分區標識
0000:062C ADD SI,+10 指向下一個表項(+16)
0000:062F DEC BL 迴圈標誌減一
0000:0631 JNZ 0000:061D 繼續迴圈
0000:0633 INT 18 未找到可啟動的分區,轉到ROM Basic

► 調用INT13 AH = 02h讀取啟動磁區

0000:0635 MOV DX,[SI] 設置INT 13調用中Head和Driver的值
0000:0637 MOV CX,[SI+02] 設置INT 13調用中Cylinder的值
0000:063A MOV BP,SI 保存活動分區入口表項位址

0000:065D MOV DI,0005 設置讀取的重試次數
0000:0660 MOV BX,7C00 將啟動磁區讀取到0000:7C00(ES:BX)
0000:0663 MOV AX,0201 準備調用INT 13讀取一個磁區AL = 01
0000:0666 PUSH DI 保存重試次數DI
0000:0667 INT 13 調用INT 13讀取一個磁區到0000:7c00
0000:0669 POP DI 恢復重試次數DI
0000:066A JNB INT13OK 如果讀取成功,則跳轉至啟動代碼
0000:066C XOR AX,AX 準備INT 13 AH = 0重定磁片
0000:066E INT 13 調用INT 13重定磁片
0000:0670 DEC DI 重試次數減一
0000:0671 JNZ 0000:0660 進行下一次重試

► 運行啟動磁區中的代碼

0000:067B MOV DI,7DFE 指向啟動磁區中的啟動標識
0000:067E CMP WORD PTR [DI],AA55 檢查啟動表識是否為0xAA55
0000:0682 JNZ DISPLAY_MSG 如果不是,則保錯
0000:0684 MOV SI,BP 恢復SI,指向活動分區入口表項
0000:0686 JMP 0000:7C00 跳轉至啟動磁區的代碼

► 結論
Boot Loader放在可啟動設備的第一個磁區中Boot Loader的大小受磁區大小和其他附加資訊的限制。在MBR中,為446位元組Boot Loader在從BIOS中接手CPU的控制權時,位於記憶體位址0000:7C00處FDISK /MBR產生的Boot Loader不具備啟動OS的能力,其本質上是一個Chain Loader,用於引導一個有啟動OS能力的Boot Loader。
3.如何引導OS:DOS Boot Loader
► Boot Sector的結構
Boot Sector位於每個分區的第一個磁區,Boot Sector的第一個部分是一個跳轉指令和一個NOP,以跳轉實際的Boot Loader的代碼中,BIOS Parameter Block中存放了和這個分區相關的一系列參數BPB之後就是實際的Boot Loader的代碼最後是一個可啟動分區標識0xAA55
► 使用Format /s對Boot Sector做的修改,在0x000h處寫入BPB,在0x03Eh處寫入DOS Boot Loader的代碼,在0x1FEh處寫入0xAA55標識。
► 此外,Format /s還要初始化FAT表,將IO.SYS和MSDOS.SYS寫入,佔據FAT表前兩個表項。
► DOS Boot Loader的功能初始化;計算根目錄所在的FAT表的磁區號讀取根目錄的第一個磁區到0000:0500檢查前兩個表項是否為IO.SYS和MSDOS.SYS將IO.SYS的前3個磁區讀入0000:0700或者0070:0000的位置中在寄存器中保留一些資訊,然後跳轉到0070:0000處執行作業系統代碼DOS Boot Loader和MBR中的Boot Loader的最大區別在於對於檔系統的理解。
► MBR中的Boot Loader不理解檔系統,所以無法啟動特定的OS
► DOS Boot Loader提供了對於FAT檔系統的支援,所以能夠啟動在FAT檔系統上的DOS
► DOS Boot Loader知道OS的內核檔的位置,其主要的工作就是將內核檔讀入記憶體,然後將控制權轉交給OS
4.硬碟定址方式:CHS vs LBA
► 最早期的CHS定址,最早期的磁片定址是通過Cylinder/Head/Sector進行的,INT 13h中給出的CHS參數直接指定了資料的物理位置例:INT 13h AH = 02。
► AH = 02 AL = 讀入的磁區數目
► CH = Cylinder低8位 CL = Cylinder高兩位
► DH = Head DL = 操作的磁片(80h for C:)
 限制:
► Cylinder <= 1024
► Head <= 16
► Sector <= 63
► 總容量 < 1024 x 16 x 63 x 512B = 528MB
► L-CHS & P-CHS;為了解決直接定址的問題,現代的HD中,CHS只有邏輯上的意義,不表達物理上的實際位置。L-CHS用在支援CHS Translation的BOIS的INT 13 AH = 0xh的調用中。
► Cylinder <= 1024
► Head <= 256
► Sector <= 63
► 總容量 <= 1024 x 256 x 63 x 512B = 8GB,P-CHS用在HD的介面上
► Cylinder <= 65535
► Head <= 16
► Sector <= 63
► 總容量 <= 65535 x 16 x 63 x 512B = 136GB
► LBA:Large Block Addressing
► LBA的出現是為了解決CHS模式位址受限的問題
► LBA提供了一種線性的定址方式:Cylinder 0,Head 0,Sector 1相當於LBA地址0。往後每增加一個磁區,LBA位址加一
► LBA位址和CHS位址可以通過如下公式轉換
► LBA = ((cylinder * heads_per_cylinder + heads) * sectors_per_track )+sector-1
► 新型的BOIS提供了INT 13h AH = 4xh的擴展磁片調用以支援LBA模式
► LBA:Large Block Addressing;INT 13h AH = 42h
► AH = 42H DL = 操作的磁片(80h for C:)
► DS:SI = Disk Address Packet;Disk Address Packet的格式
► 結論:定址方式和Boot Loader的關係;BIOS在POST過程以後調用INT 19h,使用的是CHS定址;如果一個OS自身的Boot Loader使用的是CHS定址模式,那麼在老式BIOS上不能啟動528MB以後的分區,在新式BIOS上不能啟動8G以後的分區(LILO的問題);只有支持LBA的Boot Loader才能支持啟動8G以後分區上的OS
5.支持多OS引導的Boot Loader
► 支持多OS引導的Boot Loader是:
 一段在啟動時被BIOS INT 19調用的代碼
 能夠理解系統中安裝的多個不同的OS
 用戶可以選擇啟動特定的OS
 程式根據用戶選擇,通過直接載入或者Chain Load的方法,啟動選中的OS
► 支援多OS引導的Boot Loader需要:
 自身足夠的小,或者支持多階段載入,以能夠安裝在MBR或者Boot Sector中
 能夠理解多種檔系統的格式,以便能夠找到存放在不同檔系統下的系統內核
 能夠理解多種檔格式(ELF/a.out),以便能夠正確的載入不同的系統內核
 支援CHS和LBA兩種磁片訪問模式,以便能夠正確的啟動8G以後的分區
 支持Chain Loader,以便通過特定的Boot Loader載入OS
► 支持多OS引導的Boot Loader在執行32位OS的內核代碼前,需要構建的機器狀態:
 CS必須是一個32位的可讀可執行的代碼段,偏移為0,上限為0xFFFFFFFF
 DS, ES, FS, GS和SS必須為32位的可讀寫段,偏移為0,上限為0xFFFFFFFF
 20號地址線必須在32位位址空間中可用(初始固定為0)
 分頁機制必須被關閉
 處理器中斷標記被關閉
 EAX的值為0x2BADB002
 EBX中存放了一個32位的位址,指向由Boot Loader填充的一系列啟動資訊
引自:Multiboot Specification
6.實例分析:Grub
► Grub是:
 GRand Unified Bootloader的縮寫
 是一個靈活而強大的Boot Loader
 其能夠理解多種不同的檔系統和可執行檔格式,從而能夠引導多種OS
 通過將Boot Loader所需要的功能封裝成一套腳本語言,從而能夠按照特定的方式引導OS
► Grub的I/O
 支援CHS和LBA兩種磁片訪問模式
 (device[,part-num][,bsd-subpart-letter])的方式訪問設備:(hd0), (hd1, 0), (hd0, a), (hd0, 1, a)
 檔訪問
► 通過路徑形式訪問:/boot/grub/menu.lst
► 通過磁區形式訪問:0+1,200+1,300+300
► Grub的腳本:
 root指定一個啟動的設備
 kernel指定作業系統的內核
 boot正式啟動一個OS
 makeactive啟動一個分區
 chainloader調用啟動設備上的Boot Loader
► 啟動Linux
 root (hd0,0)
 kernel /vmlinuz root=/dev/hda1
 boot
► 啟動Windows
 root (hd0,0)
 chainloader +1
 makeactive
 boot
► Grub的組成
 Stage1
► Grub的第一部分,安裝在MBR或者Boot Sector中
► 用於引導Stage2或者Stage1.5
 Stage2
► Grub的核心影像,用於提供Grub的主要功能
 Stage1.5
► Stage1與Stage2之間的橋樑,安裝在0磁軌上第一個磁區之後
► Stage1不理解檔系統,但是Stage1.5可以
► Stage1.5最終調用Stage2
 nbgrub/pxegrub
► Grub的網路啟動模組
► Stage1的結構
 為了保持和FAT/HPFS BIOS的相容性,所以保存BPB
 在BPB之後的Stage1配置資料區,在安裝的時候被填寫
 在資料區之後,才是代碼段
 最後是0xAA55啟動磁區標誌

► Stage1的流程
 在Stage1的配置資料區中存放了Stage2所在的磁片號、LBA位址以及Stage2的載入地址
 Stage1不需要理解任何的檔系統,只需要根據給出的磁區號,讀入Stage2的第一個磁區即可
 Stage1相當於前面分析的MBR中的Boot Loader
► Stage2的第一部分Start.S
 Start.S存放在Stage2檔的第一個磁區裏面
 Stage2剩餘部分的LBA位址和記憶體的載入位址是放在Start.S的firstlist和lastlist之間的,這個資料段位於Start.S代碼的尾部,在安裝的時候被寫入,稱為Block List
 Block List以全0項結尾
► Stage2的第一部分Start.S
 在Start.S的代碼開始執行的時候,DS:SI所指向的記憶體位址的內容是Stage1中準備好的,用於為INT 13h調用準備參數
 Start.S的功能就是根據Block List,將Stage2剩餘的部分讀入記憶體,然後跳轉到0x8200h處執行Stage2的功能代碼
► Stage2的第二部分ASM.S
 在ASM.S中定義了一系列的函數的實現,包括Grub得主入口函數main
 在main函數中,完成了如下的工作:
► DS = ES = SS = 0
► 建立實模式/BIOS棧,esp = 0x2000 - 0x10,向低位址方向增長
► 轉入保護模式
► 建立並清空保護模式棧
► 調用cmain,進入Grub的C代碼中(Stage2.c)
► 在cmain中,完成了如下的工作:
 設法打開/boot/grub/menu.lst這個配置檔
 根據配置檔,構建用戶功能表
 如果功能表構建成功,則調用run_menu
 如果功能表構建失敗,則調用enter_cmdline
► 問題:檔系統
 在這個時候,Grub已經開始訪問檔系統
 Grub如何對付不同的檔系統?
► Grub中的檔系統層:Disk_IO.C
 Grub中為每一個檔系統提供了一個抽象層
 檔系統用fsys_entry描述(filesys.h)
struct fsys_entry
{
char *name;
int (*mount_func) (void);
int (*read_func) (char *buf, int len);
int (*dir_func) (char *dirname);
void (*close_func) (void);
int (*embed_func) (int *start_sector, int needed_sectors);
};
 總體變數fsys_table包含了Grub支援所有檔系統,通過fsys_table和fsys_type,從而可以以統一的方式訪問不同的檔系統
 Grub中的命令處理:buildin
 Grub支持的每個命令,均有一個buildin和其對應
 這些buildin被定義在buildins.c中
 分析以下3個命令:
 chainloader
 kernel
 boot

► chainloader
 檢查--force標記
 調用grub_open打開檔,這裏的檔用block list表示(+1)
 調用grub_read讀入一個磁區到0000:7C00的位置
 檢查啟動磁區標誌0xAA55
► kernel
 檢查--type和--no_mem_option標誌
 調用load_image,讀入指定的內核檔
► load_image中處理了ELF和A.OUT的各種變形
► load_image通過對於內核檔的分析,識別出被啟動的OS的種類(通過內核檔的Magic Number)
► load_image針對不同種類的OS的內核提供了特定的載入代碼(這裏的代碼異常複雜,牽涉到了不同的OS的實現細節,未作分析)
► 最終填充mbi (MultiBoot Information)結構
► boot
 通過執行chainloader或者kernel以後,當前需要啟動的內核的類型已經確定了
 在執行boot的時候,根據確定的內核類型,每一種內核均有一種啟動的方法
► 對於linux,最後調用了stop函數實現控制權的轉移
► 對於chainloader,最後也是調用了stop函數實現控制權的轉移
► boot
 通過執行chainloader或者kernel以後,當前需要啟動的內核的類型已經確定了
 在執行boot的時候,根據確定的內核類型,每一種內核均有一種啟動的方法
► 對於linux,最後調用了stop函數實現控制權的轉移
► 對於chainloader,最後也是調用了stop函數實現控制權的轉移

2010年10月20日 星期三

GRUB2太龐大,所以先轉戰GRUB4DOS

圖一
圖二
下載http://nufans.net/grub4dos/current_release/ 之grub4dos-0.4.4-2009-10-16-src.zip,在我的ubuntu910 下./configure結果產生錯誤如下:
checking whether objcopy works for absolute addresses... no
configure: error: GRUB requires a working absolute objcopy; upgrade your binutils
結果查了一下我的binutils版本已經是最新的。隨後又到其他連結download其它版本的source code,編譯一樣產生相同錯誤。所以本來想放棄,後來想說反正我的virtualbox裡頭還有ubuntu710版本,其gcc也相對比較舊。果不其然原來是binutils太新了,就這樣可以正常編譯成功了。後來我又試了其他版本的binutils 其objcopy版本只要大於GNU objcopy (GNU Binutils for Ubuntu) 2.18.0,則執行./configure就會出錯。
它的使用方法網路上已經很多了在此就不贅述。還是來研究它的source code,比較實際。
首先進入source code目錄,並下make clean及make distclean,接著再作一次./configure並產生Makefile。查看目錄結構如圖一,圖二為其他一起產生的子目錄Makefile;今天我們先說明這個Makefile如何產生的吧!其實這個在之前已經說明過,而且又查獲此處講解autoconf及automake,說明的太詳細了,所以直接參考它就可以了。

2010年4月20日 星期二

GRUB Tracing 2

先列出configure.ac的巨集:截錄自gcc/gdb/make/autotool 文件/教學
AC_INIT(FILE)這個巨集用來檢查原始碼所在的路徑,autoscan會自動產生,我們不必修改它。
AM_INIT_AUTOMAKE(PACKAGE,VERSION)使用 Automake 所必備的巨集,PACKAGE是我們所要產生軟體套件的名稱,VERSION 是版本編號。
AC_CONFIG_AUX_DIR(dir)configure 系統時所使用的所有檔案( install-sh,config.sub,config.guess )若不想放在 configure 所在的目錄下, 可以利用此巨集指定在子目錄中
AC_CONFIG_HEADER(header.h) autoheader 會根據 AC_CHECK_HEADERS、AC_DEFINE 等巨集,產生 header.h.in , configure 參考此檔產生 header.h, 套件中的 header files只要含入header.h,即可解決編譯時大量的 '-D' 選項 。
AC_PROG_CC檢查系統可用的 C 編譯器,如果原始程式是用 C 寫的就需要這個巨集。其他程式檢查巨集請查看 autoconf/acprograms
AC_PROG_INSTALL檢察系統內是否有 BSD 相容 install 工具程式, 否則以 automake 的install-sh 替代。
AC_SUBST(VARIABLE) configure 程式會將 AC_OUTPUT 巨集所列出檔案中與指定變數名稱相同的位置,取代為該變數的值。
AC_CHECK_HEADERS( header.h)檢查系統中是否存在 header.h
AC_DEFINE(variable, define , comment)定義 C preprocessor variable, 可省略後兩項參數, 但預先必須在acconfig.h 中定義。ex: #undef USE_DNS
AC_DEFINE_UNQUOTED(variable, 可展開的 definem, comment )類似 AC_DEFINE , 其中的 define 若包含變數, 則會以內容展開變數ex:
AC_CONFIG_HEADER( conf.h )
AC_CHECK_HEADERS( unistd.h )
AC_DEFINE(USE_DNS, 1 , 使用DNS)
AC_DEFINEi_UNQUOTED(EDITOR, "$EDITOR", editor 的路徑)

則 autoheader 產生 conf.h.in 內含:
------------------------------------------------------------
/* Define as 1 if you have unistd.h */
#define HAVE_UNISTD_H 0
/* 使用DNS */
#undef USE_DNS
/* editor 的路徑 */
#undef EDITOR
若 configure 時, 若有 unistd.h、及指定 EDITOR=/usr/bin/vi 則
產生之 conf.h 如下
-----------------------------------------------------------
/* Define as 1 if you have unistd.h */
#define HAVE_UNISTD_H 1
/* 使用DNS */
#define USE_DNS 1
/* editor 的路徑 */
#define EDITOR /usr/bin/vi
AC_CHECK_PROG(variable,program,value-if-found,value-if-found,path1:path2)依指定路徑 path1:path2 尋找指定的 program, 若找到將 variable指定為 value-if-found, 若沒有找到 variable 指定為value-if-not-found,設定 variable 使用檔案的絕對檔案名稱。
AC_PATH_PROG(variable, program, value-if-not-found, path1:path2)依指定路徑 path1:path2 尋找指定的 program, 若找到將 variable指定為其完整路徑, 若沒有找到 variable 指定為 value-if-not-found。
AC_CANONICAL_HOST檢查系統類型, 並將其值存入 $host 變數
AC_CHECK_FUNCS(funcs, action-if-found, action-if-not-found)找尋是否有指定的 function 存在, 若存在執行 active-if-found 之 shell command, 反之則執行 active-if-not-found。可被檢查函式請參考autoconf/acfunctions
AC_ARG_ENABLE( feature,help-string,action-if-given,action-if-not-given)如果執行 configure 給定 --enable-feature 或 --disable-feature, 則會啟動相關的 action , enable 會執行 action-if-given, disable 會執行action-if-not-given。通常與 AC_DEFINE 搭配, 定義是否編譯某一項功能。
AC_OUTPUT(FILE)設定 configure 所要產生的檔案,如果是 Makefile 的話,configure便會把它檢查出來的結果 Makefile.in 檔然後產生合適的 Makefile。
ex:
configure.in 片段 Makefile.in 片段
--------------------------------------------------------------
FOO="hello" CFLAGS = -D@FOO@
AC_SUBST(FOO)
則執行 configure 後會產生 Makefile 包含以下片段: CFLAGS = -Dhello
* 2. 編輯 Makefilea.am , automake 根據 configure.in 中的巨集,將 Makefile.am轉變成 Makefile.in, 執行 automake --add-missing --copy 即可。
--add-missing 將包裝好 configure 所需之檔案補齊( 預設為 link 的方式 )
--copy 所需的檔案以 copy 的方式補齊
automake 支援、認可的 configure.in 巨集
----------------------------------------------------------------------
AC_INIT_AUTOMAKE(package, version)定義 PACKAGE、VERSION 兩變數
AM_CONFIG_HEADER( header.h )讓 automake 產生可自動再生成 header.h 的規則,使用此巨集必須先定義stamp-h.in, 用於標記 header.h 產生的時間。
AC_CANONICAL_HOST
AC_CHECK_TOOL automake 會確認 config.guess 及 config.sub 的存在。config.guess 用於猜測系統類型、config.sub用於提供檢查工具。
Makefile.am 選項說明
----------------------------------------------------------------------------
AUTOMAKE_OPTIONS設定 automake 的選項。Automake 主要是幫助開發 GNU 軟體的人員維護軟體套件,所以在執行 automake 時,會檢查目錄下是否存在標準GNU 軟體套件中應具備的文件檔案,例如 'NEWS'、'AUTHOR'、'ChangeLog'等文件檔。設成 foreign 時,automake 會改用一般軟體套件的標準來檢查。
SUBDIRS automake 會產生能夠遞迴進入指定目錄的 Makefile 規則。
bin_PROGRAMS定義我們所要產生的執行檔檔名。如果要產生多個執行檔,每個檔名用空白字元隔開。
hello_SOURCES定義 'hello' 這個執行檔所需要的原始檔。如果 'hello' 這個程式是由多個原始檔所產生,必須把它所用到的原始檔都列出來,以空白字元隔開。假設 'hello' 這個程式需要 'hello.c'、'main.c'、'hello.h'三個檔案的話,則定義hello_SOURCES= hello.c main.c hello.h
如果我們定義多個執行檔,則對每個執行檔都要定義相對的 filename_SOURCES
pkgdata_DATA將 pkgdata_DATA 所指定的檔案安裝到 pkgdatadir 指定的目錄,*_DATA 對應*dir,如 localstate_DATA 安裝至 localstatedir。
EXTRA_DIST在 make dist 時將指定的檔案一起打包。
AC_PREREQ確保使用的是足夠新的Autoconf版本。如果用於創建configure的Autoconf的版本比version 要早,就在標準錯誤輸出列印一條錯誤消息並不會創建configure。
AC_CONFIG_SRCDIR([main.c])用來偵測所指定的源碼檔是否存在,來確定源碼目錄的有效性。
AC_CONFIG_HEADER([config.h])用於生成config.h檔,以便autoheader使用。
* 3. 建構 GNU build 系統aclocal -> autoheader -> autoconf -> automake
GRUB中文指南
我想到此就可以知道大概了;可以直接到makefile

2010年4月15日 星期四

學習GRUB Programming初啼

GRUB(Gran Unified Bootloader)是個bootloader,為何我對它這樣著迷,其實應該說道理還是一樣,它看起來不大,而且目前已經可以支援ATA SATA USB CDROM 等開機,因此絕對可以學到很多實作及概念並認識各式檔案系統如何運作,未來將OS安裝在USB DEVICE的狀況將會越來越常見 .重點當然也是因為它和linux kernel比較起來小很多,我想trace code應該也會比較得心應手.說真的像linux kernel那麼大也別想用source insight來管理,就連U-Boot都有點吃力了.所以越大的project還是要在linux環境來trace code,較沒有效能上的疑慮.然而我實際編譯一次後才發現它的檔案量還真不少,install到系統的模組也有1百多個;網上相關的編程教學也少的可憐;看來是要自立自強了。
一開始只知道它的評價比LILO好,其他說真的還一無所知.首先先來點開味菜ㄅ:雖然本文並非討論GRUB如何使用,但trace code需要的狀況還是會稍微提到。
configure及makefile;這個makefile是執行configure shell script後產生的;這是很正常的程序,只是我覺得寫這個shell script的人不正常。
然而實際上configure這個shell script也是自動產生的;說著說著好像越來越模糊了。說真的我越來越怕看到(自動)這兩個字了;因為那表示又有一些相關技術要k了。果然網上隨便輸入"automake" "autoconf" "makefile" "makefile.am" "makefile.in"等關鍵字保證又是一堆資料等著你去把相關知識給整合起來。要完成一個自動產生的makefile,流程大概如下:
1.autoscan 產生一個 configure.scan,更名為 configure.in
2.修改 configure.in 的內容
3.執行 aclocal 和 autoconf,分別會產生 aclocal.m4 及 configure 兩個檔案
4.使用編輯器,建立 Makefile.am 檔
5.使用 automake --add-missing 將 Makefile.in 產生出來
6.執行 ./configure,產生makefile
然而在grub project裡頭將以上幾個步驟寫成autogen.sh shell script內容如下:而且它的實作方式稍微不同,它不採用makefile.am
#! /bin/sh
set -e
aclocal
autoconf #到此產生configure
autoheader
# FIXME: automake doesn't like that there's no Makefile.am
automake -a -c -f true
echo timestamp > stamp-h.in
python util/import_gcry.py lib/libgcrypt/ .
for rmk in conf/*.rmk ${GRUB_CONTRIB}/*/conf/*.rmk
do
if test -e $rmk ; then
ruby genmk.rb < $rmk > `echo $rmk sed 's/\.rmk$/.mk/'`
fi
done
sh gendistlist.sh > DISTLIST
exit 0
雖然不到20行,但是卻牽扯到python、sed、ruby,所以我說學習shell programming並不簡單,應該是說你不可能只學習一種script語言;這樣會使你創作能力降低,但要一下子就搞這麼多東西;我想我會瘋掉、會瘋掉。更何況有用的script還有像perl、awk‧‧‧就是說光想在linux上programming;並不是想像中那樣;但script畢竟是比較簡單;雖然很多人總寫出一堆令人費解的敘述;寫到這裡不禁感嘆:年事已高的我,未來的路還很長。
無論我們是否要搞懂這個autogen.sh至少要知道這個檔是否對未來整個專案的理解有很大關聯;所以實際去執行./configure後直接把makefile打開來看;果不其然還真有寸步難行之感。所以還是要乖乖就範,把這個autogen.sh給搞懂;因為它和之前描述的那6個步驟作法不同,也沒有執行autoscan所以沒有configure.scan,因此就不會有configure.in。不使用makefile.am,有看到執行automake,但是卻早有makefile.in這個檔案;總之用ㄌ一些自動化的工具卻不走正統路線;所以只好再去找一些相關資訊,例如:GNU Coding Standards FreeBSD Porter 手册 autoconf 和 automake 生成 Makefile 文件
原來GRUB使用configure.ac,我想接下來應該是把重點擺到這個檔案上。因為aclocal是一個perl 腳本程式,它的定義是:aclocal - create aclocal.m4 by scanning configure.ac
終於好不容易找到了以下連結可供參考:Configure Makefile.am Makefile.in Makefile文件之間關係 automake Introduction英文 簡體 GRUB 運作原理