Linux内核分析之基础知识-1

韩乔落

第1章:x86_64 BIOS Legacy 启动流程

从上电到内核引导的完整旅程


本章概述

BIOS (Basic Input/Output System) Legacy 启动是传统 PC 的标准启动方式。本章详细讲解从按下电源键到内核开始执行的完整过程。

启动流程概览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
┌─────────────────────────────────────────────────────────────────┐
│ BIOS Legacy 启动流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌───────────────┐ ┌─────────────────┐ │
│ │ BIOS POST │ → │ MBR 引导 │ → │ 引导程序 │ │
│ │ (固件) │ │ (512字节) │ │ (GRUB/LILO) │ │
│ └──────────────┘ └───────────────┘ └─────────────────┘ │
│ │ │ │ │
│ └───────────────────┴───────────────────┘ │
│ ↓ │
│ ┌──────────────────────┐ │
│ │ 内核引导头部 │ │
│ │ (header.S) │ │
│ └──────────────────────┘ │
│ ↓ │
│ ┌──────────────────────┐ │
│ │ 实模式初始化 │ │
│ │ (main.c) │ │
│ └──────────────────────┘ │
│ ↓ │
│ ┌──────────────────────┐ │
│ │ 保护模式切换 │ │
│ │ (pm.c) │ │
│ └──────────────────────┘ │
│ ↓ │
│ ┌──────────────────────┐ │
│ │ 压缩内核入口 │ │
│ │ (head_64.S) │ │
│ └──────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

1.1 BIOS POST 阶段

1.1.1 上电自检 (Power-On Self-Test)

当按下电源键后,CPU 的重置向量指向 BIOS ROM 的入口点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* 典型的 BIOS 入口 (简化) */
RESET_VECTOR:
jmp BIOS_INIT

BIOS_INIT:
; 初始化 CPU 寄存器
xor ax, ax
mov ds, ax
mov es, ax

; 检测内存大小
call detect_memory

; 测试内存
call test_memory

; 初始化硬件设备
call init_hardware

; 构建中断向量表
call setup_IVT

; 读取启动设备配置
call read_boot_config

; 加载主引导记录
call load_MBR

1.1.2 实模式内存布局

BIOS 在 16 位实模式下运行,只能寻址 1MB 内存空间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
地址范围        大小     用途
────────────────────────────────────────────────
0x00000000 - 0x000003FF 1 KB 中断向量表 (IVT)
- 256 个中断向量
- 每个 4 字节 (段:偏移)

0x00000400 - 0x000004FF 256 B BIOS 数据区 (BDA)
- 0x400: 端口地址
- 0x413: 可用内存 (KB)
- 0x417: 键盘状态标志

0x00000500 - 0x00007BFF ~30 KB 常规内存可用区域

0x00007C00 - 0x00007DFF 512 B MBR 加载位置
- BIOS 将第一个扇区加载到这里

0x00007E00 - 0x0007FFFF ~480 KB BIOS/操作系统数据区
- boot_params 将被复制到 0x90000

0x00080000 - 0x0009FFFF 128 KB 扩展 BIOS 数据区 (EBDA)

0x000A0000 - 0x000BFFFF 128 KB 视频内存 (VGA)
- 0xA0000: 图形模式
- 0xB8000: 文本模式

0x000C0000 - 0x000C7FFF 32 KB 视频 BIOS
- 0xC0000: VGA BIOS 入口

0x000C8000 - 0x000EFFFF 160 KB 各种适配器 ROM

0x000F0000 - 0x000FFFFF 64 KB 系统 BIOS
- 0xF0000: BIOS 代码
- 0xFFFF0: 重置向量

0x00100000 - >1 MB 扩展内存 (XMS)
- 需要保护模式才能访问

1.1.3 BIOS 中断调用 (BIOS Interrupt Calls)

BIOS 提供软件中断接口,实模式代码可以通过 INT 指令调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/* 常用 BIOS 中断 */

