- 十七、linux内核原理、移植
- 17.1 概述
- 17.2 内核Makefile
- 17.2.1 内核系统配置
- 17.2.2 Makefile 中变量
- 17.2.3 内核配置文件
- 17.3 内核启动原理
- 17.3.1 Linux镜像
- 17.3.2 Linux 程序入口
- 17.3.3 linux 第一阶段启动
- 17.3.4 linux 第二阶段启动
- 17.4 内核移植
- 17.5 linux内核配置、编译
Linux作为嵌入式操作系统的最主流操作系统之一,凭借其源码开源、稳定性好、免费、市场占备受开发设计者人员青睐。
linux内核下载:
https://www.kernel.org/
xilinx内核下载:
https://github.com/Xilinx/linux-xlnx17.2 内核Makefile 17.2.1 内核系统配置
Makefile :定义了内核编译规则
Kconfig :给用户提供配置选择功能
配置工具:包括配置命令解析器和配置用户界面,包括:make configm命令、make menuconfig命令、make xconfig命令
Linux内核中Makefile和相关文件:
Makefile : 顶层Makefile是整个内核配置、编译的总体控制文件 .config : 内核配置文件,包含由用户选择的配置选项,用来存放内核配置结果 arch/arm/Makefile :是针对arm平台的Makefile 各子目录下的Mkaefile :负责管理子目录下源代码编译
核心思路:
用户通过make_xxx_config配置后,产生.config,顶层Makefile读入.config中配置选项。顶层Makefile递归进入子目录,分别调用内核配置选中的子目录源代码和子目录Makefile编译。
版本信息:
Linux内核版本号从源代码顶层Makefile中看到,V5.10
# SPDX-License-Identifier: GPL-2.0 VERSION = 5 PATCHLEVEL = 10 SUBLEVEL = 0 EXTRAVERSION = NAME = Kleptomaniac Octopus
CPU体系架构:
比如arm:
ARCH := arm
路径信息:
TOPDIR //定义内核源代码的根目录 SUBDIR //定义进入拿一些子目录
编译规则设置
CROSS_COMPIL = arm-xilinx-linux-gnueabi- //定义交叉编译器 CC = $(CROSS_COMPIL)gcc LD = $(CROSS_COMPIL)ld17.2.3 内核配置文件
配置变量CONFIG_*中.config文件中有许多配置变量等式,用来说明用户配置结果,如:CONFIG_MODULES=y表明用户选择Linux内核的模块功能。
内核配置文件备注:
内核源码树目录下有Kconfig和Makefile两个文件,分不到各个目录下,构成了一个分布式的内核配置数据库,每个Kconfig分别描述所属目录源文件相关内核配置菜单。在内核配置make
menuconfig时,从Kconfig种读取出菜单,用户选择后保存到.config的内核配置文档中。在主Makefile调用.config文件,就可以编译出用户配置的内核文件了。
config属性:
bool :yes/no类型 depend on :依赖项 default :默认值 select :反向依赖 tristate :三态(内建、模块、移除) string :字符串 hex :十六进制 inter :整数 help :帮助信息17.3 内核启动原理
Bootloader加载完成之后,将非易失性存储器(Flash或SD卡)中的Linux内核复制到RAM中,开始执行内核第一条指令,从而启动Linux内核。
17.3.1 Linux镜像两种linux镜像:
1、非压缩内核 Image
2、压缩内核 zImage
因为zImage是压缩之后的Image,所以执行起来比Image要慢。但采用zImage占用较少的存储空间,一般选取压缩式内核方式。
arch/arm/boot/compressed/head.S文件的主要内容就是解压缩zImage,然后跳转到vmlinux执行内核;zImage启动时,先执行的代码也是head.S文件,流程如下:
Bootloader调用内核前,需要做好以下准备:
17.3.2 Linux 程序入口1、CPU寄存器R0、R1、R2设置
2、CPU工作模式设置:禁止中断、CPU工作在SVC管理模式(Supervisor)
3、数据Cache关闭、MMU关闭
Linux程序入口_start,文件路径如下:
arch/arm/boot/compressed/head.S
进入head.S之后,依次完成以下工作:
1. 开启MMU和Cache 2. 调用decompress_kernel()解压内核 3. 调用call_kernel()进入非压缩内核Image的启动。
head.S中call_kernel部分代码:
mov r0, r4 mov r1, sp @ malloc space above stack add r2, sp, #MALLOC_SIZE @ 64k max mov r3, r7 bl decompress_kernel get_inflated_image_size r1, r2, r3 mov r0, r4 @ start of inflated image add r1, r1, r0 @ end of inflated image bl cache_clean_flush bl cache_off #ifdef CONFIG_ARM_VIRT_EXT mrs r0, spsr @ Get saved CPU boot mode and r0, r0, #MODE_MASK cmp r0, #HYP_MODE @ if not booted in HYP mode... bne __enter_kernel @ boot kernel directly adr r12, .L__hyp_reentry_vectors_offset ldr r0, [r12] add r0, r0, r12 bl __hyp_set_vectors __HVC(0) @ otherwise bounce to hyp mode b . @ should never be reached .align 2 .L__hyp_reentry_vectors_offset: .long __hyp_reentry_vectors - . #else b __enter_kernel #endif17.3.3 linux 第一阶段启动
完成zImage自解压之后,跳转到解压后的内核中的arh/arm/kernel/vmlinux.lds.S(最终的链接脚本实现)。
内核启动入口点,arh/arm/kernel/head.S文件如下:
#include#include #include #include #include #include #include #include #include #include #if defined(CONFIG_DEBUG_LL) && !defined(CONFIG_DEBUG_SEMIHOSTING) #include CONFIG_DEBUG_LL_INCLUDE #endif #define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET) #if (KERNEL_RAM_VADDR & 0xffff) != 0x8000 #error KERNEL_RAM_VADDR must start at 0xXXXX8000 #endif #ifdef CONFIG_ARM_LPAE #define PG_DIR_SIZE 0x5000 #define PMD_ORDER 3 #else #define PG_DIR_SIZE 0x4000 #define PMD_ORDER 2 #endif .globl swapper_pg_dir .equ swapper_pg_dir, KERNEL_RAM_VADDR - PG_DIR_SIZE .macro pgtbl, rd, phys add rd, phys, #TEXT_OFFSET sub rd, rd, #PG_DIR_SIZE .endm .arm __HEAD ENTRY(stext) ARM_BE8(setend be ) @ ensure we are in BE8 mode THUMB( badr r9, 1f ) @ Kernel is always entered in ARM. THUMB( bx r9 ) @ If this is a Thumb-2 kernel, THUMB( .thumb ) @ switch to Thumb now. THUMB(1: ) #ifdef CONFIG_ARM_VIRT_EXT bl __hyp_stub_install #endif @ ensure svc mode and all interrupts masked safe_svcmode_maskall r9 mrc p15, 0, r9, c0, c0 @ get processor id bl __lookup_processor_type @ r5=procinfo r9=cpuid movs r10, r5 @ invalid processor (r5=0)? THUMB( it eq ) @ force fixup-able long branch encoding beq __error_p @ yes, error 'p' #ifdef CONFIG_ARM_LPAE mrc p15, 0, r3, c0, c1, 4 @ read ID_MMFR0 and r3, r3, #0xf @ extract VMSA support cmp r3, #5 @ long-descriptor translation table format? THUMB( it lo ) @ force fixup-able long branch encoding blo __error_lpae @ only classic page table format #endif #ifndef CONFIG_XIP_KERNEL adr r3, 2f ldmia r3, {r4, r8} sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET) add r8, r8, r4 @ PHYS_OFFSET #else ldr r8, =PLAT_PHYS_OFFSET @ always constant in this case #endif bl __vet_atags #ifdef CONFIG_SMP_ON_UP bl __fixup_smp #endif #ifdef CONFIG_ARM_PATCH_PHYS_VIRT bl __fixup_pv_table #endif bl __create_page_tables ldr r13, =__mmap_switched @ address to jump to after @ mmu has been enabled badr lr, 1f @ return (PIC) address #ifdef CONFIG_ARM_LPAE mov r5, #0 @ high TTBR0 mov r8, r4, lsr #12 @ TTBR1 is swapper_pg_dir pfn #else mov r8, r4 @ set TTBR1 to swapper_pg_dir #endif ldr r12, [r10, #PROCINFO_INITFUNC] add r12, r12, r10 ret r12 1: b __enable_mmu ENDPROC(stext) .ltorg #ifndef CONFIG_XIP_KERNEL 2: .long . .long PAGE_OFFSET #endif __create_page_tables: pgtbl r4, r8 @ page table address mov r0, r4 mov r3, #0 add r6, r0, #PG_DIR_SIZE 1: str r3, [r0], #4 str r3, [r0], #4 str r3, [r0], #4 str r3, [r0], #4 teq r0, r6 bne 1b #ifdef CONFIG_ARM_LPAE mov r0, r4 add r3, r4, #0x1000 @ first PMD table address orr r3, r3, #3 @ PGD block type mov r6, #4 @ PTRS_PER_PGD mov r7, #1 << (55 - 32) @ L_PGD_SWAPPER 1: #ifdef CONFIG_CPU_ENDIAN_BE8 str r7, [r0], #4 @ set top PGD entry bits str r3, [r0], #4 @ set bottom PGD entry bits #else str r3, [r0], #4 @ set bottom PGD entry bits str r7, [r0], #4 @ set top PGD entry bits #endif add r3, r3, #0x1000 @ next PMD table subs r6, r6, #1 bne 1b add r4, r4, #0x1000 @ point to the PMD tables #ifdef CONFIG_CPU_ENDIAN_BE8 add r4, r4, #4 @ we only write the bottom word #endif #endif ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags adr r0, __turn_mmu_on_loc ldmia r0, {r3, r5, r6} sub r0, r0, r3 @ virt->phys offset add r5, r5, r0 @ phys __turn_mmu_on add r6, r6, r0 @ phys __turn_mmu_on_end mov r5, r5, lsr #SECTION_SHIFT mov r6, r6, lsr #SECTION_SHIFT 1: orr r3, r7, r5, lsl #SECTION_SHIFT @ flags + kernel base str r3, [r4, r5, lsl #PMD_ORDER] @ identity mapping cmp r5, r6 addlo r5, r5, #1 @ next section blo 1b add r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER) ldr r6, =(_end - 1) orr r3, r8, r7 add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER) 1: str r3, [r0], #1 << PMD_ORDER add r3, r3, #1 << SECTION_SHIFT cmp r0, r6 bls 1b #ifdef CONFIG_XIP_KERNEL #define XIP_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR) mov r3, pc mov r3, r3, lsr #SECTION_SHIFT orr r3, r7, r3, lsl #SECTION_SHIFT add r0, r4, #(XIP_START & 0xff000000) >> (SECTION_SHIFT - PMD_ORDER) str r3, [r0, #((XIP_START & 0x00f00000) >> SECTION_SHIFT) << PMD_ORDER]! ldr r6, =(_edata_loc - 1) add r0, r0, #1 << PMD_ORDER add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER) 1: cmp r0, r6 add r3, r3, #1 << SECTION_SHIFT strls r3, [r0], #1 << PMD_ORDER bls 1b #endif mov r0, r2, lsr #SECTION_SHIFT movs r0, r0, lsl #SECTION_SHIFT subne r3, r0, r8 addne r3, r3, #PAGE_OFFSET addne r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER) orrne r6, r7, r0 strne r6, [r3], #1 << PMD_ORDER addne r6, r6, #1 << SECTION_SHIFT strne r6, [r3] #if defined(CONFIG_ARM_LPAE) && defined(CONFIG_CPU_ENDIAN_BE8) sub r4, r4, #4 @ Fixup page table pointer @ for 64-bit descriptors #endif #ifdef CONFIG_DEBUG_LL #if !defined(CONFIG_DEBUG_ICEDCC) && !defined(CONFIG_DEBUG_SEMIHOSTING) addruart r7, r3, r0 mov r3, r3, lsr #SECTION_SHIFT mov r3, r3, lsl #PMD_ORDER add r0, r4, r3 mov r3, r7, lsr #SECTION_SHIFT ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags orr r3, r7, r3, lsl #SECTION_SHIFT #ifdef CONFIG_ARM_LPAE mov r7, #1 << (54 - 32) @ XN #ifdef CONFIG_CPU_ENDIAN_BE8 str r7, [r0], #4 str r3, [r0], #4 #else str r3, [r0], #4 str r7, [r0], #4 #endif #else orr r3, r3, #PMD_SECT_XN str r3, [r0], #4 #endif #else ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags #endif #if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS) add r0, r4, #0xff000000 >> (SECTION_SHIFT - PMD_ORDER) orr r3, r7, #0x7c000000 str r3, [r0] #endif #ifdef CONFIG_ARCH_RPC add r0, r4, #0x02000000 >> (SECTION_SHIFT - PMD_ORDER) orr r3, r7, #0x02000000 str r3, [r0] add r0, r4, #0xd8000000 >> (SECTION_SHIFT - PMD_ORDER) str r3, [r0] #endif #endif #ifdef CONFIG_ARM_LPAE sub r4, r4, #0x1000 @ point to the PGD table #endif ret lr ENDPROC(__create_page_tables) .ltorg .align __turn_mmu_on_loc: .long . .long __turn_mmu_on .long __turn_mmu_on_end #if defined(CONFIG_SMP) .text .arm ENTRY(secondary_startup_arm) THUMB( badr r9, 1f ) @ Kernel is entered in ARM. THUMB( bx r9 ) @ If this is a Thumb-2 kernel, THUMB( .thumb ) @ switch to Thumb now. THUMB(1: ) ENTRY(secondary_startup) ARM_BE8(setend be) @ ensure we are in BE8 mode #ifdef CONFIG_ARM_VIRT_EXT bl __hyp_stub_install_secondary #endif safe_svcmode_maskall r9 mrc p15, 0, r9, c0, c0 @ get processor id bl __lookup_processor_type movs r10, r5 @ invalid processor? moveq r0, #'p' @ yes, error 'p' THUMB( it eq ) @ force fixup-able long branch encoding beq __error_p adr r4, __secondary_data ldmia r4, {r5, r7, r12} @ address to jump to after sub lr, r4, r5 @ mmu has been enabled add r3, r7, lr ldrd r4, r5, [r3, #0] @ get secondary_data.pgdir ARM_BE8(eor r4, r4, r5) @ Swap r5 and r4 in BE: ARM_BE8(eor r5, r4, r5) @ it can be done in 3 steps ARM_BE8(eor r4, r4, r5) @ without using a temp reg. ldr r8, [r3, #8] @ get secondary_data.swapper_pg_dir badr lr, __enable_mmu @ return address mov r13, r12 @ __secondary_switched address ldr r12, [r10, #PROCINFO_INITFUNC] add r12, r12, r10 @ initialise processor @ (return control reg) ret r12 ENDPROC(secondary_startup) ENDPROC(secondary_startup_arm) ENTRY(__secondary_switched) ldr sp, [r7, #12] @ get secondary_data.stack mov fp, #0 b secondary_start_kernel ENDPROC(__secondary_switched) .align .type __secondary_data, %object __secondary_data: .long . .long secondary_data .long __secondary_switched #endif __enable_mmu: #if defined(CONFIG_ALIGNMENT_TRAP) && __LINUX_ARM_ARCH__ < 6 orr r0, r0, #CR_A #else bic r0, r0, #CR_A #endif #ifdef CONFIG_CPU_DCACHE_DISABLE bic r0, r0, #CR_C #endif #ifdef CONFIG_CPU_BPREDICT_DISABLE bic r0, r0, #CR_Z #endif #ifdef CONFIG_CPU_ICACHE_DISABLE bic r0, r0, #CR_I #endif #ifdef CONFIG_ARM_LPAE mcrr p15, 0, r4, r5, c2 @ load TTBR0 #else mov r5, #DACR_INIT mcr p15, 0, r5, c3, c0, 0 @ load domain access register mcr p15, 0, r4, c2, c0, 0 @ load page table pointer #endif b __turn_mmu_on ENDPROC(__enable_mmu) .align 5 .pushsection .idmap.text, "ax" ENTRY(__turn_mmu_on) mov r0, r0 instr_sync mcr p15, 0, r0, c1, c0, 0 @ write control reg mrc p15, 0, r3, c0, c0, 0 @ read id reg instr_sync mov r3, r3 mov r3, r13 ret r3 __turn_mmu_on_end: ENDPROC(__turn_mmu_on) .popsection #ifdef CONFIG_SMP_ON_UP __HEAD __fixup_smp: and r3, r9, #0x000f0000 @ architecture version teq r3, #0x000f0000 @ CPU ID supported? bne __fixup_smp_on_up @ no, assume UP bic r3, r9, #0x00ff0000 bic r3, r3, #0x0000000f @ mask 0xff00fff0 mov r4, #0x41000000 orr r4, r4, #0x0000b000 orr r4, r4, #0x00000020 @ val 0x4100b020 teq r3, r4 @ ARM 11MPCore? reteq lr @ yes, assume SMP mrc p15, 0, r0, c0, c0, 5 @ read MPIDR and r0, r0, #0xc0000000 @ multiprocessing extensions and teq r0, #0x80000000 @ not part of a uniprocessor system? bne __fixup_smp_on_up @ no, assume UP @ Core indicates it is SMP. Check for Aegis SOC where a single @ Cortex-A9 CPU is present but SMP operations fault. mov r4, #0x41000000 orr r4, r4, #0x0000c000 orr r4, r4, #0x00000090 teq r3, r4 @ Check for ARM Cortex-A9 retne lr @ Not ARM Cortex-A9, @ If a future SoC *does* use 0x0 as the PERIPH_base, then the @ below address check will need to be #ifdef'd or equivalent @ for the Aegis platform. mrc p15, 4, r0, c15, c0 @ get SCU base address teq r0, #0x0 @ '0' on actual UP A9 hardware beq __fixup_smp_on_up @ So its an A9 UP ldr r0, [r0, #4] @ read SCU Config ARM_BE8(rev r0, r0) @ byteswap if big endian and r0, r0, #0x3 @ number of CPUs teq r0, #0x0 @ is 1? retne lr __fixup_smp_on_up: adr r0, 1f ldmia r0, {r3 - r5} sub r3, r0, r3 add r4, r4, r3 add r5, r5, r3 b __do_fixup_smp_on_up ENDPROC(__fixup_smp) .align 1: .word . .word __smpalt_begin .word __smpalt_end .pushsection .data .align 2 .globl smp_on_up smp_on_up: ALT_SMP(.long 1) ALT_UP(.long 0) .popsection #endif .text __do_fixup_smp_on_up: cmp r4, r5 reths lr ldmia r4!, {r0, r6} ARM( str r6, [r0, r3] ) THUMB( add r0, r0, r3 ) #ifdef __ARMEB__ THUMB( mov r6, r6, ror #16 ) @ Convert word order for big-endian. #endif THUMB( strh r6, [r0], #2 ) @ For Thumb-2, store as two halfwords THUMB( mov r6, r6, lsr #16 ) @ to be robust against misaligned r3. THUMB( strh r6, [r0] ) b __do_fixup_smp_on_up ENDPROC(__do_fixup_smp_on_up) ENTRY(fixup_smp) stmfd sp!, {r4 - r6, lr} mov r4, r0 add r5, r0, r1 mov r3, #0 bl __do_fixup_smp_on_up ldmfd sp!, {r4 - r6, pc} ENDPROC(fixup_smp) #ifdef __ARMEB__ #define LOW_OFFSET 0x4 #define HIGH_OFFSET 0x0 #else #define LOW_OFFSET 0x0 #define HIGH_OFFSET 0x4 #endif #ifdef CONFIG_ARM_PATCH_PHYS_VIRT __HEAD __fixup_pv_table: adr r0, 1f ldmia r0, {r3-r7} mvn ip, #0 subs r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET add r4, r4, r3 @ adjust table start address add r5, r5, r3 @ adjust table end address add r6, r6, r3 @ adjust __pv_phys_pfn_offset address add r7, r7, r3 @ adjust __pv_offset address mov r0, r8, lsr #PAGE_SHIFT @ convert to PFN str r0, [r6] @ save computed PHYS_OFFSET to __pv_phys_pfn_offset strcc ip, [r7, #HIGH_OFFSET] @ save to __pv_offset high bits mov r6, r3, lsr #24 @ constant for add/sub instructions teq r3, r6, lsl #24 @ must be 16MiB aligned THUMB( it ne @ cross section branch ) bne __error str r3, [r7, #LOW_OFFSET] @ save to __pv_offset low bits b __fixup_a_pv_table ENDPROC(__fixup_pv_table) .align 1: .long . .long __pv_table_begin .long __pv_table_end 2: .long __pv_phys_pfn_offset .long __pv_offset .text __fixup_a_pv_table: adr r0, 3f ldr r6, [r0] add r6, r6, r3 ldr r0, [r6, #HIGH_OFFSET] @ pv_offset high word ldr r6, [r6, #LOW_OFFSET] @ pv_offset low word mov r6, r6, lsr #24 cmn r0, #1 #ifdef CONFIG_THUMB2_KERNEL moveq r0, #0x200000 @ set bit 21, mov to mvn instruction lsls r6, #24 beq 2f clz r7, r6 lsr r6, #24 lsl r6, r7 bic r6, #0x0080 lsrs r7, #1 orrcs r6, #0x0080 orr r6, r6, r7, lsl #12 orr r6, #0x4000 b 2f 1: add r7, r3 ldrh ip, [r7, #2] ARM_BE8(rev16 ip, ip) tst ip, #0x4000 and ip, #0x8f00 orrne ip, r6 @ mask in offset bits 31-24 orreq ip, r0 @ mask in offset bits 7-0 ARM_BE8(rev16 ip, ip) strh ip, [r7, #2] bne 2f ldrh ip, [r7] ARM_BE8(rev16 ip, ip) bic ip, #0x20 orr ip, ip, r0, lsr #16 ARM_BE8(rev16 ip, ip) strh ip, [r7] 2: cmp r4, r5 ldrcc r7, [r4], #4 @ use branch for delay slot bcc 1b bx lr #else #ifdef CONFIG_CPU_ENDIAN_BE8 moveq r0, #0x00004000 @ set bit 22, mov to mvn instruction #else moveq r0, #0x400000 @ set bit 22, mov to mvn instruction #endif b 2f 1: ldr ip, [r7, r3] #ifdef CONFIG_CPU_ENDIAN_BE8 @ in BE8, we load data in BE, but instructions still in LE bic ip, ip, #0xff000000 tst ip, #0x000f0000 @ check the rotation field orrne ip, ip, r6, lsl #24 @ mask in offset bits 31-24 biceq ip, ip, #0x00004000 @ clear bit 22 orreq ip, ip, r0 @ mask in offset bits 7-0 #else bic ip, ip, #0x000000ff tst ip, #0xf00 @ check the rotation field orrne ip, ip, r6 @ mask in offset bits 31-24 biceq ip, ip, #0x400000 @ clear bit 22 orreq ip, ip, r0 @ mask in offset bits 7-0 #endif str ip, [r7, r3] 2: cmp r4, r5 ldrcc r7, [r4], #4 @ use branch for delay slot bcc 1b ret lr #endif ENDPROC(__fixup_a_pv_table) .align 3: .long __pv_offset ENTRY(fixup_pv_table) stmfd sp!, {r4 - r7, lr} mov r3, #0 @ no offset mov r4, r0 @ r0 = table start add r5, r0, r1 @ r1 = table size bl __fixup_a_pv_table ldmfd sp!, {r4 - r7, pc} ENDPROC(fixup_pv_table) .data .align 2 .globl __pv_phys_pfn_offset .type __pv_phys_pfn_offset, %object __pv_phys_pfn_offset: .word 0 .size __pv_phys_pfn_offset, . -__pv_phys_pfn_offset .globl __pv_offset .type __pv_offset, %object __pv_offset: .quad 0 .size __pv_offset, . -__pv_offset #endif #include "head-common.S"
相关调用代码arh/arm/kernel/head_common.S定义,secondary_start_kernel 进入第二段C函数去执行,在init/main.c中定义。
17.3.4 linux 第二阶段启动linux操作系统进入系统内核初始化的入口是start_kernel函数,主要完第一阶段没有初始化的硬件平台相关初始化。随后,进入第一个用户进程init进程 并等待用户进程的执行,这样整个linux进程启动完毕。
start_kernel完成具体工作:
1、调用setup_arch()函数镜像与体系相关的初始化,ARM平台调用的是arch/arm/kernel/Setup.c函数;
2、先对处理器内核进行初始化,然后同bootmem_init()函数;
3、对内存结构初始化,最后调用paging_init()开启MMU创建页表,映射所有物理地址和IO空间。创建异常向量表和初始化中断处理机制;
4、初始化串口控制台(serial-console),这样内核在启动过程中就可以通过串口输出信息一遍开发者或用户了解系统的启动进度;
5、创建初始化系统Cache,为各种内存调用机制提供缓存,包括动态内存分配、虚拟文件系统(VitrualFile System)及页缓存。
6、初始化内存管理,检测内存大小及被内核占用的内存情况;
7、初始化系统的进程间通信机制(IPC);
8、当所有初始化工作都结束后,start_kernel调用rest_init()函数进行最后的初始化,包括创建第一个进程init进程;
9、init进程初始化一系列硬件之后,会通过命令行传递来的参数挂在文件系统,当所有初始化工作都结束之后,cpu_idle()函数会被调用来使系统处于闲置(idle)状态并等待用户程序执行;
10、至此,整个linux内核启动完毕。
start_kernel函数所在路径linux源码主目录下init/main.c
下面是start_kernel函数源码:
asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
{
char *command_line;
char *after_dashes;
set_task_stack_end_magic(&init_task);
smp_setup_processor_id();
debug_objects_early_init();
cgroup_init_early();
local_irq_disable(); //关闭系统总中断
early_boot_irqs_disabled = true;
boot_cpu_init(); //激活当前CPU
page_address_init(); //高端内存操作
pr_notice("%s", linux_banner);
early_security_init();
setup_arch(&command_line); //内核架构初始化
setup_boot_config(command_line);
setup_command_line(command_line); //对command-line进行备份、保存
setup_nr_cpu_ids(); //以上三个函数对SMP处理器,否则为空
setup_per_cpu_areas();
smp_prepare_boot_cpu();
boot_cpu_hotplug_init();
build_all_zonelists(NULL); //设置内存相关节点和启动的内存数据结构
page_alloc_init();
pr_notice("Kernel command line: %sn", saved_command_line);
jump_label_init();
parse_early_param();
after_dashes = parse_args("Booting kernel",
static_command_line, __start___param,
__stop___param - __start___param,
-1, -1, NULL, &unknown_bootoption);
if (!IS_ERR_OR_NULL(after_dashes))
parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
NULL, set_init_arg);
if (extra_init_args)
parse_args("Setting extra init args", extra_init_args,
NULL, 0, -1, -1, NULL, set_init_arg);
setup_log_buf(0); //使用bootmem分配一个启动信息的缓冲区
vfs_caches_init_early(); //前期VFS缓冲初始化
sort_main_extable(); //对内核异常表排序
trap_init(); //初始化内核相加异常
mm_init(); //初始化内核内存分配器,启动信息中的内存信息来自此函数中的mem_init函数
ftrace_init();
early_trace_init();
sched_init(); //初始化调度器函数,并创建运行队列
preempt_disable(); //禁止使用抢占和中断,早期启动时期,调度是极其脆弱
if (WARN(!irqs_disabled(),
"Interrupts were enabled *very* early, fixing itn"))
local_irq_disable();
radix_tree_init();
housekeeping_init();
workqueue_init_early();
rcu_init(); //内核RCU机制初始化
trace_init();
if (initcall_debug)
initcall_debug_enable();
context_tracking_init();
early_irq_init(); //前期外部中断描述符初始化
init_IRQ(); //架构相关中断初始化
tick_init();
rcu_init_nohz();
init_timers(); //以下函数是软中断和内核时钟机制
hrtimers_init();
softirq_init();
timekeeping_init();
rand_initialize();
add_latent_entropy();
add_device_randomness(command_line, strlen(command_line));
boot_init_stack_canary();
time_init();
perf_event_init();
profile_init(); //profile子系统初始化,内核性能调试和工具
call_function_init();
WARN(!irqs_disabled(), "Interrupts were enabled earlyn");
early_boot_irqs_disabled = false;
local_irq_enable(); //开启总中断
kmem_cache_init_late(); //slab分配器后期初始化
console_init(); //初始化控制台
if (panic_later) //检查内核恐慌标志,打印信息
panic("Too many boot %s vars at `%s'", panic_later,
panic_param);
lockdep_init();
locking_selftest();
mem_encrypt_init();
//检查initrd的位置是否符合要求
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.n",
page_to_pfn(virt_to_page((void *)initrd_start)),
min_low_pfn);
initrd_start = 0;
}
#endif
setup_per_cpu_pageset(); //分CPU页组,并初始化
numa_policy_init(); //设置每个CPU页组,并初始化
acpi_early_init();
if (late_time_init)
late_time_init();
sched_clock_init(); //初始化调度时钟
calibrate_delay();
pid_idr_init();
anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled(EFI_RUNTIME_SERVICES))
efi_enter_virtual_mode();
#endif
thread_stack_cache_init();
cred_init(); //任务信用系统初始化
fork_init(); //进程创建初始化
proc_caches_init();
uts_ns_init();
buffer_init(); //缓存系统初始化、创建缓存头空间、检查大小限制
key_init(); //密匙管理系统初始化
security_init(); //内核安全框架初始化
dbg_late_init(); //内核调试系统后期初始化
vfs_caches_init(); //虚拟文件系统缓存初始化
pagecache_init();
signals_init(); //信号管理初始化
seq_file_init();
proc_root_init(); //proc文件系统初始化
nsfs_init();
cpuset_init(); //control grpoup初始化
cgroup_init(); //CPUSET初始化
taskstats_init_early(); //任务状态早期初始化,为任务过去高速缓存并初始化互斥机制
delayacct_init(); //任务延迟机制初始化
poking_init();
check_bugs();
acpi_subsystem_init();
arch_post_acpi_subsys_init();
sfi_init_late();
kcsan_init();
arch_call_rest_init();
prevent_tail_call_optimization();
}
17.4 内核移植
从https://www.kernel.org/下载的linux官方内核。
Linux内核源码结构:
arch :包含源代码支持的硬件体系相关的核心代码 block :包含块设备驱动 crypto : 包含加密算法 documentation : 参考文档 drivers : 设备驱动代码,如字符设备驱动driver/char firmware:Linux系统固件 fs :Linux支持的文件系统代码 include :包含大多三include文件 init :启动代码 ipc :进程间通信代码 mm :内存管理代码,与硬件相关的举例内存管理代码位于arch/arm/mm目录下 net :网络相关协议部分代码,每个子目录对应网络的一个方面代码 sample :Linux内核例子 scripts :用于配置内核的脚本文档 security:包含SElinux模块 sound :音频驱动 tools :工具相关代码 modules :可动态加载的模块 kernel :内核核心相关代码,和处理器体系相关的核心代码/arch/arm/kernel目录下 lib :核心代码库,和处理器体系相关的核心代码放在/arch/arm/lib目录下
编译内核第一步是内核镜像配置,一般使用板级厂商或芯片厂家提供的配置进行修改,如arch/arm/config/digilent_zed_defconfig。
17.5 linux内核配置、编译配置:
make ARCH = arm CROSS_COMPILE = arm-xilinx-linux-gnueabi- digilent_zed_deconfig
编译:
make ARCH = arm CROSS_COMPILE = arm-xilinx-linux-gnueabi-
编译成功后在arch/arm/boot目录下生成压缩内核镜像文件zImage
感谢阅读,祝君成功!
-by aiziyou



