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

韩乔落

第2章:x86_64 UEFI 启动流程

统一可扩展固件接口 - 现代系统启动标准


本章概述

UEFI (Unified Extensible Firmware Interface) 是 BIOS 的现代替代方案。本章详细讲解 UEFI 启动流程,包括 EFI Stub 机制。

UEFI vs BIOS Legacy 对比

特性 BIOS Legacy UEFI
运行模式 16位实模式 32/64位保护模式
寻址能力 1MB 限制 完整内存访问
启动方式 MBR + 引导代码 ESP 分区 + EFI 应用
分区表 MBR GPT (支持更大磁盘)
图形支持 VGA 文本模式 UEFI GOP (图形输出协议)
配置界面 基于文本 图形化界面
模块化 固化在 ROM 驱动 (.efi 文件) 可独立更新
安全启动 不支持 Secure Boot 支持
网络启动 需要扩展 ROM 原生 HTTP/iSCSI 支持

2.1 UEFI 架构概览

2.1.1 UEFI 启动阶段

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
┌─────────────────────────────────────────────────────────────────┐
│ UEFI 启动流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌───────────────┐ ┌─────────────────┐ │
│ │ SEC │ → │ PEI │ → │ DXE │ │
│ │ (安全验证) │ │ (早期初始化) │ │ (驱动执行) │ │
│ └──────────────┘ └───────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ BDS (启动设备选择) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 加载 EFI 应用程序 (引导管理器) │ │
│ │ - \EFI\BOOT\BOOTX64.EF │ │
│ │ - GRUB EFI 版本 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 加载并启动 Linux 内核 │ │
│ │ 方式1: GRUB 加载 vmlinuz.efi │ │
│ │ 方式2: EFI Stub 直接启动 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

2.1.2 UEFI 阶段详解

SEC (Security Phase)

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
/* UEFI SEC 阶段职责 */
struct sec_phase {
/*
* 1. 系统上电/重置处理
* 2. 硬件信任根建立
* 3. 临时内存初始化
* 4. 向 PEI 传递控制权
*/
};

/* 典型操作 */
void sec_phase(void)
{
/* CPU 初始化 */
cpu_init();

/* 缓存初始化 */
cache_init();

/* 内存控制器初始化 */
memory_controller_init();

/* 安全验证 */
if (secure_boot_enabled()) {
verify_boot_firmware();
}

/* 传递控制到 PEI */
pass_control_to_pei();
}

PEI (Pre-EFI Initialization)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* PEI 阶段职责 */
struct pei_phase {
/*
* 1. 内存初始化
* 2. CPU 特性检测
* 3. 芯片组初始化
* 4. 固件卷 (FV) 解析
* 5. 向 DXE 传递控制权
*/
};

/* PEI 模块示例 */
typedef struct _EFI_PEI_SERVICES {
EFI_PEI_SERVICES_INSTAL;
EFI_PEI_SERVICES_XXX;
/* ... 更多服务 ... */
} EFI_PEI_SERVICES;

DXE (Driver Execution Environment)

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
/* DXE 阶段职责 */
struct dxe_phase {
/*
* 1. 启动服务 (Boot Services) 初始化
* 2. 运行时服务 (Runtime Services) 初始化
* 3. 驱动程序执行
* 4. 协议安装
* 5. 向 BDS 传递控制权
*/
};

/* 关键 DXE 原型 */
typedef struct _EFI_BOOT_SERVICES {
EFI_TASK_PRIORITY_LEVEL RaiseTPL;
EFI_TASK_PRIORITY_LEVEL RestoreTPL;
EFI_ALLOCATE_PAGES AllocatePages;
EFI_FREE_PAGES FreePages;
EFI_GET_MEMORY_MAP GetMemoryMap;
EFI_ALLOCATE_POOL AllocatePool;
EFI_FREE_POOL FreePool;
EFI_CREATE_EVENT CreateEvent;
/* ... 更多服务 ... */
} EFI_BOOT_SERVICES;

