linux内核启动汇编部分详解

参考文档:https://blog.csdn.net/haoge921026/article/details/46785995

找到入口ENTRY(stext)开始分析

  setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9      @ ensure svc mode     @ and irqs disabled

设置cpu为svc模式,禁止中断



  mrc p15, 0, r9, c0, c0 @ get processor id

读取ARM协处理器cp15的c0寄存器获得CPU ID,存放在r9

CPU ID格式如下:
---------------------------------------------------------------------
厂商编号  | 产品子编号 | ARM 体系版本号 | 产品主编号 |处理器版本号  |
---------------------------------------------------------------------

查手册: cotex_a8_r3p2_trm.pdf 3.2.2节可以获得如下信息 
厂商编号      [31:24] : 0x41 表示ARM公司  
产品子编号    [23:20] : 0x3  
ARM体系版本号 [19:16] : 0x1->ARMv4, ...,0xF ->ARMv7 
产品主编号    [15: 4] : 0xc08->CortexA8 
处理器版本号  [ 3: 0] : 0x2

r9 : cpuid(0x413fc082)      r1:机器码  r2:atag指针



  bl __lookup_processor_type @ r5=procinfo r9=cpuid

linux内核支持多个cpu,每个cpu对应一个procinfo结构体,这句指令是去查找procinfo结构体中查找是否有这个cpu

查看代码:

__lookup_processor_type:

adr r3, 3f                         //将procinfo结构体集的链接首地址的运行指针赋给r3
ldmia r3, {r5 - r7}  

r3 -> 局部标号3运行地址
r5 -> __proc_info_begin  [虚拟地址]   链接地址
r6 -> __proc_info_end    [虚拟地址]  链接地址
r7 -> 局部标号4的链接地址[虚拟地址:0xcxxx,xxxx]       

add r3, r3, #8
sub r3, r3, r7 @ get offset between virt&phys

r3 -> 运行地址与链接地址的偏移
add r5, r5, r3 @ convert virt addresses to
add r6, r6, r3 @ physical address space

r5 -> __proc_info_begin 运行地址
r6 -> __proc_info_end    运行地址

1: ldmia r5, {r3, r4} @ value, mask

and r4, r4, r9 @ mask wanted bits
teq r3, r4
beq 2f        //cpuid的value相等就跳转
add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list) //跳到下一个procinfo的地址
cmp r5, r6                                  // 已经查完最后一个procinto了
blo 1b            
mov r5, #0 @ unknown processor      //匹配失败r5=0
2: mov pc, lr                                        //返回
ENDPROC(__lookup_processor_type)

查看代码:

.align 2
3: .long __proc_info_begin  
.long __proc_info_end   
4: .long .

r5=procinfo       r9=cpuid  r1:机器码  r2:atag指针



movs r10, r5 @ invalid processor (r5=0)?
beq __error_p @ yes, error ‘p‘

r5返回0则去"nError:

unrecognized/unrecognized processor variant(0x<cpuid>).\n",然后进入死循环 。

r9=cpuid        r10=procinfo   r1:机器码  r2:atag指针



bl __lookup_machine_type @ r5=machinfo
movs r8, r5 @ invalid machine (r5=0)?
beq __error_a @ yes, error ‘a‘

Linux内核支持多个开发板,每个开发板对应多个machinfo结构体,去查找这个结构体集看看是否存在与r1传进来的机器码匹配

