第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
|
#define BIOS_VIDEO_SERVICES 0x10
#define BIOS_DISK_SERVICES 0x13
#define BIOS_SYSTEM_SERVICES 0x15
#define BIOS_KEYBOARD_SERVICES 0x16
#define BIOS_TIME_SERVICES 0x1A
|
内核中的使用示例 (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);
ireg.ah = 0x02; intcall(0x16, &ireg, &oreg); boot_params.kbd_status = oreg.al;
ireg.ax = 0x0305; intcall(0x16, &ireg, NULL); }
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
| #define E820_TYPE_RAM 1 #define E820_TYPE_RESERVED 2 #define E820_TYPE_ACPI 3 #define E820_TYPE_NVS 4 #define E820_TYPE_UNUSABLE 5 #define E820_TYPE_PMEM 7
struct e820_entry { u64 addr; u64 size; u32 type; } __attribute__((packed));
|
内核查询 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;
initregs(&ireg); ireg.ax = 0xe820; ireg.cx = sizeof(buf); ireg.edx = SMAP; ireg.di = (size_t)&buf;
do { intcall(0x15, &ireg, &oreg);
if (oreg.eflags & X86_EFLAGS_CF) break;
if (oreg.eax != SMAP) { count = 0; break; }
*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
|
menuentry "Linux 6.12.38" { insmod gzio insmod part_gpt insmod ext2
set root='hd0,gpt2'
echo 'Loading Linux kernel ...' linux /vmlinuz-6.12 \ root=/dev/sda3 \ ro \ console=ttyS0,115200n8 \ quiet
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
| struct setup_header { u8 setup_sects; u16 root_flags; u32 syssize; u16 ram_size; u16 video_mode; u16 root_dev; u16 boot_flag; u16 jump; u32 header; u16 version; u32 type_of_loader; u32 loadflags; u32 setup_move_size; u32 code32_start; u32 cmd_line_ptr; u32 initrd_addr_max; u32 kernel_alignment; u8 relocatable_kernel; u8 min_alignment; u32 cmdline_size; };
|
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 #define BOOT_PROTO_V2 0x0200 #define BOOT_PROTO_V2_10 0x0201 #define BOOT_PROTO_V2_12 0x020C #define BOOT_PROTO_V2_15 0x020F
|
1.4.3 引导程序类型标识
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #define LOADER_TYPE_NONE 0 #define LOADER_TYPE_LOADBIN 1 #define LOADER_TYPE_BOOTSECT_LOADER 2 #define LOADER_TYPE_SYSLINUX 3 #define LOADER_TYPE_ETHBOOT 4 #define LOADER_TYPE_KERNEL 5 #define LOADER_TYPE_GRUB 6 #define LOADER_TYPE_UBOOT 7 #define LOADER_TYPE_TILO 8 #define LOADER_TYPE_ZIPL 9 #define LOADER_TYPE_KEXEC 10
#define EXT_LOADER_TYPE_NONE 0 #define EXT_LOADER_TYPE_QEMU 1 #define EXT_LOADER_TYPE_EDK2 2 #define EXT_LOADER_TYPE_FIRMWARE 3
|
1.5 实模式初始化 (main.c)
1.5.1 main() 函数流程
arch/x86/boot/main.c 的 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
| void main(void) { copy_boot_params();
init_heap();
keyboard_init(); query_ist(); query_vbe(); query_mca();
set_video();
detect_memory();
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; u16 cl_offset; };
const struct old_cmdline * const oldcmd = absolute_pointer(OLD_CL_ADDRESS);
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(); 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 = _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) {
if (detect_memory_e820() > 0) return;
if (detect_memory_e801() > 0) return;
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) { do_restore(); } else { set_mode(video_mode); }
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) { realmode_switch_hook();
if (enable_a20()) { puts("A20 gate not responding, unable to boot...\n"); die(); }
reset_coprocessor();
mask_all_interrupts();
setup_idt(); setup_gdt();
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) {
if (a20_bios()) return 0;
if (a20_kbc()) return 0;
if (a20_fast()) return 0;
return -1; }
static int enable_a20_fast(void) { u8 port_a;
port_a = inb(0x92);
port_a |= 0x02;
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) { static const u64 boot_gdt[] __attribute__((aligned(16))) = { [GDT_ENTRY_BOOT_CS] = GDT_ENTRY(DESC_CODE32, 0, 0xfffff),
[GDT_ENTRY_BOOT_DS] = GDT_ENTRY(DESC_DATA32, 0, 0xfffff),
[GDT_ENTRY_BOOT_TSS] = GDT_ENTRY(DESC_TSS32, 4096, 103), };
static struct gdt_ptr gdt;
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
|
关键数据结构
- boot_params - 4KB 的启动参数结构
- e820_entry - 内存映射条目
- setup_header - 启动协议头部
- GDT - 全局描述符表
关键内存位置
| 地址 |
用途 |
| 0x7C00 |
MBR 加载位置 |
| 0x90000 |
boot_params 目标位置 |
| 0x100000 |
32/64 位内核入口点 |