/* INT 10h - 视频服务 */
#define BIOS_VIDEO_SERVICES 0x10
/* AH=0x00: 设置视频模式 */
/* AH=0x02: 设置光标位置 */
/* AH=0x0E: 写入字符 */
/* AH=0x12: VBE 扩展功能 */

/* INT 13h - 磁盘服务 */
#define BIOS_DISK_SERVICES 0x13
/* AH=0x02: 读扇区到内存 */
/* AH=0x03: 从内存写扇区 */
/* AH=0x08: 获取驱动器参数 */

/* INT 15h - 系统服务 */
#define BIOS_SYSTEM_SERVICES 0x15
/* AH=0x88: 获取扩展内存大小 */
/* AH=0xE8: E820 内存映射 */
/* AH=0xE9: Intel SpeedStep (IST) */

/* INT 16h - 键盘服务 */
#define BIOS_KEYBOARD_SERVICES 0x16
/* AH=0x00: 读取键盘输入 */
/* AH=0x01: 检查按键状态 */
/* AH=0x02: 获取键盘标志 */

/* INT 1Ah - 时钟服务 */
#define BIOS_TIME_SERVICES 0x1A
/* AH=0x00: 读取系统时钟 */
/* AH=0x04: 读取日期 */

内核中的使用示例 (arch/x86/boot/main.c):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/* 查询键盘状态 */
static void keyboard_init(void)
{
struct biosregs ireg, oreg;

initregs(&ireg);

/* AH=0x02: 获取键盘状态 */
ireg.ah = 0x02;
intcall(0x16, &ireg, &oreg);
boot_params.kbd_status = oreg.al;

/* AX=0x0305: 设置键盘重复率 */
ireg.ax = 0x0305;
intcall(0x16, &ireg, NULL);
}

/* 调用 BIOS 中断的通用函数 */
static void intcall(int intno, struct biosregs *ireg,
struct biosregs *oreg)
{
struct biosregs tmp;

asm volatile("pushfl; pushw %%fs; "
"movw %%ds, %%ax; movw %%ax, %%fs; "
"pushw %%es; "
"intcall_wrapper: int %2; "
"pushw %%es; popw %%ds; "
"popw %%es; popw %%fs; popfl"
: "=a"(tmp.eax), "=b"(tmp.ebx),
"=c"(tmp.ecx), "=d"(tmp.edx)
: "i"(intno), "a"(ireg->eax),
"b"(ireg->ebx), "c"(ireg->ecx),
"d"(ireg->edx)
: "memory", "cc");

if (oreg)
*oreg = tmp;
}

1.1.4 E820 内存映射

E820 是 BIOS 提供的内存映射查询接口,比传统的 INT 15h/AH=88h 更强大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* E820 内存类型定义 */
#define E820_TYPE_RAM 1 /* 可用 RAM */
#define E820_TYPE_RESERVED 2 /* 保留区域 */
#define E820_TYPE_ACPI 3 /* ACPI 可再利用 */
#define E820_TYPE_NVS 4 /* ACPI NVS (非易失性存储) */
#define E820_TYPE_UNUSABLE 5 /* 不可用内存 */
#define E820_TYPE_PMEM 7 /* 可保留内存 (Persistent) */

/* E820 条目结构 */
struct e820_entry {
u64 addr; /* 基地址 */
u64 size; /* 大小 (字节) */
u32 type; /* 内存类型 */
} __attribute__((packed));

/* 典型的 E820 内存映射示例 */
/*
* 0x0000000000000000 - 0x000000000009FC00 RAM (639 KB)
* 0x000000000009FC00 - 0x00000000000A0000 Reserved
* 0x00000000000E0000 - 0x0000000000100000 Reserved (BIOS area)
* 0x0000000000100000 - 0x000000003FFF0000 RAM (约 1 GB)
* 0x000000003FFF0000 - 0x0000000040000000 Reserved (ACPI NVS)
* 0x0000000100000000 - 0x0000000420000000 RAM (约 16 GB, 以上)
*/