typedef struct _EFI_RUNTIME_SERVICES {
EFI_GET_TIME GetTime;
EFI_SET_TIME SetTime;
EFI_GET_WAKEUP_TIME GetWakeupTime;
EFI_SET_WAKEUP_TIME SetWakeupTime;
EFI_SET_VIRTUAL_ADDRESS_MAP SetVirtualAddressMap;
EFI_CONVERT_POINTER ConvertPointer;
/* ... 更多服务 ... */
} EFI_RUNTIME_SERVICES;

BDS (Boot Device Selection)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* BDS 阶段职责 */
struct bds_phase {
/*
* 1. 枚举启动设备
* 2. 执行启动策略
* 3. 加载启动管理器
* 4. 执行或提供用户界面
*/
};

/* 典型 BDS 流程 */
void bds_phase(void)
{
/* 连接所有设备 */
gBS->ConnectAllControllers();

/* 枚举启动选项 */
enumerate_boot_options();

/* 如果没有自动启动, 显示启动管理器 */
if (!auto_boot()) {
launch_boot_manager();
}
}

2.2 UEFI 系统表和协议

2.1.1 EFI_SYSTEM_TABLE

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
/* 系统表是 UEFI 环境的核心数据结构 */
typedef struct _EFI_SYSTEM_TABLE {
EFI_TABLE_HEADER Hdr;

CHAR16 *FirmwareVendor;
UINT32 FirmwareRevision;

EFI_HANDLE ConsoleInHandle;
EFI_SIMPLE_TEXT_IN_PROTOCOL *ConIn;

EFI_HANDLE ConsoleOutHandle;
EFI_SIMPLE_TEXT_OUT_PROTOCOL *ConOut;

EFI_HANDLE StandardErrorHandle;
EFI_SIMPLE_TEXT_OUT_PROTOCOL *StdErr;

EFI_RUNTIME_SERVICES *RuntimeServices;
EFI_BOOT_SERVICES *BootServices;

UINTN NumberOfTableEntries;
EFI_CONFIGURATION_TABLE *ConfigurationTable;

} EFI_SYSTEM_TABLE;

/* 内核中的使用 */
efi_system_table_t *efi_system_table;

void efi_call_vendor(efi_system_table_t *sys_table,
efi_guid_t *protocol,
void **interface)
{
/* 查找并调用协议 */
status = sys_table->BootServices->LocateProtocol(
protocol,
NULL,
interface
);
}

2.2.2 关键 UEFI 协议

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
/* 1. 加载镜像协议 (Loaded Image Protocol) */
#define EFI_LOADED_IMAGE_PROTOCOL_GUID \
{ 0x5B1B31A1, 0x9562, 0x11d2, {0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B} }

typedef struct _EFI_LOADED_IMAGE {
UINT32 Revision;
EFI_HANDLE ParentHandle;
EFI_SYSTEM_TABLE *SystemTable;

EFI_HANDLE DeviceHandle;
EFI_DEVICE_PATH_PROTOCOL *FilePath;
VOID *Reserved;

UINT32 LoadOptionsSize;
VOID *LoadOptions;

VOID *ImageBase;
UINT64 ImageSize;
EFI_MEMORY_TYPE ImageCodeType;
EFI_MEMORY_TYPE ImageDataType;
EFI_IMAGE_UNLOAD Unload;

} EFI_LOADED_IMAGE_PROTOCOL;

/* 2. 设备路径协议 (Device Path Protocol) */
#define EFI_DEVICE_PATH_PROTOCOL_GUID \
{ 0x09576E91, 0x6D3F, 0x11D2, {0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B} }

/* 3. 块 I/O 协议 (Block I/O Protocol) */
#define EFI_BLOCK_IO_PROTOCOL_GUID \
{ 0x964E5B21, 0x6459, 0x11D2, {0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B} }

typedef struct _EFI_BLOCK_IO_PROTOCOL {
UINT32 Revision;
EFI_BLOCK_IO_MEDIA *Media;

EFI_BLOCK_RESET Reset;
EFI_BLOCK_READ ReadBlocks;
EFI_BLOCK_WRITE WriteBlocks;
EFI_BLOCK_FLUSH FlushBlocks;

} EFI_BLOCK_IO_PROTOCOL;

/* 4. 图形输出协议 (Graphics Output Protocol) */
#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \
{ 0x9042A9DE, 0x23DC, 0x4A38, {0x96, 0xFB, 0x7A, 0xDE, 0xD0, 0x80, 0x51, 0x6A} }

