struct boot_params struct boot_params是 x86 架构 Linux 内核启动流程中一个极其关键的数据结构可以把它看作一份由引导程序Bootloader传递给内核的“硬件信息清单”。它的主要定义和文档位于内核源码树的以下位置定义头文件arch/x86/include/uapi/asm/bootparam.h详细文档Documentation/x86/zero-page.rst核心作用承上启下的信息载体这份“清单”的作用是在引导程序将控制权交给内核之前把从 BIOS/UEFI 固件和硬件探测中收集到的所有关键信息有序地传递给内核。它包含了内核启动早期所需的各种参数例如内存信息如完整的 E820 内存布局表 (e820_table)。显示与帧缓冲如屏幕信息 (screen_info) 和 EDID 信息 (edid_info)。固件接口如 ACPI RSDP 表地址 (acpi_rsdp_addr)、EFI 信息 (efi_info)。启动参数如内核命令行指针 (cmd_line_ptr)、初始 RAM 磁盘initrd的地址和大小 (ramdisk_image,ramdisk_size)。安全启动如secure_boot标志位用于指示固件是否开启了安全启动。校验标志如sentinel字段用于帮助内核检测引导程序是否正确地将整个结构体初始化为零以防止因未初始化的字段导致错误。数据组织方式固定布局与扩展struct boot_params的设计兼顾了向后兼容和功能扩展固定部分 (struct setup_header)结构体最前面一部分是setup_header子结构。它遵循一个古老的、固定格式的启动协议包含了最基本的启动参数如内核对齐要求 (kernel_alignment) 和映像大小 (init_size) 等。扩展数据 (struct setup_data)在setup_header之后是一个用于扩展的链表节点 (setup_data)。引导程序可以通过这个链表将一些无法在固定结构中定义的新数据如设备树、PCI 信息等传递给内核实现了良好的可扩展性。在启动流程中的角色传统 BIOS 启动setup.bin实模式启动代码会负责从 BIOS 收集信息填充好struct boot_params然后跳转到保护模式的内核代码。UEFI 启动EFI stub内核镜像中的 UEFI 应用程序会从 UEFI 固件中获取信息并从零开始创建这个结构体。在最新的混合模式Mixed Mode实现中甚至会在栈上分配一个临时的struct boot_params专门用来为 32 位启动代码提供最精简的信息以便它能正确建立页表。在struct boot_params中查看内存大小主要看两个字段一个是详细的内存映射表另一个是传统的总内存大小。字段名偏移/大小说明e820_table0x2D0 / 0xA00主要的内存信息。这是一个包含多个struct e820_entry的数组详细列出了系统中每一块内存区域的起始地址、长度和类型如可用内存、保留内存、ACPI数据等是内核了解物理内存布局最核心的数据来源。e820_entries0x1E8 / 0x001e820_table中的条目数表示e820_table数组里有多少个有效的内存描述项。alt_mem_k0x1E0 / 0x004备用内存大小。这是一个早期、更简单的内存探测结果以KB为单位表示系统可用的内存总量。它提供的信息没有e820_table详细但可以作为一个快速参考。如何理解和使用这些信息首选e820_table这是在现代Linux系统中获取完整、准确内存布局的标准方式。内核会遍历e820_table中的所有条目统计所有标记为E820_RAM的内存区域从而得到系统可用的物理内存总量和分布。alt_mem_k作为补充这是一个 legacy传统字段信息相对简单不能反映复杂的内存布局。在启动早期或e820_table不可用时内核可能会参考它但主要的内存管理决策都基于e820_table。简单来说引导程序通过%rsi传递的struct boot_params指针其核心的e820_table字段就是一份详尽的内存“藏宝图”内核通过解析它来知道系统里有多少内存、分别在哪里。在理解了struct boot_params作为信息载体的作用后可以再次审视在arch/x86/kernel/head_64.S里分析的startup_64入口其中的%rsi寄存器通常就指向了这个结构体的地址。/** Perform pagetable fixups. Additionally, if SME is active, encrypt* the kernel and retrieve the modifier (SME encryption mask if SME* is active) to be added to the initial pgdir entry that will be* programmed into CR3.*/leaq _text(%rip), %rdi /* rdi 0x1000000 */pushq %rsi /* struct boot_params */call __startup_64 /* arch/x86/kernel/head64.c */popq %rsi /* 恢复 boot_params */这段汇编代码是 Linux 内核启动过程中在arch/x86/kernel/head_64.S的startup_64入口处调用 C 语言函数__startup_64执行页表修正和内存加密处理的关键步骤。可以将这三行指令逐行拆解理解其设计意图。逐行代码解析leaq _text(%rip), %rdi作用使用基于%rip指令指针的相对寻址将_text这个符号的运行时物理地址加载到%rdi寄存器。注释说明/* rdi 0x1000000 */是一个典型值示例表示内核被加载到物理地址16MB处。这行代码为后续的__startup_64函数准备了第一个参数即内核映像在内存中的实际起始物理地址。pushq %rsi作用将当前%rsi寄存器的值即指向struct boot_params结构体的指针压入栈中保存。原因因为接下来要调用__startup_64函数根据 x86_64 的 C 调用约定%rdi和%rsi是传递前两个参数的主要寄存器。为了避免%rsi中的boot_params指针被函数调用覆盖需要先将其保存起来。call __startup_64作用调用在arch/x86/kernel/head64.c中实现的 C 函数__startup_64。传入参数%rdi中保存了内核的物理起始地址_text%rsi保存了boot_params指针。执行任务该函数负责执行以下核心操作修正页表项中的物理地址遍历并更新早期页表如early_top_pgt、level3_kernel_pgt等中所有条目的物理地址加上内核实际加载的偏移量phys_base确保它们指向正确的物理内存位置。处理 SME/SEV 内存加密如果检测到 AMD SME安全内存加密处于活动状态函数会执行相关处理并返回一个加密掩码SME encryption mask。返回修正结果函数执行完毕后会将修正后的相关值如最终的页表基址可能已包含加密掩码通过%rax寄存器返回。popq %rsi作用从栈中恢复之前保存的boot_params指针到%rsi寄存器。目的因为__startup_64函数的调用可能改变了%rsi的值为了确保后续的汇编代码仍能正确访问boot_params结构体需要将其恢复。整体设计意图这段代码的核心逻辑可以总结为地址计算获取内核的实际物理起始地址。参数保存保护关键的boot_params指针不被破坏。核心修正调用 C 函数完成复杂的页表修正和加密处理。环境恢复恢复boot_params指针为后续汇编代码的执行做好准备。通过将复杂的逻辑如页表遍历、加密掩码处理交给 C 语言实现而将关键的地址计算和参数传递保留在汇编中这个设计保证了在启动早期这个极其敏感的时期复杂操作的正确性和灵活性。__startup_64函数的返回值通常是一个修正后的地址或掩码会供后续代码使用以完成最终页表基址的加载。(gdb) p *(struct boot_params*)$rsi$8 {screen_info {orig_x 0 \000, orig_y 24 \030, ext_mem_k 64512,orig_video_page 0, orig_video_mode 3 \003, orig_video_cols 80 P, flags 0 \000,unused2 0 \000, orig_video_ega_bx 3, unused3 0, orig_video_lines 25 \031,orig_video_isVGA 1 \001, orig_video_points 16, lfb_width 0, lfb_height 0,lfb_depth 0, lfb_base 0, lfb_size 0, cl_magic 0, cl_offset 0, lfb_linelength 0,red_size 0 \000, red_pos 0 \000, green_size 0 \000, green_pos 0 \000,blue_size 0 \000, blue_pos 0 \000, rsvd_size 0 \000, rsvd_pos 0 \000,vesapm_seg 0, vesapm_off 0, pages 0, vesa_attributes 0, capabilities 0,ext_lfb_base 0, _reserved \000}, apm_bios_info {version 0, cseg 0, offset 0,cseg_16 0, dseg 0, flags 0, cseg_len 0, cseg_16_len 0, dseg_len 0},_pad2 \000\000\000, tboot_addr 0, ist_info {signature 34432, command 0, event 0,perf_level 1196640579}, acpi_rsdp_addr 1006256, _pad3 \000\000\000\000\000\000\000,hd0_info \000 repeats 15 times, hd1_info \000 repeats 15 times, sys_desc_table {length 0, table \000 repeats 13 times}, olpc_ofw_header {ofw_magic 0,ofw_version 0, cif_handler 0, irq_desc_table 0}, ext_ramdisk_image 0,ext_ramdisk_size 0, ext_cmd_line_ptr 0, _pad4 \000 repeats 115 times, edid_info {dummy \000 repeats 127 times}, efi_info {efi_loader_signature 0, efi_systab 0,efi_memdesc_size 0, efi_memdesc_version 0, efi_memmap 0, efi_memmap_size 0,efi_systab_hi 0, efi_memmap_hi 0}, alt_mem_k 3144576, scratch 1048609,e820_entries 7 \a, eddbuf_entries 0 \000, edd_mbr_sig_buf_entries 0 \000,kbd_status 0 \000, secure_boot 0 \000, _pad5 \000, sentinel 0 \000, _pad6 ,hdr {setup_sects 32 , root_flags 1, syssize 405304, ram_size 0, vid_mode 65535,root_dev 0, boot_flag 43605, jump 26347, header 1400005704, version 525,realmode_swtch 0, start_sys_seg 4096, kernel_version 13088, type_of_loader 176 \260,loadflags 129 \201, setup_move_size 32768, code32_start 1048576,ramdisk_image 3218857984, ramdisk_size 2232783, bootsect_kludge 0, heap_end_ptr 65024,ext_loader_ver 0 \000, ext_loader_type 0 \000, cmd_line_ptr 131072,initrd_addr_max 2147483647, kernel_alignment 2097152, relocatable_kernel 1 \001,min_alignment 21 \025, xloadflags 63, cmdline_size 2047, hardware_subarch 0,hardware_subarch_data 0, payload_offset 945, payload_length 6247659, setup_data 0,pref_address 16777216, init_size 26730496, handover_offset 400},_pad7 \000 repeats 39 times, edd_mbr_sig_buffer {0 repeats 16 times}, e820_table {{addr 0, size 654336, type 1}, {addr 654336, size 1024, type 2}, {addr 983040,size 65536, type 2}, {addr 1048576, size 3220045824, type 1}, {addr 3221094400,size 131072, type 2}, {addr 4294705152, size 262144, type 2}, {addr 4294967296,size 1073741824, type 1}, {addr 0, size 0, type 0} repeats 121 times},_pad8 \000 repeats 47 times, eddbuf {{device 0 \000, version 0 \000,interface_support 0, legacy_max_cylinder 0, legacy_max_head 0 \000,legacy_sectors_per_track 0 \000, params {length 0, info_flags 0,num_default_cylinders 0, num_default_heads 0, sectors_per_track 0,number_of_sectors 0, bytes_per_sector 0, dpte_ptr 0, key 0,device_path_info_length 0 \000, reserved2 0 \000, reserved3 0,host_bus_type \000\000\000, interface_type \000\000\000\000\000\000\000,interface_path {isa {base_address 0, reserved1 0, reserved2 0}, pci {bus 0 \000, slot 0 \000, function 0 \000, channel 0 \000,reserved 0}, ibnd {reserved 0}, xprs {reserved 0}, htpt {reserved 0},unknown {reserved 0}}, device_path {ata {device 0 \000,reserved1 0 \000, reserved2 0, reserved3 0, reserved4 0}, atapi {device 0 \000, lun 0 \000, reserved1 0 \000, reserved2 0 \000,reserved3 0, reserved4 0}, scsi {id 0, lun 0, reserved1 0, reserved2 0},usb {serial_number 0, reserved 0}, i1394 {eui 0, reserved 0}, fibre {wwid 0, lun 0}, i2o {identity_tag 0, reserved 0}, raid {array_number 0,reserved1 0, reserved2 0}, sata {device 0 \000, reserved1 0 \000,reserved2 0, reserved3 0, reserved4 0}, unknown {reserved1 0,reserved2 0}}, reserved4 0 \000, checksum 0 \000}}, {device 0 \000,version 0 \000, interface_support 0, legacy_max_cylinder 0,legacy_max_head 0 \000, legacy_sectors_per_track 0 \000, params {length 0,info_flags 0, num_default_cylinders 0, num_default_heads 0, sectors_per_track 0,number_of_sectors 0, bytes_per_sector 0, dpte_ptr 0, key 0,device_path_info_length 0 \000, reserved2 0 \000, reserved3 0,host_bus_type \000\000\000, interface_type \000\000\000\000\000\000\000,interface_path {isa {base_address 0, reserved1 0, reserved2 0}, pci {bus 0 \000, slot 0 \000, function 0 \000, channel 0 \000,reserved 0}, ibnd {reserved 0}, xprs {reserved 0}, htpt {reserved 0},unknown {reserved 0}}, device_path {ata {device 0 \000,reserved1 0 \000, reserved2 0, reserved3 0, reserved4 0}, atapi {device 0 \000, lun 0 \000, reserved1 0 \000, reserved2 0 \000,reserved3 0, reserved4 0}, scsi {id 0, lun 0, reserved1 0, reserved2 0},usb {serial_number 0, reserved 0}, i1394 {eui 0, reserved 0}, fibre {wwid 0, lun 0}, i2o {identity_tag 0, reserved 0}, raid {array_number 0,reserved1 0, reserved2 0}, sata {device 0 \000, reserved1 0 \000,reserved2 0, reserved3 0, reserved4 0}, unknown {reserved1 0,reserved2 0}}, reserved4 0 \000, checksum 0 \000}}, {device 0 \000,version 0 \000, interface_support 0, legacy_max_cylinder 0,legacy_max_head 0 \000, legacy_sectors_per_track 0 \000, params {length 0,info_flags 0, num_default_cylinders 0, num_default_heads 0, sectors_per_track 0,number_of_sectors 0, bytes_per_sector 0, dpte_ptr 0, key 0,device_path_info_length 0 \000, reserved2 0 \000, reserved3 0,host_bus_type \000\000\000, interface_type \000\000\000\000\000\000\000,interface_path {isa {base_address 0, reserved1 0, reserved2 0}, pci {bus 0 \000, slot 0 \000, function 0 \000, channel 0 \000,reserved 0}, ibnd {reserved 0}, xprs {reserved 0}, htpt {reserved 0},unknown {reserved 0}}, device_path {ata {device 0 \000,reserved1 0 \000, reserved2 0, reserved3 0, reserved4 0}, atapi {device 0 \000, lun 0 \000, reserved1 0 \000, reserved2 0 \000,reserved3 0, reserved4 0}, scsi {id 0, lun 0, reserved1 0, reserved2 0},usb {serial_number 0, reserved 0}, i1394 {eui 0, reserved 0}, fibre {wwid 0, lun 0}, i2o {identity_tag 0, reserved 0}, raid {array_number 0,reserved1 0, reserved2 0}, sata {device 0 \000, reserved1 0 \000,reserved2 0, reserved3 0, reserved4 0}, unknown {reserved1 0,reserved2 0}}, reserved4 0 \000, checksum 0 \000}}, {device 0 \000,version 0 \000, interface_support 0, legacy_max_cylinder 0,legacy_max_head 0 \000, legacy_sectors_per_track 0 \000, params {length 0,info_flags 0, num_default_cylinders 0, num_default_heads 0, sectors_per_track 0,number_of_sectors 0, bytes_per_sector 0, dpte_ptr 0, key 0,device_path_info_length 0 \000, reserved2 0 \000, reserved3 0,host_bus_type \000\000\000, interface_type \000\000\000\000\000\000\000,interface_path {isa {base_address 0, reserved1 0, reserved2 0}, pci {bus 0 \000, slot 0 \000, function 0 \000, channel 0 \000,reserved 0}, ibnd {reserved 0}, xprs {reserved 0}, htpt {reserved 0},unknown {reserved 0}}, device_path {ata {device 0 \000,reserved1 0 \000, reserved2 0, reserved3 0, reserved4 0}, atapi {device 0 \000, lun 0 \000, reserved1 0 \000, reserved2 0 \000,reserved3 0, reserved4 0}, scsi {id 0, lun 0, reserved1 0, reserved2 0},usb {serial_number 0, reserved 0}, i1394 {eui 0, reserved 0}, fibre {wwid 0, lun 0}, i2o {identity_tag 0, reserved 0}, raid {array_number 0,reserved1 0, reserved2 0}, sata {device 0 \000, reserved1 0 \000,reserved2 0, reserved3 0, reserved4 0}, unknown {reserved1 0,reserved2 0}}, reserved4 0 \000, checksum 0 \000}}, {device 0 \000,version 0 \000, interface_support 0, legacy_max_cylinder 0,legacy_max_head 0 \000, legacy_sectors_per_track 0 \000, params {length 0,info_flags 0, num_default_cylinders 0, num_default_heads 0, sectors_per_track 0,number_of_sectors 0, bytes_per_sector 0, dpte_ptr 0, key 0,device_path_info_length 0 \000, reserved2 0 \000, reserved3 0,host_bus_type \000\000\000, interface_type \000\000\000\000\000\000\000,interface_path {isa {base_address 0, reserved1 0, reserved2 0}, pci {bus 0 \000, slot 0 \000, function 0 \000, channel 0 \000,reserved 0}, ibnd {reserved 0}, xprs {reserved 0}, htpt {reserved 0},unknown {reserved 0}}, device_path {ata {device 0 \000,reserved1 0 \000, reserved2 0, reserved3 0, reserved4 0}, atapi {device 0 \000, lun 0 \000, reserved1 0 \000, reserved2 0 \000,reserved3 0, reserved4 0}, scsi {id 0, lun 0, reserved1 0, reserved2 0},usb {serial_number 0, reserved 0}, i1394 {eui 0, reserved 0}, fibre {wwid 0, lun 0}, i2o {identity_tag 0, reserved 0}, raid {array_number 0,reserved1 0, reserved2 0}, sata {device 0 \000, reserved1 0 \000,reserved2 0, reserved3 0, reserved4 0}, unknown {reserved1 0,reserved2 0}}, reserved4 0 \000, checksum 0 \000}}, {device 0 \000,version 0 \000, interface_support 0, legacy_max_cylinder 0,legacy_max_head 0 \000, legacy_sectors_per_track 0 \000, params {length 0,info_flags 0, num_default_cylinders 0, num_default_heads 0, sectors_per_track 0,number_of_sectors 0, bytes_per_sector 0, dpte_ptr 0, key 0,device_path_info_length 0 \000, reserved2 0 \000, reserved3 0,host_bus_type \000\000\000, interface_type \000\000\000\000\000\000\000,interface_path {isa {base_address 0, reserved1 0, reserved2 0}, pci {bus 0 \000, slot 0 \000, function 0 \000, channel 0 \000,reserved 0}, ibnd {reserved 0}, xprs {reserved 0}, htpt {reserved 0},unknown {reserved 0}}, device_path {ata {device 0 \000,reserved1 0 \000, reserved2 0, reserved3 0, reserved4 0}, atapi {device 0 \000, lun 0 \000, reserved1 0 \000, reserved2 0 \000,reserved3 0, reserved4 0}, scsi {id 0, lun 0, reserved1 0, reserved2 0},usb {serial_number 0, reserved 0}, i1394 {eui 0, reserved 0}, fibre {wwid 0, lun 0}, i2o {identity_tag 0, reserved 0}, raid {array_number 0,reserved1 0, reserved2 0}, sata {device 0 \000, reserved1 0 \000,reserved2 0, reserved3 0, reserved4 0}, unknown {reserved1 0,reserved2 0}}, reserved4 0 \000, checksum 0 \000}}},_pad9 \000 repeats 275 times}