使用GRUB Multiboot2引导自制操作系统
使用GRUB Multiboot2引導(dǎo)自制操作系統(tǒng)
前言
之前花了一周時(shí)間,從頭學(xué)習(xí)了傳統(tǒng) BIOS 的啟動流程。驚訝于背后豐富的技術(shù)細(xì)節(jié)的同時(shí),也感嘆 x86 架構(gòu)那厚重的歷史包袱。畢竟,誰能想到,一個(gè)現(xiàn)代 CPU 竟然需要通過操作“鍵盤控制器寄存器”來啟用一條地址線呢。
最終,出于兼容性和功能性的考慮,我還是決定投入 GRUB 的懷抱。況且,讓自己寫的操作系統(tǒng)和本機(jī)的 Linux 一同出現(xiàn)在 GRUB 菜單中并成功引導(dǎo)啟動,也是一件非常有成就感的事。
完整代碼在附錄部分
開發(fā)環(huán)境
| 項(xiàng)目 | 版本 |
|---|---|
| 系統(tǒng) | Windows Subsystem for Linux - Arch (2.3.24) |
| 編譯器 | gcc version 14.2.1 20240910 (GCC) |
| 引導(dǎo)程序 | grub-install (GRUB) 2:2.12-3 |
| 虛擬機(jī) | QEMU emulator version 9.1.50 / VirtualBox 7.0.6 |
1 Multiboot2 規(guī)范
想讓 GRUB 識別并引導(dǎo)我們自制的操作系統(tǒng),就得先了解 Multiboot2 規(guī)范。
官方文檔:Multiboot2 Specification version 2.0
1.1 Multiboot2 header
An OS image must contain an additional header called Multiboot2 header, besides the headers of the format used by the OS image. The Multiboot2 header must be contained completely within the first 32768 bytes of the OS image, and must be 64-bit aligned. In general, it should come as early as possible, and may be embedded in the beginning of the text segment after the real executable header.
這段話的大致意思是:
在 Multiboot2 規(guī)范中,操作系統(tǒng)鏡像的前 32768 字節(jié)內(nèi),必須包含一個(gè)名為 Multiboot2 header 的數(shù)據(jù)結(jié)構(gòu),并且起始地址必須 64-bit 對齊。
Multiboot2 header 的具體結(jié)構(gòu)如下:
| Offset | Type | Field Name |
|---|---|---|
| 0 | u32 | magic |
| 4 | u32 | architecture |
| 8 | u32 | header_length |
| 12 | u32 | checksum |
| 16-XX | tags |
1.1.1 Magic fields
Multiboot2 header 的前四個(gè)字段,也就是0~15字節(jié)的內(nèi)容,被稱為 magic fields
magic 字段是 Multiboot2 header 的識別標(biāo)志,它必須是十六進(jìn)制值 0xE85250D6
architecture 字段指定 CPU 的架構(gòu)。0 表示 i386 的 32 位保護(hù)模式,4 表示 32 位 MIPS 架構(gòu)
header_length 字段記錄了 Multiboot2 header 的長度(以字節(jié)為單位)
checksum 字段是一個(gè) 32 位無符號數(shù),當(dāng)它與 magic fields 其他字段(即 magic 、 architecture 和 header_length )相加時(shí),其 32 位無符號和必須為零。
1.1.2 Tags
Tags 由一個(gè)接一個(gè)的 tag 結(jié)構(gòu)體組成,在必要時(shí)進(jìn)行填充,以確保每個(gè) tag 都從 8 字節(jié)對齊的地址開始。Tags 以一個(gè) type = 0 且 size = 8 的 tag 作為結(jié)束標(biāo)志(相當(dāng)于字符串結(jié)尾的'\0')。
值得注意的是,官方文檔給出的 boot.S 代碼并 沒有 給 tag 的地址進(jìn)行 8 字節(jié)對齊。這會導(dǎo)致 GRUB 讀取 tag 時(shí)出現(xiàn)錯(cuò)位,出現(xiàn)各種奇怪的錯(cuò)誤(比如 error: unsupported tag: xxx)
每個(gè) tag 都有如下基本結(jié)構(gòu):
+-------------------+
u16 | type |
u16 | flags |
u32 | size |
+-------------------+
type 用于表示 tag 的類型,因?yàn)椴煌?tag 在 size 之后可能還會有其他數(shù)據(jù)字段。
如果flags 的第 0 位(也稱為 optional)為 1,表示如果引導(dǎo)加載程序缺乏相關(guān)支持,它可以忽略這個(gè) tag。
size 表示整個(gè) tag 的長度。
例如,表示程序入口地址的 entry address tag 結(jié)構(gòu)如下:
+-------------------+
u16 | type = 3 |
u16 | flags |
u32 | size |
u32 | entry_addr |
+-------------------+
GRUB 會根據(jù) type = 3 判斷它是 entry address tag,并在準(zhǔn)備工作完成后,跳轉(zhuǎn)到 entry_addr 字段中的地址運(yùn)行操作系統(tǒng)。
除此之外,還有專門在 EFI 引導(dǎo)使用的 EFI i386 entry address tag:
+-------------------+
u16 | type = 8 |
u16 | flags |
u32 | size |
u32 | entry_addr |
+-------------------+
其結(jié)構(gòu)與 entry address tag 相同,只是 type = 8 。既然都是指定程序入口,那么二者同時(shí)存在時(shí)會發(fā)生什么呢?
在使用 EFI 引導(dǎo)的情況下,entry address tag 將被忽略,引導(dǎo)加載程序會跳轉(zhuǎn)到 EFI i386 entry address tag 提供的地址。
而使用傳統(tǒng) BIOS 引導(dǎo)時(shí),引導(dǎo)加載程序會直接報(bào)錯(cuò) error: unsupported tag: 0x08 。這是因?yàn)閭鹘y(tǒng)引導(dǎo)方式不支持 EFI 相關(guān)的 tag ,此時(shí) flags 就派上用場了。只要把 flags 最低位設(shè)為 1 ,就可以讓引導(dǎo)程序在不兼容時(shí)忽略這個(gè) tag,而在 EFI 引導(dǎo)時(shí)又能正常使用它。
如此一來,我們的啟動程序就能夠兼容多種啟動方式。
除此之外的 tag 類型,可以在 Multiboot2 Specification version 2.0 的 3.1.3 ~ 3.1.13 節(jié)找到詳細(xì)說明。
1.2 機(jī)器狀態(tài)
官方文檔中的 3.3 I386 machine state 說明了引導(dǎo)加載程序調(diào)用 32 位操作系統(tǒng)時(shí)的機(jī)器狀態(tài)。
除了開啟保護(hù)模式和啟用 A20 gate 這些常規(guī)操作以外,還有兩條比較重要的內(nèi)容:
EAX必須包含標(biāo)識值0x36d76289,該值的存在向操作系統(tǒng)表明它是由兼容 Multiboot2 的引導(dǎo)加載程序加載的。EBX必須包含引導(dǎo)加載程序提供的 Multiboot2 信息結(jié)構(gòu)體的 32 位物理地址(請參閱 Boot information format)。
這兩個(gè)寄存器中的數(shù)據(jù),會在后續(xù)內(nèi)核代碼中用到。
2 代碼實(shí)現(xiàn)
官方文檔提供了對應(yīng)的示例代碼:4.4 Example OS code
但是!
前文有提過,文檔給出的 boot.S 是有 BUG 的!因?yàn)?沒有 給 tag 的地址進(jìn)行 8 字節(jié)對齊,導(dǎo)致了 GRUB 讀取 tag 會出現(xiàn)錯(cuò)位。
我將完整的代碼放在了附錄部分。 本章節(jié)主要內(nèi)容是依據(jù) Multiboot2 規(guī)范,對代碼片段進(jìn)行分析。
2.1 定義 Multiboot header
#include "multiboot2.h"
.text
multiboot_header:
/* Align 64 bits boundary. */
.align 8
/* magic */
.long MULTIBOOT2_HEADER_MAGIC
/* ISA: i386 */
.long MULTIBOOT_ARCHITECTURE_I386
/* Header length. */
.long multiboot_header_end - multiboot_header
/* checksum */
.long -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + (multiboot_header_end - multiboot_header))
entry_address_tag_start:
/* 每個(gè) tag 都要 8 字節(jié)對齊 */
.align 8
.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:
/* 終止 tag 表示 tags 部分結(jié)束 */
end_tag_start:
/* 每個(gè) tag 都要 8 字節(jié)對齊 */
.align 8
.short MULTIBOOT_HEADER_TAG_END
.short 0
.long end_tag_end - end_tag_start
end_tag_end:
multiboot_header_end:
multiboot_entry:
/* ...... */
為了代碼的可讀性,使用宏定義來表示數(shù)值。具體內(nèi)容參考: multiboot2.h
根據(jù)規(guī)范要求,我們將 Multiboot header 定義在了 text 段的開頭,使數(shù)據(jù)盡量靠前。
這里使用了 entry address tag 表示程序入口的地址,也就是 GRUB 執(zhí)行內(nèi)核時(shí)要跳轉(zhuǎn)的目標(biāo)。不過,其實(shí)還有一個(gè)方法可以設(shè)置程序入口。
#include "multiboot2.h"
.text
jmp multiboot_entry
multiboot_header:
/* Align 64 bits boundary. */
.align 8
/* magic */
.long MULTIBOOT2_HEADER_MAGIC
/* ISA: i386 */
.long MULTIBOOT_ARCHITECTURE_I386
/* Header length. */
.long multiboot_header_end - multiboot_header
/* checksum */
.long -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + (multiboot_header_end - multiboot_header))
/* 終止 tag 表示 tags 部分結(jié)束 */
end_tag_start:
/* 每個(gè) tag 都要 8 字節(jié)對齊 */
.align 8
.short MULTIBOOT_HEADER_TAG_END
.short 0
.long end_tag_end - end_tag_start
end_tag_end:
multiboot_header_end:
multiboot_entry:
/* ...... */
相較于上一個(gè)代碼,我們在 text 段的開頭添加 jmp multiboot_entry ,并刪除 entry address tag ,也能實(shí)現(xiàn)相同的功能。
這是因?yàn)椋跊]有使用 tag 指定程序入口時(shí), GRUB 會直接執(zhí)行我們的操作系統(tǒng)程序。此時(shí),運(yùn)行的第一條指令就是我們添加的 jmp multiboot_entry
2.2 啟動入口
#include "multiboot2.h"
#ifdef HAVE_ASM_USCORE
# define EXT_C(sym) _ ## sym
#else
# define EXT_C(sym) sym
#endif
#define STACK_SIZE 0x4000
.text
multiboot_header:
/* ...... */
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
接下來,到了操作系統(tǒng)入口位置。
首先,需要做的事情是初始化棧指針:
#define STACK_SIZE 0x4000:用宏定義表示棧的大小.comm stack, STACK_SIZE:.comm指令用來分配一塊未初始化的數(shù)據(jù)段內(nèi)存,這里分配了STACK_SIZE字節(jié)的空間給stackmovl $(stack + STACK_SIZE), %esp:因?yàn)闂捻敳肯蛳律L,所以將棧的頂部地址放入棧指針寄存器%esp中
接著清空 EFLAGS 寄存器:
pushl $0:將立即數(shù) 0 壓入棧popf:將棧頂?shù)闹祻棾觯⒓虞d到 EFLAGS 寄存器
初始化完成后,就可以調(diào)用內(nèi)核入口的 C 語言函數(shù)。我們之前在 1.2 機(jī)器狀態(tài)中提到過, EAX 和 EBX 寄存器保存著標(biāo)識值和 Multiboot2 信息結(jié)構(gòu)體地址,接下來內(nèi)核會用到這些數(shù)據(jù)。由于內(nèi)核代碼是 C 函數(shù),因此我們需要根據(jù) C 語言的傳參標(biāo)準(zhǔn),按函數(shù)參數(shù)相反的順序?qū)?shù)據(jù)壓入棧中:
/* 內(nèi)核入口函數(shù)定義: */
/* void cmain (unsigned long magic, unsigned long addr) */
pushl %ebx
pushl %eax
call EXT_C(cmain)
2.3 內(nèi)核代碼
這部分的代碼和官方文檔中的 kernel.c 一致。該內(nèi)核的主要功能是在屏幕上打印出 Multiboot2 信息結(jié)構(gòu),主要用于測試 Multiboot2 引導(dǎo)加載程序,同時(shí)也可作為實(shí)現(xiàn) Multiboot2 內(nèi)核的參考示例。
2.4 代碼構(gòu)建
需要先在附錄中獲取 boot.S, kernel.c 和 multiboot2.h 代碼
CC = gcc
LD = ld
CFLAGS = -m32 -fno-builtin -fno-stack-protector -nostartfiles
LDFLAGS = -Ttext 0x100000 -melf_i386 -nostdlib
KERNEL_NAME = kernel.bin
all: $(KERNEL_NAME)
$(KERNEL_NAME): boot.o kernel.o
$(LD) $(LDFLAGS) $^ -o $@
boot.o: boot.S
$(CC) -c $(CFLAGS) $< -o $@
kernel.o: kernel.c
$(CC) -c $(CFLAGS) $< -o $@
clean:
rm -f *.o $(KERNEL_NAME)
簡單說明一下編譯選項(xiàng):
CFLAGS = -m32 -fno-builtin -fno-stack-protector -nostartfiles
-m32:讓編譯器生成 32 位代碼-fno-builtin:禁用編譯器對標(biāo)準(zhǔn)庫函數(shù)(如 memcpy、strlen 等)的內(nèi)建優(yōu)化實(shí)現(xiàn)。確保這些函數(shù)不被替換為編譯器優(yōu)化版本-fno-stack-protector:禁用棧保護(hù)功能,否則會編譯報(bào)錯(cuò)-nostartfiles:不使用標(biāo)準(zhǔn)啟動文件,因?yàn)椴僮飨到y(tǒng)的啟動方式與常規(guī)程序不同
LDFLAGS = -Ttext 0x100000 -melf_i386 -nostdlib
這些是傳遞給鏈接器(如 ld)的選項(xiàng),用于控制鏈接行為:
-Ttext 0x100000:表示程序的代碼段(text segment)將被放置在內(nèi)存地址 0x100000 處,這是加載操作系統(tǒng)常用的位置-melf_i386:指定輸出文件的目標(biāo)格式是 32 位的 ELF 格式(適用于 i386 架構(gòu)),這是 Multiboot2 規(guī)范建議的格式-nostdlib:讓鏈接器不要鏈接標(biāo)準(zhǔn)庫。這是必要的,因?yàn)樵趦?nèi)核或引導(dǎo)程序中,不會使用標(biāo)準(zhǔn) C 庫,而是會自己實(shí)現(xiàn)所需的功能或直接操作硬件
3 鏡像制作
總所周知,現(xiàn)代 PC 有 Legacy 和 UEFI 兩種啟動方式,而接下來的鏡像將會使用 Legacy + MBR 的啟動方式。
選擇
Legacy的主要原因是,EFI似乎只能輸出像素,無法直接打印文本。這會導(dǎo)致我們無法查看內(nèi)核打印的信息。具體說法可以參考這個(gè)鏈接:https://forum.osdev.org/viewtopic.php?f=1&t=28429
3.1 創(chuàng)建鏡像
使用 dd 命令創(chuàng)建一個(gè) 64M 的文件:
dd if=/dev/zero of=disk.img bs=1M count=64
使用 parted 為鏡像文件分區(qū):
parted -s disk.img mklabel msdos mkpart primary ext2 1MiB 100%
將鏡像文件作為虛擬磁盤掛載,并創(chuàng)建 ext2 文件系統(tǒng)和掛載分區(qū)
sudo losetup -P /dev/loop0 disk.img
sudo mkfs.ext2 /dev/loop0p1
sudo mount /dev/loop0p1 /mnt
3.2 安裝 GRUB
安裝 Legacy 啟動兼容的 GRUB ,并創(chuàng)建 grub.cfg 配置文件
sudo grub-install --target=i386-pc --boot-directory=/mnt/boot /dev/loop0
sudo mkdir -p /mnt/boot/grub
cat <<EOF > /mnt/boot/grub/grub.cfg
set timeout=20
set default=0
menuentry "MyOS" {
multiboot2 /boot/kernel.bin
boot
}
EOF
安裝完成后,取消掛載
sudo umount /mnt
sudo losetup -d /dev/loop0
4 虛擬機(jī)運(yùn)行
4.1 QEMU
運(yùn)行目標(biāo)平臺為 i386 的 QEMU,內(nèi)存要大于 128 M
qemu-system-i386 -m 1G -hda disk.img
這條藍(lán)線也是 kernel.c 繪制的。如果你的機(jī)器和 GRUB 是 EFI 啟動,就會發(fā)現(xiàn)無法輸出字符,但是這個(gè)藍(lán)線還在。因?yàn)椋峭ㄟ^設(shè)置 framebuffer 像素實(shí)現(xiàn)的。
4.2 VirtualBox
在 VirtualBox 中運(yùn)行需要先將鏡像文件轉(zhuǎn)換為 vdi 格式
VBoxManage convertdd disk.img disk.vdi --format VDI
接著注冊硬盤,并添加到虛擬機(jī)的 IDE 控制器中
在設(shè)置中關(guān)閉 EFI
這樣就可以正常啟動了
附錄:完整代碼
write_grub_cfg.sh
別忘了加上可執(zhí)行權(quán)限
chmod +x write_grub_cfg.sh
#!/bin/bash
KERNEL_NAME=$1
GRUB_CFG_PATH=$2
cat <<EOF > $GRUB_CFG_PATH
set timeout=20
set default=0
menuentry "MyOS" {
multiboot2 /boot/$KERNEL_NAME
boot
}
EOF
Makefile
CC = gcc
LD = ld
CFLAGS = -m32 -fno-builtin -fno-stack-protector -nostartfiles
LDFLAGS = -Ttext 0x100000 -melf_i386 -nostdlib
KERNEL_NAME = kernel.bin
IMG_NAME = disk.img
IMG_SIZE = 64
all: img
$(KERNEL_NAME): boot.o kernel.o
$(LD) $(LDFLAGS) $^ -o $@
boot.o: boot.S
$(CC) -c $(CFLAGS) $< -o $@
kernel.o: kernel.c
$(CC) -c $(CFLAGS) $< -o $@
.PHONY: img
img: $(IMG_NAME) $(KERNEL_NAME)
sudo losetup -P /dev/loop0 $(IMG_NAME)
sudo mount /dev/loop0p1 /mnt
sudo ./write_grub_cfg.sh $(KERNEL_NAME) /mnt/boot/grub/grub.cfg
sudo cp $(KERNEL_NAME) /mnt/boot/
sudo umount /mnt
sudo losetup -d /dev/loop0
$(IMG_NAME):
dd if=/dev/zero of=$(IMG_NAME) bs=1M count=$(IMG_SIZE)
parted -s $(IMG_NAME) mklabel msdos mkpart primary ext2 1MiB 100%
sudo losetup -P /dev/loop0 $(IMG_NAME)
sudo mkfs.ext2 /dev/loop0p1
sudo mount /dev/loop0p1 /mnt
sudo grub-install --target=i386-pc --boot-directory=/mnt/boot /dev/loop0
sudo mkdir -p /mnt/boot/grub
sudo umount /mnt
sudo losetup -d /dev/loop0
qemu: img
qemu-system-i386 -m 1G -hda $(IMG_NAME)
clean:
sudo umount /mnt || true
sudo losetup -d /dev/loop0 || true
rm -f *.o $(KERNEL_NAME) $(IMG_NAME)
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
.text
multiboot_header:
/* Align 64 bits boundary. */
.align 8
/* magic */
.long MULTIBOOT2_HEADER_MAGIC
/* ISA: i386 */
.long MULTIBOOT_ARCHITECTURE_I386
/* Header length. */
.long multiboot_header_end - multiboot_header
/* checksum */
.long -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + (multiboot_header_end - multiboot_header))
entry_address_tag_start:
.align 8
.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:
end_tag_start:
.align 8
.short MULTIBOOT_HEADER_TAG_END
.short 0
.long end_tag_end - end_tag_start
end_tag_end:
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;
}
}
}
}
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 ANY
* 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 */
參考文獻(xiàn)
Multiboot2 Specification version 2.0
寫操作系統(tǒng) 之 GRUB 到 multiboot ——從 multiboot 開始對接內(nèi)核(轉(zhuǎn)載)
[操作系統(tǒng)原理與實(shí)現(xiàn)]Multiboot與GRUB_multiboot2-CSDN博客
grub2詳解(翻譯和整理官方手冊) - 駿馬金龍 - 博客園
用 GRUB 引導(dǎo)自己的操作系統(tǒng)_切換grub以實(shí)現(xiàn)系統(tǒng)的正常引導(dǎo)-CSDN博客
操作系統(tǒng)實(shí)現(xiàn) - 055 multiboot2 頭_嗶哩嗶哩_bilibili
MBR和GPT_mbr gap-CSDN博客
本文發(fā)布于2023年10月8日
最后修改于2024年10月8日
總結(jié)
以上是生活随笔為你收集整理的使用GRUB Multiboot2引导自制操作系统的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 卷积神经网络训练的三个概念(Epoch,
- 下一篇: bugku-Web 求getshell