typedef struct _EFI_GRAPHICS_OUTPUT_PROTOCOL {
UINT32 Mode;
EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode;
EFI_GRAPHICS_OUTPUT_BLT Blt;
EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE_QUERY_MODE *QueryMode;
EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE *SetMode;
EFI_GRAPHICS_OUTPUT_PROTOCOL_MAX_MODE *MaxMode;

} EFI_GRAPHICS_OUTPUT_PROTOCOL;

/* 5. 简单文件系统协议 (Simple File System Protocol) */
#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \
{ 0x0964E5B22, 0x6459, 0x11D2, {0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B} }

2.3 EFI Stub 机制

2.3.1 EFI Stub 原理

EFI Stub 使 Linux 内核可以直接作为 EFI 应用程序运行,无需引导程序。

1
2
3
4
5
6
7
8
9
10
11
传统启动方式:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ UEFI │ → │ GRUB │ → │ Kernel │
└──────────┘ └──────────┘ └──────────┘
固件 引导程序 内核

EFI Stub 方式:
┌──────────┐ ┌──────────┐
│ UEFI │ ──────────→ │ Kernel │
└──────────┘ (直接) └──────────┘
固件 内核 (自带 EFI 应用)

2.3.2 内核 PE 头部结构

arch/x86/boot/header.S 中定义的 PE 头部:

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
/* PE 可选头 */
struct pe_opt_header {
uint16_t magic; /* PE32/PE32+ 魔数 */
uint8_t major_linker_version;
uint8_t minor_linker_version;
uint32_t size_of_code;
uint32_t size_of_initialized_data;
uint32_t size_of_uninitialized_data;
uint32_t address_of_entry_point; /* EFI 入口点 */
uint32_t base_of_code;
uint64_t image_base;
uint32_t section_alignment;
uint32_t file_alignment;
uint16_t major_os_version;
uint16_t minor_os_version;
uint16_t major_image_version;
uint16_t minor_image_version;
uint16_t major_subsystem_version;
uint16_t minor_subsystem_version;
uint32_t win32_version_value;
uint32_t size_of_image;
uint32_t size_of_headers;
uint32_t checksum;
uint16_t subsystem; /* EFI 应用 */
uint16_t dll_characteristics;
uint64_t size_of_stack_reserve;
uint64_t size_of_stack_commit;
uint64_t size_of_heap_reserve;
uint64_t size_of_heap_commit;
uint32_t loader_flags;
uint32_t number_of_rva_and_sizes;
uint64_t data_directory[16];
};

/* PE 节头 */
struct pe_section {
char name[8]; /* ".setup", ".text", etc. */
uint32_t virtual_size;
uint32_t virtual_address;
uint32_t size_of_raw_data;
uint32_t pointer_to_raw_data;
uint32_t pointer_to_relocations;
uint32_t pointer_to_line_numbers;
uint16_t number_of_relocations;
uint16_t number_of_line_numbers;
uint32_t characteristics;
};

2.3.3 EFI 入口点

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
/* arch/x86/boot/compressed/efi_mixed.S */

.code64
.globl efi_pe_entry
efi_pe_entry:
/*
* EFI 入口点 (x86_64)
* 寄存器约定 (Microsoft x64 calling convention):
* rcx - ImageHandle
* rdx - SystemTable
*/

/* 保存 EFI 句柄 */
movq %rcx, %rbx # EFI_HANDLE

/* 保存系统表指针 */
movq %rdx, %r12 # EFI_SYSTEM_TABLE*

/* 调用 C 语言入口 */
jmp efi_main

.code32
.globl efi32_pe_entry
efi32_pe_entry:
/*
* EFI 入口点 (32位)
* 寄存器约定 (Microsoft i386 calling convention):
* [esp+4] - ImageHandle
* [esp+8] - SystemTable
*/

/* 保存参数 */
movl 4(%esp), %eax # ImageHandle
movl 8(%esp), %edx # SystemTable

/* 切换到 64 位模式 */
jmp efi_stub_entry

2.3.4 efi_main() 函数

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
/* arch/x86/boot/compressed/efi.c */

