最後更新於2020年6月7日。
Multiboot與GRUB
- 一、Multiboot2規範 v2.0
- 1、介紹
- 2、措辭與術語
- 3、`Multiboot2`規範的確切定義
- 3.1、操作系統映像格式
- 3.1.1、`Multiboot2`頭的定義
- 3.1.2、`Multiboot2`頭中的字段
- 3.1.3、標籤`tag`結構體
- 3.1.4、`Multiboot2` information request
- 3.1.5、`Multiboot2`頭中的地址標籤`tag`
- 3.1.6、`Multiboot2`頭中的項目地址Tag
- 3.1.7、`Multiboot2`頭中的EFI i386項目地址Tag
- 3.1.8、`Multiboot2`頭中的EFI amd64項目地址Tag
- 3.1.9、標誌Tag
- 3.1.10、幀緩衝Tag
- 3.1.11、模塊對齊Tag
- 3.1.12、EFI啓動服務Tag
- 3.1.13、重定位頭Tag
- 3.2、MIPS機器狀態
- 3.3、I386機器狀態
- 3.4、具有可啓動服務的EFI i386機器狀態
- 3.5、具有可啓動服務的EFI amd64機器狀態
- 3.6、啓動信息格式
- 3.6.1、啓動信息格式
- 3.6.2、基本標籤`tag`結構體
- 3.6.3、基本內存信息
- 3.6.4、BIOS啓動設備
- 3.6.5、啓動命令行
- 3.6.6、模塊
- 3.6.7、ELF節頭表
- 3.6.8、內存映射
- 3.6.9、引導加載器名稱
- 3.6.10、APM表
- 3.6.11、VBE信息
- 3.6.12、幀緩衝信息
- 3.6.13、ELF32位系統表指針
- 3.6.14、ELF64位系統表指針
- 3.6.15、SMBIOS表
- 3.6.16、舊RSDP(ACPI 1.0)
- 3.6.17、新RSDP(ACPI 2.0及以上)
- 3.6.18、網絡信息
- 3.6.19、EFI內存映射
- 3.6.20、EFI啓動服務未被終止
- 3.6.21、EFI32位映像處理指針
- 3.6.22、EFI64位映像處理指針
- 3.6.23、映像加載物理基地址
- 4、示例
- 5、本規範更新日誌
- 6、索引
- 二、GRUB
- 三、代碼實例
一、Multiboot2規範 v2.0
- 本規範是漢化版本,最後更新於2020年6月1日。由於能力有限,翻譯可能存在錯誤。現附上規範原文The Multiboot2 Specification version 2.0
,如果錯誤之處,請指正。 - 本翻譯先通過金山詞霸在線翻譯,然後對機器翻譯的結果進行調整。譯者儘可能從原始的角度翻譯原規範,有一部分內容是從理解的角度翻譯的,如果翻譯有誤,請遵照原文。
1、介紹
本章粗略地描述了關於Multiboot2
規範的一些信息。 請注意,這不是規範本身的一部分。
1.1、背景內容
當前設計的每個操作系統可能都有其自己的引導程序。在機器上安裝一個新的操作系統通常需要安裝一套全新的引導機制,這些引導機制在安裝過程中及引導過程都有一個相互完全不同用戶界面。在一臺機器上安裝有多個操作系統,如果用典型的鏈式引導機制讓這些操作系統可靠地共存,這是不現實的。對於一個特定的操作系統來說,可選的引導加載器不是很多,甚至根本沒有可選的解決方案。如果不能按照您的要求隨心所欲地使用操作系統隨附的引導加載器,或者引導加載器無法在您的機器上工作,這個時候您會感到非常困惑甚至無所適從。
因爲版權問題,我們可能無法在私有操作系統中解決這個問題。但對於自由操作系統社區的一些人來說,他們可以通過各種方式聚在一起爲流行的自由操作系統解決此類問題不是很困難。這就是制定這個規範的目的所在。基本上,該規範指定了引導加載程序與操作系統之間的接口,這樣任何符合規範的引導加載程序都應該能夠加載任何符合的操作系統。本規範沒有指定引導加載程序應該如何工作,只是規定了它們必須如何與正在加載的操作系統一起使用該接口。
1.2、目標架構
此規範主要針對PC,因爲它們是最常見的,同時在其上運行着大量的操作系統和引導程序。 但是,如果需要在其他體系結構上引導內核,而在該架構上還沒有合適的引導規範,則可以將本規範中特定於x86平臺的細節去掉並做出適當的調整,即可將本規範適應於特定的體系架構。
1.3、目標系統
本規範主要針對的是自由的32爲操作系統。這些操作系統不經過大幅度調整,就可以很容易地進行修改以支持規範。本規範主要是爲Linux、Free BSD、Net BSD、Mach以及VSTa等操作系統或內核設計,同時也希望其他新興的自由操作系統從一開始就採用它,從而能使用現有的引導加載器。很高興能有私有操作系統供應商最終也採用這個規範,但這可能不太現實。
1.4、啓動方式
應該編寫可以兼容的引導加載程序,這樣操作系統鏡像就可以從諸如軟盤、硬盤,甚至網絡等不同來源被加載。
基於磁盤的引導加載器可以使用各種技術來查找磁盤上的操作系統映像和引導模塊。例如,可以通過特定的文件系統加載系統(BSD/Mach 引導加載器);也可以使用預先規定好的磁盤塊列表加載系統(LILO);還可以從特定的啓動分區加載系統(OS/2);甚至,還可以另一個操作系統中加載系統(例如,可以從DOS中加載VSTa引導代碼);類似地,基於網絡的引導加載器可以使用各種不同的網絡硬件和協議。
同時,我們都希望創建的引導加載器可以支持多種不同的加載機制,這樣可以提高其可移植性、健壯性和用戶友好性。
1.5、在引導階段配置操作系統
處於某種原因,用戶往往希望在加載啓動時能夠動態地向操作系統提供一些配置信息。雖然本規範不應該規定引導加載程序如何獲取此配置信息,但它應該爲此提供一個標準化的途徑。
1.6、如何更容易地開發操作系統
操作系統映像應該是很容易被生成的。理想情況下,操作系統映像應該是一個普通的32位可執行文件,這個可執行文件可以是操作系統通常使用的任何一種文件格式。操作系統映像應該是可以被nm工具或反彙編的,看起來就像是一個正常可執行文件。不應該要求使用專門的工具以一種特殊的文件格式來創建系統映像。因爲,引導加載程序所使用的內存空間通常可在引導結束後再次被使用,而通常系統映像中的所有代碼都必須永遠保持在內存中。所以,將一些工作從操作系統轉移到引導加載程序是合適的。如果要將操作系統數據加載到1MB以上的內存空間,則必須提前將CPU工作模式從16位實模式切換到32位保護模式。操作系統不應該承擔這一過程的責任,因爲這一部分的代碼通常是在引導加載程序中的。如果在設計操作系統時不遵守這種約定,那麼將會導致創建操作系統映像會變得更加困難。
不幸的是,即便是在自由的類Unix的PC操作系統中,也有各種各樣形式不同的可執行文件格式。通常,不同的操作系統都會採用不同的可執行文件格式。雖然,大多數自由操作系統都在使用一種基於a.out的變體格式,但好在其中的一些正在向ELF可執行文件格式遷移。不需要解釋所有已存在的不同類型的可執行文件格式來加載操作系統映像,這是一件對於引導加載器來說很期望的事情。否則,引導加載器將不再是通用型的了,從而變成只針對於某些特定操作系統的“專屬加載器”了。
爲了解決這個問題,本規範採用了一種折衷的解決方案。這個解決方案要求符合Multiboot2
規範的操作系統映像必須包含一個特殊的Multiboot2
頭(查看3.1節 [操作系統鏡像格式])。操作系統映像包含Multiboot2
頭後,引導加載程序在加載映像時不再需要理解各種不同的a.out的變體或其他可執行格式。這個特殊的結構體(Multiboot2
頭)不需要出現在可執行文件最開始的地方。這樣,內核映像就可以同時符合特定的可執行文件格式和Multiboot2
規範了。
1.7、啓動模塊
許多現代操作系統內核,諸如Mach和VSTa中的微內核,它們自身並不包含足夠的機制來使系統獨立運行。相反,它們需要在系統啓動時加載額外的軟件模塊,以便能夠訪問設備、安裝文件系統等。雖然這些附加的模塊可以與內核本身一起嵌入到磁盤映像中,但是,如果引導加載程序能夠首先獨立地加載這些附加模塊,然後操作系統映像在控制序列的操控下分割磁盤映像並逐一加載,這樣會讓操作系統和用戶都感覺更加靈活,更加節省空間,也更加方便。
因此,本規範爲引導加載程序提供了一種標準方法,這種方法使得引導加載程序向操作系統告知了這些輔助引導模塊如何被加載。雖然引導程序不是必須要加載這些輔助引導模塊,但是對於那些特定的操作系統這是必須的。因爲沒有這些輔助引導模塊,那些操作系統將無法完成引導啓動。
2、措辭與術語
【在將措辭與術語的解釋進行翻譯的過程中,會不可避免地引進一些誤差或錯誤,而這些措辭屬於比較號理解。爲了表述的嚴謹性,這一節只列出措辭術語的名詞,相關解釋請查閱本規範原文】
- must
- should
- may
- boot loader
- OS image, kernel
- boot module
- Multiboot2-compliant
- u8
- u16
- u32
- u64
3、Multiboot2
規範的確切定義
引導加載器與操作系統映像之間的接口主要有三個方面:
- 引導加載程序所看到的操作系統映像的格式。
- 引導加載程序啓動操作系統時機器的狀態。
- 引導加載程序傳遞給操作系統的信息格式。
3.1、操作系統映像格式
操作系統映像可以是某一特定操作系統採用的標準的普通的32可執行文件,但它也可以被鏈接在一個非默認的加載地址,這樣可以避免加載在PC頂部的I/O地址空間或其他保留的地址空間。並且,磁盤映像文件不允許使用共享庫或其他有趣的特性功能。
OS映像自身使用某一種格式的文件頭,但除此之外,它還必須包含一個稱爲Multiboot2
的頭部。Multiboot2
頭必須包含在OS映像的前32768字節內,而且不許是64位對齊,也就是4字節對齊。通常情況下,應該將Multiboot
頭放在儘可能前的地方。它通常是嵌入到可執行文件頭部之後代碼段的起始部位。
3.1.1、Multiboot2
頭的定義
以下爲Multiboot2
頭的格式:
Offset | Type | Field Name | Note |
---|---|---|---|
0 | u32 | magic | required |
4 | u32 | architecture | required |
8 | u32 | header_length | required |
12 | u32 | checksum | required |
16-XX | tags | required |
- 字段
magic
、architecture
、header_length
和checksum
在3.1.2 [Multiboot2
頭中的字段] 中定義; - 字段
tags
在3.1.3 [標籤tag
結構體] 中定義; - 所有的字段的存儲順序依據當前系統是大端還是小端。
3.1.2、Multiboot2
頭中的字段
3.1.2.1、 magic
字段magic
是指示Multiboot
頭的魔數,該字段必須爲16進制值0xE85250D6。
3.1.2.2、 architecture
字段architecture
指定CPU指令集體系結構。由於magic
字段不是迴文,Multiboot2規範具體實現可以據此來確定當前平臺的大小端情況。architecture
字段的值爲0時,則表示是i386的32位(受保護)模式;如果是4則表示是32位MIPS。
3.1.2.3、 header_length
字段header_length
以字節爲單位指定Multiboot2
頭的長度,其中包括magic
字段。
3.1.2.4、 checksum
字段checksum
是一個32位無符號值,使得magic
、architecture
、header_length
、checksum
這四個字段的值的和必須是32位無符號0。
3.1.3、標籤tag
結構體
標籤tag
是結構體緩衝區,這個結構體緩衝區在必要的時候進行填充,以便每個標籤tag
會從8字節對齊的地址處開始。所有的標籤tag
排列組成一個標籤列表,作爲終止標籤的最後一個標籤,其類型爲0、大小爲8。每一個標籤tag
結構體都具有以下結構:
大小 | 名稱 |
---|---|
u16 | type |
u16 | flags |
u32 | size |
type
分爲兩部分。type
類型的低字節部分包含了標籤tag
的類型。size
字段包含了該標籤tag
中所有字段的大小總和。flags
字段的位0指示了該標籤tag
是可選的。如果flags
字段的位0被置位,那麼當缺少相關支持時,引導加載程序會忽略此標籤tag
。
3.1.4、Multiboot2
information request
大小 | 名稱 |
---|---|
u16 | type = 1 |
u16 | flags |
u32 | size |
u32 | mbi_tag_types |
字段mbi_tag_types
是一個32位無符號整型數組,每一個元素表示一個請求信息。
對於這個標籤tag
,如果設置字段flags
中可選標誌位位0,則引導加載程序必須支持這個請求的標籤tag
,並且如果圖形模式可用,則必須爲操作系統提供相關的圖形模式信息。如果引導加載程序不支持所請求的標記的含義,則必須以錯誤失敗結束。然而,如果引導加載程序支持給定的標記,但爲其所傳遞的信息不可用,則引導加載程序就不會在Multiboot2
信息結構中提供所請求的標記,並且將控制權交給被加載的系統映像。
注:以上說明意味着不能保證任何類型的mbi_tag_types
標籤實際上都是存在的。例如,在視頻系統中,即便您請求標籤類型爲8的標籤tag
,同時引導加載程序也支持它,但Multiboot2
信息結構中也不會出現對應的標籤tag
。
3.1.5、Multiboot2
頭中的地址標籤tag
大小 | 名稱 |
---|---|
u16 | type = 2 |
u16 | flags |
u32 | size |
u32 | header_addr |
u32 | load_addr |
u32 | load_end_addr |
u32 | bss_end_addr |
該標籤tag
中的所有的地址字段均時物理地址。每個字段的含義如下:
3.1.5.1、header_addr
該字段包含將操作系統映像加載到內存後,Multiboot2
在內存中的物理內存地址。該字段用於同步操作系統內核與物理內存地址之間的映射。
3.1.5.2、load_addr
該字段包含Multiboot2頭所在代碼段的起始物理地址。在操作系統內核映像中,引導加載器確定文本段相對於Multiboot2
頭的相對偏移是通過計算得到的:header_addr - load_addr
。這樣就可以正確地確定映像中文本段的起始位置。load_addr
字段必須小於或等於header_addr
,這樣所得到的相對偏移數值爲正。另外,也可以賦值爲特殊值-1,表明必須從磁盤映像文件的起始處開始加載。
3.1.5.3、load_end_addr
該字段包含數據段末尾的物理地址。load_end_addr - load_addr
表示有多少字節的數據需要被加載進內存。這也意味着文本段和數據段在操作系統映像中必須是連續的。現有的a.out
可執行文件格式是否和此要求的。對於特殊值0,表示代碼段和數據段佔用了整個操作系統磁盤映像文件。
3.1.5.4、bss_end_addr
3.1.6、Multiboot2
頭中的項目地址Tag
3.1.7、Multiboot2
頭中的EFI i386項目地址Tag
3.1.8、Multiboot2
頭中的EFI amd64項目地址Tag
3.1.9、標誌Tag
3.1.10、幀緩衝Tag
3.1.11、模塊對齊Tag
3.1.12、EFI啓動服務Tag
3.1.13、重定位頭Tag
3.2、MIPS機器狀態
3.3、I386機器狀態
3.4、具有可啓動服務的EFI i386機器狀態
3.5、具有可啓動服務的EFI amd64機器狀態
3.6、啓動信息格式
3.6.1、啓動信息格式
引導加載程序將控制權交給操作系統後,EBX
寄存器包含Multiboot2
信息數據結構的物理地址,引導加載程序通過該結構將重要信息傳遞給操作系統。操作系統可以選擇或忽略Multiboot2
信息數據結構中的任何部分。引導加載程序傳遞的所有信息僅僅只能作爲查詢使用。
Multiboot2
信息結構及其相關子結構可以由引導加載程序放置在內存的任何地方。當然,這些數據不會被放置在內核加載以及引導模塊保留的內存區域中。當引導加載程序將控制權交給操作系統後,操作系統在使用完這些信息之前,必須確保這塊區域不能被覆蓋。
3.6.2、基本標籤tag
結構體
啓動引導信息是由固定部分和一系列標籤組成。這些標籤的起始地址是以8字節對齊的。固定部分如下:
大小 | 名稱 |
---|---|
u32 | total_size |
u32 | reserved |
- 字段
total_size
包含引導信息的大小總和,這其中也包括了該字段本身以及以字節爲單位的終止標記。 - 字段
reserved
總是被設定爲0,操作系統應該總是忽略這個字段。
每一個標籤tag
都以以下字段開始:
大小 | 名稱 |
---|---|
u32 | type |
u32 | size |
- 字段
type
包含標籤tag
其餘部分內容的標識符,也就是標籤tag
的類型。 - 字段
size
包含標籤tag
的大小,其中包括頭字段,但不包括填充部分。
這些標籤必須相互跟隨(原文在此處的描述是:“Tags follow one another padded when necessary in order for each tag to start at 8-bytes aligned address.”),也就是說在內存空間中緊密排列,以便每個標籤都是從8字節對齊的地址開始。這些標籤組成了一個標籤列表,這個列表以type
爲0並且size
爲8的標籤結束。
3.6.3、基本內存信息
大小 | 名稱 |
---|---|
u32 | type = 4 |
u32 | size = 16 |
u32 | mem_lower |
u32 | mem_upper |
字段mem_lower
和mem_upper
指示低端內存和高端內存的大小,其值是以KB爲單位。低端內存從是從地址0處開始的,高端內存是從1MB地址處開始的。低端內存的最大值可能是640KB。高端內存的值是存儲器最大存儲值減去1MB的數值。但是,引導加載程序不能保證這些值的有效性。
在一些EFI平臺上,如果啓用了EFI啓動服務並且可以加載映像,引導加載器可能不會支持這個標籤。在Multiboot2
信息結構中存在未終止的標籤tag
(原文在此的描述是:“EFI boot services not terminated tag exists in Multiboot2 information structure”)。
3.6.4、BIOS啓動設備
大小 | 名稱 |
---|---|
u32 | type = 5 |
u32 | size = 20 |
u32 | biosdev |
u32 | partition |
u32 | sub_parition |
這個標籤tag
指明瞭引導加載程序是從哪一個BIOS磁盤設備加載這個操作系統映像的。如果操作系統映像不是從一個BIOS磁盤設備引導的,那麼這個標籤tag
不會出現。操作系統可以使用這個字段來確定其根設備,但並不是必須這麼作。
剩餘的三個字段指明瞭引導分區。(原文在此的描述是:“The three remaining bytes specify the boot partition.”,並且位置也不在此處。如果有疑問,請閱讀本節的原文描述。)
- 字段
biosdev
包含了BIOS設備號,這個設備號是由底層磁盤接口BIOS INT 0x13 中斷提供的。例如:第一個軟盤的設備號是0x00,第二個硬盤的設備號是0x80。 - 字段
partition
指明瞭引導分區的主分區號。 - 字段
sub_partition
指明瞭引導分區在所在主分區中的次分區號。
分區號總是從0開始的。未使用的字段必須被設置爲0xFFFFFFFF,例如,如果一個磁盤被分區爲一個簡單的單級DOS分區格式,那麼字段partition
中保存着DOS分區號,並且字段sub_partition
被設置爲0xFFFFFFFF。在另一個例子中,如果把DOS主分區進一步劃分爲幾個BSD分區,那麼字段partition
中保存着DOS主分區號,同時字段sub_partition
保存着在BSD分區子級分區在DOS主分區中的子分區號。
DOS擴展分區的分區號是從4開始的,並且遞增。這一點和嵌套的子分區不同,儘管擴展分區的底層磁盤佈局具有層次性。(原文在此的描述是:“DOS extended partitions are indicated as partition numbers starting from 4 and increasing, rather than as nested sub-partitions, even though the underlying disk layout of extended partitions is hierarchical in nature.”)舉個例子,在一個DOS分區格式的磁盤上,如果引導加載程序是從磁盤第二個擴展分區引導系統的,那麼字段partition
將是5,而字段sub_partiton
將是0xFFFFFFFF。
3.6.5、啓動命令行
大小 | 名稱 |
---|---|
u32 | type = 1 |
u32 | size |
u8[n] | string |
- 字段
string
中包含了啓動命令行。這個命令行是一個UTF-8編碼的以0結束的普通的C風格字符串。
3.6.6、模塊
大小 | 名稱 |
---|---|
u32 | type = 3 |
u32 | size |
u32 | mod_start |
u32 | mod_end |
u32 | string |
這個標籤tag
向內核指示了有哪些引導模塊是與內核映像一同被加載進內存的,以及在內存何處可以被找到這些模塊。
- 字段
mod_start
包含了引導模塊的起始物理地址。 - 字段
mod_end
包含了引導模塊的結束物理地址。 - 字段
string
包含了與特定引導模塊相關的一個描述性字符串。與內核命令行一樣,這是一個UTF-8編碼的以0結束的字符串。
通常,字符串可能是命令行(如果操作系統將引導模塊視爲可執行程序),也可能是路徑名(如果操作系統將引導模塊視爲文件系統中的文件),但其確切用途是依據特定操作系統而定的。
每一個模塊會對應一個標籤tag
。這個標籤tag
可能會出現多次。
3.6.7、ELF節頭表
大小 | 名稱 |
---|---|
u32 | type = 9 |
u32 | size |
u16 | num |
u16 | entsize |
u16 | shndx |
u16 | reserved |
u16 | section headers |
(依據給出的頭文件multiboot2.h
判斷,字段size
、entsize
、shndx
、reserved
應該是u32
類型的,字段section headers
應該是u8
的數組。這一點與規範中此處的描述不符。)
如果被加載的內核映像是一個ELF文件內核,則此標籤tag
包含了其節頭表(section header table),節頭表中的每一個條目的大小,以及字符串表中用來引用節名的偏移量(原文使用“index”)。它們相當於ELF規範中在ELF頭(原文使用“the program header”,但ELF中另有程序頭表)的“shdr_*”這樣的條目(例如“shdr_num”)。所有的節都會被加載。
- 字段
num
中保存ELF節頭表中的項數。 - 字段
entsize
中保存ELF節頭表中每一項的大小。 - 字段
shndx
中保存節名錶在節表中的的序號。 - 字段
section headers
中保存節頭表內容。此內容和ELF中的節頭表內容完全一致。
ELF節頭表條目結構如下:
#define EI_NIDENT (16)
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
3.6.8、內存映射
這個標籤tag
提供內存映射。
大小 | 名稱 |
---|---|
u32 | type = 6 |
u32 | size |
u32 | entry_size |
u32 | entry_version |
varies | entries |
- 字段
size
包含了這個標籤tag
的大小,且包括字段entries
。(原文在此的描述是:“‘size’ contains the size of current entry including this field itself.”,且位置也不再此處。) - 字段
entry_size
包含每一個條目的大小,以便將來可以在其中添加新字段。這個字段的值必須是8的倍數。 - 字段
entry_version
當前必須被設置爲0。在未來的版本中會遞增這個字段,當然未來的版本是向後兼容舊版本的。
每一個條目有下面的結構:
大小 | 名稱 |
---|---|
u64 | base_addr |
u64 | length |
u32 | type |
u32 | reserved |
- 字段
base_addr
是所表示內存區域的起始地址。 - 字段
length
是所表示內存區域的大小。 - 字段
type
是所表示內存區域的類型。其中,數值1表示可用RAM;數值3表示保存ACPI信息的可用內存;數值4表示需要在休眠時保存數據用的保留內存(原文在此的描述是:“value of 4 indicates reserved memory which needs to be preserved on hibernation“);數值5表示有缺陷的RAM模塊(原文在此的描述是:“value of 5 indicates a memory which is occupied by defective RAM modules“);所有其他的值當前都表示保留的內存區域。 - 字段
reserved
被引導加載程序設置爲0,操作系統內核應該忽略這個字段。
type值 | 助記符 | 含義 |
---|---|---|
1 | AddressRangeMemory | 這一段內存是可以供操作系統使用的 |
2 | AddressRangeReserved | 這一段內存已經被用了,或者保留起來的,不被OS的memory manager去分配 |
3 | AddressRangeACPI | 當OS 讀過ACPI 表之後,就可使用的一段範圍 |
4 | AddressRangeNVS | ACPI NVS 內存,這一段內存已經被用了或者保留起來,是不能被OS去用的 |
5 | AddressRangeUnusuable | 這是一段不能有的地址,因爲檢測到了錯誤 |
6 | AddressRangeDisabled | 一段沒有被enabled的地址, 也是ospm 不可以用的 |
其他 | Undefined | 未定義,保留或者將來用。 |
提供的內存映射都是可被正常使用的,這些區域中可能包含內核、mbi數據、段和模塊。內核不應該覆蓋這些區域。(原文在此的描述是:“This type however includes the regions occupied by kernel, mbi, segments
and modules. Kernel must take care not to overwrite these regions.”)
在一些EFI平臺上,如果啓用了EFI啓動服務並且可以加載映像,引導加載器可能不會支持這個標籤。在Multiboot2
信息結構中存在未終止的標籤tag
(原文在此的描述是:“EFI boot services not terminated tag exists in Multiboot2 information structure”)。
3.6.9、引導加載器名稱
大小 | 名稱 |
---|---|
u32 | type = 2 |
u32 | size |
u8[n] | string |
- 字段
string
包含了加載內核的引導加載器的名稱。這個名稱是一個UTF-8編碼的以0結束的C風格字符串。
3.6.10、APM表
3.6.11、VBE信息
3.6.12、幀緩衝信息
3.6.13、ELF32位系統表指針
3.6.14、ELF64位系統表指針
3.6.15、SMBIOS表
3.6.16、舊RSDP(ACPI 1.0)
3.6.17、新RSDP(ACPI 2.0及以上)
3.6.18、網絡信息
3.6.19、EFI內存映射
3.6.20、EFI啓動服務未被終止
3.6.21、EFI32位映像處理指針
3.6.22、EFI64位映像處理指針
3.6.23、映像加載物理基地址
4、示例
4.1、C結構體成員對齊與填充
4.2、在PC上需要注意的問題
4.3、BIOS設備映射技術
4.3.1、數據比較技術
4.3.2、I/O限制技術
4.4、操作系統示例代碼
4.4.1、multiboot2.h
4.4.2、boot.S
4.4.3、kernel.c
4.4.4、Makefile
4.4.5、其他Multiboot2
內核
4.5、引導程序示例代碼
5、本規範更新日誌
6、索引
二、GRUB
三、代碼實例
multiboot2.h
/* multiboot2.h - Multiboot 2 header file. */
/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL A
* DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef MULTIBOOT_HEADER
#define MULTIBOOT_HEADER 1
/* How many bytes from the start of the file we search for the header. */
#define MULTIBOOT_SEARCH 32768
#define MULTIBOOT_HEADER_ALIGN 8
/* The magic field should contain this. */
#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6
/* This should be in %eax. */
#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289
/* Alignment of multiboot modules. */
#define MULTIBOOT_MOD_ALIGN 0x00001000
/* Alignment of the multiboot info structure. */
#define MULTIBOOT_INFO_ALIGN 0x00000008
/* Flags set in the ’flags’ member of the multiboot header. */
#define MULTIBOOT_TAG_ALIGN 8
#define MULTIBOOT_TAG_TYPE_END 0
#define MULTIBOOT_TAG_TYPE_CMDLINE 1
#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2
#define MULTIBOOT_TAG_TYPE_MODULE 3
#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4
#define MULTIBOOT_TAG_TYPE_BOOTDEV 5
#define MULTIBOOT_TAG_TYPE_MMAP 6
#define MULTIBOOT_TAG_TYPE_VBE 7
#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8
#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9
#define MULTIBOOT_TAG_TYPE_APM 10
#define MULTIBOOT_TAG_TYPE_EFI32 11
#define MULTIBOOT_TAG_TYPE_EFI64 12
#define MULTIBOOT_TAG_TYPE_SMBIOS 13
#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14
#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15
#define MULTIBOOT_TAG_TYPE_NETWORK 16
#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17
#define MULTIBOOT_TAG_TYPE_EFI_BS 18
#define MULTIBOOT_TAG_TYPE_EFI32_IH 19
#define MULTIBOOT_TAG_TYPE_EFI64_IH 20
#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21
#define MULTIBOOT_HEADER_TAG_END 0
#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1
#define MULTIBOOT_HEADER_TAG_ADDRESS 2
#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3
#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4
#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5
#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6
#define MULTIBOOT_HEADER_TAG_EFI_BS 7
#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 8
#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9
#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10
#define MULTIBOOT_ARCHITECTURE_I386 0
#define MULTIBOOT_ARCHITECTURE_MIPS32 4
#define MULTIBOOT_HEADER_TAG_OPTIONAL 1
#define MULTIBOOT_LOAD_PREFERENCE_NONE 0
#define MULTIBOOT_LOAD_PREFERENCE_LOW 1
#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2
#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1
#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2
#ifndef ASM_FILE
typedef unsigned char multiboot_uint8_t;
typedef unsigned short multiboot_uint16_t;
typedef unsigned int multiboot_uint32_t;
typedef unsigned long long multiboot_uint64_t;
struct multiboot_header
{
/* Must be MULTIBOOT MAGIC - see above. */
multiboot_uint32_t magic;
/* ISA */
multiboot_uint32_t architecture;
/* Total header length. */
multiboot_uint32_t header_length;
/* The above fields plus this one must equal 0 mod 2^32. */
multiboot_uint32_t checksum;
};
struct multiboot_header_tag
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
};
struct multiboot_header_tag_information_request
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
multiboot_uint32_t requests[0];
};
struct multiboot_header_tag_address
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
multiboot_uint32_t header_addr;
multiboot_uint32_t load_addr;
multiboot_uint32_t load_end_addr;
multiboot_uint32_t bss_end_addr;
};
struct multiboot_header_tag_entry_address
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
multiboot_uint32_t entry_addr;
};
struct multiboot_header_tag_console_flags
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
multiboot_uint32_t console_flags;
};
struct multiboot_header_tag_framebuffer
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
multiboot_uint32_t width;
multiboot_uint32_t height;
multiboot_uint32_t depth;
};
struct multiboot_header_tag_module_align
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
};
struct multiboot_header_tag_relocatable
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
multiboot_uint32_t min_addr;
multiboot_uint32_t max_addr;
multiboot_uint32_t align;
multiboot_uint32_t preference;
};
struct multiboot_color
{
multiboot_uint8_t red;
multiboot_uint8_t green;
multiboot_uint8_t blue;
};
struct multiboot_mmap_entry
{
multiboot_uint64_t addr;
multiboot_uint64_t len;
#define MULTIBOOT_MEMORY_AVAILABLE 1
#define MULTIBOOT_MEMORY_RESERVED 2
#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3
#define MULTIBOOT_MEMORY_NVS 4
#define MULTIBOOT_MEMORY_BADRAM 5
multiboot_uint32_t type;
multiboot_uint32_t zero;
};
typedef struct multiboot_mmap_entry multiboot_memory_map_t;
struct multiboot_tag
{
multiboot_uint32_t type;
multiboot_uint32_t size;
};
struct multiboot_tag_string
{
multiboot_uint32_t type;
multiboot_uint32_t size;
char string[0];
};
struct multiboot_tag_module
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t mod_start;
multiboot_uint32_t mod_end;
char cmdline[0];
};
struct multiboot_tag_basic_meminfo
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t mem_lower;
multiboot_uint32_t mem_upper;
};
struct multiboot_tag_bootdev
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t biosdev;
multiboot_uint32_t slice;
multiboot_uint32_t part;
};
struct multiboot_tag_mmap
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t entry_size;
multiboot_uint32_t entry_version;
struct multiboot_mmap_entry entries[0];
};
struct multiboot_vbe_info_block
{
multiboot_uint8_t external_specification[512];
};
struct multiboot_vbe_mode_info_block
{
multiboot_uint8_t external_specification[256];
};
struct multiboot_tag_vbe
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint16_t vbe_mode;
multiboot_uint16_t vbe_interface_seg;
multiboot_uint16_t vbe_interface_off;
multiboot_uint16_t vbe_interface_len;
struct multiboot_vbe_info_block vbe_control_info;
struct multiboot_vbe_mode_info_block vbe_mode_info;
};
struct multiboot_tag_framebuffer_common
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint64_t framebuffer_addr;
multiboot_uint32_t framebuffer_pitch;
multiboot_uint32_t framebuffer_width;
multiboot_uint32_t framebuffer_height;
multiboot_uint8_t framebuffer_bpp;
#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0
#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1
#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2
multiboot_uint8_t framebuffer_type;
multiboot_uint16_t reserved;
};
struct multiboot_tag_framebuffer
{
struct multiboot_tag_framebuffer_common common;
union {
struct
{
multiboot_uint16_t framebuffer_palette_num_colors;
struct multiboot_color framebuffer_palette[0];
};
struct
{
multiboot_uint8_t framebuffer_red_field_position;
multiboot_uint8_t framebuffer_red_mask_size;
multiboot_uint8_t framebuffer_green_field_position;
multiboot_uint8_t framebuffer_green_mask_size;
multiboot_uint8_t framebuffer_blue_field_position;
multiboot_uint8_t framebuffer_blue_mask_size;
};
};
};
struct multiboot_tag_elf_sections
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t num;
multiboot_uint32_t entsize;
multiboot_uint32_t shndx;
char sections[0];
};
struct multiboot_tag_apm
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint16_t version;
multiboot_uint16_t cseg;
multiboot_uint32_t offset;
multiboot_uint16_t cseg_16;
multiboot_uint16_t dseg;
multiboot_uint16_t flags;
multiboot_uint16_t cseg_len;
multiboot_uint16_t cseg_16_len;
multiboot_uint16_t dseg_len;
};
struct multiboot_tag_efi32
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t pointer;
};
struct multiboot_tag_efi64
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint64_t pointer;
};
struct multiboot_tag_smbios
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint8_t major;
multiboot_uint8_t minor;
multiboot_uint8_t reserved[6];
multiboot_uint8_t tables[0];
};
struct multiboot_tag_old_acpi
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint8_t rsdp[0];
};
struct multiboot_tag_new_acpi
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint8_t rsdp[0];
};
struct multiboot_tag_network
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint8_t dhcpack[0];
};
struct multiboot_tag_efi_mmap
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t descr_size;
multiboot_uint32_t descr_vers;
multiboot_uint8_t efi_mmap[0];
};
struct multiboot_tag_efi32_ih
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t pointer;
};
struct multiboot_tag_efi64_ih
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint64_t pointer;
};
struct multiboot_tag_load_base_addr
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t load_base_addr;
};
#endif /* ! ASM FILE */
#endif /* ! MULTIBOOT HEADER */
boot.S
/* boot.S - bootstrap the kernel */
/* Copyright (C) 1999, 2001, 2010 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#define ASM_FILE 1
#include "multiboot2.h"
/* C symbol format. HAVE ASM USCORE is defined by configure. */
#ifdef HAVE_ASM_USCORE
# define EXT_C(sym) _ ## sym
#else
# define EXT_C(sym) sym
#endif
/* The size of our stack (16KB). */
#define STACK_SIZE 0x4000
/* The flags for the Multiboot header. */
#ifdef __ELF__
# define AOUT_KLUDGE 0
#else
# define AOUT_KLUDGE MULTIBOOT_AOUT_KLUDGE
#endif
#define GRUB_MULTIBOOT_ARCHITECTURE_I386 MULTIBOOT_ARCHITECTURE_I386
.text
.globl start, _start
start:
_start:
jmp multiboot_entry
/* Align 64 bits boundary. */
.align 8
/* Multiboot header. */
multiboot_header:
/* magic */
.long MULTIBOOT2_HEADER_MAGIC
/* ISA: i386 */
.long GRUB_MULTIBOOT_ARCHITECTURE_I386
/* Header length. */
.long multiboot_header_end - multiboot_header
/* checksum */
.long -(MULTIBOOT2_HEADER_MAGIC + GRUB_MULTIBOOT_ARCHITECTURE_I386 + (multiboot_header_end - multiboot_header))
#ifndef __ELF__
.align 8
address_tag_start:
.short MULTIBOOT_HEADER_TAG_ADDRESS
.short MULTIBOOT_HEADER_TAG_OPTIONAL
.long address_tag_end - address_tag_start
/* header_addr */
.long multiboot_header
/* load_addr */
.long _start
/* load_end_addr */
.long _edata
/* bss end addr */
.long _end
address_tag_end:
.align 8
entry_address_tag_start:
.short MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS
.short MULTIBOOT_HEADER_TAG_OPTIONAL
.long entry_address_tag_end - entry_address_tag_start
/* entry addr */
.long multiboot_entry
entry_address_tag_end:
#endif /* ELF */
.align 8
framebuffer_tag_start:
.short MULTIBOOT_HEADER_TAG_FRAMEBUFFER
.short MULTIBOOT_HEADER_TAG_OPTIONAL
.long framebuffer_tag_end - framebuffer_tag_start
.long 1024
.long 768
.long 32
framebuffer_tag_end:
.align 8
.short MULTIBOOT_HEADER_TAG_END
.short 0
.long 8
multiboot_header_end:
multiboot_entry:
/* Initialize the stack pointer. */
movl $(stack + STACK_SIZE), %esp
/* Reset EFLAGS. */
pushl $0
popf
/* Push the pointer to the Multiboot information structure. */
pushl %ebx
/* Push the magic value. */
pushl %eax
/* Now enter the C main function... */
call EXT_C(cmain)
/* Halt. */
pushl $halt_message
call EXT_C(printf)
loop:
hlt
jmp loop
halt_message:
.asciz "Halted."
/* Our stack area. */
.comm stack, STACK_SIZE
kernel.c
/* kernel.c - the C part of the kernel */
/* Copyright (C) 1999, 2010 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "multiboot2.h"
/* Macros. */
/* Some screen stuff. */
/* The number of columns. */
#define COLUMNS 80
/* The number of lines. */
#define LINES 24
/* The attribute of an character. */
#define ATTRIBUTE 7
/* The video memory address. */
#define VIDEO 0xB8000
/* Variables. */
/* Save the X position. */
static int xpos;
/* Save the Y position. */
static int ypos;
/* Point to the video memory. */
static volatile unsigned char *video;
/* Forward declarations. */
void cmain(unsigned long magic, unsigned long addr);
static void cls(void);
static void itoa(char *buf, int base, int d);
static void putchar(int c);
void printf(const char *format, ...);
/* Check if MAGIC is valid and print the Multiboot information structure pointed by ADDR. */
void cmain(unsigned long magic, unsigned long addr)
{
struct multiboot_tag *tag;
unsigned size;
/* Clear the screen. */
cls();
/* Am I booted by a Multiboot-compliant boot loader? */
if (magic != MULTIBOOT2_BOOTLOADER_MAGIC)
{
printf("Invalid magic number: 0x%x\n", (unsigned)magic);
return;
}
if (addr & 7)
{
printf("Unaligned mbi: 0x%x\n", addr);
return;
}
size = *(unsigned *)addr;
printf("Announced mbi size 0x%x\n", size);
for (tag = (struct multiboot_tag *)(addr + 8);
tag->type != MULTIBOOT_TAG_TYPE_END;
tag = (struct multiboot_tag *)((multiboot_uint8_t *)tag + ((tag->size + 7) & ~7)))
{
printf("Tag 0x%x, Size 0x%x\n", tag->type, tag->size);
switch (tag->type)
{
case MULTIBOOT_TAG_TYPE_CMDLINE:
printf("Command line = %s\n", ((struct multiboot_tag_string *)tag)->string);
break;
case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME:
printf("Boot loader name = %s\n", ((struct multiboot_tag_string *)tag)->string);
break;
case MULTIBOOT_TAG_TYPE_MODULE:
printf("Module at 0x%x-0x%x. Command line %s\n",
((struct multiboot_tag_module *)tag)->mod_start,
((struct multiboot_tag_module *)tag)->mod_end,
((struct multiboot_tag_module *)tag)->cmdline);
break;
case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO:
printf("mem_lower = %uKB, mem_upper = %uKB\n",
((struct multiboot_tag_basic_meminfo *)tag)->mem_lower,
((struct multiboot_tag_basic_meminfo *)tag)->mem_upper);
break;
case MULTIBOOT_TAG_TYPE_BOOTDEV:
printf("Boot device 0x%x,%u,%u\n",
((struct multiboot_tag_bootdev *)tag)->biosdev,
((struct multiboot_tag_bootdev *)tag)->slice,
((struct multiboot_tag_bootdev *)tag)->part);
break;
case MULTIBOOT_TAG_TYPE_MMAP:
{
multiboot_memory_map_t *mmap;
printf("mmap\n");
for (mmap = ((struct multiboot_tag_mmap *)tag)->entries;
(multiboot_uint8_t *)mmap < (multiboot_uint8_t *)tag + tag->size;
mmap = (multiboot_memory_map_t *)((unsigned long)mmap + ((struct multiboot_tag_mmap *)tag)->entry_size))
printf(" base_addr = 0x%x%x,"
" length = 0x%x%x, type = 0x%x\n",
(unsigned)(mmap->addr >> 32),
(unsigned)(mmap->addr & 0xffffffff),
(unsigned)(mmap->len >> 32),
(unsigned)(mmap->len & 0xffffffff),
(unsigned)mmap->type);
}
break;
case MULTIBOOT_TAG_TYPE_FRAMEBUFFER:
{
multiboot_uint32_t color;
unsigned i;
struct multiboot_tag_framebuffer *tagfb = (struct multiboot_tag_framebuffer *)tag;
void *fb = (void *)(unsigned long)tagfb->common.framebuffer_addr;
switch (tagfb->common.framebuffer_type)
{
case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED:
{
unsigned best_distance, distance;
struct multiboot_color *palette;
palette = tagfb->framebuffer_palette;
color = 0;
best_distance = 4 * 256 * 256;
for (i = 0; i < tagfb->framebuffer_palette_num_colors; i++)
{
distance = (0xff - palette[i].blue) * (0xff - palette[i].blue) + palette[i].red * palette[i].red + palette[i].green * palette[i].green;
if (distance < best_distance)
{
color = i;
best_distance = distance;
}
}
}
break;
case MULTIBOOT_FRAMEBUFFER_TYPE_RGB:
color = ((1 << tagfb->framebuffer_blue_mask_size) - 1) << tagfb->framebuffer_blue_field_position;
break;
case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT:
color = '\\' | 0x0100;
break;
default:
color = 0xffffffff;
break;
}
for (i = 0; i < tagfb->common.framebuffer_width && i < tagfb->common.framebuffer_height; i++)
{
switch (tagfb->common.framebuffer_bpp)
{
case 8:
{
multiboot_uint8_t *pixel = fb + tagfb->common.framebuffer_pitch * i + i;
*pixel = color;
}
break;
case 15:
case 16:
{
multiboot_uint16_t *pixel = fb + tagfb->common.framebuffer_pitch * i + 2 * i;
*pixel = color;
}
break;
case 24:
{
multiboot_uint32_t *pixel = fb + tagfb->common.framebuffer_pitch * i + 3 * i;
*pixel = (color & 0xffffff) | (*pixel & 0xff000000);
}
break;
case 32:
{
multiboot_uint32_t *pixel = fb + tagfb->common.framebuffer_pitch * i + 4 * i;
*pixel = color;
}
break;
}
}
break;
}
}
}
tag = (struct multiboot_tag *)((multiboot_uint8_t *)tag + ((tag->size + 7) & ~7));
printf("Total mbi size 0x%x\n", (unsigned)tag - addr);
}
/* Clear the screen and initialize VIDEO, XPOS and 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;
}
/* Convert the integer D to a string and save the string in BUF. If
BASE is equal to ’d’, interpret that D is decimal, and if BASE is
equal to ’x’, interpret that D is hexadecimal. */
static void itoa(char *buf, int base, int d)
{
char *p = buf;
char *p1, *p2;
unsigned long ud = d;
int divisor = 10;
/* If %d is specified and D is minus, put ‘-’ in the head. */
if (base == 'd' && d < 0)
{
*p++ = '-';
buf++;
ud = -d;
}
else if (base == 'x')
divisor = 16;
/* Divide UD by DIVISOR until UD == 0. */
do
{
int remainder = ud % divisor;
*p++ = (remainder < 10) ? remainder + '0' : remainder + 'a' - 10;
} while (ud /= divisor);
/* Terminate BUF. */
*p = 0;
/* Reverse BUF. */
p1 = buf;
p2 = p - 1;
while (p1 < p2)
{
char tmp = *p1;
*p1 = *p2;
*p2 = tmp;
p1++;
p2--;
}
}
/* Put the character C on the screen. */
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;
}
/* Format a string and print it on the screen, just like the libc
function 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, *p2;
int pad0 = 0, pad = 0;
c = *format++;
if (c == '0')
{
pad0 = 1;
c = *format++;
}
if (c >= '0' && c <= '9')
{
pad = c - '0';
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:
for (p2 = p; *p2; p2++)
;
for (; p2 < p + pad; p2++)
putchar(pad0 ? '0' : ' ');
while (*p)
putchar(*p++);
break;
default:
putchar(*((int *)arg++));
break;
}
}
}
}
Makefile
CC = gcc
LD = ld
# -g -- 生成符號表以供調試使用
# -m32 -- 編譯成32位目標文件
# -Wall -- 打開絕大多數警告
# -Wno-implicit-function-declaration --
# -std=c11 -- 遵循C11標準
# -std=gnu11 -- 遵循C11標準並且打開GNU的相關擴展
# -ffreestanding -- 採用獨立環境
# -fno-pic -- 解決“對‘_GLOBAL_OFFSET_TABLE_’未定義的引用”問題
# -nostdinc -- 項目中的頭文件可能會和開發環境中的系統頭文件文件名稱發生衝突,採用這個編譯選項時不檢索系統默認的頭文件目錄,但之後需要手動添加需檢索的提供獨立環境頭文件的目錄。
# -Iinclude -- 指定包含文件目錄
# -fomit-frame-pointer -- 函數操作時不保存棧幀到寄存器(%ebp)(如果使用這個編譯參數,則在gdb中調試會出錯,因爲gdb調試依賴棧幀)
CFLAGS = -g -m32 -Wall -Wno-implicit-function-declaration -std=gnu11 -ffreestanding -fno-pic -nostdinc #-fomit-frame-pointer
# -Ttext -- 設置 .text 節的地址
# -m -- 設置仿真
LDFLAGS = -Ttext 0x100000 -m elf_i386
%.o: %.S
$(CC) $(CFLAGS) -c $< -o $@
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
%: %.o
$(LD) $^ -o $@ $(LDFLAGS)
LOOP_DEVICE_NUMBER = 12
LOOP_DEVICE = /dev/loop$(LOOP_DEVICE_NUMBER)
LOOP_DEVICE_MOUNT_ROOT_DIR = /mnt/vdisk
KERNEL_NAME = kernel
DISK_IMAGE = hd.img
DISK_IMAGE_SECTORS = 20160
all: mkimage
boot.o: boot.S
kernel.o: kernel.c
$(KERNEL_NAME): boot.o kernel.o
mkimage: kernel $(DISK_IMAGE)
make mount
sudo cp $(KERNEL_NAME) $(LOOP_DEVICE_MOUNT_ROOT_DIR)/p1
make umount
$(DISK_IMAGE):
dd if=/dev/zero of=$(DISK_IMAGE) bs=512 count=$(DISK_IMAGE_SECTORS)
parted $(DISK_IMAGE) 'mklabel msdos mkpart primary fat16 1MB -1 set 1 boot on'
sudo losetup -P $(LOOP_DEVICE) $(DISK_IMAGE)
sudo mkfs.msdos $(LOOP_DEVICE)p1
sudo mount $(LOOP_DEVICE)p1 $(LOOP_DEVICE_MOUNT_ROOT_DIR)/p1
sudo grub-install --boot-directory=$(LOOP_DEVICE_MOUNT_ROOT_DIR)/p1 --target=i386-pc $(LOOP_DEVICE)
echo "echo \"multiboot (hd0,msdos1)/kernel\nboot\" > $(LOOP_DEVICE_MOUNT_ROOT_DIR)/p1/grub/grub.cfg" | sudo sh
sync
make umount
mount:
sudo losetup -P $(LOOP_DEVICE) $(DISK_IMAGE)
sudo mount $(LOOP_DEVICE)p1 $(LOOP_DEVICE_MOUNT_ROOT_DIR)/p1
umount:
sudo umount $(LOOP_DEVICE_MOUNT_ROOT_DIR)/p1
sudo losetup -d $(LOOP_DEVICE)
clean:
rm -f *.o kernel $(DISK_IMAGE)