1. 程式人生 > >用 GRUB 引導自己的作業系統

用 GRUB 引導自己的作業系統

在 PC 機上搗鼓自己的作業系統遇到的第一個難題就是如何將核心載入到記憶體中執行。如果讀過於淵寫的《自己動手寫作業系統》就會知道這部分的工作還是蠻繁瑣的。而且實際上這部分工作和作業系統沒太大的關係。好在隨著 linux 等開源作業系統的發展,開源的引導載入程式也已經發展的很成熟了。我們可以利用前人的成果,將自己的作業系統改造成可以用現有引導載入程式引導的核心。
 
引導載入程式(BootLoader)是系統加電後執行的第一段軟體程式碼。x86 系統中的引導載入程式由 BIOS(Basic Input Output System,基本輸入輸出系統)和位於硬碟主引導記錄(MBR,Master Boot Record)中的作業系統引導載入程式(比如,LILO 和 GRUB 等)一起組成。BIOS 在完成硬體檢測和資源分配後,將硬碟 MBR 中的引導載入程式讀到系統的 RAM 中,然後將控制權交給作業系統引導載入程式。引導載入程式的主要執行任務就是將核心映象從硬碟上讀到 RAM 中,然後跳轉到核心的入口點去執行,也即開始啟動作業系統。 
 
引導載入程式並非作業系統的一部分,但是沒有引導載入程式載入作業系統,作業系統是無法自動執行起來的。可以在 x86 系統中執行的作業系統超過 100 種,其中較為有名的如微軟公司出品的 DOS、Windows 系列,開放原始碼的 Linux、FreeBS D等也不下 10 餘種。這些各具特色的作業系統幾乎都有其專用的引導載入程式,並且互相之間並不相容。
 
經過對比各種常見的引導載入程式的功能和可靠性,我選擇了多重作業系統啟動管理器 GRUB 作為引導載入程式。GRUB 是一個來自自由軟體基金會專案的多作業系統啟動程式,它允許使用者可以在計算機內同時擁有多個作業系統,並在計算機啟動時選擇希望執行的作業系統。GRUB 可用於選擇作業系統分割槽上的不同核心,也可用於向這些核心傳遞啟動引數。
 
GRUB 引導載入程式廣泛應用於 Linux、各種 BSD 系統的引導,具有極高的可靠性。滿足多重引導規範(The Multiboot Specification),可以引導各種滿足多重引導規範的作業系統核心。並且可以通過配置檔案配置為多引導模式,當載入的系統出現故障無法工作時可以自動啟用備用系統,極大的提高了系統的可靠性。
 
多重引導規範 
 
多重引導規範並不強制要求核心的格式,但是如果採用 ELF 格式,將會帶來許多方便。本文下面的介紹都是基於核心採用 ELF 格式。如果您的核心碰巧不能採用 ELF 格式,請您參考多重引導規範的官方文字中 3.1 節關於 Multiboot Header 的介紹。
 
能夠被 GRUB 引導的核心有兩個條件: 
(1) 需要有一個 Multiboot Header ,這個  Multiboot Header 必須在核心映象的前 8192 個位元組內,並且是首地址是 4 位元組對其的。  
(2) 核心的載入地址在 1MB 以上的記憶體中,這個要求是 GRUB 附加的,並非多重引導規範的規定。 
 
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  
    域是標誌頭的魔數,它必須等於十六進位制值 0x1BADB002。 
 