efi_status_t efi_main(efi_handle_t image_handle,
struct efi_system_table *sys_table_arg)
{
efi_status_t status;
struct efi_boot_params *boot_params;

/* 初始化全局系统表指针 */
efi_system_table = sys_table_arg;

/* 初始化 EFI 库 */
efi_init();

/* 获取图形输出协议 (用于早期显示) */
status = efi_bs_call(locate_protocol,
&graphics_output_protocol_guid,
NULL, (void **)&gop);
if (status == EFI_SUCCESS) {
efi_gop = gop;
}

/* 获取内存映射 */
status = efi_get_memory_map(&map, &map_size, &key,
&desc_size, &desc_ver);
if (status != EFI_SUCCESS) {
efi_printk("Failed to get memory map\n");
return status;
}

/* 分配 boot_params 结构 */
status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
sizeof(*boot_params),
(void **)&boot_params);
if (status != EFI_SUCCESS)
return status;

/* 初始化 boot_params */
memset(boot_params, 0, sizeof(*boot_params));

/* 获取启动选项 */
status = efi_parse_options(load_options, load_options_size);
if (status != EFI_SUCCESS)
goto fail;

/* 获取 ACPI 表 */
efi_acpi_init();

/* 获取 SMBIOS 表 */
efi_smbios_init();

/* 退出启动服务 */
status = efi_bs_call(exit_boot_services, image_handle, key);
if (status != EFI_SUCCESS) {
/* 重新获取内存映射并重试 */
efi_get_memory_map(&map, &map_size, &key,
&desc_size, &desc_ver);
status = efi_bs_call(exit_boot_services, image_handle, key);
if (status != EFI_SUCCESS)
goto fail;
}

/* 跳转到内核入口 */
efi_stub_entry(image_handle, sys_table_arg, boot_params);

fail:
efi_bs_call(free_pool, boot_params);
return status;
}

2.4 UEFI 启动详解

2.4.1 ESP 分区结构

EFI 系统分区 (ESP) 是 UEFI 启动的核心:

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
磁盘布局示例:
┌─────────────────────────────────────────────────────┐
│ Protective MBR (LBA 0) │
├─────────────────────────────────────────────────────┤
│ GPT Header (LBA 1) │
│ GPT Partition Entries (LBA 2-33) │
├─────────────────────────────────────────────────────┤
│ ESP 分区 (通常第一个分区) │
│ ├── /EFI/BOOT/ │
│ │ └── BOOTX64.EF # 默认引导加载器 │
│ ├── /EFI/GRUB/ │
│ │ └── GRUBX64.EF # GRUB 引导加载器 │
│ ├── /EFI/systemd/ │
│ │ └── systemd-bootx64.efi │
│ └── /EFI/Microsoft/ │
│ └── Boot/bootmgfw.efi # Windows 启动 │
├─────────────────────────────────────────────────────┤
│ Linux 根分区 │
├─────────────────────────────────────────────────────┤
│ Linux 交换分区 │
└─────────────────────────────────────────────────────┘

ESP 分区特点:
- FAT32 文件系统
- 分区类型 GUID: C12A7328-F81F-11D2-BA4B-00A0C93EC93B
- 必须是可启动分区
- 通常 100MB - 500MB 大小

2.4.2 UEFI 启动管理器

UEFI 固件使用 NVRAM 变量存储启动配置:

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
/* UEFI 启动选项格式 */
typedef struct _EFI_LOAD_OPTION {
UINT32 Attributes;
UINT16 FilePathListLength;
CHAR16 Description[];
/* EFI_DEVICE_PATH_PROTOCOL FilePathList[] */
/* UINT8 OptionalData[] */
} EFI_LOAD_OPTION;

/* 启动选项属性 */
#define LOAD_OPTION_ACTIVE 0x00000001
#define LOAD_OPTION_FORCE_RECONNECT 0x00000002
#define LOAD_OPTION_HIDDEN 0x00000008
#define LOAD_OPTION_CATEGORY 0x00001F00

/* EFI 全局变量 */
#define EFI_GLOBAL_VARIABLE_GUID \
{ 0x8BE4DF61, 0x93CA, 0x11D2, {0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C} }

