linux x86 setup_arch代码注释

 个人理解加查的资料,不保证正确,有不对可以评论

// 查找个人电脑相关固件占用的地址(来自bootparam中的handler)
olpc_ofw_detect();
// 将 early_idts 设置到 idt_table 并应用 lidt,包含 asm_exc_debug asm_exc_int3(breakpoint)
idt_setup_early_traps(); 
// cpu 供应商等简单检查
early_cpu_init();
// 初始化用于动态修改汇编的 jump label 内存
jump_label_init();
// 根据静态调用的结构体生成汇编代码
static_call_init();
// 初始化用于remap的内存,它用于把物理内存映射到pgtable中,用于临时虚拟地址访问,一个remap slot最大可映射32页的内存
early_ioremap_init();
// 将外设的页表项设置到swapper_pg_dir页表的OLPC_OFW_PDE_NR项上,swapper_pg_dir是初始化阶段用的内核页表
setup_olpc_ofw_pgd();

early_reserve_memory();
e820__memory_setup();
// 设置支持 not execute
x86_configure_nx();
// reserve boot 的 setup_data中的每个保留内存到页表和e820_table、e820_table_kexec

e820__reserve_setup_data();
e820__finish_early_params();

// 为 reserve bft 区域保留内存到页表和memblock 512kB - 1MB
reserve_ibft_region();
// 初始化用于访问bios的每个mem设置到dmi_memdev
dmi_setup();
// 时钟周期数寄器,可用于计时
tsc_early_init();
// 检测 video system extension adapter 的 rom 区,并注册request_resource
x86_init.resources.probe_roms();
// 将_text到_end之间的代码区标记为RAM类型
e820_add_kernel_range();
// 标记0-4k从ram转为reserved,将640Kb -> 1Mb的bios区域从e820表去掉,更新e820表,
trim_bios_range();
// 初始化mtrr, pat。MTRR与PAT都是用于对不同区域采用不同缓存策略(如不缓存,写合并,写透,写保护等。),mtrr是用寄存器来控制权限,pat是在页表上控制权限,显然mtrr控制粒度比pat粗。cache_bp_init();
// 为每个 memory region 添加随机的 base
kernel_randomize_memory();
// 检查是否可用高级可编程中断控制器(代替旧中断控制器,如8259A芯片)
check_x2apic();
// 找到 ram 的最大 pfn,其上的内存属于 high_memory
e820__end_of_ram_pfn();
// 从 brk 区分配pgt buffer(32k),用于临时分配p4d,pud,pmd,pte页表项,超出这个范围时如果brk区用完了,将用memblock来分配
early_alloc_pgt_buf();
// 将brk区在memblock中标记(reserve)出来,
reserve_brk();
// 把512m内核内存中_text到_brk_end之外的pmd内存映射抹去
cleanup_highmap();
// 限制分配物理内存只能在1m以内
memblock_set_current_limit(ISA_END_ADDRESS);
// 将 e820 表中ram与kernel reserved区域加入memblock中,用于后面的动态分配
e820__memblock_setup();
// 限制swiotlb为总物理内存的6%,swiotlb是用于虚拟机设备与主机之间的数据缓冲区
sev_setup_arch();
// 初始化扩展固件的内存,镜像,资源表,服务(efi 初始化)
efi_fake_memmap();
efi_find_mirror();
efi_esrt_init();
efi_mokvar_table_init();
efi_reserve_boot_services();
// reserve 用于进程间通信表的内存
e820__memblock_alloc_reserved_mpc_new();
// 保留0-1m内存和realmode.bin使用的内存,0-1m要保留是因为efi可能会以实模式去使用它
x86_platform.realmode_reserve();
// 1m 以下用max_pfn_mapped来标记这之前全部是直接映射
// 1m 以上到max_low_pfn的区域,为每页在页表中分别创建直接映射,并更新页表
init_mem_mapping();
// 在中断表中设置缺页中断handler
idt_setup_early_pf();
// 关闭PCID功能(开启pcid时允许进程上下文切换时不清除tlb),这个功能只在长模式下有用
mmu_cr4_features = __read_cr4() & ~X86_CR4_PCIDE;
setup_log_buf();
// 将ramdisk的镜像(initrd)copy到1m以下的内存中。
reserve_initrd();
// 从__initramfs_start的表中读取kernel/firmware/acpi/文件内容至 acpi_tables_addr,并解析 acpi,的全称是ACPI (Advanced Configuration and Power Interface) 是一种标准化的电脑系统管理接口,用于处理电源管理、硬件配置和设备状态等方面的功能
acpi_table_upgrade();
acpi_boot_table_init();
// 如果是在虚拟机环境下,读取虚拟 cpu 的最大cpu数与ctl信息
vsmp_init();