内核查询 E820 的代码 (arch/x86/boot/memory.c):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
static int detect_memory_e820(void)
{
int count = 0;
struct biosregs ireg, oreg;
struct e820_entry *desc = boot_params.e820_table;
u32 buf_size;

/* SMAP = 0x534D4150 ('SMAP') */
initregs(&ireg);
ireg.ax = 0xe820;
ireg.cx = sizeof(buf);
ireg.edx = SMAP;
ireg.di = (size_t)&buf;

/* 循环查询所有内存条目 */
do {
/* INT 15h, AX=E820h: 查询系统地址映射 */
intcall(0x15, &ireg, &oreg);

if (oreg.eflags & X86_EFLAGS_CF)
break; /* 进位标志设置 = 错误 */

/* 检查签名 */
if (oreg.eax != SMAP) {
count = 0;
break;
}

/* 复制条目到 boot_params */
*desc++ = buf;
count++;

/* 下一个条目 */
ireg.bx = oreg.bx;
} while (ireg.bx && count < E820_MAX_ENTRIES);

boot_params.e820_entries = count;
return count;
}

1.2 MBR 引导阶段

1.2.1 主引导记录 (Master Boot Record)

MBR 是磁盘的第一个扇区 (512 字节),BIOS 将其加载到内存 0x7C00 处。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
MBR 结构 (512 字节):
┌─────────────────────────────────────────────────────┐
│ 偏移 大小 内容 │
├─────────────────────────────────────────────────────┤
│ 0x000 446 B 引导代码 (Boot Code) │
│ │ │
│ └─ GRUB Stage 1 或其他引导程序 │
├─────────────────────────────────────────────────────┤
│ 0x1BE 64 B 分区表 (Partition Table) │
│ │ │
│ ├─ 4 个主分区表项 │
│ │ 每项 16 字节: │
│ │ - 0x00: 活动标志 (0x80=活动) │
│ │ - 0x01-0x03: 起始 CHS │
│ │ - 0x04: 分区类型 │
│ │ - 0x05-0x07: 结束 CHS │
│ │ - 0x08-0x0B: 起始 LBA │
│ │ - 0x0C-0x0F: 扇区数 │
├─────────────────────────────────────────────────────┤
│ 0x1FE 2 B 签名 (0x55 0xAA) │
└─────────────────────────────────────────────────────┘

1.2.2 分区类型代码

1
2
3
4
5
6
7
8
9
10
11
12
/* 常见分区类型 */
#define MBR_PARTITION_TYPE_EMPTY 0x00
#define MBR_PARTITION_TYPE_FAT12 0x01
#define MBR_PARTITION_TYPE_FAT16_LBA 0x0E
#define MBR_PARTITION_TYPE_EXTENDED 0x05
#define MBR_PARTITION_TYPE_EXTENDED_LBA 0x0F
#define MBR_PARTITION_TYPE_NTFS 0x07
#define MBR_PARTITION_TYPE_LINUX 0x83
#define MBR_PARTITION_TYPE_LINUX_EXTENDED 0x85
#define MBR_PARTITION_TYPE_LINUX_LVM 0x8E
#define MBR_PARTITION_TYPE_LINUX_SWAP 0x82
#define MBR_PARTITION_TYPE_GPT_PROTECTIVE 0xEE

1.2.3 从 MBR 到引导程序

典型的引导过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
; MBR 代码 (简化)
[ORG 0x7C00]

start:
cli ; 禁用中断
xor ax, ax
mov ds, ax
mov es, ax

; 保存启动驱动器号 (DL 寄存器)
mov [boot_drive], dl

; 加载分区表中的活动分区
call load_active_partition

; 跳转到加载的代码
jmp 0x0000:0x7E00

boot_drive: db 0

times 510-($-$$) db 0 ; 填充
dw 0xAA55 ; 签名

1.3 引导程序 (GRUB) 阶段

