首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > UNIXLINUX >

linux内核启动流程(下)

2013-03-21 
linux内核启动流程(上)由内核Makefile分析可知,文件linux/arch/arm/boot/compressed/head.S是linux内核启

linux内核启动流程(上)
由内核Makefile分析可知,文件linux/arch/arm/boot/compressed/head.S是linux内核启动过程执行的第一个文件。.alignstart:.type start,#function    //type指定start这个符号是函数类型.rept 8mov r0, r0    //空操作,重复八次.endr
b 1f    //跳转.word 0x016f2818 @ Magic numbers to help the loader//魔数0x016f2818是在bootloader中用于判断zImage的存在,这是内核和bootloader约定好的。.word start @ absolute load/run zImage address.word _edata @ zImage end address1: mov r7, r1 @ save architecture ID   //bootloader 传递过来的r1 r2mov r8, r2 @ save atags pointer//r1和r2中分别存放着由bootloader传递过来的architecture ID和指向标记列表的指针
#ifndef __ARM_ARCH_2__/* * Booting from Angel - need to enter SVC mode and disable * FIQs/IRQs (numeric definitions from angel arm.h source). * We only do this if we were in user mode on entry. */mrs r2, cpsr @ get current modetst r2, #3 @ not user?bne not_angelmov r0, #0x17 @ angel_SWIreason_EnterSVCswi 0x123456 @ angel_SWI_ARMnot_angel:mrs r2, cpsr @ turn off interrupts toorr r2, r2, #0xc0 @ prevent angel from runningmsr cpsr_c, r2        //关闭IRQ FIQ#elseteqp pc, #0x0c000003 @ turn off interrupts#endif
                .textadr r0, LC0    //下面有解释ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp}subs r0, r0, r1 @ calculate the delta offset 获取偏移量
@ if delta is zero, we arebeq not_relocated @ running at the address we@ were linked at.//如果内核没有进行搬移,就跳转。一般是需要的/* * We're running at a different address.  We need to fix * up various pointers: *   r5 - zImage base address *   r6 - GOT start *   ip - GOT end */add r5, r5, r0    //修改内核映像基地址add r6, r6, r0    //修改got表的起始和结束地址add ip, ip, r0/*其中LC0表是链接文件(arch/arm/boot/compressed/vmlinux.lds)的各段入口。源码如下:OUTPUT_ARCH(arm)ENTRY(_start)SECTIONS{  . = 0;  _text = .;
  .text : {    _start = .;    *(.start)    *(.text)    *(.text.*)    *(.fixup)    *(.gnu.warning)    *(.rodata)    *(.rodata.*)    *(.glue_7)    *(.glue_7t)    *(.piggydata)    . = ALIGN(4);  }
  _etext = .;
  _got_start = .;  .got : { *(.got) }  _got_end = .;  .got.plt : { *(.got.plt) }  .data : { *(.data) }  _edata = .;
  . = ALIGN(4);  __bss_start = .;  .bss : { *(.bss) }  _end = .;
  .stack (NOLOAD) : { *(.stack) }
  .stab 0 : { *(.stab) }  .stabstr 0 : { *(.stabstr) }  .stab.excl 0 : { *(.stab.excl) }  .stab.exclstr 0 : { *(.stab.exclstr) }  .stab.index 0 : { *(.stab.index) }  .stab.indexstr 0 : { *(.stab.indexstr) }  .comment 0 : { *(.comment) }}依次是.text .got .data .bss .stack .stab,另外连接地址都是位置无关的,即都是以0地址为偏移的。而此时内核已被bootloader搬移,链接地址应该加上这个偏移。*/
#ifndef CONFIG_ZBOOT_ROM/* * If we're running fully PIC === CONFIG_ZBOOT_ROM = n, * we need to fix up pointers into the BSS region. *   r2 - BSS start     *   r3 - BSS end *   sp - stack pointer */add r2, r2, r0    //修改bss段和堆栈的地址add r3, r3, r0add sp, sp, r0
/* * Relocate all entries in the GOT table. */1: ldr r1, [r6, #0] @ relocate entries in the GOT 重定位got表add r1, r1, r0 @ table.  This fixes up thestr r1, [r6], #4 @ C references.cmp r6, ipblo 1b         //bhs 大于等于跳转,bls小于等于跳转,blo不相等跳转#else
/* * Relocate entries in the GOT table.  We only relocate * the entries that are outside the (relocated) BSS region. */1: ldr r1, [r6, #0] @ relocate entries in the GOTcmp r1, r2 @ entry < bss_start ||cmphs r3, r1 @ _end < entryaddlo r1, r1, r0 @ table.  This fixes up thestr r1, [r6], #4 @ C references.cmp r6, ipblo 1b       #endif
not_relocated: movr0, #01: str r0, [r2], #4 @ clear bss 清bss段,所有arm程序都需要做这些str r0, [r2], #4str r0, [r2], #4str r0, [r2], #4cmp r2, r3blo 1b
/* * The C runtime environment should now be setup * sufficiently.  Turn the cache on, set up some * pointers, and start decompressing. */bl cache_on    //打开cache
mov r1, sp @ malloc space above stackadd r2, sp, #0x10000 @ 64k max 解压函数需要的内存缓存/* * Check to see if we will overwrite ourselves. *   r4 = final kernel address *   r5 = start of this image *   r2 = end of malloc space (and therefore this image) * We basically want: *   r4 >= r2 -> OK *   r4 + image length <= r5 -> OK */cmp r4, r2    //r4现在在.text,而r2在.stack地址的后面,那个刚分配给解压函数的64K,显然r2>r4,所以不跳转。bhs wont_overwrite    //branch if higher or samesub r3, sp, r5 @ > compressed kernel size  得到映像大小add r0, r4, r3, lsl #2 @ allow for 4x expansion 将这个大小乘以4cmp r0, r5bls wont_overwrite
mov r5, r2 @ decompress after malloc spacemov r0, r5    //r5为映像解压后的起始地址,紧接着r2mov r3, r7    //r7中存放的是architecture IDbl decompress_kernel
add r0, r0, #127 + 128 @ alignment + stackbic r0, r0, #127 @ align the kernel length 清除127位,与上面的128字节对齐/*其中 decompress_kernel解压函数在arch/arm/boot/compressed/misc.c中。ulgdecompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p,  int arch_id){output_data= (uch *)output_start;/* Points to kernel start */解压后内核输出的起始地址free_mem_ptr= free_mem_ptr_p;     解压函数缓存的起始地址free_mem_ptr_end= free_mem_ptr_end_p;    解压函数缓存的结束地址__machine_arch_type= arch_id;    体系结构ID
arch_decomp_setup();
makecrc();putstr("Uncompressing Linux...");gunzip();putstr(" done, booting the kernel.\n");       //这个打印信息是不是很熟悉啊return output_ptr;}
*/此时,r0:解压后内核的长度,后存放映像解压后的起始地址    movr0, r5,重新指定为解压后内核的长度r1:解压函数缓存的起始地址r2:解压函数缓存的结束地址r3:原来存映像大小,后来存放体系结构ID     movr3, r7r4:内核执行地址r5:映像解压后的起始地址    movr5, r2r6:处理器IDr7:体系结构IDr8:标记列表地址r9-r14:corrupted所以decompress_kernel函数带入的参数就是r0-r3
                add r1, r5, r0@ end of decompressed kernel  r1:解压后内核代码的结束地址adr r2, reloc_start        //设定重定义的开始地址和结束地址ldr r3, LC1add r3, r2, r31: ldmia r2!, {r9 - r14} @ copy relocation code 拷贝内核重定位的代码,不至于被覆盖stmia r1!, {r9 - r14}ldmia r2!, {r9 - r14}stmia r1!, {r9 - r14}cmp r2, r3blo 1badd sp, r1, #128 @ relocate the stack 改变堆栈指针
bl cache_clean_flush    刷新cacheadd pc, r5, r0 @ call relocation code 唤醒内核重定义的代码
/* * We're not in danger of overwriting ourselves.  Do this the simple way. * * r4     = kernel execution address * r7     = architecture ID */wont_overwrite: movr0, r4        //假如内核映像没有被bootloader移动过,就会跳到此处mov r3, r7bl decompress_kernelb call_kernel

.align 5reloc_start: addr9, r5, r0    //r0+r5 = 解压后内核代码的结束地址加上128字节栈空间sub r9, r9, #128 @ do not copy the stackdebug_reloc_startmov r1, r41:.rept 4ldmia r5!, {r0, r2, r3, r10 - r14} @ relocate kernelstmia r1!, {r0, r2, r3, r10 - r14}.endr
cmp r5, r9blo 1badd sp, r1, #128 @ relocate the stackdebug_reloc_end
call_kernel: blcache_clean_flushbl cache_off        //关闭cachemov r0, #0 @ must be zero    清零r0mov r1, r7 @ restore architecture numbermov r2, r8 @ restore atags pointermov pc, r4 @ call kernel//内核映像zImage是由压缩后的内核piggy.o,加上一段初始化及解压功能的代码组成的。
linux/arch/arm/kernel/head.S是linux内核映像解压后执行的第一个文件。//PAGE_OFFSET = 0xc0000000; TEXT_OFFSET = 0x00008000;//PHYS_OFFSET = 0xa0000000;#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)#define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET)/*链接脚本文件arch/arm/kernel/vmlinux.lds指定了编译时程序段存放的位置。OUTPUT_ARCH(arm)ENTRY(stext)                //这里对应head.S中的ENTRY(stext)jiffies = jiffies_64;SECTIONS{ . = (0xc0000000) + 0x00008000; .text.head : {  _stext = .;  _sinittext = .;  *(.text.head) } .init : { /* Init code and data */   *(.init.text) *(.cpuinit.text) *(.meminit.text)  _einittext = .;  __proc_info_begin = .;   *(.proc.info.init)  __proc_info_end = .;  __arch_info_begin = .;   *(.arch.info.init)  __arch_info_end = .;  __tagtable_begin = .;   *(.taglist.init)  __tagtable_end = .;  . = ALIGN(16);  __setup_start = .;   *(.init.setup)  __setup_end = .;  __early_begin = .;   *(.early_param.init)  __early_end = .;  __initcall_start = .;……}*//*   .section是GNU ASM的语法。格式如下:    .section name[,"flags"[,@type]]   其中,name是必须的,flags是可选。    "ax"表示:a为section is allocatable,x为executable。*/.section ".text.head", "ax".type stext, %functionENTRY(stext)        //kernel的入口点函数//MSR:是ARM汇编指令,用来将数据copy到status register寄存器中。cpsr_c表示要操作msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode  禁止FIQ、IRQ,设定SVC模式@ and irqs disabledmrc p15, 0, r9, c0, c0 @ get processor id    //可以去查看arm CPU ID各字段的涵义bl __lookup_processor_type @ r5=procinfo r9=cpuid //检测cpu类型,如果支持,r5返回一个用来描述处理器结构体的地址,否则返回0.movs r10, r5 @ invalid processor (r5=0)?beq __error_p @ yes, error 'p'bl __lookup_machine_type @ r5=machinfo  //检测开发板类型,即machine ID,如果支持,r5返回一个用来描述开发板结构体的地址,否则返回0.movs r8, r5 @ invalid machine (r5=0)?beq __error_a @ yes, error 'a'bl __vet_atags    //检测bootloader传入的参数列表atags的合法性bl __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    //将列表__switch_data存到r13中,在head-common.S中。@ mmu has been enabledadr lr, __enable_mmu @ return (PIC) address    //使能mmuadd pc, r10, #PROCINFO_INITFUNC//r10中存放的基地址是从__lookup_processor_type中得到的,如上面movs r10, r5
ENTRY(secondary_startup)……第二个cpu的检测和设置。
下面主要来说说__lookup_processor_type__lookup_machine_type。它们都在/arch/arm/kernel/head-common.S实现。
__lookup_processor_type 检测CPU内核支持的,每一种CPU类型都由结构体proc_info_list来描述,在linux/include/asm-arm/procinfo.h定义struct proc_info_list {unsigned intcpu_val;unsigned intcpu_mask;unsigned long__cpu_mm_mmu_flags;/* used by head.S */unsigned long__cpu_io_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 processor*proc;struct cpu_tlb_fns*tlb;struct cpu_user_fns*user;struct cpu_cache_fns*cache;};对于我们的CPU来说,其对应的结构体在文件arch/arm/mm/proc-xsc3.S中.section ".proc.info.init", #alloc, #execinstr
.type __xsc3_proc_info,#object__xsc3_proc_info:.long 0x69056000.long 0xffffe000.long PMD_TYPE_SECT | \PMD_SECT_BUFFERABLE | \PMD_SECT_CACHEABLE | \PMD_SECT_AP_WRITE | \PMD_SECT_AP_READ.long PMD_TYPE_SECT | \PMD_SECT_AP_WRITE | \PMD_SECT_AP_READb __xsc3_setup.long cpu_arch_name.long cpu_elf_name.long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP.long cpu_xsc3_name.long xsc3_processor_functions.long v4wbi_tlb_fns.long xsc3_mc_user_fns.long xsc3_cache_fns.size __xsc3_proc_info, . - __xsc3_proc_info不同的proc_info_list结构被用来支持不同的CPU,它们都定义在“.proc.info.init”段中。在链接文件arch/arm/kernel/vmlinux.lds中可知,__proc_info_begin = .;   *(.proc.info.init)  __proc_info_end = .;所有CPU类型对应的被初始化的proc_info_list结构体都放在__proc_info_begin和__proc_info_end之间。
/* * Read processor ID register (CP#15, CR0), and look up in the linker-built * supported processor list.  Note that we can't use the absolute addresses * for the __proc_info lists since we aren't running with the MMU on * (and therefore, we are not in the correct address space).  We have to * calculate the offset. * * r9 = cpuid * Returns: * r3, r4, r6 corrupted * r5 = proc_info pointer in physical address space * r9 = cpuid (preserved) */.type __lookup_processor_type, %function__lookup_processor_type:adr r3, 3f        //r3:标记3处的物理地址  r7:标记3处的虚拟地址ldmda r3, {r5 - r7}    //过后减少装载    ldm是load multiple register的意思,它的作用是将[r3]对应的内存内容存储到r5,r6,r7寄存器中,每传递一次,r3递减4个字节sub r3, r3, r7 @ get offset between virt&phys    //得到虚拟地址和物理地址之间的offsetadd r5, r5, r3 @ convert virt addresses toadd r6, r6, r3 @ physical address space    将r5和r6中保存的虚拟地址转变为物理地址1: ldmia r5, {r3, r4} @ value, mask    //r3=cpu_val r4=cpu_maskand r4, r4, r9 @ mask wanted bitsteq r3, r4beq 2f    //如果匹配成功则返回//PROC_INFO_SZ (proc_info_list结构的长度,在这等于48),跳到下一个proc_info_list处add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list)cmp r5, r6    //判断是否已经到了结构体proc_info_list存放区域的末尾__proc_info_endblo 1bmov r5, #0 @ unknown processor2: mov pc, lr        //子程序返回
/* * This provides a C-API version of the above function. */ENTRY(lookup_processor_type)stmfd sp!, {r4 - r7, r9, lr}mov r9, r0bl __lookup_processor_typemov r0, r5ldmfd sp!, {r4 - r7, 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 __arch_info_begin.long __arch_info_end

__lookup_machine_type 检测开发板与lookup_processor_type类似,每一个CPU平台都可能有其不一样的结构体,描述这个平台的结构体是machine_desc。这个结构体在文件/include/asm/mach/arch.h中定义struct machine_desc {/* * Note! The first four elements are used * by assembler code in head.S, head-common.S */unsigned intnr;/* architecture number*/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_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);struct sys_timer*timer;/* system tick timer*/void (*init_machine)(void);};对应的结构在文件arch/arm/mach-pxa/littleton.cMACHINE_START(LITTLETON, "Marvell Form Factor Development Platform (aka Littleton)").phys_io = 0x40000000,.boot_params= 0xa0000100,.io_pg_offst= (io_p2v(0x40000000) >> 18) & 0xfffc,.map_io = pxa_map_io,.init_irq= pxa3xx_init_irq,.timer = &pxa_timer,.init_machine= littleton_init,MACHINE_END内核中对于每种支持的开发板都会使用宏MACHINE_START、MACHINE_END来定义一个machine_desc结构,宏MACHINE_START的具体定义也在文件/include/asm/mach/arch.h中。#define MACHINE_START(_type,_name) \static const struct machine_desc __mach_desc_##_type\ __used \ __attribute__((__section__(".arch.info.init"))) = {\.nr = MACH_TYPE_##_type, \.name = _name,
#define MACHINE_END \};
#endif/* * Lookup machine architecture in the linker-build list of architectures. * Note that we can't use the absolute addresses for the __arch_info * lists since we aren't running with the MMU on (and therefore, we are * not in the correct address space).  We have to calculate the offset. * *  r1 = machine architecture number * Returns: *  r3, r4, r6 corrupted *  r5 = mach_info pointer in physical address space */.type __lookup_machine_type, %function__lookup_machine_type:adr r3, 3bldmia r3, {r4, r5, r6}sub r3, r3, r4 @ get offset between virt&physadd r5, r5, r3 @ convert virt addresses toadd r6, r6, r3 @ physical address space1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine typeteq r3, r1 @ matches loader number?beq 2f @ foundadd r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desccmp r5, r6blo 1bmov r5, #0 @ unknown machine2: mov pc, lr
/* * This provides a C-API version of the above function. */ENTRY(lookup_machine_type)stmfd sp!, {r4 - r6, lr}mov r1, r0bl __lookup_machine_typemov r0, r5ldmfd sp!, {r4 - r6, pc}
__vet_atags  检测参数列表//检查bootloader传入的参数列表atags的合法性。先看看结构体在include/asm-arm/setup.hstruct tag {struct tag_header hdr;union {struct tag_corecore;struct tag_mem32mem;struct tag_videotextvideotext;struct tag_ramdiskramdisk;struct tag_initrdinitrd;struct tag_serialnrserialnr;struct tag_revisionrevision;struct tag_videolfbvideolfb;struct tag_cmdlinecmdline;
/* * Acorn specific */struct tag_acornacorn;
/* * DC21285 specific */struct tag_memclkmemclk;} u;};struct tag_header {__u32 size;__u32 tag;};//其中 size:表示整个 tag 结构体的大小(用字的个数来表示,而不是字节的个数),等于tag_header的大小加上 u联合体的大小。#define ATAG_CORE0x54410001        atag开始#define ATAG_NONE0x00000000        atag结束#define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
/* Determine validity of the r2 atags pointer.  The heuristic requires * that the pointer be aligned, in the first 16k of physical RAM and * that the ATAG_CORE marker is first and present.  Future revisions * of this function may be more lenient with the physical address and * may also be able to move the ATAGS block if necessary. * * r8  = machinfo * * Returns: *  r2 either valid atags pointer, or zero *  r5, r6 corrupted */
.type __vet_atags, %function__vet_atags:tst r2, #0x3 @ aligned?    //r2指向该参数链表的起始位置,此处判断它是否字对齐bne 1f
ldr r5, [r2, #0] @ is first tag ATAG_CORE? 获取第一个tag结构的sizesubs r5, r5, #ATAG_CORE_SIZE        //比较长度是否有效bne 1fldr r5, [r2, #4]        //获取第一个tag结构体的标记ldr r6, =ATAG_COREcmp r5, r6        //判断第一个tag标记是不是ATAG_COREbne 1f
mov pc, lr @ atag pointer is ok
1: mov r2, #0mov pc, lr

__create_page_tables 建立页表/* * 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 */.type __create_page_tables, %function__create_page_tables:pgtbl r4 @ page table address 转换表的物理基地址
/* * Clear the 16K level 1 swapper page table  //为内核代码存储区域创建页表,首先将内核起始地址-0x4000~内核起始地址之间的16K 存储器清0,将创建的页表存于此处。 */mov r0, r4mov r3, #0add r6, r0, #0x40001: str r3, [r0], #4str r3, [r0], #4str r3, [r0], #4str r3, [r0], #4teq r0, r6bne 1b
ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags    //从proc_info_list结构中获取字段__cpu_mm_mmu_flags,该字段包含了存储空间访问权限等
/* * 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, pc, 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]!    r0存放转换表的起始地址ldr r6, =(KERNEL_END - 1)     //获取内核结束地址add r0, r0, #4    //计算第一个地址条目存放的地址add r6, r4, r6, lsr #18    //计算最好一个地址条目存放的位置1: cmp r0, r6add r3, r3, #1 << 20strls r3, [r0], #4bls 1b
#ifdef CONFIG_XIP_KERNEL    //如果是XIP就进行以下映射,这只是将内核代码存储的空间重新映射/* * 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 #181: 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.映射开始的1M空间 */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_LL            //下面是为了调试而做的相关映射,可以跳过ldr 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, r71: 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.ltorg
setup 禁止cache在head.S中add pc, r10, #PROCINFO_INITFUNC  就是调用__xsc3_setup。.type __xsc3_setup, #function__xsc3_setup:mov r0, #PSR_F_BIT|PSR_I_BIT|SVC_MODEmsr cpsr_c, r0mcr p15, 0, ip, c7, c7, 0 @ invalidate L1 caches and BTBmcr p15, 0, ip, c7, c10, 4 @ data write barriermcr p15, 0, ip, c7, c5, 4 @ prefetch flushmcr p15, 0, ip, c8, c7, 0 @ invalidate I and D TLBs#if L2_CACHE_ENABLEorr r4, r4, #0x18 @ cache the page table in L2#endifmcr p15, 0, r4, c2, c0, 0 @ load page table pointer
mov r0, #0 @ don't allow CP accessmcr p15, 0, r0, c15, c1, 0 @ write CP access register
mrc p15, 0, r0, c1, c0, 1 @ get auxiliary control regand r0, r0, #2 @ preserve bit P bit setting#if L2_CACHE_ENABLEorr r0, r0, #(1 << 10) @ enable L2 for LLR cache#endifmcr p15, 0, r0, c1, c0, 1 @ set auxiliary control reg
adr r5, xsc3_crvalldmia r5, {r5, r6}mrc p15, 0, r0, c1, c0, 0 @ get control registerbic r0, r0, r5 @ ..V. ..R. .... ..A.orr r0, r0, r6 @ ..VI Z..S .... .C.M (mmu)@ ...I Z..S .... .... (uc)#if L2_CACHE_ENABLEorr  r0, r0, #0x04000000 @ L2 enable#endifmov pc, lr     //跳到adr lr, __enable_mmu 
.size __xsc3_setup, . - __xsc3_setup
.type xsc3_crval, #objectxsc3_crval:crval clear=0x04002202, mmuset=0x00003905, ucset=0x00001900
__INITDATA
__enable_mmu  使能mmu/* * Setup common bits before finally enabling the MMU.  Essentially * this is just loading the page table pointer and domain access * registers. */.type __enable_mmu, %function__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        //禁止数据cache#endif#ifdef CONFIG_CPU_BPREDICT_DISABLEbic r0, r0, #CR_Z#endif#ifdef CONFIG_CPU_ICACHE_DISABLEbic r0, r0, #CR_I        //禁止指令cache#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 register    //将访问权限写入协处理器mcr p15, 0, r4, c2, c0, 0 @ load page table pointer        //将页表基地址写入基址寄存器C2b __turn_mmu_on
/* * 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.type __turn_mmu_on, %function__turn_mmu_on:mov r0, r0mcr p15, 0, r0, c1, c0, 0 @ write control reg    写入懂控制寄存器,打开mmu,打开cachemrc p15, 0, r3, c0, c0, 0 @ read id reg        读取ID寄存器mov r3, r3mov r3, r3            //空操作,等待前面所取得的指令得以执行mov pc, r13        //程序跳转 ldr r13, __switch_data
__switch_data  数据转换在文件linux/arch/arm/kernel/head-common.S中:.type __switch_data, %object        //定义一个对象__switch_data:.long __mmap_switched        //跳转到__mmap_switched.long __data_loc @ r4    数据存放地址.long __data_start @ r5    数据开始地址.long __bss_start @ r6    bss开始地址.long _end @ r7    bss结束地址,也是内核结束地址.long processor_id @ r4.long __machine_arch_type @ r5.long __atags_pointer @ r6.long cr_alignment @ r7.long init_thread_union + THREAD_START_SP @ sp
/* * The following fragment of code is executed with the MMU on in MMU mode, * and uses absolute addresses; this is not position independent. * *  r0  = cp#15 control register *  r1  = machine ID *  r2  = atags pointer *  r9  = processor ID */.type __mmap_switched, %function__mmap_switched:adr r3, __switch_data + 4
ldmia r3!, {r4, r5, r6, r7}cmp r4, r5 @ Copy data segment if needed1: cmpne r5, r6ldrne fp, [r4], #4strne fp, [r5], #4bne 1b
mov fp, #0 @ Clear BSS (and zero fp)1: cmp r6, r7strcc fp, [r6],#4bcc 1b
ldmia r3, {r4, r5, r6, r7, sp}str r9, [r4] @ Save processor IDstr r1, [r5] @ Save machine typestr r2, [r6] @ Save atags pointerbic r4, r0, #CR_A @ Clear 'A' bitstmia r7, {r0, r4} @ Save control register valuesb start_kernel
小结:第一阶段主要做以下两个步骤:1.连接内核时使用的虚拟地址,所以要设置页表,使能mmu,之前要确定是否支持cpu和开发板2.调用C函数start_kernel的准备工作:复制数据段,清除bss段,设置栈指针,保存processor ID,保存machine type,调用start_kernel。第一阶段到此结束。


热点排行