flags 
    flags域指出OS映像需要載入程式提供或支援的特性。0-15 位指出需求:如果載入程式發現某些值被設定但出於某種原因不理解或不能不能滿足相應的需求,它必須告知使用者並宣告引導失敗。16-31位指出可選的特性:如果載入程式不能支援某些位,它可以簡單的忽略它們並正常引導。自然,所有 flags 字中尚未定義的位必須被置為 0。這樣,flags 域既可以用於版本控制也可以用於簡單的特性選擇。
 
    如果設定了 flags 字中的 0 位,所有的引導模組將按頁(4KB)邊界對齊。有些作業系統能夠在啟動時將包含引導模組的頁直接對映到一個分頁的地址空間,因此需要引導模組是頁對齊的。
 
    如果設定了 flags 字中的 1 位,則必須通過 Multiboot 資訊結構(參見引導資訊格式)的 mem_* 域包括可用記憶體的資訊。如果載入程式能夠傳遞記憶體分佈(mmap_*域)並且它確實存在,則也包括它。
 
    如果設定了 flags 字中的 2 位,有關視訊模式表(參見引導資訊格式)的資訊必須對核心有效。 
 
    如果設定了 flags 字中的 16 位,則 Multiboot 頭中偏移量 8-24 的域有效,載入程式應該使用它們而不是實際可執行頭中的域來計算將 OS 映象載入到那裡。如果核心映象為 ELF 格式則不必提供這樣的資訊,但是如果映象是 a.out 格式或者其他什麼格式的話就必須提供這些資訊。
 
checksum 
    域 checksum 是一個 32 位的無符號值,當與其他的 magic 域(也就是 magic 和 flags)相加時,結果必須是 32 位的無符號值 0(即magic + flags + checksum = 0)
 
header_addr  
    這裡往後的 32 個位元組不是必須的,並且對於核心為 ELF 格式時是不需要的,因此就不介紹了。 
 
當載入程式呼叫32位作業系統時,機器狀態必須如下: 
 
EAX 
    必須包含魔數 0x2BADB002;這個值指出作業系統是被一個符合 Multiboot 規範的載入程式載入的。 
EBX 
    必須包含由載入程式提供的 Multiboot 資訊結構的實體地址(參見引導資訊格式)。 
CS 
    必須是一個偏移量位於 0 到 0xFFFFFFFF 之間的 32 位可讀/可執行程式碼段。這裡的精確值未定義。 
DS 
ES 
FS 
GS 
SS 
    必須是一個偏移量位於 0 到 0xFFFFFFFF 之間的 32 位可讀/可執行程式碼段。這裡的精確值未定義。 
A20 gate 
    必須已經開啟。 
CR0 
    第31位(PG)必須為 0。第 0 位(PE)必須為 1。其他位未定義。 
EFLAGS 
    第17位(VM)必須為 0。第 9 位(IF)必須為 1 。其他位未定義。 
 
所有其他的處理器暫存器和標誌位未定義。這包括: 
 
ESP 
    當需要使用堆疊時,OS 映象必須自己建立一個。 
GDTR 
    儘管段暫存器像上面那樣定義了,GDTR 也可能是無效的,所以 OS 映象決不能載入任何段暫存器(即使是載入相同的值也不行!)直到它設定了自己的 GDT。 
IDTR 
    OS 映象必須在設定完它的 IDT 之後才能開中斷。 
 
Multiboot 資訊結構(就目前為止定義的)的格式如下: 
 
             +-------------------+ 
     0       | flags                |    (必需) 
             +-------------------+ 
     4       | mem_lower         |    (如果flags[0]被置位則出現) 
     8       | mem_upper         |    (如果flags[0]被置位則出現) 
             +-------------------+ 
     12      | boot_device       |    (如果flags[1]被置位則出現) 
             +-------------------+ 
     16      | cmdline           |    (如果flags[2]被置位則出現) 
             +-------------------+ 
     20      | mods_count        |    (如果flags[3]被置位則出現) 
     24      | mods_addr         |    (如果flags[3]被置位則出現) 
             +-------------------+ 
     28 - 40 | syms              |    (如果flags[4]或flags[5]被置位則出現) 
             |                   |                  
             +-------------------+ 
     44      | mmap_length       |    (如果flags[6]被置位則出現) 
     48      | mmap_addr         |    (如果flags[6]被置位則出現) 
             +-------------------+ 
     52      | drives_length     |    (如果flags[7]被置位則出現) 
     56      | drives_addr       |    (如果flags[7]被置位則出現) 
             +-------------------+ 
     60      | config_table      |    (如果flags[8]被置位則出現) 
             +-------------------+ 
     64      | boot_loader_name  |    (如果flags[9]被置位則出現) 
             +-------------------+ 
     68      | apm_table         |    (如果flags[10]被置位則出現) 
             +-------------------+ 
     72      | vbe_control_info  |    (如果flags[11]被置位則出現) 
     76      | vbe_mode_info     | 
     80      | vbe_mode          | 
     82      | vbe_interface_seg | 
     84      | vbe_interface_off | 
     86      | vbe_interface_len | 
             +-------------------+ 
      