1.3.1 GRUB 启动阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌─────────────────────────────────────────────────────────────┐
│ GRUB 启动流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Stage 1: MBR 中的 446 字节 │
│ - 加载 Stage 1.5 或 Stage 2 │
│ │
│ Stage 1.5: 文件系统驱动 (可选) │
│ - 存储 /boot 分区的引导区 │
│ - 包含文件系统驱动 │
│ │
│ Stage 2: 完整引导程序 │
│ - 显示菜单 │
│ - 加载内核配置 │
│ - 加载内核镜像到内存 │
│ │
│ 加载内核后: │
│ - 设置 boot_params 结构 │
│ - 跳转到内核入口点 │
│ │
└─────────────────────────────────────────────────────────────┘

1.3.2 GRUB 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# /boot/grub/grub.cfg

menuentry "Linux 6.12.38" {
insmod gzio # GZIP 解压支持
insmod part_gpt # GPT 分区支持
insmod ext2 # ext2 文件系统支持

# 设置根设备
set root='hd0,gpt2'

# 加载内核
echo 'Loading Linux kernel ...'
linux /vmlinuz-6.12 \
root=/dev/sda3 \ # 根文件系统
ro \ # 只读挂载
console=ttyS0,115200n8 \ # 串口控制台
quiet

# 加载 initrd
echo 'Loading initial ramdisk ...'
initrd /initrd.img-6.12
}

menuentry "Linux 6.12.38 (Recovery Mode)" {
set root='hd0,gpt2'
linux /vmlinuz-6.12 \
root=/dev/sda3 \
ro \
single # 单用户模式
initrd /initrd.img-6.12
}

1.3.3 GRUB 传递给内核的信息

GRUB 根据启动协议填充 boot_params 结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* boot_params.hdr 的关键字段 */
struct setup_header {
u8 setup_sects; /* setup 代码扇区数 */
u16 root_flags; /* 根文件系统挂载标志 */
u32 syssize; /* 系统代码大小 (16字节对齐) */
u16 ram_size; /* 旧式内存大小 (KB) */
u16 video_mode; /* 视频模式 */
u16 root_dev; /* 根设备号 */
u16 boot_flag; /* 启动标志 (0xAA55) */
u16 jump; /* 跳转指令 */
u32 header; /* 头部签名 ("HdrS") */
u16 version; /* 启动协议版本 */
u32 type_of_loader; /* 引导程序类型 (0xFF=GRUB) */
u32 loadflags; /* 加载标志 */
u32 setup_move_size; /* 需要移动的大小 */
u32 code32_start; /* 32位入口点 (0x100000) */
u32 cmd_line_ptr; /* 命令行指针 */
u32 initrd_addr_max; /* initrd 最大地址 */
u32 kernel_alignment; /* 内核对齐要求 */
u8 relocatable_kernel; /* 可重定位内核 */
u8 min_alignment; /* 最小对齐 */
u32 cmdline_size; /* 命令行大小 */
/* ... 更多字段 ... */
};

1.4 内核引导头部 (header.S)

1.4.1 header.S 完整结构

arch/x86/boot/header.S 是内核映像的第一部分,被 BIOS/引导程序加载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/* arch/x86/boot/header.S */

.code16
.section ".bstext", "ax"

/* ============================================================================
* 第一部分: MSDOS/PE 头部 (用于 EFI Stub)
* ============================================================================ */

#ifdef CONFIG_EFI_STUB
.word MZ_MAGIC /* "MZ" DOS 签名 */
.org 0x18
.long PE_MAGIC /* 指向 PE 头的偏移 */

/* PE 头从这里开始 */
pe_header:
.long PE_MAGIC /* PE\0\0 */

coff_header:
#ifdef CONFIG_X86_64
.word IMAGE_FILE_MACHINE_AMD64 /* AMD64 */
#else
.word IMAGE_FILE_MACHINE_I386 /* i386 */
#endif
.word section_count /* 节数量 */
.long 0 /* 时间戳 */
.long 1 /* 符号表指针 */
.word section_table - optional_header /* 可选头大小 */
.word IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DEBUG_STRIPPED

