arm linux kernel 从入口到startkernel 的代码分析.docx
- 文档编号:12812046
- 上传时间:2023-06-08
- 格式:DOCX
- 页数:51
- 大小:34.47KB
arm linux kernel 从入口到startkernel 的代码分析.docx
《arm linux kernel 从入口到startkernel 的代码分析.docx》由会员分享,可在线阅读,更多相关《arm linux kernel 从入口到startkernel 的代码分析.docx(51页珍藏版)》请在冰点文库上搜索。
armlinuxkernel从入口到startkernel的代码分析
[精彩]armlinuxkernel从入口到start_kernel的代码分析
作者:
xpl发表于:
2009-07-3016:
34:
26
【发表评论】【查看原文】【Linux讨论区】【关闭】
最近研究了一下armlinux的入口部分的代码,code不是太多,所以写了个笔记,详细的分析了每一条语句.
大家看看,交流一下.
下面使正文.由于内容比较多,分几层楼发吧
____________________________________________________________________________________
本文针对armlinux,从kernel的第一条指令开始分析,一直分析到进入start_kernel()函数.
我们当前以linux-2.6.19内核版本作为范例来分析,本文中所有的代码,前面都会加上行号以便于和源码进行对照,
例:
在文件init/main.c中:
00478:
asmlinkagevoid__initstart_kernel(void)
前面的"00478:
"表示478行,冒号后面的内容就是源码了.
在分析代码的过程中,我们使用缩进来表示各个代码的调用层次.
由于启动部分有一些代码是平台特定的,虽然大部分的平台所实现的功能都比较类似,但是为了更好的对code进行说明,对于平台相关的代码,我们选择at91(ARM926EJS)平台进行分析.
另外,本文是以uncompressedkernel开始讲解的.对于内核解压缩部分的code,在arch/arm/boot/compressed中,本文不做讨论.
xpl回复于:
2008-07-3016:
26:
34
一.启动条件
通常从系统上电到执行到linuxkenel这部分的任务是由bootloader来完成.
关于bootloader的内容,本文就不做过多介绍.
这里只讨论进入到linuxkernel的时候的一些限制条件,这一般是bootloader在最后跳转到kernel之前要完成的:
1.CPU必须处于SVC(supervisor)模式,并且IRQ和FIQ中断都是禁止的;
2.MMU(内存管理单元)必须是关闭的,此时虚拟地址对物理地址;
3.数据cache(Datacache)必须是关闭的
4.指令cache(Instructioncache)可以是打开的,也可以是关闭的,这个没有强制要求;
5.CPU通用寄存器0(r0)必须是0;
6.CPU通用寄存器1(r1)必须是ARMLinuxmachinetype(关于machinetype,我们后面会有讲解)
7.CPU通用寄存器2(r2)必须是kernelparameterlist的物理地址(parameterlist是由bootloader传递给kernel,用来描述设备信息属性的列表,详细内容可参考"BootingARMLinux"文档).
xpl回复于:
2008-07-3016:
27:
07
二.startingkernel
首先,我们先对几个重要的宏进行说明(我们针对有MMU的情况):
宏位置默认值说明
KERNEL_RAM_ADDRarch/arm/kernel/head.S+260xc0008000kernel在RAM中的的虚拟地址
PAGE_OFFSETinclude/asm-arm/memeory.h+500xc0000000内核空间的起始虚拟地址
TEXT_OFFSETarch/arm/Makefile+1370x00008000内核相对于存储空间的偏移
TEXTADDRarch/arm/kernel/head.S+490xc0008000kernel的起始虚拟地址
PHYS_OFFSETinclude/asm-arm/arch-xxx/memory.h平台相关RAM的起始物理地址
内核的入口是stext,这是在arch/arm/kernel/vmlinux.lds.S中定义的:
00011:
ENTRY(stext)
对于vmlinux.lds.S,这是ldscript文件,此文件的格式和汇编及C程序都不同,本文不对ldscript作过多的介绍,只对内核中用到的内容进行讲解,关于ld的详细内容可以参考ld.info
这里的ENTRY(stext)表示程序的入口是在符号stext.
而符号stext是在arch/arm/kernel/head.S中定义的:
下面我们将armlinuxboot的主要代码列出来进行一个概括的介绍,然后,我们会逐个的进行详细的讲解.
在arch/arm/kernel/head.S中72-94行,是armlinuxboot的主代码:
00072:
ENTRY(stext)
00073:
msrcpsr_c,#PSR_F_BIT|PSR_I_BIT|SVC_MODE@ensuresvcmode
00074:
@andirqsdisabled
00075:
mrcp15,0,r9,c0,c0@getprocessorid
00076:
bl__lookup_processor_type@r5=procinfor9=cpuid
00077:
movsr10,r5@invalidprocessor(r5=0)?
00078:
beq__error_p@yes,error'p'
00079:
bl__lookup_machine_type@r5=machinfo
00080:
movsr8,r5@invalidmachine(r5=0)?
00081:
beq__error_a@yes,error'a'
00082:
bl__create_page_tables
00083:
00084:
/*
00085:
*ThefollowingcallsCPUspecificcodeinapositionindependent
00086:
*manner.Seearch/arm/mm/proc-*.Sfordetails.r10=baseof
00087:
*xxx_proc_infostructureselectedby__lookup_machine_type
00088:
*above.Onreturn,theCPUwillbereadyfortheMMUtobe
00089:
*turnedon,andr0willholdtheCPUcontrolregistervalue.
00090:
*/
00091:
ldrr13,__switch_data@addresstojumptoafter
00092:
@mmuhasbeenenabled
00093:
adrlr,__enable_mmu@return(PIC)address
00094:
addpc,r10,#PROCINFO_INITFUNC
其中,73行是确保kernel运行在SVC模式下,并且IRQ和FIRQ中断已经关闭,这样做是很谨慎的.
armlinuxboot的主线可以概括为以下几个步骤:
1.确定processortype(75-78行)
2.确定machinetype(79-81行)
3.创建页表(82行)
4.调用平台特定的__cpu_flush函数(在structproc_info_list中)(94行)
5.开启mmu(93行)
6.切换数据(91行)
最终跳转到start_kernel(在__switch_data的结束的时候,调用了bstart_kernel)
下面,我们按照这个主线,逐步的分析Code.
xpl回复于:
2008-07-3016:
27:
56
1.确定processortype
arch/arm/kernel/head.S中:
00075:
mrcp15,0,r9,c0,c0@getprocessorid
00076:
bl__lookup_processor_type@r5=procinfor9=cpuid
00077:
movsr10,r5@invalidprocessor(r5=0)?
00078:
beq__error_p@yes,error'p'
75行:
通过cp15协处理器的c0寄存器来获得processorid的指令.关于cp15的详细内容可参考相关的arm手册
76行:
跳转到__lookup_processor_type.在__lookup_processor_type中,会把processortype存储在r5中
77,78行:
判断r5中的processortype是否是0,如果是0,说明是无效的processortype,跳转到__error_p(出错)
__lookup_processor_type函数主要是根据从cpu中获得的processorid和系统中的proc_info进行匹配,将匹配到的proc_info_list的基地址存到r5中,0表示没有找到对应的processortype.
下面我们分析__lookup_processor_type函数
arch/arm/kernel/head-common.S中:
00145:
.type__lookup_processor_type,%function
00146:
__lookup_processor_type:
00147:
adrr3,3f
00148:
ldmdar3,{r5-r7}
00149:
subr3,r3,r7@getoffsetbetweenvirt&phys
00150:
addr5,r5,r3@convertvirtaddressesto
00151:
addr6,r6,r3@physicaladdressspace
00152:
1:
ldmiar5,{r3,r4}@value,mask
00153:
andr4,r4,r9@maskwantedbits
00154:
teqr3,r4
00155:
beq2f
00156:
addr5,r5,#PROC_INFO_SZ@sizeof(proc_info_list)
00157:
cmpr5,r6
00158:
blo1b
00159:
movr5,#0@unknownprocessor
00160:
2:
movpc,lr
00161:
00162:
/*
00163:
*ThisprovidesaC-APIversionoftheabovefunction.
00164:
*/
00165:
ENTRY(lookup_processor_type)
00166:
stmfdsp!
{r4-r7,r9,lr}
00167:
movr9,r0
00168:
bl__lookup_processor_type
00169:
movr0,r5
00170:
ldmfdsp!
{r4-r7,r9,pc}
00171:
00172:
/*
00173:
*Lookininclude/asm-arm/procinfo.handarch/arm/kernel/arch.[ch]for
00174:
*moreinformationaboutthe__proc_infoand__arch_infostructures.
00175:
*/
00176:
.long__proc_info_begin
00177:
.long__proc_info_end
00178:
3:
.long.
00179:
.long__arch_info_begin
00180:
.long__arch_info_end
145,146行是函数定义
147行:
取地址指令,这里的3f是向前symbol名称是3的位置,即第178行,将该地址存入r3.
这里需要注意的是,adr指令取址,获得的是基于pc的一个地址,要格外注意,这个地址是3f处的"运行时地址",由于此时MMU还没有打开,也可以理解成物理地址(实地址).(详细内容可参考arm指令手册)
148行:
因为r3中的地址是178行的位置的地址,因而执行完后:
r5存的是176行符号__proc_info_begin的地址;
r6存的是177行符号__proc_info_end的地址;
r7存的是3f处的地址.
这里需要注意链接地址和运行时地址的区别.r3存储的是运行时地址(物理地址),而r7中存储的是链接地址(虚拟地址).
__proc_info_begin和__proc_info_end是在arch/arm/kernel/vmlinux.lds.S中:
00031:
__proc_info_begin=.;
00032:
*(.proc.info.init)
00033:
__proc_info_end=.;
这里是声明了两个变量:
__proc_info_begin和__proc_info_end,其中等号后面的"."是locationcounter(详细内容请参考ld.info)
这三行的意思是:
__proc_info_begin的位置上,放置所有文件中的".proc.info.init"段的内容,然后紧接着是__proc_info_end的位置.
kernel使用structproc_info_list来描述processortype.
在include/asm-arm/procinfo.h中:
00029:
structproc_info_list{
00030:
unsignedintcpu_val;
00031:
unsignedintcpu_mask;
00032:
unsignedlong__cpu_mm_mmu_flags;/*usedbyhead.S*/
00033:
unsignedlong__cpu_io_mmu_flags;/*usedbyhead.S*/
00034:
unsignedlong__cpu_flush;/*usedbyhead.S*/
00035:
constchar*arch_name;
00036:
constchar*elf_name;
00037:
unsignedintelf_hwcap;
00038:
constchar*cpu_name;
00039:
structprocessor*proc;
00040:
structcpu_tlb_fns*tlb;
00041:
structcpu_user_fns*user;
00042:
structcpu_cache_fns*cache;
00043:
};
我们当前以at91为例,其processor是926的.
在arch/arm/mm/proc-arm926.S中:
00464:
.section".proc.info.init",#alloc,#execinstr
00465:
00466:
.type__arm926_proc_info,#object
00467:
__arm926_proc_info:
00468:
.long0x41069260@ARM926EJ-S(v5TEJ)
00469:
.long0xff0ffff0
00470:
.longPMD_TYPE_SECT|\
00471:
PMD_SECT_BUFFERABLE|\
00472:
PMD_SECT_CACHEABLE|\
00473:
PMD_BIT4|\
00474:
PMD_SECT_AP_WRITE|\
00475:
PMD_SECT_AP_READ
00476:
.longPMD_TYPE_SECT|\
00477:
PMD_BIT4|\
00478:
PMD_SECT_AP_WRITE|\
00479:
PMD_SECT_AP_READ
00480:
b__arm926_setup
00481:
.longcpu_arch_name
00482:
.longcpu_elf_name
00483:
.longHWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_VFP|HWCAP_EDSP|HWCAP_JAVA
00484:
.longcpu_arm926_name
00485:
.longarm926_processor_functions
00486:
.longv4wbi_tlb_fns
00487:
.longv4wb_user_fns
00488:
.longarm926_cache_fns
00489:
.size__arm926_proc_info,.-__arm926_proc_info
从464行,我们可以看到__arm926_proc_info被放到了".proc.info.init"段中.
对照structproc_info_list,我们可以看到__cpu_flush的定义是在480行,即__arm926_setup.(我们将在"4.调用平台特定的__cpu_flush函数"一节中详细分析这部分的内容.)
从以上的内容我们可以看出:
r5中的__proc_info_begin是proc_info_list的起始地址,r6中的__proc_info_end是proc_info_list的结束地址.
149行:
从上面的分析我们可以知道r3中存储的是3f处的物理地址,而r7存储的是3f处的虚拟地址,这一行是计算当前程序运行的物理地址和虚拟地址的差值,将其保存到r3中.
150行:
将r5存储的虚拟地址(__proc_info_begin)转换成物理地址
151行:
将r6存储的虚拟地址(__proc_info_end)转换成物理地址
152行:
对照structproc_info_list,可以得知,这句是将当前proc_info的cpu_val和cpu_mask分别存r3,r4中
153行:
r9中存储了processorid(arch/arm/kernel/head.S中的75行),与r4的cpu_mask进行逻辑与操作,得到我们需要的值
154行:
将153行中得到的值与r3中的cpu_val进行比较
155行:
如果相等,说明我们找到了对应的processortype,跳到160行,返回
156行:
(如果不相等),将r5指向下一个proc_info,
157行:
和r6比较,检查是否到了__proc_info_end.
158行:
如果没有到__proc_info_end,表明还有proc_info配置,返回152行继续查找
159行:
执行到这里,说明所有的proc_info都匹配过了,但是没有找到匹配的,将r5设置成0(unknownprocessor)
160行:
返回
xpl回复于:
2008-07-3016:
29:
12
2.确定machinetype
arch/arm/kernel/head.S中:
00079:
bl__lookup_machine_type@r5=machinfo
00080:
movsr8,r5@invalidmachine(r5=0)?
00081:
beq__error_a@yes,error'a'
79行:
跳转到__lookup_machine_type函数,在__lookup_machine_type中,会把structmachine_desc的基地址(machinetype)存储在r5中
80,81行:
将r5中的machine_desc的基地址存储到r8中,并判断r5是否是0,如果是0,说明是无效的machinetype,跳转到__error_a(出错)
__lookup_machine_type函数
下面我们分析__lookup_machine_type函数:
arch/arm/kernel/head-common.S中:
00176:
.long__proc_info_begin
00177:
.long__proc_info_end
00178:
3:
.long.
00179:
.long__arch_info_begin
00180:
.long__arch_info_end
00181:
00182:
/*
00183:
*Lookupmachinearchitectureinthelinker-buildlistofarchitectures.
00184:
*Notethatwecan'tusetheabsoluteaddressesforthe__arch_info
00185:
*listssincewearen'trunningwiththeMMUon(andtherefore,weare
00186:
*notinthecorrectaddressspace).Wehavetocalculatetheoffset.
00187:
*
00188:
*r1=machinearchitecturenumber
00189:
*Returns:
00190:
*r3,r4,r6corrupted
00191:
*r5=mach_infopointerinphysicaladdressspace
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- arm linux kernel 从入口到startkernel 的代码分析 入口 startkernel 代码 分析