第一個 longword 指出 Multiboot 資訊結構中的其它域是否有效。所有目前未定義的位必須被載入程式設為 0。作業系統應該忽略任何它不理解的位。因此,flags 域也可以視作一個版本標誌符,這樣可以無破壞的擴充套件Multiboot資訊結構。
 
如果設定了 flags 中的第 0 位,則 mem_* 域有效。mem_lower 和 mem_upper 分別指出了低端和高階記憶體的大小,單位是 K。低端記憶體的首地址是 0,高階記憶體的首地址是 1M。低端記憶體的最大可能值是 640K。返回的高階記憶體的最大可能值是最大值減去 1M。但並不保證是這個值。
 
flags 的其他位我沒有用到,這裡就不介紹了。需要了解的請自己閱讀相關文件。 
 
下面是一個最簡單的例子: 
 
 
  1. /**  
  2.  * boot.S  
  3.  */  
  4. #define MULTIBOOT_HEADER_MAGIC          0x1BADB002  
  5. #define MULTIBOOT_HEADER_FLAGS          0x00000003  
  6. #define STACK_SIZE                      0x4000  
  7. .text  
  8.         .globl  start, _start  
  9. start:  
  10. _start:  
  11.         jmp     multiboot_entry  
  12.         .align  4  
  13. multiboot_header:  
  14.         .long   MULTIBOOT_HEADER_MAGIC  
  15.         .long   MULTIBOOT_HEADER_FLAGS  
  16.         .long   -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)  
  17. multiboot_entry:  
  18.         /* 初始化堆疊指標。 */  
  19.         movl    $(stack + STACK_SIZE), %esp  
  20.         /* 重置 EFLAGS。 */  
  21.         pushl   $0  
  22.         popf  
  23.         pushl   %ebx  
  24.         pushl   %eax  
  25.         /* 現在進入 C main 函式... */  
  26.         call    cmain  
  27. loop:   hlt  
  28.         jmp     loop  
  29.         .comm   stack, STACK_SIZE  


  1. /** 
  2.  * kernel.c 
  3.  */
  4. /* a.out 符號表。 */
  5. typedefstruct aout_symbol_table  
  6. {  
  7.     unsigned long tabsize;  
  8.     unsigned long strsize;  
  9.     unsigned long addr;  
  10.     unsigned long reserved;  
  11. } aout_symbol_table_t;  
  12. /* ELF 的 section header table。 */
  13. typedefstruct elf_section_header_table  
  14. {  
  15.     unsigned long num;  
  16.     unsigned long size;  
  17.     unsigned long addr;  
  18.     unsigned long shndx;  
  19. } elf_section_header_table_t;  
  20. /* Multiboot 資訊。 */
  21. typedefstruct multiboot_info  
  22. {  
  23.     unsigned long flags;  
  24.     unsigned long mem_lower;  
  25.     unsigned long mem_upper;  
  26.     unsigned long boot_device;  
  27.     unsigned long cmdline;  
  28.     unsigned long mods_count;  
  29.     unsigned long mods_addr;  
  30.     union
  31.     {  
  32.         aout_symbol_table_t aout_sym;  
  33.         elf_section_header_table_t elf_sec;  
  34.     } u;  
  35.     unsigned long mmap_length;  
  36.     unsigned long mmap_addr;  
  37. } multiboot_info_t;  
  38. /* 檢測 FLAGS 中的位 BIT 是否被置位。 */
  39. #define CHECK_FLAG(flags,bit)   ((flags) & (1 << (bit)))
  40. /* 與顯示相關的設定。 */
  41. #define COLUMNS                 80
  42. #define LINES                   24
  43. #define ATTRIBUTE               7
  44. #define VIDEO                   0xB8000
  45. staticint xpos; /* X 座標。 */
  46. staticint ypos; /* Y 座標。 */
  47. staticvolatile unsigned char *video; /* 指向視訊記憶體。 */
  48. staticvoid cls (void);  
  49. staticvoid itoa (char *buf, int base, int d);  
  50. staticvoid putchar (int c);  
  51. void printf (constchar *format, ...);  
  52. void cmain (unsigned long magic, unsigned long addr)  
  53. {  
  54.     multiboot_info_t *mbi;  
  55.     /* 清屏。 */
  56.     cls ();  
  57.     /* 將 MBI 指向 Multiboot 資訊結構。 */
  58.     mbi = (multiboot_info_t *) addr;  
  59.     /* mem_* 是否有效? */
  60.     if (CHECK_FLAG (mbi->flags, 0))  
  61.         printf ("mem_lower = %uKB, mem_upper = %uKB\n", (unsigned) mbi->mem_lower, (unsigned) mbi->mem_upper);  
  62.     /* your code here. */
  63. }  
  64. /* 清屏並初始化 VIDEO,XPOS 和 YPOS。 */
  65. staticvoid cls (void)  
  66. {  
  67.     int i;  
  68.     video = (unsigned char *) VIDEO;  
  69.     for (i = 0; i < COLUMNS * LINES * 2; i++)  
  70.         *(video + i) = 0;  
  71. 相關推薦

    GRUB 引導自己作業系統

    在 PC 機上搗鼓自己的作業系統遇到的第一個難題就是如何將核心載入到記憶體中執行。如果讀過於淵寫的《自己動手寫作業系統》就會知道這部分的工作還是蠻繁瑣的。而且實際上這部分工作和作業系統沒太大的關係。好在隨著 linux 等開源作業系統的發展,開源的引導載入程式也已經發展的

    在U盤上GRUB引導Linux

    [前言]    今天有人問我,如何用U盤引導Linux,我就試了一下。把過程寫在這上面,備忘。   之後,就BOOT的問題繼續展開。  [準備]  U盤(愛國者,NEWMAN之類的FLASH盤都可以)  PC(BIOS一定要能夠支援從USB引導)  Linux作業系統(我

    通過GRUB引導Windows作業系統

    1、通過編輯 menu.lst 來引導Windows 系統; 如果您的Windows所處於的分割槽在(hd0,0),可以在menu.lst 加如下的一段就能引導起來了; title WinXp        rootnoverify (hd0,0)        chain

    easybcd引導ubuntu卻進入grub命令列的遭遇與解決

    今天早上開機,電腦突然讀不到固態硬碟(系統盤)了,用老方法拔掉機械硬碟後重啟,成功啟動。 下午的時候想用Ubuntu,發現選擇linux的時候卻進入grub命令列 回到Windows用easybcd重新設定引導 無果 上網找的方案都無法解決 無意中開啟BIOS設定 在“啟動

    Linux系統中修復GRUB引導故障手動引導進入作業系統

             GRUB是大多數Linux系統預設使用的載入程式,可以通過啟動選單的方式選擇進入不同的作業系統(如果有的話)。當“/boot/grub/grub.conf”配置檔案丟失,或者關鍵配置出現錯誤,或者MBR記錄中的載入程式遭到破壞時,Linux主機啟動後可能只

    iOS App初次啟動時的引導頁制作實例分享

    復制代碼 bound gpo 並且 fault launch boa com content iOS App初次啟動時的用戶引導頁制作實例分享 作者:老初 字體:[增加 減小] 類型:轉載 時間:2016-03-09 我要評論 這篇文章主要介紹了iOS App初次啟動

    Js引導插件intro

    ons ansi position hint click utf done 地址 htm 1.demo直接貼上來了,有什麽不懂的,直接去官網上看,地址:https://introjs.com/。 2.這個intro插件的版本是v2.7.0,復制下來代碼,引入庫應該直接可以運

    13.2、grub引導程序介紹

    操作 otf selinux 配置 def table 進程 nac making 1、linux系統啟動流程: post--->根據主板上ROM固件中的系統設置、判斷從哪個設備啟動--->讀取設備的第一扇區的前512字節(MBR),判斷用什麽bootlo

    ubuntu live cd修復grub引導

    硬盤分區 處理方法 ase stage ace body try roo details 1. 通過Ubuntu Live CD(安裝盤,選擇try Ubuntu)進入Ubuntu系統 打開終端,依次進行如下操作: [plain] view plain copy

    flask編寫自己的博客(2)

    flask python web 本文對blog項目的文件結構進行簡單說明app ---> 程序主目錄api-->測試中的apiauth -->登錄認證文件main-->主入口文件static--> 靜態文件template-->html 模板文件migrati

    19、vftpd基於PAM_MYSQL進行虛擬戶的認證且每個戶有自己的獨立目錄及不同的訪問權限

    dir 請求 種類型 erl 歡迎信息 註意 連接服務器 主動模式 red 1、vsftp相關介紹FTP 是File Transfer Protocol(文件傳輸協議)的英文簡稱 兩個連接:命令連接、數據連接(相對服務器來講)FTP連接支持兩種模式:主動模式(Port模式)

    Photoshop製作自己喜歡的桌面

    今天工作完,準備關電腦時,發現桌面已不像樣子了,很是頭痛,然後就試著用Photoshop製作了一個簡單分門類別的桌面,先截個樣本 我的左上角一般放置計算機方面的圖示,中間綠色的框放置開發工具,右上角放置設計工具,左下角是娛樂方面的圖示,右下角都是簡易工具圖示,中間空的那塊放臨時的檔案之

    JAVA做自己喜歡的事情,我的船在遠方

    最早接觸Java是小時候的遊戲,覺得做遊戲的人也太厲害了。之後慢慢的也接觸一些程式設計,純屬興趣了。來到上海黑馬Java54期學習,所謂隔行如隔山,零基礎學起來確實有些難受,會感覺很缺理論知識。但關鍵還是看個人,零基礎學成大佬、大牛的人大有人在。經過了十六天的基礎班課程學習,來到了就業班。當然上就業

    caffe訓練自己的資料集(三)

    本文主要參考了:https://blog.csdn.net/heimu24/article/details/53581362                     https://blog.csd

    caffe訓練自己的資料集(二)

    本文主要參考了:https://blog.csdn.net/heimu24/article/details/53581362                     https://blog.c

    caffe訓練自己的資料集(一)

    本文主要參考了:https://blog.csdn.net/heimu24/article/details/53581362                     https://blog.csd

    Python給自己手機寫了個人臉識別!拉開普通手機識別一萬倍!

        本文首先會介紹人臉識別模型的內部工作原理。隨後結合一個簡單的案例,我們將通過Python進行案例實踐。在本文的最後部分,你將完成你的第一個人臉識別模型! 目錄 理解人臉識別的工作原理 案例學習 Python應用 理解Py

    Win10yolov3訓練自己的資料

    哈哈,我們的效率還是很棒的,先自誇一下~廢話不多說,下面就是正宮娘娘: 接上次的部落格(yolo環境配好以後) 製作自己的資料集 首先就是製作資料集啦,我們是自己在校園裡面拍的共享單車,訓練集大概有兩三百張的樣子,還留了一小部分估計也有一百張的樣子做測試集。當然也有SAMA的部落格直

    tp3.2 ajax上傳圖片(可以,樣式要自己寫)

    html頁面 <form id="mbInfoForm" action="" method="POST"> <input type="hidden" name="idcard_up" id="idcard_up"> <table class="formTbl">

    系統排錯3:若誤刪grub引導檔案,如何恢復?

    系統排錯 若誤刪grub引導檔案,如何恢復? (1)刪除grub引導檔案但系統並未重啟 1).模擬實驗環境 [[email protected] ~]# cd /boot/grub2 [[email protected] grub2]# ls device