// 判断是否是苹果机,有很多driver的逻辑会不一样
early_platform_quirks();
// 加载解析过的 acpi 配置至acpi_gbl_root_table_list中,并加载一些全局变量,如cmos的port(sbf_port),i8042,计时器(ACPI PM Timer)等
early_acpi_boot_init();
// 初始化NUMA节点,其中cpu与内存的关系记录在SRAT中
initmem_init();
// 将已经映射的1m以下内存记录在dma_mmu_remap中,这些内存会在页表中被全部映射为dma,并且以整个vm_area的形式记录到static_vmlist中
dma_contiguous_reserve(max_pfn_mapped << PAGE_SHIFT);
// 用于coredump
reserve_crashkernel();
// 标记dma_reserve值,在计算一次分配多大内存时(根据当前有多少free的dma内存),这部分dma范围不作为free内存计算。
memblock_find_dma_reserve();
// paging_init(主要是sparse_init和zone_sizes_init)
// sparse_init 中将每个memory region 没有reserved的部分,按找到它对应的section(每个section 大小为128k页=512M,section是按二维组织的,index为(root_idx,sec_idx)),标记上它的numa nid,在对应tag 上标记present和online,表示这个section有内存可用。之后为每个present的section分配128k个struct page作为memmap。
// zone_sizes_init 为每个numa node 初始化它们的每个zone,包括记录起始页、可用页数、总覆盖页数等(可以从memblock.memory.regions中找到每个numa node的所有可用大小(present),总覆盖长度指end - start,包含了中间的空洞,代码在free_area_init_node)。同时会初始化每个zone中的物理页(设置zone类型,numa node id,引用数为1,映射到文件次数为-1,上次使用的cpuid为-1),并为不可访问的页标记PG_reserved(memmap_init)
zone_spanned_pages_in_node();
x86_init.paging.pagetable_init();

kasan_init();

sync_initial_page_table();
// 检查是否支持Trusted Boot(tBoot),tboot可用于检查内核启动结果是否正常。
tboot_probe();
// __vsyscall_page(位于最高地址-10M的一页),它是用户可访问的一个内核页,在gate_vma标记这一页可执行
map_vsyscall();
// 检测apic driver可用
generic_apic_probe();

early_quirks();
// 重加载一些 acpi 配置
acpi_boot_init();
x86_dtb_init();

get_smp_config();

// 为每个acpi表创建fix映射
init_apic_mappings();

prefill_possible_map();

init_cpu_to_node();
// 将不需要内存的cpu设置 online
init_gi_nodes();

io_apic_init_mappings();

x86_init.hyper.guest_late_init();
// 为e820_table每个表项分配iomem_resource
e820__reserve_resources();
// 注册电脑休眠状态下不需要保存的内存(ram和kernelreserve以外的内存区域)
e820__register_nosave_regions(max_pfn);
// 为硬件端口分配ioport resource(dma,pic,timer,keyboard,fpu等)
x86_init.resources.reserve_resources();
e820__setup_pci_gap();
x86_init.oem.banner();
x86_init.timers.wallclock_init();
therm_lvt_init();
mcheck_init();
register_refined_jiffies(CLOCK_TICK_RATE);
unwind_init();
}