存在则返回r5=machinfo,不存在(r5=0)打印Error: unrecognized/unsupported machine ID (r1 = 0x<machine_ID>")然后进入死循环

r9=cpuid        r10=procinfo   r1:机器码  r8=machinfo  r2:atag指针



bl __vet_atags

检查atag格式是否正确,不正确会r2=0



bl __create_page_tables

在0x30004000-0x30008000之间创建16k的页表,将内核代码范围的地址映射到0x30000000



ldr r13, __switch_data @ address to jump to after
@ mmu has been enabled
adr lr, BSYM(__enable_mmu) @ return (PIC) address
ARM( add pc, r10, #PROCINFO_INITFUNC )

r10为主机procinfo结构体首地址,跳转__v7_setup 主要干的事情有:
清除cahe,设置控制MMU的协处理器寄存器的值,将r4寄存器的值写入了cp15的c2寄存器。
最终在找页表所在基地址的时候, MMU会自动从cp15的c2寄存器中读取。我们在lr寄存器中保存了 
__enable_mmu,所以__v7_setup函数返回的时候,就去执行__enable_mmu了。在__enable_mmu中设置些参数打开mmu

跳转__switch_data

__switch_data中主要请bss,设置栈,保存cpuid,机器码,atags指针

最后用b start_kernel 跳转内核启动c部分

 

 

原文地址:https://www.cnblogs.com/genshu123/p/11216502.html

时间: 2024-11-09 18:31:30

linux内核启动汇编部分详解的相关文章

Linux内核线程kernel thread详解--Linux进程的管理与调度(十)

日期 内核版本 架构 作者 GitHub CSDN 2016-06-02 Linux-4.5 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度-之-进程的描述 内核线程 为什么需要内核线程 Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求). 内核需要多个执行流并行,为了防止可能的阻塞,支持多线程是必要的. 内核线程就是内核的分身,一个分身可以处理一件特定事情.内核线程的调度由内核负责,一个内核线程处于阻

Linux内核中kzalloc函数详解

***************************************************************************************************************************作者:EasyWave                                                                                 时间:2013.02.06 类别:Linux 内核驱动源码分析    

Linux 内核参数 arp_ignore &amp; arp_announce 详解

arp_ignore定义了对目标地址为本机IP的ARP询问的不同应答模式. arp_announce对网络接口(网卡)上发出的ARP请求包中的源IP地址作出相应的限制:主机会根据这个参数值的不同选择使用IP数据包的源IP或当前网络接口卡的IP地址作为ARP请求包的源IP地址. arp_ignore 在内核参数中除了每个网卡都有自己的arp_ignore配置外,还有两个(一个是默认default,一个是全局all)需要用到arp_ignore配置.所有配置项如下面的代码段: net.ipv4.co

Linux/CentOS 服务安装/卸载,开机启动chkconfig命令详解|如何让MySQL、Apache开机启动?

chkconfig chkconfig在命令行操作时会经常用到.它可以方便地设置和查询不同运行级上的系统服务.这个可要好好掌握,用熟练之后,就可以轻轻松松的管理好你的启动服务了. 注:谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接. 语法: chkconfig       [--add]      [--del]     [--list]      [系统服务] chkconfig       [--level/levels]      [等级代号]     

Linux进程上下文切换过程context_switch详解--Linux进程的管理与调度(二十一)【转】

转自:http://blog.csdn.net/gatieme/article/details/51872659 版权声明:本文为博主原创文章 && 转载请著名出处 @ http://blog.csdn.net/gatieme 目录(?)[-] 前景回顾 1 Linux的调度器组成 2 调度工作 进程上下文 1 进程上下文的概念 2 上下文切换 context_switch进程上下文切换 1 context_switch完全注释 2 prepare_arch_switch切换前的准备工作

linux移植u-boot(一)——U-Boot详解+自定义命令实战

linux移植u-boot(一)--U-Boot详解+自定义命令实战 2015-02-07 一.Bootloader ????简单地说:Bootloader主要功能就是 在系统上电时开始执行,初始化硬件和设备,准备好软件环境,最后调用操作系统. ????具体的包含:关闭你看门狗WATCHDOG,改变系统时钟,初始化存储控制器 ,将操作系统内核代码复制到内存中去运行. ????为了开发方便,可以增加网络功能,从PC上通过串口或者网络下载文件,烧写文件,将flash上的内核代码解压后运行等. Boo

linux内核启动第二阶段之setup_arch()函数分析-2.6.36

执行setup_arch()函数 回到start_kernel当中,569行,调用setup_arch函数,传给他的参数是那个未被初始化的内部变量command_line.这个setup_arch()函数是start_kernel阶段最重要的一个函数,每个体系都有自己的setup_arch()函数,是体系结构相关的,具体编译哪个体系的setup_arch()函数,由顶层Makefile中的ARCH变量决定: 它首先通过检测出来的处理器类型进行处理器内核的初始化,然后通过 bootmem_init

Linux内核启动及根文件系统载入过程

上接博文<u-boot之u-boot-2009.11启动过程分析> Linux内核启动及文件系统载入过程 当u-boot開始运行bootcmd命令,就进入Linux内核启动阶段.与u-boot类似,普通Linux内核的启动过程也能够分为两个阶段,但针对压缩了的内核如uImage就要包含内核自解压过程了.本文以linux-2.6.37版源代码为例分三个阶段来描写叙述内核启动全过程.第一阶段为内核自解压过程,第二阶段主要工作是设置ARM处理器工作模式.使能MMU.设置一级页表等,而第三阶段则主要为

Linux内核启动及文件系统加载过程

上接博文<u-boot之u-boot-2009.11启动过程分析> 当u-boot开始执行bootcmd命令,就进入Linux内核启动阶段,与u-boot类似,普通Linux内核的启动过程也可以分为两个阶段,但针对压缩了的内核如uImage就要包括内核自解压过程了.本文以项目中使用的linux-2.6.37版源码为例分三个阶段来描述内核启动全过程.第一阶段为内核自解压过程,第二阶段主要工作是设置ARM处理器工作模式.使能MMU.设置一级页表等,而第三阶段则主要为C代码,包括内核初始化的全部工作