optional_header:
#ifdef CONFIG_X86_64
.word PE_OPT_MAGIC_PE32PLUS /* PE32+ 格式 */
#else
.word PE_OPT_MAGIC_PE32 /* PE32 格式 */
#endif
.byte 0x02 /* 主版本号 */
.byte 0x14 /* 次版本号 */
.long ZO__data /* 代码大小 */
.long ZO__end - ZO__data /* 已初始化数据大小 */
.long 0 /* 未初始化数据大小 */
.long setup_size + ZO_efi_pe_entry /* 入口点地址 */
.long setup_size /* 代码基址 */

extra_header_fields:
#ifdef CONFIG_X86_64
.quad 0 /* ImageBase */
#else
.long 0
#endif
.long salign /* SectionAlignment */
.long falign /* FileAlignment */
.word 0, 0 /* OS 版本 */
.word LINUX_EFISTUB_MAJOR_VERSION
.word LINUX_EFISTUB_MINOR_VERSION
.word 0, 0 /* Subsystem 版本 */
.long 0 /* Win32VersionValue */
.long setup_size + ZO__end /* SizeOfImage */
.long salign /* SizeOfHeaders */
.long 0 /* CheckSum */
.word IMAGE_SUBSYSTEM_EFI_APPLICATION
.word IMAGE_DLLCHARACTERISTICS_NX_COMPAT
#ifdef CONFIG_X86_64
.quad 0, 0, 0, 0 /* 栈/堆保留 */
#else
.long 0, 0, 0, 0
#endif
.long 0 /* LoaderFlags */
.long (section_table - .) / 8 /* NumberOfRvaAndSizes */

/* 数据目录 */
.quad 0 /* ExportTable */
.quad 0 /* ImportTable */
.quad 0 /* ResourceTable */
.quad 0 /* ExceptionTable */
.quad 0 /* CertificationTable */
.quad 0 /* BaseRelocationTable */

/* 节表 */
section_table:
.ascii ".setup"
.byte 0, 0
.long pecompat_fstart - salign
.long salign
.long pecompat_fstart - salign
.long salign
.long 0, 0, 0
.long IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE

.ascii ".text"
.byte 0, 0, 0
.long ZO__end - ZO__data
.long salign
.long ZO__edata - ZO__data
.long salign
.long 0, 0, 0
.long IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE
#endif /* CONFIG_EFI_STUB */

/* ============================================================================
* 第二部分: 引导协议头部
* ============================================================================ */

.globl hdr
hdr:
setup_sects:
.byte 0 /* 默认为 4 */
root_flags:
.byte ROOT_RDONLY
syssize:
.long 0
ram_size:
.word 0
vid_mode:
.word SVGA_MODE
root_dev:
.word 0
boot_flag:
.word 0xAA55 /* 必须是这个值 */

/* 跳转到 setup 代码 */
.globl _start
_start:
#ifdef CONFIG_EFI_STUB
/*
* 对于 EFI Stub, 我们需要执行 setup 代码
* EFI 入口点在 efi_pe_entry
*/
jmp start_of_setup
#else
/* 非 EFI 情况: 直接跳转到 start_of_setup */
jmp start_of_setup
#endif

/* ============================================================================
* 第三部分: setup 头部扩展 (版本 2.10+)
* ============================================================================ */

.ascii "HdrS"
.word 0x021F /* 启动协议版本 2.15 */
.globl realmode_swtch
realmode_swtch: .word 0
start_sys_seg:
.word 0
kernel_version:
.word 0
.globl type_of_loader
type_of_loader:
.byte 0 /* 0xFF = GRUB */
.globl loadflags
loadflags:
.byte LOADED_HIGH | CAN_USE_HEAP | QUIET_FLAG
setup_move_size:
.long 0x8000
code32_start:
.long 0x100000 /* 32/64 位入口点 */
ramdisk_image:
.long 0
ramdisk_size:
.long 0
bootsect_kludge:
.word 0
heap_end_ptr:
.word _end
.globl ext_loader_ver
ext_loader_ver:
.byte 0
.globl ext_loader_type
ext_loader_type:
.byte 0
cmd_line_ptr:
.long 0 /* 命令行指针 */
initrd_addr_max:
.long 0x7fffffff
kernel_alignment:
.long CONFIG_PHYSICAL_ALIGN
relocatable_kernel:
.byte 1
min_alignment:
.byte 0
xloadflags:
.word 0
cmdline_size:
.long COMMAND_LINE_SIZE
hardware_subarch:
.long 0
hardware_subarch_data:
.quad 0
payload_offset:
.long 0
payload_length:
.long 0
setup_data:
.quad 0
pref_address:
.quad CONFIG_PHYSICAL_START
init_size:
.long 0
handover_offset:
.long 0

