栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

xilinx zynq(七)

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

xilinx zynq(七)

xilinx zynq(七)
  • 十七、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内核原理、移植 17.1 概述

Linux作为嵌入式操作系统的最主流操作系统之一,凭借其源码开源、稳定性好、免费、市场占备受开发设计者人员青睐。

linux内核下载:

https://www.kernel.org/

xilinx内核下载:

https://github.com/Xilinx/linux-xlnx
17.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编译。

17.2.2 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)ld		
17.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调用内核前,需要做好以下准备:

1、CPU寄存器R0、R1、R2设置
2、CPU工作模式设置:禁止中断、CPU工作在SVC管理模式(Supervisor)
3、数据Cache关闭、MMU关闭

17.3.2 Linux 程序入口

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
#endif
17.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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/468740.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号