/* 关键变量名 */
L"Boot0000" - L"BootFFFF" /* 启动选项 */
L"BootOrder" /* 启动顺序 */
L"BootCurrent" /* 当前启动项 */
L"BootNext" /* 下次启动项 */
L"Timeout" /* 菜单超时 */

2.4.3 使用 efibootmgr 管理启动项

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
# 列出当前启动项
efibootmgr

# 输出示例:
# BootCurrent: 0001
# Timeout: 3 seconds
# BootOrder: 0001, 0000, 0002
# Boot0000* Windows Boot Manager
# Boot0001* Linux
# Boot0002* UEFI Shell

# 添加新的启动项
efibootmgr -c -d /dev/sda -p 1 -L "Linux" -l '\EFI\BOOT\BOOTX64.EF'

# 设置启动顺序
efibootmgr -o 0001,0000,0002

# 删除启动项
efibootmgr -b 0002 -B

# 设置超时
efibootmgr -t 5

# 设置下次启动项
efibootmgr -n 0002

2.5 UEFI 手动加载协议

2.5.1 启动协议参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* boot_params.hdr 的关键字段 (UEFI 情况下) */
struct setup_header {
/* ... */
u32 loadflags; /* 位标志 */
u32 code32_start; /* 32/64位入口点 */
u32 cmd_line_ptr; /* 命令行指针 */
u32 initrd_addr_max; /* initrd 最大地址 */
u32 kernel_alignment; /* 内核对齐 */
/* ... */
};

/* loadflags 位定义 */
#define LOADED_HIGH 0x01 /* 内核加载到高位地址 */
#define CAN_USE_HEAP 0x80 /* 可以使用堆 */
#define QUIET_FLAG 0x8000 /* 安静启动 */

/* EFI Stub 特定标志 */
#define LOADFLAGS_EFI_STUB (1 << 0) /* EFI Stub 已加载 */

2.5.2 内存布局 (UEFI 情况)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
UEFI 启动时的内存布局:
┌─────────────────────────────────────────────────────┐
│ 物理内存地址 │
├─────────────────────────────────────────────────────┤
│ 0x00000000 - 0x000FFFFF 保留给 BIOS/UEFI │
│ 0x00100000 - 0x00FFFFFF 可用常规内存 │
│ 0x01000000 - 0xXXXXXXX 内核加载位置 │
│ (通常是 0x1000000) │
│ │
│ initrd 位置: │
│ - 由 initrd_addr_max 限制 │
│ - 通常加载在内核上方 │
│ │
│ 命令行参数: │
│ - 由 cmd_line_ptr 指向 │
│ - 最大 COMMAND_LINE_SIZE (4096) 字节 │
└─────────────────────────────────────────────────────┘

2.6 UEFI 和内核交互

2.6.1 EFI 内存映射到 E820

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
/* 内核需要将 UEFI 内存映射转换为 E820 格式 */
void efi_map_memmap(void)
{
efi_memory_desc_t *md;
u64 start, end;
int i;

/* 遍历 UEFI 内存映射 */
for_each_efi_memory_desc(md) {
start = md->PhysicalStart;
end = md->PhysicalStart + md->NumberOfPages * EFI_PAGE_SIZE;

/* 转换为 E820 类型 */
switch (md->Type) {
case EFI_LOADER_CODE:
case EFI_LOADER_DATA:
case EFI_BOOT_SERVICES_CODE:
case EFI_BOOT_SERVICES_DATA:
case EFI_CONVENTIONAL_MEMORY:
/* 可用内存 */
e820__range_add(start, end, E820_TYPE_RAM);
break;

case EFI_UNUSABLE_MEMORY:
/* 不可用内存 */
e820__range_add(start, end, E820_TYPE_UNUSABLE);
break;

case EFI_ACPI_RECLAIM_MEMORY:
/* ACPI 可回收内存 */
e820__range_add(start, end, E820_TYPE_ACPI);
break;

case EFI_ACPI_MEMORY_NVS:
/* ACPI NVS 内存 */
e820__range_add(start, end, E820_TYPE_NVS);
break;

case EFI_PERSISTENT_MEMORY:
/* 持久内存 */
e820__range_add(start, end, E820_TYPE_PMEM);
break;

default:
/* 其他类型视为保留 */
e820__range_add(start, end, E820_TYPE_RESERVED);
break;
}
}
}

