1、Arm linux启动分析Arm linux启动分析(1)下周准备做linux启动的技术讲座,在这里我慢慢整理下自己的材料,这次我写的是Image的启动过程,也即使zImage解压缩结束后的启动代码,这时候的代码开始地址仍然是0x30008000,下面我结合代码来讲吧:Image的启动代码是在/arch/arm/kernel/head.S中的:/* linux/arch/arm/kernel/head.S* Kernel startup code for all 32-bit CPUs*/* 内核启动入口点 * Kernel startup entry point. * 这里通常在解压后直接调
2、用。 * 处理器基本状态要求: * MMU关闭,D-cach关闭,I-cache不用关系; * r0 = 0,r1 = 系统号(machine number) * 这段代码几乎是位置无关的。 * 如果链接内核在0xc0008000,调用的地址为相应的物理地址_pa(0xc0008000)。 * r1的系统号参考arch/arm/tools/mach-types文件的列表。 * 尽量不要在这里添加系统号相关的代码,那应该放在bootloader的代码中。 * 保持这里的代码的整洁。 */_INIT.typestext, %function/* 这个地方就是 kernel 的入口点 */ENTRY
3、(stext)msrcpsr_c, #PSR_F_BIT | PSR_I_BIT | MODE_SVC ensure svc mode and irqs disabled/* 调用 _lookup_processor_type 检查现在运行的 cpu 的 ID 值和 linux 编译支持的id 值是否相等。 */bl_lookup_processor_type r5=procinfo r9=cpuid/* 从该函数返回后,寄存器内容如下: R9 = cpu ID R5 = pointer to processor structure 详细的内容请看_lookup_processor_type
4、的分析 */.type_lookup_processor_type, %function_lookup_processor_type:/* 把标号 2 的地址送给 r3, 3f = lable 3 forward */adrr3, 3f/* 把 r3 指向内存的地址的内容赋值给 r5,r6,r9 所以,参照标号 3处的声明,我们可以知道: _proc_info_begin r5 _proc_info_end r6 3b r9 _proc_info_end 和 _proc_info_begin 这两个标号都是在/linux/arch/arm/vmlinux.ld 这个脚本中定义的。在连接的时候,
5、ld 会把相应cpu信息proc_info 放到这两个标号之间。 _proc_info_begin = .; *(.proc.info) _proc_info_end = .; */ldmdar3, r5, r6, r9 ldmda弹栈顺序是从右到左,r3-r9,r3-4-r6,r3-8-r5/*r3 = 标号 3 的加载地址地址,r9 = 标号 3 的连接地址 ,r3是根据pc值确定的,r9是链接阶段就确定的是链接地址*/subr3, r3, r9 get offset between virt&phys/ r3 = 加载地址和连接地址的差值 /现在,r5 = _proc_info_begi
6、n 的加载地址,即在 RAM 中的地址 addr5, r5, r3 convert virt addresses toaddr6, r6, r3 physical address spacemrcp15, 0, r9, c0, c0 get processor id协处理器指令获取cpu id号r9=0x41807202(sep4020)/* 在本例中, r5 = _arm720_proc_info 这 个 标记定义在 linux/arch/arm/mm/proc-arm720.S _arm720_proc_info: .long 0x41807200 r3 = cpu_value .long
7、 0xffffff00 r4 = cpu_mask .long 0x00000c1e mmuflags,一级段描述符 b _arm720_setup . . */ldmia弹栈顺序是从左到右,r5-r3,r54-r4 ,即低地址的内容放到低编号的寄存器,高地址的内容放到高编号的寄存器,指令结束后r5的指依然为_arm720_proc_info1:ldmiar5, r3, r4 value, mask andr4, r4, r9 mask wanted bits/将r9屏上0xffffff00看sep4020是否是arm720t的内核teqr3, r4beq2f 若是arm720t内核则直接跳转
8、到标签2/* proc_info_list 定义在 linux/include/asm-arm/procinfo.h struct proc_info_list unsigned intcpu_val;unsigned intcpu_mask;unsigned long_cpu_mmu_flags;/ used by head.S unsigned long_cpu_flush;/ used by head.S const char*arch_name;const char*elf_name;unsigned intelf_hwcap;const char*cpu_name;struct pr
9、ocessor*proc;struct cpu_tlb_fns*tlb;struct cpu_user_fns*user;struct cpu_cache_fns*cache; 每一项都是 4 个字节,所以 sizeof(proc_info_list) 48 byte */addr5, r5, #PROC_INFO_SZ sizeof(proc_info_list)=48cmpr5, r6blo1bmovr5, #0 unknown processor2:movpc, lr/* This provides a C-API version of the above function.*/ENTR
10、Y(lookup_processor_type)stmfdsp!, r4 - r6, r9, lrbl_lookup_processor_typemovr0, r5ldmfdsp!, r4 - r6, r9, pc/* Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.ch for* more information about the _proc_info and _arch_info structures.*/.long_proc_info_begin.long_proc_info_end3:.long.long_arc
11、h_info_begin.long_arch_info_end /*从_lookup_processor_type返回 */movsr10, r5 是有效720t核吗 (r5=0)?beq_error_p yes, error p/* _lookup_machine_type 通过 R1 寄存器,判断体系类型,R1 = machine architecture number */bl_lookup_machine_type r5=machinfo/* */* r1 = machine architecture number* Returns:* r3, r4, r6 corrupted* r5
12、 = mach_info pointer in physical address space*/.type_lookup_machine_type, %function_lookup_machine_type:adrr3, 3b/* 把 r3 指向内存的地址的内容赋值给 r4,r5,r6 所以,参照标号 3处的声明,我们可以知道: 3b r4 _arch_info_beginr5_arch_info_endr6 _arch_info_end 和 _arch_info_begin 这两个标号都是在/linux/arch/arm/vmlinux.ld 这个脚本中定义的。在连接的时候,ld 会把相应
13、体系架构arch_info 放到这两个标号之间。 _arch_info_begin = .; *(.arch.info.init) _arch_info_end = .;*/ldmiar3, r4, r5, r6subr3, r3, r4 get offset between virt&phys/r5=_arch_info_begin的加载地址addr5, r5, r3 convert virt addresses toaddr6, r6, r3 physical address space/* _arch_info_begin 和_arch_info_end 的类型都是 struct mac
14、hine_desc。其实就是指向一个 machine_desc 结构首尾的两个地址标号。 struct machine_desc 定义在 linux/include/asm-arm/mach/arch.h 中 struct machine_desc /* * Note! The first four elements are used * by assembler code in head.S */ unsigned intnr;/* architecture number*/unsigned int _deprecated phys_ram;/* start of physical ram
15、*/unsigned intphys_io;/* start of physical io*/unsigned intio_pg_offst;/* byte offset for io * page tabe entry*/const char*name;/* architecture name*/unsigned longboot_params;/* tagged list*/unsigned intvideo_start;/* start of video RAM*/unsigned intvideo_end;/* end of video RAM*/unsigned intreserve
16、_lp0 :1;/* never has lp0*/unsigned intreserve_lp1 :1;/* never has lp1*/unsigned intreserve_lp2 :1;/* never has lp2*/unsigned intsoft_reboot :1;/* soft reboot*/void(*fixup)(struct machine_desc *, struct tag *, char *, struct meminfo *);void(*map_io)(void);/* IO mapping function*/void(*init_irq)(void)
17、;struct sys_timer*timer;/* system tick timer*/void(*init_machine)(void); 而对于我们的SEP4020其真正的定义是在/arch/arm/mach-sep4020/4020.c中MACHINE_START(GFD4020, 4020 board).phys_io= 0x10000000,.io_pg_offst= (0xe0000000) 18) & 0xfffc,.boot_params= 0x30000100,.fixup= fixup_gfd4020,.map_io= sep4020_map_io,.init_irq=
18、 sep4020_init_irq,.init_machine= sep4020_init,.timer= &sep4020_timer,MACHINE_END 看到这里,我们就不难明白下边这条指令了,struct machine_desc 中第一个就是nr,即 architecture number r3 = MACH_TYPE_GFD4020*/1:ldrr3, r5, #MACHINFO_TYPE get machine type ,MACHINFO_TYPE = 0/r1是由解压缩程序/arch/arm/boot/compressed/head.S最后传过来的,或者是uboot传过来的
19、体系结构号teqr3, r1 matches loader number?beq2f foundaddr5, r5, #SIZEOF_MACHINE_DESC next machine_desccmpr5, r6blo1bmovr5, #0 unknown machine2:movpc, lr/*从_lookup_machine_type返回 */movsr8, r5 invalid machine (r5=0)?是不是我们的SEP4020系统结构beq_error_a yes, error a/* 设置 mmu 之前,设置临时内核页表 */bl_create_page_tables/* /*
20、 我们在这里只映射内核启动的临时页表* 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 cpu 的ID* r10 = procinfo cpu信息* Returns:* r0, r3, r6, r7 corrupted* r4 = physical page
21、 table address*/.type_create_page_tables, %function_create_page_tables:/* */ Page offset: 3GB 内核页表的偏移在/inculde/asm/memory.h#define PAGE_OFFSETUL(0xc0000000)#ifndef _virt_to_phys#define _virt_to_phys(x)(x) - PAGE_OFFSET + PHYS_OFFSET)#define _phys_to_virt(x)(x) - PHYS_OFFSET + PAGE_OFFSET)#endif而这其中的
22、PHYS_OFFSET则是我们需要在我们的SEP4020的定义自己的主存ram的基址的物理地址,我们是在/include/asm-arm/arch-sep4020/memory.h中定义的#define PHYS_OFFSETUL(0x30000000)/*TEXT_OFFSET 是在在arch/arm/Makefile第140行,有 TEXT_OFFSET := $(textofs-y) 第90行有 textofs-y := 0x00008000 所以TEXT_OFFSET := 0x00008000 在153行有export TEXT_OFFSET将此变量输出。*/#define KERN
23、EL_RAM_ADDR(PAGE_OFFSET + TEXT_OFFSET) 其中TEXT_OFFSET 0x8000/swapper_pg_dir是放启动时的临时页表的页表基址(虚地址).globlswapper_pg_dir.equswapper_pg_dir, KERNEL_RAM_ADDR - 0x4000/这个宏就是根据内核ram首址(虚拟地址)计算出我们内核页表的页表基址(物理地址).macropgtbl, rdldrrd, =(_virt_to_phys(KERNEL_RAM_ADDR - 0x4000).endm */pgtblr4 page table address/这样r
24、4 = 内核页表的页表基址(物理地址)/* * Clear the 16K level 1 swapper page table */movr0, r4movr3, #0/r6 = 内核的KERNEL_RAM_ADDRaddr6, r0, #0x4000/首先对16k的一级页表内容清01:strr3, r0, #4strr3, r0, #4strr3, r0, #4strr3, r0, #4teqr0, r6bne1b/PROCINFO_MMUFLAGS = 8;这样 r7 = 0x00000c1e mmuflags,一级段描述符 ,在proc-arm720.S 中定义ldrr7, r10, #PROCINFO_MMUFLAGS
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1