/* ============================================================================
* 第四部分: 实模式代码和数据
* ============================================================================ */

.code16
.globl start_of_setup
start_of_setup:
/* 保存 DS:SI (boot_params 指针) */
pushw %ds
pushw %si

/* 设置栈 */
movw $stack_start, %sp
movw %ss, %ax
movw %ax, %ds

/* 调用 main.c 中的 main() 函数 */
call main

/* main 不应该返回, 如果返回则等待 */
call keyboard_wait
jmp .Lhlt

.Lhlt:
hlt
jmp .Lhlt

/* 栈定义 */
.bss
.balign 16
stack_start:
.fill 512, 1, 0 /* 512 字节栈 */
stack_end:

1.4.2 启动协议版本

Linux 内核启动协议版本演进:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* 版本历史 */
#define BOOT_PROTO_V1 0x0102 /* Linux 1.3.73 */
#define BOOT_PROTO_V2 0x0200 /* Linux 2.4.0 */
#define BOOT_PROTO_V2_10 0x0201 /* Linux 2.6.x - 引入 type_of_loader */
#define BOOT_PROTO_V2_12 0x020C /* 支持 EFI */
#define BOOT_PROTO_V2_15 0x020F /* 当前版本 */

/* 关键版本特性 */
/*
* 2.00+: 支持大内核 (bzImage)
* 2.01+: 添加 type_of_loader 字段
* 2.02+: 支持内核模块
* 2.03+: 支持/initrd= 参数
* 2.04+: 支持视频模式选择
* 2.05+: 支持 mem= 参数
* 2.06+: 支持 SATA/AHCI
* 2.10+: EFI 支持
* 2.11+: 支持可重定位内核
* 2.12+: 支持 64 位 EFI
* 2.13+: 支持 xsave
* 2.14+: 支持加载任意地址
* 2.15+: 改进的 EFI handover 协议
*/

1.4.3 引导程序类型标识

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* boot_params.hdr.type_of_loader 值 */
#define LOADER_TYPE_NONE 0 /* 未加载 (直接启动) */
#define LOADER_TYPE_LOADBIN 1 /* loadlin */
#define LOADER_TYPE_BOOTSECT_LOADER 2 /* bootsect-loader */
#define LOADER_TYPE_SYSLINUX 3 /* SYSLINUX */
#define LOADER_TYPE_ETHBOOT 4 /* Etherboot */
#define LOADER_TYPE_KERNEL 5 /* 内核自解压 */
#define LOADER_TYPE_GRUB 6 /* GRUB */
#define LOADER_TYPE_UBOOT 7 /* U-Boot */
#define LOADER_TYPE_TILO 8 /* tilo */
#define LOADER_TYPE_ZIPL 9 /* zIPL (s390) */
#define LOADER_TYPE_KEXEC 10 /* kexec */

/* 扩展引导程序类型 */
#define EXT_LOADER_TYPE_NONE 0
#define EXT_LOADER_TYPE_QEMU 1 /* QEMU */
#define EXT_LOADER_TYPE_EDK2 2 /* EDK2/UEFI */
#define EXT_LOADER_TYPE_FIRMWARE 3 /* 固件 */

1.5 实模式初始化 (main.c)

1.5.1 main() 函数流程