2.6.2 UEFI 运行时服务

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
/* 退出启动服务后的内存映射 */
void efi_runtime_update_mappings(void)
{
/*
* 在 ExitBootServices() 之后:
*
* 1. 只有运行时服务区域保留映射
* 2. 其他内存可以被内核重新使用
* 3. 需要调用 SetVirtualAddressMap() 建立虚拟映射
*/

efi_status_t status;
u32 attr = EFI_MEMORY_RUNTIME | EFI_MEMORY_WB;
u64 size;
void **va;

/* 获取运行时服务区域 */
status = efi_get_memory_map(&map, &map_size, NULL, NULL, NULL);

/* 建立虚拟地址映射 */
for_each_efi_memory_desc(md) {
if (md->Attribute & EFI_MEMORY_RUNTIME) {
/* 分配虚拟地址 */
va = efi_map_region(md, attr);
}
}

/* 调用 SetVirtualAddressMap */
status = efi_rs_call(set_virtual_address_map,
size, desc_size, desc_ver,
efi_map);
}

2.7 Secure Boot (安全启动)

2.7.1 Secure Boot 工作原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌─────────────────────────────────────────────────────────┐
│ Secure Boot 验证链 │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ KEK │ ←── │ PK │ │
│ │ (密钥交换密钥) │ │ (平台密钥) │ │
│ └──────────────┘ └──────────────┘ │
│ │ │
│ ↓ │
│ ┌──────────────┐ │
│ │ db │ │
│ │ (签名数据库) │ │
│ └──────────────┘ │
│ │ │
│ ↓ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ shim │ ←── │ GRUB/Kernel │ │
│ │ (第一阶段) │ │ (验证签名) │ │
│ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘

2.7.2 Secure Boot 变量

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
/* Secure Boot 相关的 EFI 变量 */
#define EFI_IMAGE_SECURITY_DATABASE_GUID \
{ 0xd719b2cb, 0x3d3a, 0x4596, {0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f} }

/* 关键数据库 */
L"PK" # Platform Key (平台密钥)
L"KEK" # Key Exchange Key (密钥交换密钥)
L"db" # Signature Database (签名数据库 - 允许)
L"dbx" # Forbidden Signature Database (禁止数据库)
L"dbt" # Revocation Timebase (撤销时间基准)

/* 检查 Secure Boot 状态 */
bool efi_secureboot_enabled(void)
{
efi_guid_t guid = EFI_GLOBAL_VARIABLE_GUID;
u8 secboot, setupmode;
unsigned long size;

/* 读取 SecureBoot 变量 */
size = sizeof(secboot);
if (efi_get_variable(L"SecureBoot", &guid, NULL, &size, &secboot))
return false;

/* 读取 SetupMode 变量 */
size = sizeof(setupmode);
if (efi_get_variable(L"SetupMode", &guid, NULL, &size, &setupmode))
return false;

/* Secure Boot 启用且不在 Setup 模式 */
return (secboot == 1 && setupmode == 0);
}

2.8 本章小结

UEFI 启动流程回顾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
UEFI 固件 (SEC/PEI/DXE/BSD)

ESP 分区 /EFI/BOOT/BOOTX64.EF

[方式1] GRUB → 加载内核
[方式2] EFI Stub → 直接启动

efi_pe_entry → efi_main

获取内存映射、ACPI 表等

ExitBootServices()

跳转到 startup_64()

UEFI vs BIOS 关键差异

方面 UEFI BIOS
入口点 efi_pe_entry (PE) header.S (0x7C00)
参数传递 EFI_HANDLE + SystemTable boot_params (0x90000)
内存信息 EFI_MEMORY_DESCRIPTOR e820_map
早期输出 GOP 协议 VGA 文本模式
配置存储 NVRAM 变量 CMOS/BIOS 设置
  • Title: Linux内核分析之基础知识-2
  • Author: 韩乔落
  • Created at : 2026-01-08 02:39:50
  • Updated at : 2026-01-19 13:40:14
  • Link: https://jelasin.github.io/2026/01/08/Linux内核分析之基础知识-2/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments