内核源码——汇编阶段的head.S文件

以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除。

前言

1、内容总结

汇编阶段,或者说内核引导阶段,主要是arch/arm/kernel/head.S文件,主要完成以下内容:

(1)校验启动合法性(CPU ID,机器码,uboot给内核的传参格式等)。

(2)建立段式映射的页表,并开启MMU以方便使用内存。

(3)构建C运行环境,跳入C阶段。

2、head.S文件代码

/**  linux/arch/arm/kernel/head.S**  Copyright (C) 1994-2002 Russell King*  Copyright (c) 2003 ARM Limited*  All Rights Reserved** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License version 2 as* published by the Free Software Foundation.**  Kernel startup code for all 32-bit CPUs*/
#include <linux/linkage.h>
#include <linux/init.h>#include <asm/assembler.h>
#include <asm/domain.h>
#include <asm/ptrace.h>
#include <asm/asm-offsets.h>
#include <asm/memory.h>
#include <asm/thread_info.h>
#include <asm/system.h>#if (PHYS_OFFSET & 0x001fffff)
#error "PHYS_OFFSET must be at an even 2MiB boundary!"
#endif#define KERNEL_RAM_VADDR	(PAGE_OFFSET + TEXT_OFFSET)
#define KERNEL_RAM_PADDR	(PHYS_OFFSET + TEXT_OFFSET)/** swapper_pg_dir is the virtual address of the initial page table.* We place the page tables 16K below KERNEL_RAM_VADDR.  Therefore, we must* make sure that KERNEL_RAM_VADDR is correctly set.  Currently, we expect* the least significant 16 bits to be 0x8000, but we could probably* relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.*/
#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000
#error KERNEL_RAM_VADDR must start at 0xXXXX8000
#endif.globl	swapper_pg_dir.equ	swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000.macro	pgtbl, rdldr	\rd, =(KERNEL_RAM_PADDR - 0x4000).endm#ifdef CONFIG_XIP_KERNEL
#define KERNEL_START	XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
#define KERNEL_END	_edata_loc
#else
#define KERNEL_START	KERNEL_RAM_VADDR
#define KERNEL_END	_end
#endif/** Kernel startup entry point.* ---------------------------** This is normally called from the decompressor code.  The requirements* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,* r1 = machine nr, r2 = atags pointer.** This code is mostly position independent, so if you link the kernel at* 0xc0008000, you call this at __pa(0xc0008000).** See linux/arch/arm/tools/mach-types for the complete list of machine* numbers for r1.** We're trying to keep crap to a minimum; DO NOT add any machine specific* crap here - that's what the boot loader (or in extreme, well justified* circumstances, zImage) is for.*/__HEAD
ENTRY(stext)setmode	PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode@ and irqs disabledmrc	p15, 0, r9, c0, c0		@ get processor idbl	__lookup_processor_type		@ r5=procinfo r9=cpuidmovs	r10, r5				@ invalid processor (r5=0)?beq	__error_p			@ yes, error 'p'bl	__lookup_machine_type		@ r5=machinfomovs	r8, r5				@ invalid machine (r5=0)?beq	__error_a			@ yes, error 'a'bl	__vet_atagsbl	__create_page_tables/** The following calls CPU specific code in a position independent* manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of* xxx_proc_info structure selected by __lookup_machine_type* above.  On return, the CPU will be ready for the MMU to be* turned on, and r0 will hold the CPU control register value.*/ldr	r13, __switch_data		@ address to jump to after@ mmu has been enabledadr	lr, BSYM(__enable_mmu)		@ return (PIC) addressARM(	add	pc, r10, #PROCINFO_INITFUNC	)THUMB(	add	r12, r10, #PROCINFO_INITFUNC	)THUMB(	mov	pc, r12				)
ENDPROC(stext)#if defined(CONFIG_SMP)
ENTRY(secondary_startup)/** Common entry point for secondary CPUs.** Ensure that we're in SVC mode, and IRQs are disabled.  Lookup* the processor type - there is no need to check the machine type* as it has already been validated by the primary processor.*/setmode	PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9mrc	p15, 0, r9, c0, c0		@ get processor idbl	__lookup_processor_typemovs	r10, r5				@ invalid processor?moveq	r0, #'p'			@ yes, error 'p'beq	__error/** Use the page tables supplied from  __cpu_up.*/adr	r4, __secondary_dataldmia	r4, {r5, r7, r12}		@ address to jump to aftersub	r4, r4, r5			@ mmu has been enabledldr	r4, [r7, r4]			@ get secondary_data.pgdiradr	lr, BSYM(__enable_mmu)		@ return addressmov	r13, r12			@ __secondary_switched addressARM(	add	pc, r10, #PROCINFO_INITFUNC	) @ initialise processor@ (return control reg)THUMB(	add	r12, r10, #PROCINFO_INITFUNC	)THUMB(	mov	pc, r12				)
ENDPROC(secondary_startup)/** r6  = &secondary_data*/
ENTRY(__secondary_switched)ldr	sp, [r7, #4]			@ get secondary_data.stackmov	fp, #0b	secondary_start_kernel
ENDPROC(__secondary_switched).type	__secondary_data, %object
__secondary_data:.long	..long	secondary_data.long	__secondary_switched
#endif /* defined(CONFIG_SMP) *//** Setup common bits before finally enabling the MMU.  Essentially* this is just loading the page table pointer and domain access* registers.*/
__enable_mmu:
#ifdef CONFIG_ALIGNMENT_TRAPorr	r0, r0, #CR_A
#elsebic	r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLEbic	r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLEbic	r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLEbic	r0, r0, #CR_I
#endifmov	r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \domain_val(DOMAIN_IO, DOMAIN_CLIENT))mcr	p15, 0, r5, c3, c0, 0		@ load domain access registermcr	p15, 0, r4, c2, c0, 0		@ load page table pointerb	__turn_mmu_on
ENDPROC(__enable_mmu)/** Enable the MMU.  This completely changes the structure of the visible* memory space.  You will not be able to trace execution through this.* If you have an enquiry about this, *please* check the linux-arm-kernel* mailing list archives BEFORE sending another post to the list.**  r0  = cp#15 control register*  r13 = *virtual* address to jump to upon completion** other registers depend on the function called upon completion*/.align	5
__turn_mmu_on:mov	r0, r0mcr	p15, 0, r0, c1, c0, 0		@ write control regmrc	p15, 0, r3, c0, c0, 0		@ read id regmov	r3, r3mov	r3, r13mov	pc, r3
ENDPROC(__turn_mmu_on)/** Setup the initial page tables.  We only setup the barest* amount which are required to get the kernel running, which* generally means mapping in the kernel code.** r8  = machinfo* r9  = cpuid* r10 = procinfo** Returns:*  r0, r3, r6, r7 corrupted*  r4 = physical page table address*/
__create_page_tables:pgtbl	r4				@ page table address/** Clear the 16K level 1 swapper page table*/mov	r0, r4mov	r3, #0add	r6, r0, #0x4000
1:	str	r3, [r0], #4str	r3, [r0], #4str	r3, [r0], #4str	r3, [r0], #4teq	r0, r6bne	1bldr	r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags/** Create identity mapping for first MB of kernel to* cater for the MMU enable.  This identity mapping* will be removed by paging_init().  We use our current program* counter to determine corresponding section base address.*/mov	r6, pcmov	r6, r6, lsr #20			@ start of kernel sectionorr	r3, r7, r6, lsl #20		@ flags + kernel basestr	r3, [r4, r6, lsl #2]		@ identity mapping/** Now setup the pagetables for our kernel direct* mapped region.*/add	r0, r4,  #(KERNEL_START & 0xff000000) >> 18str	r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!ldr	r6, =(KERNEL_END - 1)add	r0, r0, #4add	r6, r4, r6, lsr #18
1:	cmp	r0, r6add	r3, r3, #1 << 20strls	r3, [r0], #4bls	1b#ifdef CONFIG_XIP_KERNEL/** Map some ram to cover our .data and .bss areas.*/orr	r3, r7, #(KERNEL_RAM_PADDR & 0xff000000).if	(KERNEL_RAM_PADDR & 0x00f00000)orr	r3, r3, #(KERNEL_RAM_PADDR & 0x00f00000).endifadd	r0, r4,  #(KERNEL_RAM_VADDR & 0xff000000) >> 18str	r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!ldr	r6, =(_end - 1)add	r0, r0, #4add	r6, r4, r6, lsr #18
1:	cmp	r0, r6add	r3, r3, #1 << 20strls	r3, [r0], #4bls	1b
#endif/** Then map first 1MB of ram in case it contains our boot params.*/add	r0, r4, #PAGE_OFFSET >> 18orr	r6, r7, #(PHYS_OFFSET & 0xff000000).if	(PHYS_OFFSET & 0x00f00000)orr	r6, r6, #(PHYS_OFFSET & 0x00f00000).endifstr	r6, [r0]#ifdef CONFIG_DEBUG_LLldr	r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags/** Map in IO space for serial debugging.* This allows debug messages to be output* via a serial console before paging_init.*/ldr	r3, [r8, #MACHINFO_PGOFFIO]add	r0, r4, r3rsb	r3, r3, #0x4000			@ PTRS_PER_PGD*sizeof(long)cmp	r3, #0x0800			@ limit to 512MBmovhi	r3, #0x0800add	r6, r0, r3ldr	r3, [r8, #MACHINFO_PHYSIO]orr	r3, r3, r7
1:	str	r3, [r0], #4add	r3, r3, #1 << 20teq	r0, r6bne	1b
#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)/** If we're using the NetWinder or CATS, we also need to map* in the 16550-type serial port for the debug messages*/add	r0, r4, #0xff000000 >> 18orr	r3, r7, #0x7c000000str	r3, [r0]
#endif
#ifdef CONFIG_ARCH_RPC/** Map in screen at 0x02000000 & SCREEN2_BASE* Similar reasons here - for debug.  This is* only for Acorn RiscPC architectures.*/add	r0, r4, #0x02000000 >> 18orr	r3, r7, #0x02000000str	r3, [r0]add	r0, r4, #0xd8000000 >> 18str	r3, [r0]
#endif
#endifmov	pc, lr
ENDPROC(__create_page_tables).ltorg#include "head-common.S"

一、分析kernel的链接脚本

由内核配置与编译——内核的链接脚本可知,kernel的入口地址在arch/arm/kernel/head.S文件的ENTRY(stext)处。

二、分析head.S文件

1、内核运行的物理地址与虚拟地址

(1)KERNEL_RAM_VADDR(VADDR就是virtual address),这个宏定义了内核运行时的虚拟地址,值为0xC0008000。

(2)KERNEL_RAM_PADDR(PADDR就是physical address),这个宏定义内核运行时的物理地址,值为0x30008000。

(3)因此,内核运行的物理地址是0x30008000,对应的虚拟地址是0xC0008000。

2、内核的真正入口

(1)__HEAD定义了段名为.head.text的段。在/include/linux/init.h文件中,有如下代码:

/* For assembly routines */
#define __HEAD		.section	".head.text","ax" //定义了段名为.head.text的段
#define __INIT		.section	".init.text","ax"
#define __FINIT		.previous

(2)“ENTRY(stext)”表明内核的真正入口。

(3)uboot启动内核后,实际调用zImage前面的那段未经压缩的解压代码,解压代码运行时先将zImage后面的部分解压开,然后再去调用运行真正的内核入口(即这里)。

(4)内核启动需要一定先决条件,这个条件由启动内核的bootloader(比如uboot)来构建保证。

(5)ARM体系中,函数调用时实际是通过寄存器传参的。

  • 函数调用时传参有两种设计:一种是寄存器传参,另一种是栈内存传参。
  • uboot中最后theKernel (0, machid, bd->bi_boot_params);执行内核时,实际把0放入r0中,machid放入到了r1中,bd->bi_boot_params放入到了r2中。
  • ARM的这种处理技巧刚好满足了kernel启动的条件和要求。

(6)此时MMU是关闭的,因此硬件上需要的是物理地址。但是内核是一个整体(zImage)只能被链接到一个地址(不能分散加载),这个链接地址肯定是虚拟地址。因此head.S文件中尚未开启MMU之前的代码必须是位置无关码,而且其中涉及到操作硬件寄存器等时必须使用物理地址。

3、检验CPU_ID与机器码的合法性

分别通过__lookup_processor_type与__lookup_machine_type,校验CPU_ID与机器码的合法性。这两个函数都在arch/arm/kernel/head-common.S文件中。

 __lookup_processor_type函数内容如下:

 __lookup_machine_type函数内容如下:

(1)cp15协处理器的c0寄存器中读取出硬件的CPU ID号,然后调用__lookup_processor_type来进行合法性检验。如果合法则继续启动,如果不合法则停止启动,转向__error_p启动失败。

(2)__lookup_processor_type检验cpu id合法性的方法。内核会维护一个本内核支持的CPU ID号码的数组,然后该函数将从硬件中读取到的cpu id号码和数组中存储的各个id号码依次对比,如果没有一个相等则不合法,如果有一个相等的则合法。

(3)内核启动时设计这个校验,也是为了内核启动的安全性着想。

(4)__lookup_machine_type函数的设计理念和思路和上面校验cpu id的函数一样的,不同之处是本函数校验的是机器码。

4、校验uboot给内核传参的格式

利用__vet_atags函数,对uboot通过tag给内核传参的格式进行校验。

这函数在arch/arm/kernel/head-common.S文件中。

(1)该函数的设计思路和上面2个一样,用来对uboot通过tag给内核传参的格式进行校验。参数包括板子的内存分布memtag、uboot的bootargs等等。

(2)如果uboot给内核传参的格式不对,内核将启动不起来。比如uboot的bootargs设置不正确,则内核可能就会不启动。

5、建立段式页表

利用__create_page_tables函数建立段式页表。

这函数在​arch/arm/kernel/head.S文件​中。

(1)linux内核本身被链接在虚拟地址处,因此kernel希望尽快建立页表并且启动MMU进入虚拟地址工作状态。

(2)kernel建立页表分为2步。

  • 第一步,先建立一个段式页表(1MB为单位的段页表)。段式页表建立过程简单(段式页表1MB一个映射,4GB空间需要4096个页表项,每个页表项4字节,因此一共需要16KB内存来做页表),但不能精细管理内存。上面的函数就是用来建立段式页表的。
  • 第二步,然后建立一个细页表(4kb为单位的细页表),然后启用新的细页表,并废除第一步建立的段式映射页表。

(3)内核启动的早期建立段式页表,并在内核启动早期使用;内核启动的后期再次建立细页表并启用。等内核工作起来后,就只有细页表了。

6、构建C语言运行环境

(1)建立段式页表后进入__switch_data部分,它是一个函数指针数组。

 
 

(2)分析得知下一步要执行__mmap_switched函数。

  • 复制数据段、清除bss段(目的是构建C语言运行环境)。
  • 保存起来cpu id号、机器码、tag传参的首地址。
  • b start_kernel跳转到C语言运行阶段。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461775.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python第十七课(面向对象基础)

Python第17课(面向对象基础) >>>思维导图>>>中二青年 面向对象编程思想OOP 什么是面向对象 面向对象是一种编程思想&#xff0c;是前辈们总结出的经验&#xff0c;指导程序员如何编写出更好的程序&#xff0c;核心是对象&#xff0c;程序就是一系列对象的…

内核源码——C语言阶段的start_kernel函数

以下内容源于朱有鹏嵌入式课程的学习&#xff0c;如有侵权请告知删除。 前言 1、内容总结 C语言阶段的start_kernel函数位于内核源码/init/main.c文件&#xff0c;主要完成以下内容&#xff1a; &#xff08;1&#xff09;打印一些输出信息。 &#xff08;2&#xff09;初始化…

Excel2007数据透视表学习(四)改变数据透视表布局

通过在【数据透视表字段列表】对话框内将【列标签】、【行标签】和【报表筛选】区域的字段转换即可创建不同分析角度的数据透视表。 如果还是习惯Excel2003版本字段拖拽方式创建数据透视表&#xff0c;可以在数据透视表中单击鼠标右键→【数据透视表选项】→【显示】→勾选【…

经典基础算法之面试题(系列一)(转)

1. 打靶问题的递归解法 Technorati 标记: 递归算法,字典法,二分法,打靶问题,日期问题,求幂,多项式求值伦敦奥运会火热进行中&#xff0c;让我们来看个打靶的问题&#xff1a;一个射击运动员打靶&#xff0c;靶一共有10环&#xff0c;求连开10枪打中90环的可能行有多少种&#x…

2013年11月份我国网络不良与垃圾短信息分析报告

IDC评述网&#xff08;idcps.com&#xff09;12月17日报道&#xff1a;近日&#xff0c;12321网络不良与垃圾信息举报受理中心最新播报了11月份我国不良与垃圾短信息发送情况。根据报告显示&#xff0c;11月份垃圾邮件发送IP地址与上月大体相同&#xff0c;主要集中在湖北、北京…

SCOM Rule 介绍 [SCOM中文系列之六]

SCOM的Rule主要用来收集数据&#xff0c;我们看到的EventView&#xff0c;PerformanceData&#xff0c;Report所需数据大部分都是通过Rule来收集的。 SCOM Rule的基础结构是触发指定的条件&#xff0c;就执行指定的动作。这个我们通过Rule的属性界面可以看的清楚点&#xff1a;…

init进程的详解

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 参考博客 内核源码——C语言阶段的start_kernel函数_天糊土的博客-CSDN博客 分析根文件系统中的/linuxrc文件_天糊土的博客-CSDN博客 linux内核sys_mount()分析_kai_ding的博客-CSDN博客_sys_mount…

跟着石头哥哥学cocos2d-x(四)--cocos2dx中的动画以及TexturePacker使用

2019独角兽企业重金招聘Python工程师标准>>> 之前向Andreas Loew申请了一枚TexturePacker注册码&#xff0c;很快都下来了&#xff0c;作为回报我打算还是写一篇关于TexturePacker的使用博客吧&#xff0c;有兴趣的可以在这里申请密钥,http://www.codeandweb.com&am…

头像和Karma汽车

Powered by Zoundry RavenTechnorati : Karma, 头像, 汽车 Del.icio.us : Karma, 头像, 汽车 转载于:https://www.cnblogs.com/bsmagic/archive/2008/08/06/1261858.html

All-In-One Code Framework [一站式示例代码库] 【转】

All-In-One Code Framework [一站式示例代码库]2010 对一站式示例代码库&#xff0c;对奋战在一站式示例代码库上的每一位工程师来说都是不同寻常的一年。 在我们共同努力和开发社区的支持下&#xff0c;该项目从一年前的草根雏形&#xff0c;成长为CodePlex排名前三&#xff0…

Ioc容器Autofac介绍

Autofac是轻量级的开源Ioc容器&#xff0c;在这里可以下载http://code.google.com/p/autofac/。如果你用过其他的Ioc容器&#xff0c;那么学习Autofac使用也会比较容易&#xff0c;下面将通过一些例子来讲解其用法。 先看一个例子&#xff1a; 首先新建一个工程&#xff0c;添加…

用代码实现Sharepoint2010的个人信息的照片上传(2)(原创)

用代码从AD读取照片信息并同步到SharePoint2010的个人照片信息中 前言 上篇我们讲到用代码实现了照片信息同步到SharePoint2010的个人照片信息中&#xff0c;但是做的是从文件夹读取照片信息到SharePoint2010,今天我们要做的从AD的thumbnailPhoto属性中读取照片信息到SharePoin…

HTML5实现Word中文字全环绕图片效果

实现这个功能是当时看见数字报的展示出来的编排方式&#xff0c;就是图片热点&#xff0c;而且下载打开还是PDF的&#xff0c;让我这个WP用户咋个看。想实现Word中的编排方式&#xff0c;也需要浏览器支持Word插件&#xff0c;火狐、谷歌、Opera这些都要另外弄插件&#xff0c;…

Bzoj 2127 happiness 最小割

happiness 题解: 将图转换成最小割. 将割完的图中与S相连的点看做选文科, 与T相连的点看做选理科. flow(s, u) 文科值 flow(u,t) 理科值 假设u 和 v 一起选文科有奖励值z, flow(s,u) z/2 flow(s,v) z/2, flow(u,v) z/2 假设u 和 v 一起选理科有奖励值z, flow(u,t) z/2…

POJ 1149 最大流建图 PIGS

题意&#xff1a; 给出猪圈个数 m 和买家人数 n 然后给出m个猪圈的猪的头数.. 接下来 n 行.. 给出mm a1 a2 .. a(mm) k 例如 2 1 5 3 表示第i1个用户 有mm(2) 个猪圈的钥匙 就是有第 a1(1) 个和第 a2(5) 个猪圈的钥匙..还想要买得k头猪.. 问最多能够卖出多少头.. 思路&#xff…

ARM通识——ARM的7种基本工作模式、37个通用寄存器

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、ARM采用32位架构 ARM约定一个Byte是8 bits&#xff0c;一个Halfword是16 bits (2 byte)&#xff0c;一个Word是32 bits (4 byte)。 大部分ARM core 提供ARM 指令集&#xff08;32 bit&#xff0…

以NFS方式挂载rootfs的设置方法

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 更多详细内容&#xff0c;见Linux中NFS服务器的配置(/etc/export)_天糊土的博客-CSDN博客 网络文件系统&#xff08;NFS&#xff0c;network file system&#xff09;&#xff0c;是一种网络通讯协…

分析根文件系统中的/linuxrc文件(附带busybox的介绍)

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 参考博客 init进程的详解_天糊土的博客-CSDN博客_init进程 busybox详解_linuxarmsummary的博客-CSDN博客 前言 因为uboot给内核传参的bootargs中有“init/linuxrc”这个项目&#xff0c;而由前面的…

CentOS Linux搭建SVN Server配置详解

PS&#xff1a;今天给开发的同事搭建一个公网的SVN Server&#xff0c;google一下&#xff0c;发现下面这篇最详细&#xff0c;就记录下来&#xff0c;重新排版一下。顺便根据自己的使用经验&#xff0c;补充了几个细节和总结。本文配置是基于CentOS 5.x的&#xff0c;但一样适…

405 Method Not Allowed

当jquery easyui的datagrid插件使用.json文件为数据源&#xff0c;通过非http协议访问【就是直接拖进浏览器查看效果】&#xff0c;能正常加载.json文件并且生成datagrid数据源。但是通过http协议访问示例代码时&#xff0c;无法加载json文件生成datagrid的数据源。 这个是由于…