arch/x86/boot/main.cmain() 函数是实模式下的 C 入口点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void main(void)
{
/* 1. 复制引导参数 */
copy_boot_params();

/* 2. 初始化堆 (用于后续内存分配) */
init_heap();

/* 3. 查询硬件信息 */
keyboard_init(); /* 键盘状态 */
query_ist(); /* Intel SpeedStep */
query_vbe(); /* VESA BIOS Extensions */
query_mca(); /* Micro Channel Architecture */

/* 4. 设置视频模式 */
set_video();

/* 5. 内存检测 */
detect_memory();

/* 6. 切换到保护模式 */
go_to_protected_mode();
}

1.5.2 copy_boot_params()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static void copy_boot_params(void)
{
struct old_cmdline {
u16 cl_magic; /* 魔数 0xA33F */
u16 cl_offset; /* 命令行偏移 */
};

const struct old_cmdline * const oldcmd =
absolute_pointer(OLD_CL_ADDRESS);

/* 确保 boot_params 结构正确 */
BUILD_BUG_ON(sizeof(boot_params) != 4096);

/* 复制头部信息 */
memcpy(&boot_params.hdr, &hdr, sizeof(hdr));

/* 处理旧式命令行协议 */
if (!boot_params.hdr.cmd_line_ptr &&
oldcmd->cl_magic == OLD_CL_MAGIC) {

u16 cmdline_seg;

/* 确定命令行所在的段 */
if (oldcmd->cl_offset < boot_params.hdr.setup_move_size)
cmdline_seg = ds(); /* 在 setup 区域内 */
else
cmdline_seg = 0x9000; /* 在加载的高位区域 */

boot_params.hdr.cmd_line_ptr =
(cmdline_seg << 4) + oldcmd->cl_offset;
}
}

1.5.3 init_heap()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 堆初始化 - 后续内存分配的基础 */
void init_heap(void)
{
char *stack_end;

/* 栈结束位置 (向上增长) */
stack_end = (char *)&stack_start;

/* 堆开始位置 = 当前堆结束位置 */
heap_end = (char *)((size_t)heap_end - 0x200);

/* 确保堆结束位置不超过栈开始位置 */
if (heap_end > stack_end)
heap_end = stack_end;

/* HEAP 指向堆开始 */
HEAP = _end;
}

1.5.4 内存检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void detect_memory(void)
{
/* 尝试各种内存检测方法 */

/* 1. E820 查询 (最优先) */
if (detect_memory_e820() > 0)
return;

/* 2. E801 查询 */
if (detect_memory_e801() > 0)
return;

/* 3. 88h 查询 (最古老) */
detect_memory_88();

/* 如果所有方法都失败, 使用默认值 */
boot_params.alt_mem_k = 0;
boot_params.mem_size = 0;
}

1.5.5 视频初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void set_video(void)
{
/* 根据配置设置视频模式 */

/* 检查用户是否指定了视频模式 */
if (video_mode == 0xFFFF) {
/* 自动检测最佳模式 */
probe_cards(0);
} else if (video_mode == 0x0) {
/* 标准 VGA 文本模式 */
do_restore();
} else {
/* 用户指定的模式 */
set_mode(video_mode);
}

/* 保存视频信息到 boot_params */
store_video_mode();
}

1.6 保护模式切换 (pm.c)

1.6.1 go_to_protected_mode()

arch/x86/boot/pm.c 负责从实模式切换到保护模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void go_to_protected_mode(void)
{
/* 1. 调用实模式切换钩子 (如果存在) */
realmode_switch_hook();

/* 2. 启用 A20 地址线 */
if (enable_a20()) {
puts("A20 gate not responding, unable to boot...\n");
die();
}

/* 3. 重置协处理器 */
reset_coprocessor();

/* 4. 屏蔽所有中断 */
mask_all_interrupts();

/* 5. 设置 IDT 和 GDT */
setup_idt();
setup_gdt();

/* 6. 跳转到保护模式 */
protected_mode_jump(boot_params.hdr.code32_start,
(u32)&boot_params + (ds() << 4));
}

