用 GRUB 引导自己的操作系统
生活随笔
收集整理的這篇文章主要介紹了
用 GRUB 引导自己的操作系统
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
在 PC 機(jī)上搗鼓自己的操作系統(tǒng)遇到的第一個(gè)難題就是如何將內(nèi)核加載到內(nèi)存中執(zhí)行。如果讀過于淵寫的《自己動(dòng)手寫操作系統(tǒng)》就會(huì)知道這部分的工作還是蠻繁瑣的。而且實(shí)際上這部分工作和操作系統(tǒng)沒太大的關(guān)系。好在隨著 linux 等開源操作系統(tǒng)的發(fā)展,開源的引導(dǎo)加載程序也已經(jīng)發(fā)展的很成熟了。我們可以利用前人的成果,將自己的操作系統(tǒng)改造成可以用現(xiàn)有引導(dǎo)加載程序引導(dǎo)的內(nèi)核。
?
引導(dǎo)加載程序(BootLoader)是系統(tǒng)加電后運(yùn)行的第一段軟件代碼。x86 系統(tǒng)中的引導(dǎo)加載程序由 BIOS(Basic Input Output System,基本輸入輸出系統(tǒng))和位于硬盤主引導(dǎo)記錄(MBR,Master Boot Record)中的操作系統(tǒng)引導(dǎo)加載程序(比如,LILO 和 GRUB 等)一起組成。BIOS 在完成硬件檢測(cè)和資源分配后,將硬盤 MBR 中的引導(dǎo)加載程序讀到系統(tǒng)的 RAM 中,然后將控制權(quán)交給操作系統(tǒng)引導(dǎo)加載程序。引導(dǎo)加載程序的主要運(yùn)行任務(wù)就是將內(nèi)核映象從硬盤上讀到 RAM 中,然后跳轉(zhuǎn)到內(nèi)核的入口點(diǎn)去運(yùn)行,也即開始啟動(dòng)操作系統(tǒng)。?
?
引導(dǎo)加載程序并非操作系統(tǒng)的一部分,但是沒有引導(dǎo)加載程序加載操作系統(tǒng),操作系統(tǒng)是無法自動(dòng)運(yùn)行起來的??梢栽?x86 系統(tǒng)中運(yùn)行的操作系統(tǒng)超過 100 種,其中較為有名的如微軟公司出品的 DOS、Windows 系列,開放源代碼的 Linux、FreeBS D等也不下 10 余種。這些各具特色的操作系統(tǒng)幾乎都有其專用的引導(dǎo)加載程序,并且互相之間并不兼容。
?
經(jīng)過對(duì)比各種常見的引導(dǎo)加載程序的功能和可靠性,我選擇了多重操作系統(tǒng)啟動(dòng)管理器 GRUB 作為引導(dǎo)加載程序。GRUB 是一個(gè)來自自由軟件基金會(huì)項(xiàng)目的多操作系統(tǒng)啟動(dòng)程序,它允許用戶可以在計(jì)算機(jī)內(nèi)同時(shí)擁有多個(gè)操作系統(tǒng),并在計(jì)算機(jī)啟動(dòng)時(shí)選擇希望運(yùn)行的操作系統(tǒng)。GRUB 可用于選擇操作系統(tǒng)分區(qū)上的不同內(nèi)核,也可用于向這些內(nèi)核傳遞啟動(dòng)參數(shù)。
?
GRUB 引導(dǎo)加載程序廣泛應(yīng)用于 Linux、各種 BSD 系統(tǒng)的引導(dǎo),具有極高的可靠性。滿足多重引導(dǎo)規(guī)范(The Multiboot Specification),可以引導(dǎo)各種滿足多重引導(dǎo)規(guī)范的操作系統(tǒng)內(nèi)核。并且可以通過配置文件配置為多引導(dǎo)模式,當(dāng)加載的系統(tǒng)出現(xiàn)故障無法工作時(shí)可以自動(dòng)啟用備用系統(tǒng),極大的提高了系統(tǒng)的可靠性。
?
多重引導(dǎo)規(guī)范?
?
多重引導(dǎo)規(guī)范并不強(qiáng)制要求內(nèi)核的格式,但是如果采用 ELF 格式,將會(huì)帶來許多方便。本文下面的介紹都是基于內(nèi)核采用 ELF 格式。如果您的內(nèi)核碰巧不能采用 ELF 格式,請(qǐng)您參考多重引導(dǎo)規(guī)范的官方文本中 3.1 節(jié)關(guān)于 Multiboot Header 的介紹。
?
能夠被 GRUB 引導(dǎo)的內(nèi)核有兩個(gè)條件:?
(1) 需要有一個(gè) Multiboot Header ,這個(gè)? Multiboot Header 必須在內(nèi)核鏡像的前 8192 個(gè)字節(jié)內(nèi),并且是首地址是 4 字節(jié)對(duì)其的。 ?
(2) 內(nèi)核的加載地址在 1MB 以上的內(nèi)存中,這個(gè)要求是 GRUB 附加的,并非多重引導(dǎo)規(guī)范的規(guī)定。?
?
Multiboot Header?
?
Multiboot Header的分布必須如下所示:?
?
偏移量?? ?類型?? ?域名?? ????????????? 備注?
0 ?? ?u32 ?? ?magic ?? ??????? ? ???????? 必需?
4 ?? ?u32 ?? ?flags ?? ???????????????????? 必需?
8 ?? ?u32 ?? ?checksum ?? ? ? ?????? 必需?
12 ?? ?u32 ?? ?header_addr??????? 如果flags[16]被置位?
16 ?? ?u32 ?? ?load_addr???????????? 如果flags[16]被置位?
20 ?? ?u32 ?? ?load_end_addr??? 如果flags[16]被置位?
24 ?? ?u32 ?? ?bss_end_addr???? 如果flags[16]被置位?
28 ?? ?u32 ?? ?entry_addr ?? ??????? 如果flags[16]被置位?
32 ?? ?u32 ?? ?mode_type ?? ?????? 如果flags[2]被置位?
36 ?? ?u32 ?? ?width ?? ???????????????? ?如果flags[2]被置位?
40 ?? ?u32 ?? ?height ?? ??????????????? 如果flags[2]被置位?
44 ?? ?u32 ?? ?depth ?? ???????????????? 如果flags[2]被置位 ?
?
magic ?
??? 域是標(biāo)志頭的魔數(shù),它必須等于十六進(jìn)制值 0x1BADB002。?
?
flags?
??? flags域指出OS映像需要引導(dǎo)程序提供或支持的特性。0-15 位指出需求:如果引導(dǎo)程序發(fā)現(xiàn)某些值被設(shè)置但出于某種原因不理解或不能不能滿足相應(yīng)的需求,它必須告知用戶并宣告引導(dǎo)失敗。16-31位指出可選的特性:如果引導(dǎo)程序不能支持某些位,它可以簡(jiǎn)單的忽略它們并正常引導(dǎo)。自然,所有 flags 字中尚未定義的位必須被置為 0。這樣,flags 域既可以用于版本控制也可以用于簡(jiǎn)單的特性選擇。
?
??? 如果設(shè)置了 flags 字中的 0 位,所有的引導(dǎo)模塊將按頁(4KB)邊界對(duì)齊。有些操作系統(tǒng)能夠在啟動(dòng)時(shí)將包含引導(dǎo)模塊的頁直接映射到一個(gè)分頁的地址空間,因此需要引導(dǎo)模塊是頁對(duì)齊的。
?
??? 如果設(shè)置了 flags 字中的 1 位,則必須通過 Multiboot 信息結(jié)構(gòu)(參見引導(dǎo)信息格式)的 mem_* 域包括可用內(nèi)存的信息。如果引導(dǎo)程序能夠傳遞內(nèi)存分布(mmap_*域)并且它確實(shí)存在,則也包括它。
?
??? 如果設(shè)置了 flags 字中的 2 位,有關(guān)視頻模式表(參見引導(dǎo)信息格式)的信息必須對(duì)內(nèi)核有效。?
?
??? 如果設(shè)置了 flags 字中的 16 位,則 Multiboot 頭中偏移量 8-24 的域有效,引導(dǎo)程序應(yīng)該使用它們而不是實(shí)際可執(zhí)行頭中的域來計(jì)算將 OS 映象載入到那里。如果內(nèi)核映象為 ELF 格式則不必提供這樣的信息,但是如果映象是 a.out 格式或者其他什么格式的話就必須提供這些信息。
?
checksum?
??? 域 checksum 是一個(gè) 32 位的無符號(hào)值,當(dāng)與其他的 magic 域(也就是 magic 和 flags)相加時(shí),結(jié)果必須是 32 位的無符號(hào)值 0(即magic + flags + checksum = 0)
?
header_addr ?
??? 這里往后的 32 個(gè)字節(jié)不是必須的,并且對(duì)于內(nèi)核為 ELF 格式時(shí)是不需要的,因此就不介紹了。?
?
當(dāng)引導(dǎo)程序調(diào)用32位操作系統(tǒng)時(shí),機(jī)器狀態(tài)必須如下:?
?
EAX?
??? 必須包含魔數(shù) 0x2BADB002;這個(gè)值指出操作系統(tǒng)是被一個(gè)符合 Multiboot 規(guī)范的引導(dǎo)程序載入的。?
EBX?
??? 必須包含由引導(dǎo)程序提供的 Multiboot 信息結(jié)構(gòu)的物理地址(參見引導(dǎo)信息格式)。?
CS?
??? 必須是一個(gè)偏移量位于 0 到 0xFFFFFFFF 之間的 32 位可讀/可執(zhí)行代碼段。這里的精確值未定義。?
DS?
ES?
FS?
GS?
SS?
??? 必須是一個(gè)偏移量位于 0 到 0xFFFFFFFF 之間的 32 位可讀/可執(zhí)行代碼段。這里的精確值未定義。?
A20 gate?
??? 必須已經(jīng)開啟。?
CR0?
??? 第31位(PG)必須為 0。第 0 位(PE)必須為 1。其他位未定義。?
EFLAGS?
??? 第17位(VM)必須為 0。第 9 位(IF)必須為 1 。其他位未定義。?
?
所有其他的處理器寄存器和標(biāo)志位未定義。這包括:?
?
ESP?
??? 當(dāng)需要使用堆棧時(shí),OS 映象必須自己創(chuàng)建一個(gè)。?
GDTR?
??? 盡管段寄存器像上面那樣定義了,GDTR 也可能是無效的,所以 OS 映象決不能載入任何段寄存器(即使是載入相同的值也不行!)直到它設(shè)定了自己的 GDT。?
IDTR?
??? OS 映象必須在設(shè)置完它的 IDT 之后才能開中斷。?
?
Multiboot 信息結(jié)構(gòu)(就目前為止定義的)的格式如下:?
?
???????????? +-------------------+?
???? 0?????? | flags?????????????? ?|??? (必需)?
???????????? +-------------------+?
???? 4?????? | mem_lower???????? |??? (如果flags[0]被置位則出現(xiàn))?
???? 8?????? | mem_upper???????? |??? (如果flags[0]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 12????? | boot_device?????? |??? (如果flags[1]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 16????? | cmdline?????????? |??? (如果flags[2]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 20????? | mods_count??????? |??? (如果flags[3]被置位則出現(xiàn))?
???? 24????? | mods_addr???????? |??? (如果flags[3]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 28 - 40 | syms????????????? |??? (如果flags[4]或flags[5]被置位則出現(xiàn))?
???????????? |?????????????????? |???????????????? ?
???????????? +-------------------+?
???? 44????? | mmap_length?????? |??? (如果flags[6]被置位則出現(xiàn))?
???? 48????? | mmap_addr???????? |??? (如果flags[6]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 52????? | drives_length???? |??? (如果flags[7]被置位則出現(xiàn))?
???? 56????? | drives_addr?????? |??? (如果flags[7]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 60????? | config_table????? |??? (如果flags[8]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 64????? | boot_loader_name? |??? (如果flags[9]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 68????? | apm_table???????? |??? (如果flags[10]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 72????? | vbe_control_info? |??? (如果flags[11]被置位則出現(xiàn))?
???? 76????? | vbe_mode_info???? |?
???? 80????? | vbe_mode????????? |?
???? 82????? | vbe_interface_seg |?
???? 84????? | vbe_interface_off |?
???? 86????? | vbe_interface_len |?
???????????? +-------------------+?
???? ?
第一個(gè) longword 指出 Multiboot 信息結(jié)構(gòu)中的其它域是否有效。所有目前未定義的位必須被引導(dǎo)程序設(shè)為 0。操作系統(tǒng)應(yīng)該忽略任何它不理解的位。因此,flags 域也可以視作一個(gè)版本標(biāo)志符,這樣可以無破壞的擴(kuò)展Multiboot信息結(jié)構(gòu)。
?
如果設(shè)置了 flags 中的第 0 位,則 mem_* 域有效。mem_lower 和 mem_upper 分別指出了低端和高端內(nèi)存的大小,單位是 K。低端內(nèi)存的首地址是 0,高端內(nèi)存的首地址是 1M。低端內(nèi)存的最大可能值是 640K。返回的高端內(nèi)存的最大可能值是最大值減去 1M。但并不保證是這個(gè)值。
?
flags 的其他位我沒有用到,這里就不介紹了。需要了解的請(qǐng)自己閱讀相關(guān)文檔。?
?
下面是一個(gè)最簡(jiǎn)單的例子:?
?
? [plain]?view plaincopy /**?? ?*?boot.S?? ?*/?? ??? #define?MULTIBOOT_HEADER_MAGIC??????????0x1BADB002?? #define?MULTIBOOT_HEADER_FLAGS??????????0x00000003?? #define?STACK_SIZE??????????????????????0x4000?? ??? .text?? ????????.globl??start,?_start?? ??? start:?? _start:?? ????????jmp?????multiboot_entry?? ??? ????????.align??4?? ??? multiboot_header:?? ????????.long???MULTIBOOT_HEADER_MAGIC?? ????????.long???MULTIBOOT_HEADER_FLAGS?? ????????.long???-(MULTIBOOT_HEADER_MAGIC?+?MULTIBOOT_HEADER_FLAGS)?? ??? multiboot_entry:?? ????????/*?初始化堆棧指針。?*/?? ????????movl????$(stack?+?STACK_SIZE),?%esp?? ??? ????????/*?重置?EFLAGS。?*/?? ????????pushl???$0?? ????????popf?? ??? ????????pushl???%ebx?? ????????pushl???%eax?? ??? ????????/*?現(xiàn)在進(jìn)入?C?main?函數(shù)...?*/?? ????????call????cmain?? ??? loop:???hlt?? ????????jmp?????loop?? ??? ????????.comm???stack,?STACK_SIZE??
[cpp]?view plaincopy /**? ?*?kernel.c? ?*/?? ?? /*?a.out?符號(hào)表。?*/?? typedef?struct?aout_symbol_table?? {?? ????unsigned?long?tabsize;?? ????unsigned?long?strsize;?? ????unsigned?long?addr;?? ????unsigned?long?reserved;?? }?aout_symbol_table_t;?? ?? /*?ELF?的?section?header?table。?*/?? typedef?struct?elf_section_header_table?? {?? ????unsigned?long?num;?? ????unsigned?long?size;?? ????unsigned?long?addr;?? ????unsigned?long?shndx;?? }?elf_section_header_table_t;?? ?? /*?Multiboot?信息。?*/?? typedef?struct?multiboot_info?? {?? ????unsigned?long?flags;?? ????unsigned?long?mem_lower;?? ????unsigned?long?mem_upper;?? ????unsigned?long?boot_device;?? ????unsigned?long?cmdline;?? ????unsigned?long?mods_count;?? ????unsigned?long?mods_addr;?? ????union?? ????{?? ????????aout_symbol_table_t?aout_sym;?? ????????elf_section_header_table_t?elf_sec;?? ????}?u;?? ????unsigned?long?mmap_length;?? ????unsigned?long?mmap_addr;?? }?multiboot_info_t;?? ?? /*?檢測(cè)?FLAGS?中的位?BIT?是否被置位。?*/?? #define?CHECK_FLAG(flags,bit)???((flags)?&?(1?<<?(bit)))?? ?? /*?與顯示相關(guān)的設(shè)置。?*/?? #define?COLUMNS?????????????????80?? #define?LINES???????????????????24?? #define?ATTRIBUTE???????????????7?? #define?VIDEO???????????????????0xB8000?? ?? static?int?xpos;?/*?X?坐標(biāo)。?*/?? static?int?ypos;?/*?Y?坐標(biāo)。?*/?? static?volatile?unsigned?char?*video;?/*?指向顯存。?*/?? ?? static?void?cls?(void);?? static?void?itoa?(char?*buf,?int?base,?int?d);?? static?void?putchar?(int?c);?? void?printf?(const?char?*format,?...);?? ?? void?cmain?(unsigned?long?magic,?unsigned?long?addr)?? {?? ????multiboot_info_t?*mbi;?? ?? ????/*?清屏。?*/?? ????cls?();?? ?? ????/*?將?MBI?指向?Multiboot?信息結(jié)構(gòu)。?*/?? ????mbi?=?(multiboot_info_t?*)?addr;?? ?? ????/*?mem_*?是否有效??*/?? ????if?(CHECK_FLAG?(mbi->flags,?0))?? ????????printf?("mem_lower?=?%uKB,?mem_upper?=?%uKB\n",?(unsigned)?mbi->mem_lower,?(unsigned)?mbi->mem_upper);?? ?????? ????/*?your?code?here.?*/?? }?? ?? /*?清屏并初始化?VIDEO,XPOS?和?YPOS。?*/?? static?void?cls?(void)?? {?? ????int?i;?? ?? ????video?=?(unsigned?char?*)?VIDEO;?? ?? ????for?(i?=?0;?i?<?COLUMNS?*?LINES?*?2;?i++)?? ????????*(video?+?i)?=?0;?? ?? ????xpos?=?0;?? ????ypos?=?0;?? }?? ?? /*?將整數(shù)?D?轉(zhuǎn)換為字符串并保存在?BUF?中。如果?BASE?為?'d',則?D?為十進(jìn)制,如果?BASE?為?'x',則?D?為十六進(jìn)制。?*/?? static?void?itoa?(char?*buf,?int?base,?int?d)?? {?? ????char?*p?=?buf;?? ????char?*p1,?*p2;?? ????unsigned?long?ud?=?d;?? ????int?divisor?=?10;?? ?? ????/*?如果指定了?%d?并且?D?是負(fù)數(shù),在開始添上負(fù)號(hào)。?*/?? ????if?(base?==?'d'?&&?d?<?0)?? ????{?? ????????*p++?=?'-';?? ????????buf++;?? ????????ud?=?-d;?? ????}?? ????else?if?(base?==?'x')?? ????????divisor?=?16;?? ?? ????/*?用?DIVISOR?去除?UD?直到?UD?==?0。?*/?? ????do?? ????{?? ????????int?remainder?=?ud?%?divisor;?? ?? ????????*p++?=?(remainder?<?10)???remainder?+?'0'?:?remainder?+?'a'?-?10;?? ????}?? ????while?(ud?/=?divisor);?? ?? ????/*?在字符串尾添上終結(jié)符。?*/?? ????*p?=?0;?? ?? ????/*?反轉(zhuǎn)?BUF。?*/?? ????p1?=?buf;?? ????p2?=?p?-?1;?? ????while?(p1?<?p2)?? ????{?? ????????char?tmp?=?*p1;?? ????????*p1?=?*p2;?? ????????*p2?=?tmp;?? ????????p1++;?? ????????p2--;?? ????}?? }?? ?? /*?在屏幕上輸出字符?C?。?*/?? static?void?putchar?(int?c)?? {?? ????if?(c?==?'\n'?||?c?==?'\r')?? ????{?? newline:?? ????????xpos?=?0;?? ????????ypos++;?? ????????if?(ypos?>=?LINES)?? ????????????ypos?=?0;?? ????????return;?? ????}?? ?? ????*(video?+?(xpos?+?ypos?*?COLUMNS)?*?2)?=?c?&?0xFF;?? ????*(video?+?(xpos?+?ypos?*?COLUMNS)?*?2?+?1)?=?ATTRIBUTE;?? ?? ????xpos++;?? ????if?(xpos?>=?COLUMNS)?? ????????goto?newline;?? }?? ?? /*?格式化字符串并在屏幕上輸出,就像?libc?函數(shù)?printf?一樣。?*/?? void?printf?(const?char?*format,?...)?? {?? ????char?**arg?=?(char?**)?&format;?? ????int?c;?? ????char?buf[20];?? ?? ????arg++;?? ?? ????while?((c?=?*format++)?!=?0)?? ????{?? ????????if?(c?!=?'%')?? ????????????putchar?(c);?? ????????else?? ????????{?? ????????????char?*p;?? ?? ????????????c?=?*format++;?? ????????????switch?(c)?? ????????????{?? ????????????case?'d':?? ????????????case?'u':?? ????????????case?'x':?? ????????????????itoa?(buf,?c,?*((int?*)?arg++));?? ????????????????p?=?buf;?? ????????????????goto?string;?? ????????????????break;?? ?? ????????????case?'s':?? ????????????????p?=?*arg++;?? ????????????????if?(!?p)?? ????????????????????p?=?"(null)";?? ?? string:?? ????????????????while?(*p)?? ????????????????????putchar?(*p++);?? ????????????????break;?? ?? ????????????default:?? ????????????????putchar?(*((int?*)?arg++));?? ????????????????break;?? ????????????}?? ????????}?? ????}?? }??
?
引導(dǎo)加載程序(BootLoader)是系統(tǒng)加電后運(yùn)行的第一段軟件代碼。x86 系統(tǒng)中的引導(dǎo)加載程序由 BIOS(Basic Input Output System,基本輸入輸出系統(tǒng))和位于硬盤主引導(dǎo)記錄(MBR,Master Boot Record)中的操作系統(tǒng)引導(dǎo)加載程序(比如,LILO 和 GRUB 等)一起組成。BIOS 在完成硬件檢測(cè)和資源分配后,將硬盤 MBR 中的引導(dǎo)加載程序讀到系統(tǒng)的 RAM 中,然后將控制權(quán)交給操作系統(tǒng)引導(dǎo)加載程序。引導(dǎo)加載程序的主要運(yùn)行任務(wù)就是將內(nèi)核映象從硬盤上讀到 RAM 中,然后跳轉(zhuǎn)到內(nèi)核的入口點(diǎn)去運(yùn)行,也即開始啟動(dòng)操作系統(tǒng)。?
?
引導(dǎo)加載程序并非操作系統(tǒng)的一部分,但是沒有引導(dǎo)加載程序加載操作系統(tǒng),操作系統(tǒng)是無法自動(dòng)運(yùn)行起來的??梢栽?x86 系統(tǒng)中運(yùn)行的操作系統(tǒng)超過 100 種,其中較為有名的如微軟公司出品的 DOS、Windows 系列,開放源代碼的 Linux、FreeBS D等也不下 10 余種。這些各具特色的操作系統(tǒng)幾乎都有其專用的引導(dǎo)加載程序,并且互相之間并不兼容。
?
經(jīng)過對(duì)比各種常見的引導(dǎo)加載程序的功能和可靠性,我選擇了多重操作系統(tǒng)啟動(dòng)管理器 GRUB 作為引導(dǎo)加載程序。GRUB 是一個(gè)來自自由軟件基金會(huì)項(xiàng)目的多操作系統(tǒng)啟動(dòng)程序,它允許用戶可以在計(jì)算機(jī)內(nèi)同時(shí)擁有多個(gè)操作系統(tǒng),并在計(jì)算機(jī)啟動(dòng)時(shí)選擇希望運(yùn)行的操作系統(tǒng)。GRUB 可用于選擇操作系統(tǒng)分區(qū)上的不同內(nèi)核,也可用于向這些內(nèi)核傳遞啟動(dòng)參數(shù)。
?
GRUB 引導(dǎo)加載程序廣泛應(yīng)用于 Linux、各種 BSD 系統(tǒng)的引導(dǎo),具有極高的可靠性。滿足多重引導(dǎo)規(guī)范(The Multiboot Specification),可以引導(dǎo)各種滿足多重引導(dǎo)規(guī)范的操作系統(tǒng)內(nèi)核。并且可以通過配置文件配置為多引導(dǎo)模式,當(dāng)加載的系統(tǒng)出現(xiàn)故障無法工作時(shí)可以自動(dòng)啟用備用系統(tǒng),極大的提高了系統(tǒng)的可靠性。
?
多重引導(dǎo)規(guī)范?
?
多重引導(dǎo)規(guī)范并不強(qiáng)制要求內(nèi)核的格式,但是如果采用 ELF 格式,將會(huì)帶來許多方便。本文下面的介紹都是基于內(nèi)核采用 ELF 格式。如果您的內(nèi)核碰巧不能采用 ELF 格式,請(qǐng)您參考多重引導(dǎo)規(guī)范的官方文本中 3.1 節(jié)關(guān)于 Multiboot Header 的介紹。
?
能夠被 GRUB 引導(dǎo)的內(nèi)核有兩個(gè)條件:?
(1) 需要有一個(gè) Multiboot Header ,這個(gè)? Multiboot Header 必須在內(nèi)核鏡像的前 8192 個(gè)字節(jié)內(nèi),并且是首地址是 4 字節(jié)對(duì)其的。 ?
(2) 內(nèi)核的加載地址在 1MB 以上的內(nèi)存中,這個(gè)要求是 GRUB 附加的,并非多重引導(dǎo)規(guī)范的規(guī)定。?
?
Multiboot Header?
?
Multiboot Header的分布必須如下所示:?
?
偏移量?? ?類型?? ?域名?? ????????????? 備注?
0 ?? ?u32 ?? ?magic ?? ??????? ? ???????? 必需?
4 ?? ?u32 ?? ?flags ?? ???????????????????? 必需?
8 ?? ?u32 ?? ?checksum ?? ? ? ?????? 必需?
12 ?? ?u32 ?? ?header_addr??????? 如果flags[16]被置位?
16 ?? ?u32 ?? ?load_addr???????????? 如果flags[16]被置位?
20 ?? ?u32 ?? ?load_end_addr??? 如果flags[16]被置位?
24 ?? ?u32 ?? ?bss_end_addr???? 如果flags[16]被置位?
28 ?? ?u32 ?? ?entry_addr ?? ??????? 如果flags[16]被置位?
32 ?? ?u32 ?? ?mode_type ?? ?????? 如果flags[2]被置位?
36 ?? ?u32 ?? ?width ?? ???????????????? ?如果flags[2]被置位?
40 ?? ?u32 ?? ?height ?? ??????????????? 如果flags[2]被置位?
44 ?? ?u32 ?? ?depth ?? ???????????????? 如果flags[2]被置位 ?
?
magic ?
??? 域是標(biāo)志頭的魔數(shù),它必須等于十六進(jìn)制值 0x1BADB002。?
?
flags?
??? flags域指出OS映像需要引導(dǎo)程序提供或支持的特性。0-15 位指出需求:如果引導(dǎo)程序發(fā)現(xiàn)某些值被設(shè)置但出于某種原因不理解或不能不能滿足相應(yīng)的需求,它必須告知用戶并宣告引導(dǎo)失敗。16-31位指出可選的特性:如果引導(dǎo)程序不能支持某些位,它可以簡(jiǎn)單的忽略它們并正常引導(dǎo)。自然,所有 flags 字中尚未定義的位必須被置為 0。這樣,flags 域既可以用于版本控制也可以用于簡(jiǎn)單的特性選擇。
?
??? 如果設(shè)置了 flags 字中的 0 位,所有的引導(dǎo)模塊將按頁(4KB)邊界對(duì)齊。有些操作系統(tǒng)能夠在啟動(dòng)時(shí)將包含引導(dǎo)模塊的頁直接映射到一個(gè)分頁的地址空間,因此需要引導(dǎo)模塊是頁對(duì)齊的。
?
??? 如果設(shè)置了 flags 字中的 1 位,則必須通過 Multiboot 信息結(jié)構(gòu)(參見引導(dǎo)信息格式)的 mem_* 域包括可用內(nèi)存的信息。如果引導(dǎo)程序能夠傳遞內(nèi)存分布(mmap_*域)并且它確實(shí)存在,則也包括它。
?
??? 如果設(shè)置了 flags 字中的 2 位,有關(guān)視頻模式表(參見引導(dǎo)信息格式)的信息必須對(duì)內(nèi)核有效。?
?
??? 如果設(shè)置了 flags 字中的 16 位,則 Multiboot 頭中偏移量 8-24 的域有效,引導(dǎo)程序應(yīng)該使用它們而不是實(shí)際可執(zhí)行頭中的域來計(jì)算將 OS 映象載入到那里。如果內(nèi)核映象為 ELF 格式則不必提供這樣的信息,但是如果映象是 a.out 格式或者其他什么格式的話就必須提供這些信息。
?
checksum?
??? 域 checksum 是一個(gè) 32 位的無符號(hào)值,當(dāng)與其他的 magic 域(也就是 magic 和 flags)相加時(shí),結(jié)果必須是 32 位的無符號(hào)值 0(即magic + flags + checksum = 0)
?
header_addr ?
??? 這里往后的 32 個(gè)字節(jié)不是必須的,并且對(duì)于內(nèi)核為 ELF 格式時(shí)是不需要的,因此就不介紹了。?
?
當(dāng)引導(dǎo)程序調(diào)用32位操作系統(tǒng)時(shí),機(jī)器狀態(tài)必須如下:?
?
EAX?
??? 必須包含魔數(shù) 0x2BADB002;這個(gè)值指出操作系統(tǒng)是被一個(gè)符合 Multiboot 規(guī)范的引導(dǎo)程序載入的。?
EBX?
??? 必須包含由引導(dǎo)程序提供的 Multiboot 信息結(jié)構(gòu)的物理地址(參見引導(dǎo)信息格式)。?
CS?
??? 必須是一個(gè)偏移量位于 0 到 0xFFFFFFFF 之間的 32 位可讀/可執(zhí)行代碼段。這里的精確值未定義。?
DS?
ES?
FS?
GS?
SS?
??? 必須是一個(gè)偏移量位于 0 到 0xFFFFFFFF 之間的 32 位可讀/可執(zhí)行代碼段。這里的精確值未定義。?
A20 gate?
??? 必須已經(jīng)開啟。?
CR0?
??? 第31位(PG)必須為 0。第 0 位(PE)必須為 1。其他位未定義。?
EFLAGS?
??? 第17位(VM)必須為 0。第 9 位(IF)必須為 1 。其他位未定義。?
?
所有其他的處理器寄存器和標(biāo)志位未定義。這包括:?
?
ESP?
??? 當(dāng)需要使用堆棧時(shí),OS 映象必須自己創(chuàng)建一個(gè)。?
GDTR?
??? 盡管段寄存器像上面那樣定義了,GDTR 也可能是無效的,所以 OS 映象決不能載入任何段寄存器(即使是載入相同的值也不行!)直到它設(shè)定了自己的 GDT。?
IDTR?
??? OS 映象必須在設(shè)置完它的 IDT 之后才能開中斷。?
?
Multiboot 信息結(jié)構(gòu)(就目前為止定義的)的格式如下:?
?
???????????? +-------------------+?
???? 0?????? | flags?????????????? ?|??? (必需)?
???????????? +-------------------+?
???? 4?????? | mem_lower???????? |??? (如果flags[0]被置位則出現(xiàn))?
???? 8?????? | mem_upper???????? |??? (如果flags[0]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 12????? | boot_device?????? |??? (如果flags[1]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 16????? | cmdline?????????? |??? (如果flags[2]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 20????? | mods_count??????? |??? (如果flags[3]被置位則出現(xiàn))?
???? 24????? | mods_addr???????? |??? (如果flags[3]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 28 - 40 | syms????????????? |??? (如果flags[4]或flags[5]被置位則出現(xiàn))?
???????????? |?????????????????? |???????????????? ?
???????????? +-------------------+?
???? 44????? | mmap_length?????? |??? (如果flags[6]被置位則出現(xiàn))?
???? 48????? | mmap_addr???????? |??? (如果flags[6]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 52????? | drives_length???? |??? (如果flags[7]被置位則出現(xiàn))?
???? 56????? | drives_addr?????? |??? (如果flags[7]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 60????? | config_table????? |??? (如果flags[8]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 64????? | boot_loader_name? |??? (如果flags[9]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 68????? | apm_table???????? |??? (如果flags[10]被置位則出現(xiàn))?
???????????? +-------------------+?
???? 72????? | vbe_control_info? |??? (如果flags[11]被置位則出現(xiàn))?
???? 76????? | vbe_mode_info???? |?
???? 80????? | vbe_mode????????? |?
???? 82????? | vbe_interface_seg |?
???? 84????? | vbe_interface_off |?
???? 86????? | vbe_interface_len |?
???????????? +-------------------+?
???? ?
第一個(gè) longword 指出 Multiboot 信息結(jié)構(gòu)中的其它域是否有效。所有目前未定義的位必須被引導(dǎo)程序設(shè)為 0。操作系統(tǒng)應(yīng)該忽略任何它不理解的位。因此,flags 域也可以視作一個(gè)版本標(biāo)志符,這樣可以無破壞的擴(kuò)展Multiboot信息結(jié)構(gòu)。
?
如果設(shè)置了 flags 中的第 0 位,則 mem_* 域有效。mem_lower 和 mem_upper 分別指出了低端和高端內(nèi)存的大小,單位是 K。低端內(nèi)存的首地址是 0,高端內(nèi)存的首地址是 1M。低端內(nèi)存的最大可能值是 640K。返回的高端內(nèi)存的最大可能值是最大值減去 1M。但并不保證是這個(gè)值。
?
flags 的其他位我沒有用到,這里就不介紹了。需要了解的請(qǐng)自己閱讀相關(guān)文檔。?
?
下面是一個(gè)最簡(jiǎn)單的例子:?
?
? [plain]?view plaincopy
[cpp]?view plaincopy
下面是編譯命令:
gcc kernel.c -c -fno-builtin
gcc boot.S -c
ld kernel.o boot.o -o kernel -s -Ttext 0x100000 --entry=start
最后是用 bochs 運(yùn)行的結(jié)果:
?
如何運(yùn)行自己的內(nèi)核,可以參考我的文章:制作帶有 GRUB 引導(dǎo)功能的軟盤鏡像文件
總結(jié)
以上是生活随笔為你收集整理的用 GRUB 引导自己的操作系统的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 制作带有 GRUB 引导功能的软盘镜像文
- 下一篇: Django学习小记[2] —— Mod