基于arm的Linux启动代码分析
linux arm架构重启指令
linux arm架构重启指令
Linux系统在ARM架构上运行时,重启指令的执行方式与在其他架构上可能会有所不同。
ARM架构是一种精简指令集(RISC)架构,广泛应用于移动设备、嵌入式系统和物联网设备等领域。
在ARM架构上,重启指令通常是通过软件来执行的。
在Linux系统中,可以使用以下命令来执行重启操作:
bash.
sudo reboot.
当用户在终端中输入上述命令并按下回车键时,系统会执行重启操作。
在ARM架构上,这条命令会触发系统重新启动的过程,类似于在其他架构上执行的重启指令。
另外,还可以使用以下命令来延迟重启操作:
bash.
sudo shutdown -r +5。
上述命令中的"+5"表示延迟5分钟后执行重启操作。
这在某些情况下可能会更加灵活和方便,例如在需要在一段时间后才执行重启操作时。
需要注意的是,执行重启操作需要相应的权限,因此一般需要使用sudo命令来提升权限。
另外,在执行重启操作前,建议保存好所有的工作和数据,以免因重启操作导致数据丢失或程序中断。
总之,Linux系统在ARM架构上的重启指令与其他架构上的执行方式类似,通过简单的命令即可实现系统的重启操作,为用户提供了便利和灵活性。
Arm_linux_启动分析
Arm linux 启动分析(1)王利明 walimi@ 宋振宇zhenyusong@ 2003-3-201.概述:在内核运行之前需要系统引导程序(Bootloader)完成加载内核和一些辅助性的工作,然后跳转到内核代码的起始地址并执行。
本文先分析了Bootloader 的初始化工作,接着从内核镜像的起始地址进行分析。
整个arm linux 内核的启动可分为三个阶段:第一阶段主要是进行cpu 和体系结构的检查、cpu 本身的初始化以及页表的建立等;第二阶段主要是对系统中的一些基础设施进行初始化;最后则是更高层次的初始化,如根设备和外部设备的初始化。
第一阶段的初始化是从内核入口(ENTRY(stext))开始到start_kernel 前结束。
这一阶段的代码在/arch/arm/head_armv.S 中。
2.Bootloader 2.1简介 本处介绍主要来自内核源代码下的Documentation/arm/Booting 文件,适合于arm linux 2.4.18-rmk6及以上版本。
Bootloader 主要作用是初始化一些必要的设备,然后调用内核,同时传递参数给内核。
主要完成如下工作:1. 建立和初始化RAM 。
2. 初始化一个串口。
3.4.建立内核的5. 调用内核镜像。
2.2功能详细介绍 1.建立和初始化RAM 。
要求:必须功能:探测所有的RAM 位置和大小,并对RAM 进行初始化。
2.初始化一个串口。
要求:可选,建议功能:Bootloader 应该初始化并启动一个串口。
这可以让内核的串口驱动自动探测哪个串口作为内核的控制台。
另外也可以通过给内核传递“console=”参数完成此工作。
3.检测机器的系统结构。
要求:必须功能:Bootloader 应该通过某种方法探测机器类型,最后传递给内核一个MACH_TYPE_xxx 值,这些值参看4要求:必须功能:Bootloader 必须创建和初始化内核的tagged list 。
ARMLinux启动过程分析(1).
ARM Linux启动过程分析(1)摘要:嵌入式 Linux 的可移植性使得我们可以在各种电子产品上看到它的身影。
对于不同体系结构的处理器来说Linux的启动过程也有所不同。
本文以S3C2410 ARM处理器为例,详细分析了系统上电后 bootloader的执行流程及 ARM Linux的启动过程。
关键词:ARM Linux bootloader 启动过程Abstract:We can see embedded Linux in kinds of electronic productsb ecause of its portability. Linux’s start-up procedure for different processors is also different. This paper provides the analysis ofbootloader execution process and Linux kernel start-up procedure - taking the S3C2410 ARM processor as example. Keywords: ARM Linux bootloader start-up procedure 1. 引言 Linux 最初是由瑞典赫尔辛基大学的学生 Linus Torvalds在1991 年开发出来的,之后在 GNU的支持下,Linux 获得了巨大的发展。
虽然 Linux 在桌面 PC 机上的普及程度远不及微软的Windows 操作系统,但它的发展速度之快、用户数量的日益增多,也是微软所不能轻视的。
而近些年来 Linux 在嵌入式领域的迅猛发展,更是给 Linux 注入了新的活力。
一个嵌入式 Linux 系统从软件角度看可以分为四个部分:引导加载程序(bootloader), Linux 内核,文件系统,应用程序。
其中bootloader是系统启动或复位以后执行的第一段代码,它主要用来初始化处理器及外设,然后调用 Linux 内核。
ARM体系架构下的linux启动之一,从bootloader到linux内核
ARM体系架构下的linux启动之一,从bootloader到linux内核1. bootloader 的启动bootloader 本身叫做嵌入式系统的引导程序。
但是,它支持X86,MIPS,PowerPC,ARM 等多种体系架构。
对于操作系统的启动来讲,当机器开始上电时,首先加载bootloader,它用来完成最基本的硬件的初始化,然后加载Linux 内核。
用于ARM 的bootloader 一般为U-BOOT,用它来完成对linux 内核的加载设置,一般bootloader 烧写在开发板的Flash 里,包括Nor Flash 或Nand Flash,其中由于NorFlash 支持芯片内执行XIP(eXcute In Place),代码可以直接在FLASH 上执行,而NandFlash 需要把代码拷到RAM 中再去执行。
但是RAM 的处理速度比Flash 的处理速度要快得多,一般仍然把代码放在RAM 中执行。
一般的bootloader 需要完成以下五种功能:1)RAM 的初始化:为调用linux 内核做准备。
2)串口的初始化:由于一般的嵌入式系统开发板上没有视频终端,只能用串口进行开发,串口的初始化非常重要。
3)检测处理器类型:用来预处理加载内核的处理器类型的传递工作。
4)设置Linux 的启动参数:包括加载地址,启动方式(从本地分区或NFS 进行根文件系统加载),以及Linux 根文件系统的加载方式。
通常用一个标记列表来记录linux 内核启动的各个参数。
5)调用linux 内核镜像:此时ARM 的处理器中的几个特殊的寄存器值:r0=0,r1=处理器类型,r2=标记列表在RAM 中的位置。
2. linux kernel 的启动分析。
基于 Arm 的 linux 的启动分析
基于Arm 的linux 的启动分析(转)目录:一Makefile 的分析 (2)1.1 启动方案 (2)1.2 zImage 代码结构 (2)1.2.1 顶层vmlinux 的生成过程 (2)1.2.2 zImage 的生成 (6)二zImage 的启动过程 (12)2.1 compressed/vmlinux.lds 文件的分析 (12)2.2 compressed/head.s 文件的分析 (12)2.3inux/arch/arm/kernel/head.S 文件的分析 (18)2.4 arch/arm/kernel/common.s 文件的分析 (19)(看这个文章把握住两点:本文第一个过程是make zImage的过程,是在配置生成时候的过程,这个时候内核还没有往板子上下载呢,第二个过程是拷贝到板子上以后,从flash再拷贝到内存中,解压并且执行内核的第一阶段的启动)注:目录均设有超级链接,可以按住CTRL 并单击鼠标进行跟踪链接。
一Makefile 的分析1.1 启动方案在/arch/arm 文件夹下的Makefile 文件第215 行得到语句(有的在顶层目录中就有)define archhelpecho '* zImage - Compressed kernel image (arch/$(ARCH)/boot/zImage)'echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'echo '* xipImage - XIP kernel image, if configured (arch/$(ARCH)/boot/xipImage)'echo ' bootpImage - Combined zImage and initial RAM disk'由此可以知道基于ARM 的启动方案有Image 、zImage 、xipImage 和bootpImage 四种。
ARMlinux内核启动分析.
ARM linux 内核启动分析[原创2007-06-11 10:35:46 ]发表者:jimmy_leehead-armv.S主支分析head-armv.S是解压后(或未压缩的内核最先执行的一个文件,这个文件位于arch/arm/ kernel/head-armv.S,在与这个文件同目录下还有一个文件head-armo.S 与head-armv.S 很相似,但从arch/arm/下的Makefile中可以看到区别在哪里:ifeq ($(CONFIG_CPU_26,yPROCESSOR := armoifeq ($(CONFIG_ROM_KERNEL,yTEXTADDR = 0x03800000LDSCRIPT = arch/arm/vmli nu x-armo-ro m.l ds.inelseTEXTADDR = 0x02080000LDSCRIPT = arch/arm/vmli nu x-armo.lds. inen dif开篇说到,head-armv.S是进入内核最先执行的文件,为什么呢?内核可执行文件由许多链接在一起的对象文件组成。
对象文件有许多节,如文本、数据、init数据、bass等等。
这些对象文件都是由一个称为link script的文件链接并装入的。
这个link script的功能是将输入对象文件的各节映射到输出文件中;换句话说,它将所有输入对象文件都链接到单一的可执行文件中,将该可执行文件的各节装入到指定地址处。
vmlinux-armv.lds就是链接内核用到的link script,它位于arch/arm/目录下,你可能注意至U了同目录下还有一vmlinux-armv.lds.in 文件,这两文件可是有关系的,答案就在arch/arm/Makefile里。
ifeq ($(CONFIG_CPU_32,y /* 对于pxa 270 来说这里是True */PROCESSOR = armvTEXTADDR = 0xC0008000LDSCRIPT = arch/arm/vmli nux-armv.lds.inen dif$(wildcard in clude/c on fig/arch/*.h@echo ' Ge nerat ing $@'@sed 's/TEXTADDR/$(TEXTADDR/;s/DATAADDR/$(DATAADDR/' $(LDSCRIPT >$@从这个Makefile中我们可以看到,实际上arch/arm/vmlinux-armv.lds.in 就是arch/arm/vmlinux-armv」ds 是一个蓝本,在make 的时候vmlinux-armv」ds 是由sed命令来替换vmlinux-armv.lds.in文件中的TEXTADDR, DATAADDR 为特定的值而生成的。
arm-linux 启动代码分析——stage1 (1)
arm-linux 启动代码分析——stage1 (1)本文针对arm linux, 从kernel的第一条指令开始分析,一直分析到进入start_kernel()函数.我们当前以linux-2.6.19内核版本作为范例来分析,本文中所有的代码,前面都会加上行号以便于和源码进行对照.例:在文件init/main.c中:00478: asmlinkage void __init start_kernel(void)前面的"00478:" 表示478行,冒号后面的内容就是源码了.在分析代码的过程中,我们使用缩进来表示各个代码的调用层次.由于启动部分有一些代码是平台特定的,虽然大部分的平台所实现的功能都比较类似,但是为了更好的对code进行说明,对于平台相关的代码,我们选择at91(ARM926EJS)平台进行分析.另外,本文是以uncompressed kernel开始讲解的.对于内核解压缩部分的code,在arch/arm/boot/compressed中,本文不做讨论.一. 启动条件通常从系统上电到执行到linux kenel这部分的任务是由bootloader来完成.关于boot loader的内容,本文就不做过多介绍.这里只讨论进入到linux kernel的时候的一些限制条件,这一般是bootloader在最后跳转到kernel之前要完成的:1.CPU必须处于SVC(supervisor)模式,并且IRQ和FIQ中断都是禁止的;2. MMU(内存管理单元)必须是关闭的, 此时虚拟地址对物理地址;3. 数据cache(Data cache)必须是关闭的4. 指令cache(Instructioncache)可以是打开的,也可以是关闭的,这个没有强制要求;5. CPU 通用寄存器0 (r0)必须是0;6. CPU 通用寄存器1 (r1)必须是ARM Linux machinetype (关于machine type, 我们后面会有讲解)7. CPU 通用寄存器2 (r2) 必须是kernel parameterlist 的物理地址(parameter list 是由bootloader传递给kernel,用来描述设备信息属性的列表,详细内容可参考"Booting ARM Linux"文档).二. starting kernel首先,我们先对几个重要的宏进行说明(我们针对有MMU的情况): 宏 位置 默认值 说明KERNEL_RAM_ADDR arch/arm/kernel/head.S+26 0xc0008000 kernel在RAM中的的虚拟地址PAGE_OFFSET include/asm-arm/memeory.h+50 0xc0000000 内核空间的起始虚拟地址TEXT_OFFSET arch/arm/Makefile+137 0x00008000 内核相对于存储空间的偏移TEXTADDR arch/arm/kernel/head.S+49 0xc0008000 kernel的起始虚拟地址PHYS_OFFSET include/asm-arm/arch-xxx/memory.h 平台相关 RAM的起始物理地址内核的入口是stext,这是在arch/arm/kernel/vmlinux.lds.S中定义的:00011: ENTRY(stext)对于vmlinux.lds.S,这是ldscript文件,此文件的格式和汇编及C程序都不同,本文不对ldscript作过多的介绍,只对内核中用到的内容进行讲解,关于ld的详细内容可以参考这里的ENTRY(stext) 表示程序的入口是在符号stext.而符号stext是在arch/arm/kernel/head.S中定义的:下面我们将arm linuxboot的主要代码列出来进行一个概括的介绍,然后,我们会逐个的进行详细的讲解.在arch/arm/kernel/head.S中72 - 94 行,是armlinux boot的主代码:00072:ENTRY(stext) 00073: msr cpsr_c,#PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode00074: @and irqsdisabled 00075: mrc p15, 0,r9, c0, c0 @ getprocessorid 00076:bl __lookup_processor_type @r5=procinfor9=cpuid 00077: movs r10,r5 @invalid processor (r5=0)?00078:beq __error_p @yes, error‘p’ 00079:bl __lookup_machine_type @r5=machinfo 00080: movs r8,r5 @invalid machine (r5=0)?00081:beq __error_a @yes, error‘a’ 00082:bl __create_page_tables 00083: 00084: 00091: ldr r13,__switch_data @address to jump to after00092: @mmu has beenenabled 00093: adr lr,__enable_mmu @return (PIC)address 00094: add pc,r10,#PROCINFO_INITFUNC 其中,73行是确保kernel运行在SVC模式下,并且IRQ和FIRQ中断已经关闭,这样做是很谨慎的.arm linux boot的主线可以概括为以下几个步骤:1. 确定processortype (75 - 78行)2. 确定machinetype (79 - 81行)3.创建页表 (82行) 4.调用平台特定的__cpu_flush函数 (在struct proc_info_list中) (94行) 5.开启mmu (93行)6. 切换数据 (91行)最终跳转到start_kernel (在__switch_data的结束的时候,调用了b start_kernel)下面,我们按照这个主线,逐步的分析Code.1. 确定processor type arch/arm/kernel/head.S中:00075: mrc p15, 0,r9, c0, c0 @ getprocessorid 00076:bl __lookup_processor_type @r5=procinfor9=cpuid 00077: movs r10,r5 @invalid processor (r5=0)?00078:beq __error_p @yes, error‘p’ 75行: 通过cp15协处理器的c0寄存器来获得processor id的指令.关于cp15的详细内容可参考相关的arm手册76行:跳转到__lookup_processor_type.在__lookup_processor_type中,会把processortype 存储在r5中77,78行: 判断r5中的processor type是否是0,如果是0,说明是无效的processortype,跳转到__error_p(出错)__lookup_processor_type 函数主要是根据从cpu中获得的processorid和系统中的proc_info进行匹配,将匹配到的proc_info_list的基地址存到r5中,0表示没有找到对应的processor type.下面我们分析__lookup_processor_type函数arch/arm/kernel/head-common.S中:00145:.type __lookup_processor_type,%function00146: __lookup_processor_type:00147: adr r3,3f00148: ldmda r3,{r5 - r7}00149: sub r3, r3,r7 @get offset between virt&phys00150: add r5, r5,r3 @convert virt addresses to00151: add r6, r6,r3 @physical address space00152: 1: ldmia r5,{r3,r4} @value, mask00153: and r4, r4,r9 @mask wanted bits00154: teq r3,r400155:beq 2f00156: add r5, r5,#PROC_INFO_SZ @sizeof(proc_info_list)00157: cmp r5,r600158:blo 1b00159: mov r5,#0 @unknown processor00160: 2: mov pc,lr00161:00162:00165: ENTRY(lookup_processor_type)00166: stmfd sp!,{r4 - r7, r9, lr}00167: mov r9,r000168:bl __lookup_processor_type00169: mov r0,r500170: ldmfd sp!,{r4 - r7, r9, pc}00171:00172:00176:.long __proc_info_begin00177:.long __proc_info_end00178:3: .long .00179:.long __arch_info_begin00180:.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: *(.init) 00033: __proc_info_end= .; 这里是声明了两个变量:__proc_info_begin 和__proc_info_end,其中等号后面的"."是locationcounter(详细内容请参考) 这三行的意思是: __proc_info_begin 的位置上,放置所有文件中的".init"段的内容,然后紧接着是__proc_info_end 的位置. kernel 使用struct proc_info_list来描述processor type. 在include/asm-arm/procinfo.h 中: 00029: struct proc_info_list { 00030: unsignedint cpu_val; 00031: unsignedint cpu_mask; 00032: unsignedlong __cpu_mm_mmu_flags; 00033: unsignedlong __cpu_io_mmu_flags; 00034: unsignedlong __cpu_flush; 00035: constchar *arch_name; 00036: constchar *elf_name; 00037: unsignedint elf_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 ".init", #alloc,#execinstr 00465: 00466:.type __arm926_proc_info,#object 00467: __arm926_proc_info: 00468:.long 0x41069260 @ARM926EJ-S (v5TEJ) 00469:.long 0xff0ffff0 00470:.long PMD_TYPE_SECT | \ 00471: PMD_SECT_BUFFERABLE| \ 00472: PMD_SECT_CACHEABLE| \ 00473: PMD_BIT4 |\ 00474: PMD_SECT_AP_WRITE| \ 00475: PMD_SECT_AP_READ 00476:.long PMD_TYPE_SECT | \ 00477: PMD_BIT4 |\ 00478: PMD_SECT_AP_WRITE| \ 00479: PMD_SECT_AP_READ 00480:b __arm926_setup 00481:.long cpu_arch_name 00482:.long cpu_elf_name 00483:.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_VFP|HW CAP_EDSP|HWCAP_JA V A 00484:.long cpu_arm926_name 00485:.long arm926_processor_functions 00486:.long v4wbi_tlb_fns 00487:.long v4wb_user_fns 00488:.long arm926_cache_fns 00489:.size __arm926_proc_info,. - __arm926_proc_info 从464行,我们可以看到__arm926_proc_info 被放到了".init"段中. 对照struct proc_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行: 如果相等,说明我们找到了对应的processor type,跳到160行,返回156行: (如果不相等) , 将r5指向下一个proc_info,157行: 和r6比较,检查是否到了__proc_info_end.158行: 如果没有到__proc_info_end,表明还有proc_info配置,返回152行继续查找159行: 执行到这里,说明所有的proc_info都匹配过了,但是没有找到匹配的,将r5设置成0(unknownprocessor)160行: 返回2. 确定machine type arch/arm/kernel/head.S中:00079:bl __lookup_machine_type @r5=machinfo 00080: movs r8,r5 @invalid machine (r5=0)?00081:beq __error_a @yes, error ‘a’79行: 跳转到__lookup_machine_type函数,在__lookup_machine_type中,会把structmachine_desc的基地址(machine type)存储在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_begin00177:.long __proc_info_end00178:3: .long .00179:.long __arch_info_begin00180:.long __arch_info_end00181:00182: 00193:.type __lookup_machine_type,%function00194: __lookup_machine_type:00195: adr r3,3b00196: ldmia r3,{r4, r5, r6}00197: sub r3, r3,r4 @get offset between virt&phys00198: add r5, r5,r3 @convert virt addresses to00199: add r6, r6,r3 @physical address space00200: 1: ldr r3,[r5, #MACHINFO_TYPE] @ get machinetype00201: teq r3,r1 @matches loader number?00202:beq 2f @found00203: add r5, r5,#SIZEOF_MACHINE_DESC @ nextmachine_desc00204: cmp r5,r600205:blo 1b00206: mov r5,#0 @unknown machine00207: 2: mov pc,lr193, 194行: 函数声明195行: 取地址指令,这里的3b是向后symbol名称是3的位置,即第178行,将该地址存入r3. 和上面我们对__lookup_processor_type 函数的分析相同,r3中存放的是3b处物理地址.196行: r3是3b处的地址,因而执行完后: r4存的是3b处的地址 r5存的是__arch_info_begin 的地址 r6存的是__arch_info_end 的地址 __arch_info_begin 和__arch_info_end是在arch/arm/kernel/vmlinux.lds.S中: 00034: __arch_info_begin= .; 00035: *(.init) 00036: __arch_info_end= .; 这里是声明了两个变量:__arch_info_begin 和__arch_info_end,其中等号后面的"."是locationcounter(详细内容请参考) 这三行的意思是: __arch_info_begin 的位置上,放置所有文件中的".init"段的内容,然后紧接着是__arch_info_end 的位置. kernel 使用struct machine_desc 来描述machine type. 在include/asm-arm/mach/arch.h 中: 00017: struct machine_desc { 00018: 00022: unsignedint nr; 00023: unsignedint phys_io; 00024: unsignedint io_pg_offst; 00026: 00027: constchar *name; 00028: unsignedlong boot_params; 00029: 00030: unsignedint video_start; 00031: unsignedint video_end; 00032: 00033: unsignedint reserve_lp0:1; 00034: unsignedint reserve_lp1:1; 00035: unsignedint reserve_lp2:1; 00036: unsignedint soft_reboot:1; 00037:void (*fixup)(structmachine_desc *, 00038: struct tag *, char **, 00039: struct meminfo *); 00040:void (*map_io)(void); 00041:void (*init_irq)(void); 00042: structsys_timer *timer; 00043:void (*init_machine)(void); 00044: }; 00045: 00046: 00050: #defineMACHINE_START(_type,_name) \ 00051: static const struct machine_desc__mach_desc_##_type \ 00052:__attribute_used__ \ 00053:__attribute__((__section__(".init"))) ={ \ 00054:.nr =MACH_TYPE_##_type, \ 00055:.name =_name, 00056: 00057: #defineMACHINE_END \ 00058:}; 内核中,一般使用宏MACHINE_START来定义machine type. 对于at91, 在arch/arm/mach-at91rm9200/board-ek.c 中: 00137: MACHINE_START(AT91RM9200EK, "Atmel AT91RM9200-EK") 00138: 00139: .phys_io =AT91_BASE_SYS, 00140:.io_pg_offst =(AT91_VA_BASE_SYS >> 18)& 0xfffc, 00141:.boot_params =AT91_SDRAM_BASE + 0x100, 00142:.timer =&at91rm9200_timer, 00143:.map_io =ek_map_io, 00144: .init_irq =ek_init_irq, 00145:.init_machine =ek_board_init, 00146: MACHINE_END197行:r3中存储的是3b处的物理地址,而r4中存储的是3b处的虚拟地址,这里计算处物理地址和虚拟地址的差值,保存到r3中198行: 将r5存储的虚拟地址(__arch_info_begin)转换成物理地址 199行:将r6存储的虚拟地址(__arch_info_end)转换成物理地址 200行: MACHINFO_TYPE 在arch/arm/kernel/asm-offset.c 101行定义, 这里是取struct machine_desc中的nr(architecture number) 到r3中201行: 将r3中取到的machine type 和r1中的machine type(见前面的"启动条件")进行比较202行: 如果相同,说明找到了对应的machine type,跳转到207行的2f处,此时r5中存储了对应的structmachine_desc的基地址203行: (不相同), 取下一个machine_desc的地址204行: 和r6进行比较,检查是否到了__arch_info_end.205行: 如果不相同,说明还有machine_desc,返回200行继续查找.206行: 执行到这里,说明所有的machind_desc都查找完了,并且没有找到匹配的, 将r5设置成0(unknownmachine).207行: 返回3. 创建页表通过前面的两步,我们已经确定了processor type 和machine type.此时,一些特定寄存器的值如下所示:r8 = machineinfo (struct machine_desc的基地址)r9 = cpuid (通过cp15协处理器获得的cpu id)r10 =procinfo (struct proc_info_list的基地址)创建页表是通过函数__create_page_tables 来实现的.这里,我们使用的是arm的L1主页表,L1主页表也称为段页表(section page table)L1 主页表将4 GB 的地址空间分成若干个1 MB的段(section),因此L1页表包含4096个页表项(sectionentry). 每个页表项是32 bits(4 bytes)因而L1主页表占用4096 *4 = 16k的内存空间. 对于ARM926,其L1 section entry的格式为:(可参考arm926EJS TRM): 。
ARMlinux内核启动分析.
ARM linux内核启动分析[原创 2007-06-11 10:35:46 ] 发表者: jimmy_leehead-armv.S主支分析head-armv.S是解压后(或未压缩的内核最先执行的一个文件,这个文件位于arch/arm/kernel/head-armv.S,在与这个文件同目录下还有一个文件head-armo.S与head-armv.S 很相似,但从arch/arm/下的Makefile中可以看到区别在哪里:ifeq ($(CONFIG_CPU_26,yPROCESSOR := armoifeq ($(CONFIG_ROM_KERNEL,yDATAADDR = 0x02080000TEXTADDR = 0x03800000LDSCRIPT = arch/arm/vmlinux-armo-rom.lds.inelseTEXTADDR = 0x02080000LDSCRIPT = arch/arm/vmlinux-armo.lds.inendifendififeq ($(CONFIG_CPU_32,yPROCESSOR = armvTEXTADDR = 0xC0008000LDSCRIPT = arch/arm/vmlinux-armv.lds.inendif……HEAD :=arch/arm/kernel/head-$(PROCESSOR.o \arch/arm/kernel/init_task.o闲话少说,在进入分析head-armv.S之前,交待一下我所分析的内核版本号以及硬件平台,内核是2.4.19-rmk7-pxa2,对应的硬件平台为pxa 270。
开篇说到,head-armv.S是进入内核最先执行的文件,为什么呢?内核可执行文件由许多链接在一起的对象文件组成。
对象文件有许多节,如文本、数据、init 数据、bass 等等。
这些对象文件都是由一个称为link script的文件链接并装入的。
ARM+Linux的启动分析(zImage)
基于ARM的Linux的启动分析报告摘要:本文主要分析基于ARM的Linux-2.2.26内核启动过程。
将首先从/arch/arm/Makefile着手,介绍三种不同的启动方案,再剖析典型的压缩内核zImage启动方案的代码结构,最后将详细分析这种方案的启动过程,直到调用start_kernel()为止。
1、Linux内核的启动方案:由/arch/arm/Makefile的代码可以看出,主要有三种启动方案,分别是: echo '* zImage - Compressed kernel image (arch/$(ARCH)/boot/zImage)'echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'echo ' bootpImage - Combined zImage and initial RAM disk'echo ' (supply initrd image via make variable INITRD=<path>)'Linux内核有两种映像:一种是非压缩内核,叫 Image,另一种是它的压缩版本,叫zImage。
根据内核映像的不同,Linux内核的启动在开始阶段也有所不同。
zImage是Image经过压缩形成的,所以它的大小比 Image小。
但为了能使用 zImage,必须在它的开头加上解压缩的代码,将 zImage解压缩之后才能执行,因此它的执行速度比Image要慢。
但考虑到嵌入式系统的存储空容量一般比较小,采用zImage可以占用较少的存储空间,因此牺牲一点性能上的代价也是值得的。
所以一般的嵌入式系统均采用压缩内核的方式(另外bootpImage是编译包含zImage和initrd的映像,可以通过make变量INITRD=<path>提供initrd映像)。
嵌入式Linux内核启动部分代码分析
的位置上,最后跳转到 ZREALADDR 地址上,开始真正的内核启动。
arch/armnommu/boot/Makefile,定义 ZRELADDR 和 ZTEXTADDR。ZTEXTADDR 是自解压代码的起始地址,如果 从内存启动内核,设置为 0 即可,如果从 Rom/Flash 启动,则设置 ZTEXTADDR 为相应的值。ZRELADDR 是内核解 压缩后的执行地址。
__lookup_processor_type 这个函数根据芯片的 ID 从 获取 proc_info_list 结构,proc_info_list 结构定义在 include/asm-armnommu/proginfo.h 中,该结构的数据定义在 arch/armnommu/mm/proc-arm*.S 文件中,ARM7TDMI 系列
说明: 执行完 decompress_kernel 函数后,代码跳回 head.S/head-XXX.S 中,检查解压缩之后的 kernel 起始地址是否紧挨着 kernel image。如果是,beqcall_kernel,执行解压后的 kernel。如果解压缩之后的 kernel 起始地址不是紧挨着 kernelimage, 则执行 relocate,将其拷贝到紧接着 kernel image 的地方,然后跳转,执行解压后的 kernel。
5.2 setup_arch() setup_arch()函数做体系相关的初始化工作,函数的定义在 arch/armnommu/kernel/setup.c 文件中,主要涉及下列主
linux arm架构重启指令
linux arm架构重启指令
Linux ARM架构重启指令。
在Linux操作系统中,ARM架构是一种常见的处理器架构,被
广泛应用于嵌入式系统和移动设备中。
在ARM架构的Linux系统中,重启指令是一项非常重要的功能,它可以帮助用户在需要时快速地
重启设备,以解决一些系统问题或者应用程序崩溃的情况。
在Linux中,用户可以使用不同的方法来执行重启指令,其中
最常用的是使用命令行工具。
在ARM架构的Linux系统中,可以使
用以下命令来执行重启操作:
sudo reboot.
在这个命令中,“sudo”表示以超级用户权限执行命令,“reboot”表示重启操作。
当用户输入这个命令并确认后,系统将
会开始执行重启操作,关闭所有正在运行的程序并重新启动系统。
除了使用命令行工具外,用户还可以通过图形界面来执行重启
操作。
在大多数ARM架构的Linux发行版中,用户可以通过单击菜
单中的“重启”选项来执行重启操作。
这种方法更加直观和用户友好,适合那些不太熟悉命令行操作的用户使用。
无论是使用命令行工具还是图形界面,重启操作都是非常简单
和常用的功能。
在日常使用中,用户可能会遇到一些系统问题或者
应用程序崩溃的情况,这时候执行重启操作往往可以解决这些问题,恢复系统的正常运行。
总的来说,重启指令在Linux ARM架构系统中是一项非常重要
的功能,它为用户提供了一种快速解决系统问题的方法。
无论是通
过命令行工具还是图形界面,用户都可以方便地执行重启操作,确
保系统的稳定和正常运行。
ARMlinux启动分析-Nathan.Yu的专栏-CSDN博客
ARMlinux启动分析-Nathan.Yu的专栏-CSDN博客linux启动分析(1)---bootloader启动内核过程我分析的是2.4.19的内核版本,是xscale的平台,参考了网上很多有价值的帖子,也加入了自己的一些看法,陆续总结成文字,今天是第一篇:内核一般是由bootloader来引导的,通过bootloader启动内核一般要传递三个参数,第一个参数放在寄存器0中,一般都为0,r0 = 0;第二个参数放在寄存器1中,是机器类型id,r1 = Machine Type Number;第三个参数放在寄存器2中,是启动参数标记列表在ram中的起始基地址;bootloader首先要将ramdisk(如果有)和内核拷贝到ram当中,然后可以通过c语言的模式启动内核:void (*startkernel)(int zero, int arch, unsigned int params_addr) = (void(*)(int, int, unsigned int))KERNEL_RAM_BASE;startkernel(0, ARCH_NUMBER, (unsigned int)kernel_params_start);其中KERNEL_RAM_BASE为内核在ram中启动的地址,ARCH_NUMBER是Machine Type Number,kernel_params_start 是参数在ram的偏移地址。
这时候就将全力交给了内核。
linux启动分析(2)---内核启动地址的确定内核编译链接过程是依靠vmlinux.lds文件,以arm为例vmlinux.lds文件位于kernel/arch/arm/vmlinux.lds,但是该文件是由vmlinux-armv.lds.in生成的,根据编译选项的不同源文件还可以是vmlinux-armo.lds.in,vmlinux-armv-xip.lds.in。
ARM Linux启动分析----head-armv.S内幕
ARM Linux启动分析----head-armv.S内幕文章出处:/course/6_system/linux/Linuxjs/2007114/84011_6.html Linux启动后执行的第一个文件是arch/arm/kernel下的head-($PROCESSOR).S文件,processor代表的是该cpu的类型。
ARM 6及其以后的处理器核心支持32位地址空间。
这些处理器可以在26位和32位PC的模式下操作。
在26位PC模式下,R15寄存器的表现如同在以前的处理器上,代码只能运行在地址空间的最低的64M字节空间中。
在32位PC模式下,32位的R15寄存器被用做程序计数器。
使用独立的状态寄存器来存储处理器模式和状态标志。
对于26位的arm处理器类型,linux用armo来表示;对于32位的arm处理器,使用armv表示。
在include/linux/autoconf.h文件中通过#define CONFIG_CPU_32 1将处理器类型设置为支持32位PC模式。
然后在arch/arm/Makefile中通过ifeq ($(CONFIG_CPU_32),y)PROCESSOR = armvTEXTADDR = 0xC0008000LDSCRIPT = arch/arm/vmlinux-armv.lds.inendif设置处理器类型为armv,这样linux运行所执行的第一个文件就是head-armv.S。
接着,Makefile定义了内核中代码和数据所使用的虚拟地址TEXRADDR,最后,定义了链接器所使用的脚本文件,这个文件也是与处理器类型相关的。
在执行head-armv.S文件之前,有一点需要注意的是,bootloader已经在处理器的R1寄存器中存放了机器体系结构的类型号。
由于在文件的执行过程中将要针对当前的机器体系结构设置相关的参数,如果没有这个步骤,系统将显示“ERROR:a”,同时停止执行。
arm-linux启动流程分析
arm-linux启动流程分析arm-linux启动流程分析一.概述 (1)二.init进程 (1)三.inittab文件简要说明 (1)四.arm-linux系统的启动分析 (3)一.概述本文将讲述arm-linux内核启动后到进入用户shell模式这段时间的启动流程,从上电到进入kernel的过程请参见sourcesafe下“personal/yangxiyuan/资料”目录下的“ARM-linux启动流程.htm”文档。
二.init进程init进程是内核启动后的第一个用户级进程,是系统中所有进程的父进程,init进程的主要任务是按照inittab文件所提供的信息创建进程,init进程繁衍出完成通常操作所需的子进程,这些操作包括:设置机器名、检查和安装磁盘及文件系统、启动系统日志、配置网络接口并启动网络和邮件服务,启动打印服务等。
三.inittab文件简要说明inittab文件中每一记录都从新的一行开始,每个记录项最多可有512个字符,每一项的格式通常如下:id: run level:action:process,解释如下:id字段:是最多4个字符的字符串,用来唯一标志表项。
run level字段:定义该记录项被调用时的运行级别,run level可以由一个或多个运行级别构成,也可以是空,空则代表运行级别0~6。
# 0 - halt (Do NOT set initdefault to this)# - 关闭计算机,安全关闭电源# 1 - Single user mode# - 进入单用户维护模式,卸下除root以外的所有文件系统并杀死所有用户进程# 2 - Multiuser, without NFS (The same as 3, if you do not have networking)# - 多用户模式,无网络连接# 3 - Full multiuser mode# - 多用户模式,并以文本方式作为登陆方式# 4 - unused# 5 - X11# - 多用户模式,并以图形方式作为登陆方式# 6 - reboot (Do NOT set initdefault to this)# - 停止linux系统,并按照/etc/inittab默认的登记项重新引导系统。
ARM linux的启动部分源代码简略分析
ARM linux的启动部分源代码简略分析ARM linux的启动部分源代码简略分析以友善之臂的mini2440开发板为平台,以较新的内核linux-2.6.32.7版本为例,仅作说明之用。
当内核映像被加载到RAM之后,Bootloader的控制权被释放。
内核映像并不是可直接运行的目标代码,而是一个压缩过的zImage(小内核)。
但是,也并非是zImage映像中的一切均被压缩了,映像中包含未被压缩的部分,这部分中包含解压缩程序,解压缩程序会解压缩映像中被压缩的部分。
zImage使用gzip压缩的,它不仅仅是一个压缩文件,而且在这个文件的开头部分内嵌有gzip解压缩代码。
当zImage被调用时它从arch/arm/boot/compressed/head.S的start汇编例程开始执行。
这个例程进行一些基本的硬件设置,并调用arch/arm/boot/compressed/misc.c中的decompress_kernel()解压缩内核。
arch/arm/kernel/head.S文件是内核真正的启动入口点,一般是由解压缩内核的程序来调用的。
首先先看下对于运行这个文件的要求:MMU = off;D-cache = off;I-cache = 无所谓,开也可以,关也可以;r0 = 0;r1 = 机器号;r2 = atags 指针。
这段代码是位置无关的,所以,如果以地址0xC0008000来链接内核,那么就可以直接用__pa(0xc0008000)地址来调用这里的代码。
其实,在这个(Linux内核中总共有多达几十个的以head.S命名的文件)head.S文件中的一项重要工作就是设置内核的临时页表,不然mmu开起来也玩不转,但是内核怎么知道如何映射内存呢?linux的内核将映射到虚地址0xCxxx xxxx处,但他怎么知道在4GB的地址空间中有哪一片ram是可用的,从而可以映射过去呢?因为不同的系统有不通的内存映像,所以,LINUX约定,要调用内核代码,一定要满足上面的调用要求,以为最初的内核代码提供一些最重要的关于机器的信息。
ARM_Linux启动代码分析
首先,porting linux的时候要规划内存影像,如小弟的系统有64m SDRAM,地址从0x 0800 0000 -0x0bff ffff,32m flash,地址从0x0c00 0000-0x0dff ffff.规划如下:bootloader, linux kernel, rootdisk放在flash里。
具体从0x0c00 0000开始的第一个1M放bootloader,0x0c10 0000开始的2m放linux kernel,从0x0c30 0000开始都给rootdisk。
启动:首先,启动后arm920T将地址0x0c00 0000映射到0(可通过跳线设置),实际上从0x0c00 0000启动,进入我们的bootloader,但由于flash速度慢,所以bootloader前面有一小段程序把bootloader拷贝到SDRAM 中的0x0AFE0100,再从0x 0800 0000 运行bootloader,我们叫这段小程序为flashloader,flashloader必须要首先初始化SDRAM,不然往那放那些东东:.equ SOURCE, 0x0C000100 bootloader的存放地址.equ TARGET, 0x0AFE0100 目标地址.equ SDCTL0, 0x221000 SDRAM控制器寄存器// size is stored in location 0x0C0000FC.global _start_start: //入口点//;***************************************//;* Init SDRAM//;***************************************// ;***************// ;* SDRAM// ;***************LDR r1, =SDCTL0 //// ; Set Precharge CommandLDR r3, =0x92120200//ldr r3,=0x92120251STR r3, [r1]// ; Issue Precharge All CommadLDR r3, =0x8200000LDR r2, [r3]// ; Set AutoRefresh CommandLDR r3, =0xA2120200STR r3, [r1]// ; Issue AutoRefresh CommandLDR r3, =0x8000000LDR r2, [r3]LDR r2, [r3]LDR r2, [r3]LDR r2, [r3]LDR r2, [r3]LDR r2, [r3]LDR r2, [r3]LDR r2, [r3]// ; Set Mode RegisterLDR r3, =0xB2120200STR r3, [r1]// ; Issue Mode Register CommandLDR r3, =0x08111800 //; Mode Register V alue LDR r2, [r3]// ; Set Normal ModeLDR r3, =0x82124200STR r3, [r1]//;*************************************** //;* End of SDRAM and SyncFlash Init *//;***************************************// copy code from FLASH to SRAM_CopyCodes:ldr r0,=SOURCEldr r1,=TARGETsub r3,r0,#4ldr r2,[r3]_CopyLoop:ldr r3,[r0]str r3,[r1]add r0,r0,#4add r1,r1,#4sub r2,r2,#4teq r2,#0beq _EndCopyb _CopyLoop_EndCopy:ldr r0,=TARGETmov pc,r0欲知后事如何,下回分解:长篇连载--arm linux演艺---第二回--------------------------------------------------------------------------------上回书说到flashloader把bootloader load到0x0AFE0100,然回跳了过去,其实0x0AFE0100 就是烧在flash 0x0C000100中的真正的bootloader:bootloader 有几个文件组成,先是START.s,也是唯一的一个汇编程序,其余的都是C写成的,START.s 主要初始化堆栈:_start:ldr r1,=StackInitldr sp,[r1]b main//此处我们跳到了C代码的main函数,当C代码执行完后,还要调用//下面的JumpToKernel0x跳到LINXU kernel运行.equ StackInitV alue, __end_data+0x1000 // 4K __end_data在连结脚本中指定StackInit:.long StackInitV alue.global JumpToKernelJumpToKernel:// jump to the copy code (get the arguments right)mov pc, r0.global JumpToKernel0x// r0 = jump address// r1-r4 = arguments to use (these get shifted)JumpToKernel0x:// jump to the copy code (get the arguments right)mov r8, r0mov r0, r1mov r1, r2mov r2, r3mov r3, r4mov pc, r8.section ".data.boot".section ".bss.boot"欲知bootloader中的c代码如何运行,请看下集书接上回:下面让我们看看bootloader的c代码干了些什么。
ARM-Linux移植之(三)——init进程启动流程分析
ARM-Linux移植之(三)——init进程启动流程分析Y-Kee转载请注明来自于衡阳师范学院08电2 Y-Kee /ayangke, QQ:843308498我们通常使用Busybox来构建根文件系统的必要的应用程序。
Busybox通过传入的参数来决定执行何种操作。
当init进程启动时,实际上调用的是Busybox的init_main()函数,下面我们来分析这个函数,看init进程究竟是怎样一个流程。
我分析的Busybox源码是1.7.0版本的,其他版本会略有不同。
部分代码省略我们只看关键性代码。
首先看init_main函数int init_main(int argc, char **argv);int init_main(int argc, char **argv){……………………………..……………………………..//初始化控制台console_init();………………………………if (argc > 1&& (!strcmp(argv[1], "single") || !strcmp(argv[1], "-s") || LONE_CHAR(argv[1], '1'))) {new_init_action(RESPAWN, bb_default_login_shell, "");} else {//因为我们启动的init进程没有任何参数,所有argc==1,执行的是这一句parse_inittab();}……………………………………………………………………………………run_actions(SYSINIT);run_actions(WAIT);run_actions(ONCE);………………………………………………while (1) {run_actions(RESPAWN);run_actions(ASKFIRST);wpid = wait(NULL);while (wpid > 0) {a->pid = 0;}wpid = waitpid(-1, NULL, WNOHANG);}}parse_inittab实际上对/etc/inittab文件里面的配置进行解释,如果没有,则设置一些默认设置。
嵌入式linux启动代码详解
• ;Check if the boot is caused by the wake-up from POWER_OFF mode.
• ldr r1,=GSTATUS2
• ldr r0,[r1]
• tst r0,#0x2
• ;In case of the wake-up from POWER_OFF mode, go to
6、初始化内存控制器
• 1》参数配置
• ;BANK0CON
• B0_Tacs EQU 0x0 ;0clk
• B0_Tcos EQU 0x0 ;0clk
• B0_Tacc EQU 0x7 ;14clk
• B0_Tcoh EQU 0x0 ;0clk
• B0_Tah EQU 0x0 ;0clk
• B0_Tacp EQU 0x0
• str r1,[r0]
• ldr r0,=INTSUBMSK
• ldr r1,=0x7ff ;all sub interrupt disable,
2002/04/10
• str r1,[r0]
3.测试LED的显示
• [ {FALSE}
• ; rGPFDAT= (rGPFDAT& ~(0xf<<4)) | ((~data & 0xf)<<4);
•]
•|
•b ResetHandler
•]
•这段程序的开始使用ASSERT伪操作判断ENDIAN_CHANGE是否被定义,如果没有定义,ASSERT伪操作将报告
错误类型,并终止汇编。ENDIAN_CHANGE是在option.s文件中定义的,读者可以用鼠标点击2410init.s文件的编
arm_linux_从入口到start_kernel_代码详细分析2
1. 确定processor typearch/arm/kernel/head.S中:00075: mrc p15, 0, r9, c0, c0 @ get processor id 00076: bl __lookup_processor_type @ r5=procinfo r9=cpuid 00077: movs r10, r5 @ invalid processor (r5=0)?00078: beq __error_p @ yes, error 'p'75行: 通过cp15协处理器的c0寄存器来获得processor id的指令. 关于cp15的详细内容可参考相关的arm手册76行: 跳转到__lookup_processor_type.在__lookup_processor_type中,会把processor type 存储在r5中77,78行: 判断r5中的processor type是否是0,如果是0,说明是无效的processor type,跳转到__error_p(出错)__lookup_processor_type 函数主要是根据从cpu中获得的processor id和系统中的proc_info进行匹配,将匹配到的proc_info_list的基地址存到r5中, 0表示没有找到对应的processor type.下面我们分析__lookup_processor_type函数arch/arm/kernel/head-common.S中:00145: .type __lookup_processor_type, %function00146: __lookup_processor_type:00147: adr r3, 3f00148: ldmda r3, {r5 - r7}00149: sub r3, r3, r7 @ get offset between virt&phys00150: add r5, r5, r3 @ convert virt addresses to00151: add r6, r6, r3 @ physical address space00152: 1: ldmia r5, {r3, r4} @ value, mask00153: and r4, r4, r9 @ mask wanted bits00154: teq r3, r400155: beq 2f00156: add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list)00157: cmp r5, r600158: blo 1b00159: mov r5, #0 @ unknown processor00160: 2: mov pc, lr00161:00162:00165: ENTRY(lookup_processor_type)00166: stmfd sp!, {r4 - r7, r9, lr}00167: mov r9, r000168: bl __lookup_processor_type00169: mov r0, r500170: ldmfd sp!, {r4 - r7, r9, pc}00171:00172:00176: .long __proc_info_begin00177: .long __proc_info_end00178: 3: .long .00179: .long __arch_info_begin00180: .long __arch_info_end145, 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: *(.init)00033: __proc_info_end = .;这里是声明了两个变量:__proc_info_begin 和__proc_info_end,其中等号后面的"."是location counter(详细内容请参考) 这三行的意思是: __proc_info_begin 的位置上,放置所有文件中的".init" 段的内容,然后紧接着是__proc_info_end 的位置.kernel 使用struct proc_info_list来描述processor type.在include/asm-arm/procinfo.h 中:00029: struct proc_info_list {00030: unsigned int cpu_val;00031: unsigned int cpu_mask;00032: unsigned long __cpu_mm_mmu_flags;00033: unsigned long __cpu_io_mmu_flags;00034: unsigned long __cpu_flush;00035: const char *arch_name;00036: const char *elf_name;00037: unsigned int elf_hwcap;00038: const char *cpu_name;00039: struct processor *proc;00040: struct cpu_tlb_fns *tlb;00041: struct cpu_user_fns *user;00042: struct cpu_cache_fns *cache;00043: };我们当前以at91为例,其processor是926的.在arch/arm/mm/proc-arm926.S 中:00464: .section ".init", #alloc, #execinstr00465:00466: .type __arm926_proc_info,#object00467: __arm926_proc_info:00468: .long 0x41069260 @ ARM926EJ-S (v5TEJ)00469: .long 0xff0ffff000470: .long PMD_TYPE_SECT | \00471: PMD_SECT_BUFFERABLE | \00472: PMD_SECT_CACHEABLE | \00473: PMD_BIT4 | \00474: PMD_SECT_AP_WRITE | \00475: PMD_SECT_AP_READ00476: .long PMD_TYPE_SECT | \00477: PMD_BIT4 | \00478: PMD_SECT_AP_WRITE | \00479: PMD_SECT_AP_READ00480: b __arm926_setup00481: .long cpu_arch_name00482: .long cpu_elf_name00483: .longHWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HW CAP_VFP|HWCAP_EDSP|HWCAP_JAVA00484: .long cpu_arm926_name00485: .long arm926_processor_functions00486: .long v4wbi_tlb_fns00487: .long v4wb_user_fns00488: .long arm926_cache_fns00489: .size __arm926_proc_info, . - __arm926_proc_info从464行,我们可以看到__arm926_proc_info 被放到了".init"段中.对照struct proc_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行: 对照struct proc_info_list,可以得知,这句是将当前proc_info的cpu_val和cpu_mask分别存r3, r4中153行: r9中存储了processor id(arch/arm/kernel/head.S中的75行),与r4的cpu_mask进行逻辑与操作,得到我们需要的值154行: 将153行中得到的值与r3中的cpu_val进行比较155行: 如果相等,说明我们找到了对应的processor type,跳到160行,返回156行: (如果不相等) , 将r5指向下一个proc_info,157行: 和r6比较,检查是否到了__proc_info_end.158行: 如果没有到__proc_info_end,表明还有proc_info配置,返回152行继续查找159行: 执行到这里,说明所有的proc_info都匹配过了,但是没有找到匹配的,将r5设置成0(unknown processor)160行: 返回本文来自: () 详细出处参考:/html/article/qianrushiyingyong/arm_linux/2010/0909/20476.html一、编译安装流程以linux_2.6.36在i386下编译为例获取源码最新kernel下载:。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. Arm 的启动文件 .
根据 vmlinux-init 找到 head-y, init-y
根 Makefile 中以 include 的方式包含了 arm 体系结构相关部 分的 Makefile
在这个 Makefile 中
其中,变量 MMUEXT 的定义如下
. 先看 kernel.S 和 initrd.S
. kernel.S .. .
. .. . initrd.S ..
.
. ..
. . . . .
.
陈香兰(xlanchen@)
嵌入式操作系统
.
.
. .
基于 arm 的 Linux 启动代码分析 小结和作业
. 看看 bootp/init.S
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. boot 目录
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. boot 下 Makefile .
2: zImage
3: compressed/vmlinux 1: Image
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. compressed/head.S .
入口 start 关中断? 执行具体体系相关代码(不同的体系有不同的代码,通过 ld 的方式链入)
参见链接描述相关文件 vmlinux.lds.in
在根 Makefile 中 .
陈香兰(xlanchen@) 嵌入式操作系统
. . . . . .
基于 arm 的 Linux 启动代码分析 小结和作业
. 为便于阅读,了解关于命令输出的相关内容 .
.
陈香兰(xlanchen@) 嵌入式操作系统
.
.
.
.
.
.
基于 arm 的 Linux 启动代码分析 小结和作业
基于 arm 的 Linux 启动代码分析 小结和作业
. 小结
. 1 . . 基于 arm 的 Linux 启动代码分析
. 2 . . 小结和作业
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. Project2
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. 考虑 Arch 为 arm,了解源码组织
观察 Linux 源码的根目录 观察 arch 目录 观察 arch 下的 arm 目录
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. 进入 Linux 中关于 arm 的启动代码前
Bootloader Bootloader 必须完成内核代码的加载,然后跳转到入口处运 行,入口可能是
Bootp 的 Init.S Commpressed 的 head.S Kernel 的 head.S
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. 阅读 Makefile
找到缺省目标 all
找到 vmlinux 目标,并阅读
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统
.
基于 arm 的 Linux 启动代码分析 小结和作业
解释:关于 $(callif changed rule, vmlinux ) ↓ rule vmlinux
.
.
.
.
.
.
基于 arm 的 Linux 启动代码分析 小结和作业
. 结合 compressed/head.S 确定 zImage 的入口
提示:从 bootpImage 是如何开始执行 zImage 的? 确定 zImage 的入口
compressed/Makefile compressed/vmlinux.lds.in compressed/head.S
4: bootpImage
.
. . . . . .
陈香兰(xlanchen@)
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. 考虑 bootp
目录下的内容
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
参见“Documentation/kbuild/makefiles.txt”
.
陈香兰(xlanchen@) 嵌入式操作系统
.
.
.
.
.
.
基于 arm 的 Linux 启动代码分析 小结和作业
注意:
vmlinux-init vmlinux-main
vmlinux-dirs
.
.
.
.
.
.
.
.
.Hale Waihona Puke ..陈香兰(xlanchen@)
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. arm 的启动(小结)
阅读 documentation/arm/booting 阅读 documentation/arm/IXP4xx 下面阅读上述几个启动相关文件
入口 start ...
where?
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. 考虑压缩
参见“arch/arm/boot/compressed/Makefile”
其中,变量 Head 和 OBJS ... ... ... ... 关于 piggy.o 等等
基于 Arm 的 linux 的启动分析
首先进行 Makefile 的分析,在分析过程中
获知不同的启动方案 获知典型启动方案的代码结构
选择一种启动方案,进行基于 arm 的 Linux 的启动分析,分 析直到调用 start kernel 为止
. 难度:bootpImage>zImage>Image . .. 提供详细分析报告(要求:比课上的要详细)
. . .
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. Thanks! .. . ..
. .
.
The end.
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统
.
.
.
.
.
.
基于 arm 的 Linux 启动代码分析 小结和作业
. Arm 寄存器集 .
.
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. 结合 bootp/init.S 确定 bootp 的入口 .
.
陈香兰(xlanchen@) 嵌入式操作系统
确定 Image 的入口
Makefile arch/arm/Makefile
arch/arm/kernel/head.S
入口:stext 关键数据结构: switch data
关键 function: mmap switched
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统
. Bootp 下的 Makefile .
.
陈香兰(xlanchen@) 嵌入式操作系统
.
.
.
.
.
.
.
基于 arm 的 Linux 启动代码分析 小结和作业
关于 INITRD
.
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. 考虑 boot Image
Make zImage … 在 arch/arm/Makefile 中
z代表压缩;b代表大内核 对于 arm,没有大小内核之分 到 boot 目录下的 Makefile
压缩 or 非压缩 or bootp
. . . . . .
陈香兰(xlanchen@)
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统
基于 arm 的 Linux 启动代码分析 小结和作业
. 关于 head-common.S
在 kernel/head.S 中 在 kernel/head-nommu.S 中类似
.
.
.
.
.
.
陈香兰(xlanchen@)
.
.
.
.
.
.
陈香兰(xlanchen@)
嵌入式操作系统