1.6.2 A20 地址线启用

A20 Gate 控制第 21 条地址线 (A20),允许访问超过 1MB 的内存:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
static int enable_a20(void)
{
/* 尝试多种方法启用 A20 */

/* 方法 1: BIOS 函数 (INT 15h, AH=0x24) */
if (a20_bios())
return 0;

/* 方法 2: 键盘控制器 (0x92) */
if (a20_kbc())
return 0;

/* 方法 3: 快速 A20 门 */
if (a20_fast())
return 0;

return -1; /* 失败 */
}

/* 快速 A20 实现 (端口 0x92) */
static int enable_a20_fast(void)
{
u8 port_a;

/* 读取当前值 */
port_a = inb(0x92);

/* 设置 bit 1 (A20 enable) */
port_a |= 0x02;

/* 清除 bit 0 (fast reset) */
port_a &= ~0x01;

/* 写回 */
outb(port_a, 0x92);

/* 验证 */
return a20_test();
}

1.6.3 GDT 设置

全局描述符表 (GDT) 定义了内存段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static void setup_gdt(void)
{
/* GDT 必须对齐到 16 字节 */
static const u64 boot_gdt[] __attribute__((aligned(16))) = {
/* 描述符 0: 空描述符 (必须存在) */
[GDT_ENTRY_BOOT_CS] = GDT_ENTRY(DESC_CODE32, 0, 0xfffff),
/* 描述符 1: 代码段 (可读可执行, 4GB) */

[GDT_ENTRY_BOOT_DS] = GDT_ENTRY(DESC_DATA32, 0, 0xfffff),
/* 描述符 2: 数据段 (可读可写, 4GB) */

[GDT_ENTRY_BOOT_TSS] = GDT_ENTRY(DESC_TSS32, 4096, 103),
/* 描述符 3: TSS (任务状态段) */
};

static struct gdt_ptr gdt;

/* 加载 GDTR */
gdt.len = sizeof(boot_gdt) - 1;
gdt.ptr = (u32)&boot_gdt + (ds() << 4);

asm volatile("lgdtl %0" : : "m" (gdt));
}

1.6.4 protected_mode_jump()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* arch/x86/boot/pm.S */
.code16
.globl protected_mode_jump
protected_mode_jump:
/* 参数:
* eax = 32位入口点
* edx = boot_params 指针
*/

/* 设置数据段 */
movw $__BOOT_DS, %ecx
movw %cx, %ds
movw %cx, %es
movw %cx, %fs
movw %cx, %gs
movw %cx, %ss

/* 设置页目录基址寄存器 (为 64 位准备) */
xorl %edx, %edx
movl %edx, %cr3

/* 启用保护模式 (CR0.PE = 1) */
movl %cr0, %edx
orl $X86_CR0_PE, %edx
movl %edx, %cr0

/* 远跳转到 32 位代码 */
.byte 0x66, 0xea /* jmp far ptr */
.long 0 /* 偏移 (由调用者设置) */
.word __BOOT_CS /* 段选择子 */

1.7 本章小结

启动流程回顾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
BIOS POST

检测硬件, 构建 IVT

加载 MBR 到 0x7C00

GRUB Stage 1 执行

GRUB Stage 2 加载内核

header.S 被执行

main.c 实模式初始化

pm.c 保护模式切换

跳转到 head_64.S

关键数据结构

  1. boot_params - 4KB 的启动参数结构
  2. e820_entry - 内存映射条目
  3. setup_header - 启动协议头部
  4. GDT - 全局描述符表

关键内存位置

地址 用途
0x7C00 MBR 加载位置
0x90000 boot_params 目标位置
0x100000 32/64 位内核入口点
  • Title: Linux内核分析之基础知识-1
  • Author: 韩乔落
  • Created at : 2026-01-08 01:17:50
  • Updated at : 2026-01-19 13:40:36
  • Link: https://jelasin.github.io/2026/01/08/Linux内核分析之基础知识-1/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments