内核启动学习笔记

内核编译:
1 make menuconfig 生成.config文件和autoconf.h
                      .config会生成auto.conf
 
2 make

查看配置项:CONFIG_DM9000定义的位置
[root@localhost linux-2.6.38]# grep "CONFIG_DM9000" * -nR
 1 c源码中定义 arch/arm/mach-s3c64xx/mach-mini6410.c CONFIG_DM9000(宏定义)
 2 Makefile中 drivers/net/Makefile
  子目录下的makefile:obj-$(CONFIG_DM9000) += dm9000.o
  CONFIG_DM9000值起以下作用:
   obj-y+=xxx.o  表示最终会编译进内核
   obj-m+=yyy.o  表最终会编译成可加载模块.ko
 3 include/config/auto.conf,由.config生成此文件, CONFIG_DM9000的值在此定义
  config_linux_mini6410:778:CONFIG_DM9000=y  (供子目录的makefile使用)
 4 include/linux/autoconf.h
 
=======================================================================
      分析makefile
===========================================================================
注:documentation/kbuild/Makefile.txt 需学习
linux内核中makefile的分类
  1 项层Makefile
  2 .config     配置文件,在配置内核时生成,所有makefile都根据此文件来决定使用哪些文件
  3 arch/$(ARCH)/Makefile 对应体系的makefile,决定哪些体系结构的相关文件参与内核的生成
  4 scripts/Makefile.*   makefile共用的通用规则、脚本
  5 kbuild Makefile   各级子目录下的makefile,被上层makefile调用编译当前目录下文件
     编译进内核
     编译为可加载模块
     两个c文件编译为同一个可加载模块:  obj-m+=ab.o
                       ab-objs :=a.o b.o


make uImage时会跳到arch/arm/Makefile中
   示例代码如下:
   #uImage依赖于vmlinux
   zImage Image xipImage bootpImage uImage: vmlinux
    $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
    ......
    #Default value
    head-y  := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
    
    
    
在顶层Makefile中可以看到如下定义:(会将子目录makefile包含进来)
    #ARCH  ?= $(SUBARCH)
    ARCH  ?= arm
    CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)
    
    # Architecture as present in compile.h
    UTS_MACHINE  := $(ARCH)    
    SRCARCH  := $(ARCH)
    .........
   # used for 'make defconfig'
    include $(srctree)/arch/$(SRCARCH)/Makefile
    
    .........
    #以下两句=init/built-in.o
    init-y  := init/
    init-y  := $(patsubst %/, %/built-in.o, $(init-y))
    
    #以下三句=usr/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o
    core-y  := usr/    
    core-y  += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
    core-y  := $(patsubst %/, %/built-in.o, $(core-y))
    
    #以下语句=lib/lib.a lib/built-in.o
    libs-y  := lib/
    libs-y1  := $(patsubst %/, %/lib.a, $(libs-y))
    libs-y2  := $(patsubst %/, %/built-in.o, $(libs-y))
    libs-y  := $(libs-y1) $(libs-y2)
    
    
    #以下语句=drivers/built-in.o sound/built-in.o firmware/built-in.o
    drivers-y := drivers/ sound/ firmware/
    drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
    
    #以下语句=net/built-in.o
    net-y  := net/
    net-y  := $(patsubst %/, %/built-in.o, $(net-y))    
    
    #head-y定义在子目录中
    vmlinux-init := $(head-y) $(init-y)
    vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
    vmlinux-all  := $(vmlinux-init) $(vmlinux-main)
    vmlinux-lds  := arch/$(SRCARCH)/kernel/vmlinux.lds        
    
    # vmlinux image - including updated kernel symbols
     vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE   

    #查看以上内空是怎么组织进内核:1 查看makefile
                    2 直接编译 make V=1
                      查看最后一条命令


.config生成:autoconfig.h (供原码使用)
       auto.conf
在顶层Makefile中可以看到定义如下:
   # Read in config
   -include include/config/auto.conf

 

 

 arm-linux-ld -EL  -p --no-undefined -X --build-id -o vmlinux -T //生成vmlinux文件
    arch/arm/kernel/vmlinux.lds  //链接文件
    arch/arm/kernel/head.o     //第一个文件
    arch/arm/kernel/init_task.o
     //init-y
    init/built-in.o --start-group
    //core-y
    usr/built-in.o  arch/arm/nwfpe/built-in.o  arch/arm/vfp/built-in.o  arch/arm/kernel/built-in.o  arch/arm/mm/built-in.o  arch/arm/common/built-in.o  arch/arm/mach-s3c64xx/built-in.o  arch/arm/plat-samsung/built-in.o  kernel/built-in.o  mm/built-in.o  fs/built-in.o  ipc/built-in.o  security/built-in.o  crypto/built-in.o  block/built-in.o 
    //libs-y
    arch/arm/lib/lib.a  lib/lib.a  arch/arm/lib/built-in.o  lib/built-in.o
    //drivers-y
    drivers/built-in.o  sound/built-in.o  firmware/built-in.o
    //net-y
    net/built-in.o --end-group .tmp_kallsyms2.o

 

从以上分析可以得出:
1 第一个文件:arch/arm/kernel/head.S
2 链接角本:arch/arm/kernel/vmlinux.lds.S


内核所作工作:
1 处理uboot传入的参数(机器id,启动参数)
  head.S所作的事情
  a 检查是不支持该cpu架构
   b 判断是否支持该单板
   c 建立一级页表
   d 使能mmu
   e start_kernel第一个c函数
  
  
   挂载 根文件系统
    start_kernel
     setup_arch              //解析uboot传入的启动参数
     setup_command_line   //解析uboot传入的启动参数
     parse_early_param
      do_early_param  //调用early函数,命令行参数不是在此调用
     unknown_bootoption
      obsolete_checksetup //调用非early函数
     rest_init
      kernel_init (命令行已打开)
       prepare_namespace
        mount_root//挂载根文件系统
       init_post//根文件系统挂载完成后,执行应用程序
       
       
       
   命令行参数的处理:
    static int __init root_dev_setup(char *line)
   {
    strlcpy(saved_root_name, line, sizeof(saved_root_name));
    return 1;
   }
   
   __setup("root=", root_dev_setup);
  
    //include/linux/init.h文件中定义  
   #define __setup(str, fn)     \
     __setup_param(str, fn, fn, 0)   
      
      ....
     #define __setup_param(str, unique_id, fn, early)   \
   static const char __setup_str_##unique_id[] __initconst \
    __aligned(1) = str; \
   static struct obs_kernel_param __setup_##unique_id \
    __used __section(.init.setup)   \    //强行设置为.init.setup段,定义在vmlinux.S
    __attribute__((aligned((sizeof(long))))) \
    = { __setup_str_##unique_id, fn, early }
      
     
     //查看哪些函数会调用.init.setup中的内容,即从__setup_start到__setup_end
     //有以下两个函数调用
     1 static int __init obsolete_checksetup(char *line)
    {
     const struct obs_kernel_param *p;
     int had_early_param = 0;
    
     p = __setup_start;
     do {
      int n = strlen(p->str);
      if (!strncmp(line, p->str, n)) {
       if (p->early) {
        /* Already done in parse_early_param?
         * (Needs exact match on param part).
         * Keep iterating, as we can have early
         * params and __setups of same names 8( */
        if (line[n] == '\0' || line[n] == '=')
         had_early_param = 1;
       } else if (!p->setup_func) {
        printk(KERN_WARNING "Parameter %s is obsolete,"
               " ignored\n", p->str);
        return 1;
       } else if (p->setup_func(line + n))
        return 1;
      }
      p++;
     } while (p < __setup_end);
    
     return had_early_param;
    }
     
     
     
     2 /* Check for early params. */
    static int __init do_early_param(char *param, char *val)
    {
     const struct obs_kernel_param *p;
    
     for (p = __setup_start; p < __setup_end; p++) {
      if ((p->early && strcmp(param, p->str) == 0) ||
          (strcmp(param, "console") == 0 &&
           strcmp(p->str, "earlycon") == 0)
      ) {
       if (p->setup_func(val) != 0)
        printk(KERN_WARNING
               "Malformed early option '%s'\n", param);
      }
     }
     /* We accept everything at this stage. */
     return 0;
    }

  
内核的最终目的: 启动应用程序

分区表定义在: /arch/arm/mach-s3c64xx/Mach-mini6410.c文件中

=====================================================================
     arch/arm/kernel/  head.s文件
====================================================================
/*
 *  linux/arch/arm/kernel/head.S
 *
 *  Copyright (C) 1994-2002 Russell King
 *  Copyright (c) 2003 ARM Limited
 *  All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 *  Kernel startup code for all 32-bit CPUs
 */
#include <linux/linkage.h>
#include <linux/init.h>

#include <asm/assembler.h>
#include <asm/domain.h>
#include <asm/ptrace.h>
#include <asm/asm-offsets.h>
#include <asm/memory.h>
#include <asm/thread_info.h>
#include <asm/system.h>

#ifdef CONFIG_DEBUG_LL
#include <mach/debug-macro.S>
#endif

#if (PHYS_OFFSET & 0x001fffff)
#error "PHYS_OFFSET must be at an even 2MiB boundary!"
#endif

#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
#define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET)


/*
 * swapper_pg_dir is the virtual address of the initial page table.
 * We place the page tables 16K below KERNEL_RAM_VADDR.  Therefore, we must
 * make sure that KERNEL_RAM_VADDR is correctly set.  Currently, we expect
 * the least significant 16 bits to be 0x8000, but we could probably
 * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.
 */
#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000
#error KERNEL_RAM_VADDR must start at 0xXXXX8000
#endif

 .globl swapper_pg_dir
 .equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000

 .macro pgtbl, rd
 ldr \rd, =(KERNEL_RAM_PADDR - 0x4000)
 .endm

#ifdef CONFIG_XIP_KERNEL
#define KERNEL_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
#define KERNEL_END _edata_loc
#else
#define KERNEL_START KERNEL_RAM_VADDR
#define KERNEL_END _end
#endif

/*
 * Kernel startup entry point.
 * ---------------------------
 *
 * This is normally called from the decompressor code.  The requirements
 * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
 * r1 = machine nr, r2 = atags pointer.
 *
 * This code is mostly position independent, so if you link the kernel at
 * 0xc0008000, you call this at __pa(0xc0008000).
 *
 * See linux/arch/arm/tools/mach-types for the complete list of machine
 * numbers for r1.
 *
 * We're trying to keep crap to a minimum; DO NOT add any machine specific
 * crap here - that's what the boot loader (or in extreme, well justified
 * circumstances, zImage) is for.
 */
 __HEAD
ENTRY(stext)
 /*1 检查内核是否支持该处理器*/
 setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
      @ and irqs disabled
 
 mrc p15, 0, r9, c0, c0  @ get processor id
 bl __lookup_processor_type  @ r5=procinfo r9=cpuid
 movs r10, r5    @ invalid processor (r5=0)?
 THUMB( it eq )  @ force fixup-able long branch encoding
 beq __error_p   @ yes, error 'p'
 /*检查机器id */
 bl __lookup_machine_type  @ r5=machinfo 定义在head-common.S中
 movs r8, r5    @ invalid machine (r5=0)?
 THUMB( it eq )  @ force fixup-able long branch encoding
 beq __error_a   @ yes, error 'a'

 /*
  * r1 = machine no, r2 = atags,
  * r8 = machinfo, r9 = cpuid, r10 = procinfo
  */
 bl __vet_atags
#ifdef CONFIG_SMP_ON_UP
 bl __fixup_smp
#endif
 bl __create_page_tables  @2 建立页表

 /*
  * 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, =__mmap_switched  @ 4 address to jump to after  跳转到head_common.S执行
      @ mmu has been enabled
 adr lr, BSYM(1f)   @ return (PIC) address
 ARM( add pc, r10, #PROCINFO_INITFUNC )
 THUMB( add r12, r10, #PROCINFO_INITFUNC )
 THUMB( mov pc, r12    )
1: b __enable_mmu  @3 使能mmu
ENDPROC(stext)
 .ltorg

/*
 * 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, r5-r7 corrupted
 *  r4 = physical page table address
 */
__create_page_tables:
 pgtbl r4    @ page table address

 /*
  * Clear the 16K level 1 swapper page table
  */
 mov r0, r4
 mov r3, #0
 add r6, r0, #0x4000
1: str r3, [r0], #4
 str r3, [r0], #4
 str r3, [r0], #4
 str r3, [r0], #4
 teq r0, r6
 bne 1b

 ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags

 /*
  * Create identity mapping to cater for __enable_mmu.
  * This identity mapping will be removed by paging_init().
  */
 adr r0, __enable_mmu_loc
 ldmia r0, {r3, r5, r6}
 sub r0, r0, r3   @ virt->phys offset
 add r5, r5, r0   @ phys __enable_mmu
 add r6, r6, r0   @ phys __enable_mmu_end
 mov r5, r5, lsr #20
 mov r6, r6, lsr #20

1: orr r3, r7, r5, lsl #20  @ flags + kernel base
 str r3, [r4, r5, lsl #2]  @ identity mapping
 teq r5, r6
 addne r5, r5, #1   @ next section
 bne 1b

 /*
  * Now setup the pagetables for our kernel direct
  * mapped region.
  */
 mov r3, pc
 mov r3, r3, lsr #20
 orr r3, r7, r3, lsl #20
 add r0, r4,  #(KERNEL_START & 0xff000000) >> 18
 str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
 ldr r6, =(KERNEL_END - 1)
 add r0, r0, #4
 add r6, r4, r6, lsr #18
1: cmp r0, r6
 add r3, r3, #1 << 20
 strls r3, [r0], #4
 bls 1b

#ifdef CONFIG_XIP_KERNEL
 /*
  * 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)
 .endif
 add r0, r4,  #(KERNEL_RAM_VADDR & 0xff000000) >> 18
 str r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
 ldr r6, =(_end - 1)
 add r0, r0, #4
 add r6, r4, r6, lsr #18
1: cmp r0, r6
 add r3, r3, #1 << 20
 strls r3, [r0], #4
 bls 1b
#endif

 /*
  * Then map first 1MB of ram in case it contains our boot params.
  */
 add r0, r4, #PAGE_OFFSET >> 18
 orr r6, r7, #(PHYS_OFFSET & 0xff000000)
 .if (PHYS_OFFSET & 0x00f00000)
 orr r6, r6, #(PHYS_OFFSET & 0x00f00000)
 .endif
 str r6, [r0]

#ifdef CONFIG_DEBUG_LL
#ifndef CONFIG_DEBUG_ICEDCC
 /*
  * Map in IO space for serial debugging.
  * This allows debug messages to be output
  * via a serial console before paging_init.
  */
 addruart r7, r3

 mov r3, r3, lsr #20
 mov r3, r3, lsl #2

 add r0, r4, r3
 rsb r3, r3, #0x4000   @ PTRS_PER_PGD*sizeof(long)
 cmp r3, #0x0800   @ limit to 512MB
 movhi r3, #0x0800
 add r6, r0, r3
 mov r3, r7, lsr #20
 ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
 orr r3, r7, r3, lsl #20
1: str r3, [r0], #4
 add r3, r3, #1 << 20
 teq r0, r6
 bne 1b

#else /* CONFIG_DEBUG_ICEDCC */
 /* we don't need any serial debugging mappings for ICEDCC */
 ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
#endif /* !CONFIG_DEBUG_ICEDCC */

#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 >> 18
 orr r3, r7, #0x7c000000
 str 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 >> 18
 orr r3, r7, #0x02000000
 str r3, [r0]
 add r0, r4, #0xd8000000 >> 18
 str r3, [r0]
#endif
#endif
 mov pc, lr
ENDPROC(__create_page_tables)
 .ltorg
 .align
__enable_mmu_loc:
 .long .
 .long __enable_mmu
 .long __enable_mmu_end

#if defined(CONFIG_SMP)
 __CPUINIT
ENTRY(secondary_startup)
 /*
  * Common entry point for secondary CPUs.
  *
  * Ensure that we're in SVC mode, and IRQs are disabled.  Lookup
  * the processor type - there is no need to check the machine type
  * as it has already been validated by the primary processor.
  */
 setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9
 mrc p15, 0, r9, c0, c0  @ get processor id
 bl __lookup_processor_type
 movs r10, r5    @ invalid processor?
 moveq r0, #'p'   @ yes, error 'p'
 THUMB( it eq )  @ force fixup-able long branch encoding
 beq __error_p

 /*
  * Use the page tables supplied from  __cpu_up.
  */
 adr r4, __secondary_data
 ldmia r4, {r5, r7, r12}  @ address to jump to after
 sub r4, r4, r5   @ mmu has been enabled
 ldr r4, [r7, r4]   @ get secondary_data.pgdir
 adr lr, BSYM(__enable_mmu)  @ return address
 mov r13, r12   @ __secondary_switched address
 ARM( add pc, r10, #PROCINFO_INITFUNC ) @ initialise processor
        @ (return control reg)
 THUMB( add r12, r10, #PROCINFO_INITFUNC )
 THUMB( mov pc, r12    )
ENDPROC(secondary_startup)

 /*
  * r6  = &secondary_data
  */
ENTRY(__secondary_switched)
 ldr sp, [r7, #4]   @ get secondary_data.stack
 mov fp, #0
 b secondary_start_kernel
ENDPROC(__secondary_switched)

 .align

 .type __secondary_data, %object
__secondary_data:
 .long .
 .long secondary_data
 .long __secondary_switched
#endif /* defined(CONFIG_SMP) */

 

/* 
 * Setup common bits before finally enabling the MMU.  Essentially
 * this is just loading the page table pointer and domain access
 * registers.
 *
 *  r0  = cp#15 control register
 *  r1  = machine ID
 *  r2  = atags pointer
 *  r4  = page table pointer
 *  r9  = processor ID
 *  r13 = *virtual* address to jump to upon completion
 */
__enable_mmu:
#ifdef CONFIG_ALIGNMENT_TRAP
 orr r0, r0, #CR_A
#else
 bic r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
 bic r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
 bic r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
 bic r0, r0, #CR_I
#endif
 mov 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
 b __turn_mmu_on
ENDPROC(__enable_mmu)

/*
 * 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
 *  r1  = machine ID
 *  r2  = atags pointer
 *  r9  = processor ID
 *  r13 = *virtual* address to jump to upon completion
 *
 * other registers depend on the function called upon completion
 */
 .align 5
__turn_mmu_on:
 mov r0, r0
 mcr p15, 0, r0, c1, c0, 0  @ write control reg
 mrc p15, 0, r3, c0, c0, 0  @ read id reg
 mov r3, r3
 mov r3, r13
 mov pc, r3
__enable_mmu_end:
ENDPROC(__turn_mmu_on)


#ifdef CONFIG_SMP_ON_UP
 __INIT
__fixup_smp:
 and r3, r9, #0x000f0000 @ architecture version
 teq r3, #0x000f0000  @ CPU ID supported?
 bne __fixup_smp_on_up @ no, assume UP

 bic r3, r9, #0x00ff0000
 bic r3, r3, #0x0000000f @ mask 0xff00fff0
 mov r4, #0x41000000
 orr r4, r4, #0x0000b000
 orr r4, r4, #0x00000020 @ val 0x4100b020
 teq r3, r4   @ ARM 11MPCore?
 moveq pc, lr   @ yes, assume SMP

 mrc p15, 0, r0, c0, c0, 5 @ read MPIDR
 and r0, r0, #0xc0000000 @ multiprocessing extensions and
 teq r0, #0x80000000  @ not part of a uniprocessor system?
 moveq pc, lr   @ yes, assume SMP

__fixup_smp_on_up:
 adr r0, 1f
 ldmia r0, {r3 - r5}
 sub r3, r0, r3
 add r4, r4, r3
 add r5, r5, r3
 b __do_fixup_smp_on_up
ENDPROC(__fixup_smp)

 .align
1: .word .
 .word __smpalt_begin
 .word __smpalt_end

 .pushsection .data
 .globl smp_on_up
smp_on_up:
 ALT_SMP(.long 1)
 ALT_UP(.long 0)
 .popsection
#endif

 .text
__do_fixup_smp_on_up:
 cmp r4, r5
 movhs pc, lr
 ldmia r4!, {r0, r6}
 ARM( str r6, [r0, r3] )
 THUMB( add r0, r0, r3 )
#ifdef __ARMEB__
 THUMB( mov r6, r6, ror #16 ) @ Convert word order for big-endian.
#endif
 THUMB( strh r6, [r0], #2 ) @ For Thumb-2, store as two halfwords
 THUMB( mov r6, r6, lsr #16 ) @ to be robust against misaligned r3.
 THUMB( strh r6, [r0] )
 b __do_fixup_smp_on_up
ENDPROC(__do_fixup_smp_on_up)

ENTRY(fixup_smp)
 stmfd sp!, {r4 - r6, lr}
 mov r4, r0
 add r5, r0, r1
 mov r3, #0
 bl __do_fixup_smp_on_up
 ldmfd sp!, {r4 - r6, pc}
ENDPROC(fixup_smp)

#include "head-common.S"

====================================================================
      arch/arm/kernel/  head-common.S
===================================================================
/*
 *  linux/arch/arm/kernel/head-common.S
 *
 *  Copyright (C) 1994-2002 Russell King
 *  Copyright (c) 2003 ARM Limited
 *  All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#define ATAG_CORE 0x54410001
#define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
#define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2)

/*
 * Exception handling.  Something went wrong and we can't proceed.  We
 * ought to tell the user, but since we don't have any guarantee that
 * we're even running on the right architecture, we do virtually nothing.
 *
 * If CONFIG_DEBUG_LL is set we try to print out something about the error
 * and hope for the best (useful if bootloader fails to pass a proper
 * machine ID for example).
 */
 __HEAD
__error_a:
#ifdef CONFIG_DEBUG_LL
 mov r4, r1    @ preserve machine ID
 adr r0, str_a1
 bl printascii
 mov r0, r4
 bl printhex8
 adr r0, str_a2
 bl printascii
 adr r3, __lookup_machine_type_data
 ldmia r3, {r4, r5, r6}  @ get machine desc list
 sub r4, r3, r4   @ get offset between virt&phys
 add r5, r5, r4   @ convert virt addresses to
 add r6, r6, r4   @ physical address space
1: ldr r0, [r5, #MACHINFO_TYPE] @ get machine type
 bl printhex8
 mov r0, #'\t'
 bl printch
 ldr     r0, [r5, #MACHINFO_NAME] @ get machine name
 add r0, r0, r4
 bl printascii
 mov r0, #'\n'
 bl printch
 add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
 cmp r5, r6
 blo 1b
 adr r0, str_a3
 bl printascii
 b __error
ENDPROC(__error_a)

str_a1: .asciz "\nError: unrecognized/unsupported machine ID (r1 = 0x"
str_a2: .asciz ").\n\nAvailable machine support:\n\nID (hex)\tNAME\n"
str_a3: .asciz "\nPlease check your kernel config and/or bootloader.\n"
 .align
#else
 b __error
#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
 */
__lookup_machine_type:
 adr r3, __lookup_machine_type_data   @r3的地址=__lookup_machine_type_data 此时mmu还未启动,所以是物理地址
 ldmia r3, {r4, r5, r6}      @ r4=“.”(虚拟地址)   r5=__arch_info_begin  r6=__arch_info_end
 sub r3, r3, r4   @ get offset between virt&phys 虚拟地址和物理地址的偏差
 add r5, r5, r3   @ convert virt addresses to  r5+偏差=物理地址
 add r6, r6, r3   @ physical address space     r6+偏差=物理地址
1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type
 teq r3, r1    @ matches loader number?
 beq 2f    @ found
 add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
 cmp r5, r6
 blo 1b
 mov r5, #0    @ unknown machine
2: mov pc, lr
ENDPROC(__lookup_machine_type)

/*
 * Look in arch/arm/kernel/arch.[ch] for information about the
 * __arch_info structures.
 */
 .align 2
 .type __lookup_machine_type_data, %object
__lookup_machine_type_data:
 .long .
 .long __arch_info_begin   @定义在arch/arm/kernel/vmlinux.lds.S中
 .long __arch_info_end      @定义在arch/arm/kernel/vmlinux.lds.S中
 .size __lookup_machine_type_data, . - __lookup_machine_type_data
 #############################################################################
 /*  __arch_info_begin = .;
  *  *(.arch.info.init)     //.arch.info.init定义在arch/arm/include/asm/mach/arch.h中
  *  __arch_info_end = .;
  */
 // 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    \
     };


      //arch/arm/mach-s3c64xx/mach-mini6410.c的部分内容如下:
     MACHINE_START(MINI6410, "MINI6410")
      /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
      .boot_params = S3C64XX_PA_SDRAM + 0x100,
     
      .init_irq = s3c6410_init_irq,
      .map_io  = mini6410_map_io,
      .init_machine = mini6410_machine_init,
      .timer  = &s3c24xx_timer,
     MACHINE_END
    
    
     //以上宏展开后为:(机器描述的结构体)
     //该段就被放到指定的段位置去.arch.info.init
     #define MACHINE_START(_type,_name)   \
     static const struct machine_desc __mach_desc_MINI6410 \
      __used       \
      __attribute__((__section__(".arch.info.init"))) = { \
      .nr  = MACH_TYPE_MINI6410,  \
      .name  = "MINI6410",
     /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
      .boot_params = S3C64XX_PA_SDRAM + 0x100,
     
      .init_irq = s3c6410_init_irq,
      .map_io  = mini6410_map_io,
      .init_machine = mini6410_machine_init,
      .timer  = &s3c24xx_timer,
     };


    
      /*机器描述的结构体*/
      struct machine_desc {
       unsigned int  nr;  /* architecture number */
       const char  *name;  /* architecture name */
       unsigned long  boot_params; /* tagged list  */
      
       unsigned int  nr_irqs; /* number of IRQs */
      
       unsigned int  video_start; /* start of video RAM */
       unsigned int  video_end; /* end of video RAM */
      
       unsigned int  reserve_lp0 :1; /* never has lp0 */
       unsigned int  reserve_lp1 :1; /* never has lp1 */
       unsigned int  reserve_lp2 :1; /* never has lp2 */
       unsigned int  soft_reboot :1; /* soft reboot  */
       void   (*fixup)(struct machine_desc *,
            struct tag *, char **,
            struct meminfo *);
       void   (*reserve)(void);/* reserve mem blocks */
       void   (*map_io)(void);/* IO mapping function */
       void   (*init_early)(void);
       void   (*init_irq)(void);
       struct sys_timer *timer;  /* system tick timer */
       void   (*init_machine)(void);
      #ifdef CONFIG_MULTI_IRQ_HANDLER
       void   (*handle_irq)(struct pt_regs *);
      #endif
      };
       
 
  #####################################################################
 
 

/* 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
 */
__vet_atags:
 tst r2, #0x3   @ aligned?
 bne 1f

 ldr r5, [r2, #0]   @ is first tag ATAG_CORE?
 cmp r5, #ATAG_CORE_SIZE
 cmpne r5, #ATAG_CORE_SIZE_EMPTY
 bne 1f
 ldr r5, [r2, #4]
 ldr r6, =ATAG_CORE
 cmp r5, r6
 bne 1f

 mov pc, lr    @ atag pointer is ok

1: mov r2, #0
 mov pc, lr
ENDPROC(__vet_atags)

/*
 * 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
 */
 __INIT
__mmap_switched: @mmap_switched所作操作
 adr r3, __mmap_switched_data

 ldmia r3!, {r4, r5, r6, r7}
 cmp r4, r5    @ Copy data segment if needed 1复制数据段
1: cmpne r5, r6
 ldrne fp, [r4], #4
 strne fp, [r5], #4
 bne 1b

 mov fp, #0    @ Clear BSS (and zero fp) 2 清bss段
1: cmp r6, r7
 strcc fp, [r6],#4       @设置栈指针
 bcc 1b

 ARM( ldmia r3, {r4, r5, r6, r7, sp})
 THUMB( ldmia r3, {r4, r5, r6, r7} )
 THUMB( ldr sp, [r3, #16]  )        @保存机器信息
 str r9, [r4]   @ Save processor ID
 str r1, [r5]   @ Save machine type
 str r2, [r6]   @ Save atags pointer
 bic r4, r0, #CR_A   @ Clear 'A' bit
 stmia r7, {r0, r4}   @ Save control register values
 b start_kernel  @跳转到start_kernel(第一个c函数),定义在/init/main.c 中
ENDPROC(__mmap_switched)

 .align 2
 .type __mmap_switched_data, %object
__mmap_switched_data:
 .long __data_loc   @ r4
 .long _sdata    @ r5
 .long __bss_start   @ r6
 .long _end    @ r7
 .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
 .size __mmap_switched_data, . - __mmap_switched_data

/*
 * This provides a C-API version of __lookup_machine_type
 */
ENTRY(lookup_machine_type)
 stmfd sp!, {r4 - r6, lr}
 mov r1, r0
 bl __lookup_machine_type
 mov r0, r5
 ldmfd sp!, {r4 - r6, pc}
ENDPROC(lookup_machine_type)

/*
 * This provides a C-API version of __lookup_processor_type
 */
ENTRY(lookup_processor_type)
 stmfd sp!, {r4 - r6, r9, lr}
 mov r9, r0
 bl __lookup_processor_type
 mov r0, r5
 ldmfd sp!, {r4 - r6, r9, pc}
ENDPROC(lookup_processor_type)

/*
 * 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)
 */
 __CPUINIT
__lookup_processor_type:
 adr r3, __lookup_processor_type_data
 ldmia r3, {r4 - r6}
 sub r3, r3, r4   @ get offset between virt&phys
 add r5, r5, r3   @ convert virt addresses to
 add r6, r6, r3   @ physical address space
1: ldmia r5, {r3, r4}   @ value, mask
 and r4, r4, r9   @ mask wanted bits
 teq r3, r4
 beq 2f
 add r5, r5, #PROC_INFO_SZ  @ sizeof(proc_info_list)
 cmp r5, r6
 blo 1b
 mov r5, #0    @ unknown processor
2: mov pc, lr
ENDPROC(__lookup_processor_type)

/*
 * Look in <asm/procinfo.h> for information about the __proc_info structure.
 */
 .align 2
 .type __lookup_processor_type_data, %object
__lookup_processor_type_data:
 .long .
 .long __proc_info_begin
 .long __proc_info_end
 .size __lookup_processor_type_data, . - __lookup_processor_type_data

__error_p:
#ifdef CONFIG_DEBUG_LL
 adr r0, str_p1
 bl printascii
 mov r0, r9
 bl printhex8
 adr r0, str_p2
 bl printascii
 b __error
str_p1: .asciz "\nError: unrecognized/unsupported processor variant (0x"
str_p2: .asciz ").\n"
 .align
#endif
ENDPROC(__error_p)

__error:
#ifdef CONFIG_ARCH_RPC
/*
 * Turn the screen red on a error - RiscPC only.
 */
 mov r0, #0x02000000
 mov r3, #0x11
 orr r3, r3, r3, lsl #8
 orr r3, r3, r3, lsl #16
 str r3, [r0], #4
 str r3, [r0], #4
 str r3, [r0], #4
 str r3, [r0], #4
#endif
1: mov r0, r0
 b 1b
ENDPROC(__error)
==============================================================================
                                         init/main.c
==============================================================================
部分代码如下;
    asmlinkage void __init start_kernel(void)
    {
     char * command_line;
     extern const struct kernel_param __start___param[], __stop___param[];
    
     smp_setup_processor_id();
    
     /*
      * Need to run as early as possible, to initialize the
      * lockdep hash:
      */
     lockdep_init();
     debug_objects_early_init();
    
     /*
      * Set up the the initial canary ASAP:
      */
     boot_init_stack_canary();
    
     cgroup_init_early();
    
     local_irq_disable();
     early_boot_irqs_disabled = true;
    
    /*
     * Interrupts are still disabled. Do necessary setups, then
     * enable them
     */
     tick_init();
     boot_cpu_init();
     page_address_init();
     printk(KERN_NOTICE "%s", linux_banner);
     setup_arch(&command_line);      //跳转到setup.c中setup_atch函数,取得启动参数,代码如下
     mm_init_owner(&init_mm, &init_task);
     setup_command_line(command_line);
     setup_nr_cpu_ids();
     setup_per_cpu_areas();
     smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
    
     build_all_zonelists(NULL);
     page_alloc_init();
    
     printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
     parse_early_param();
     parse_args("Booting kernel", static_command_line, __start___param,
         __stop___param - __start___param,
         &unknown_bootoption);
     /*
      * These use large bootmem allocations and must precede
      * kmem_cache_init()
      */
     pidhash_init();
     vfs_caches_init_early();
     sort_main_extable();
     trap_init();
     mm_init();
     /*
      * Set up the scheduler prior starting any interrupts (such as the
      * timer interrupt). Full topology setup happens at smp_init()
      * time - but meanwhile we still have a functioning scheduler.
      */
     sched_init();
     /*
      * Disable preemption - early bootup scheduling is extremely
      * fragile until we cpu_idle() for the first time.
      */
     preempt_disable();
     if (!irqs_disabled()) {
      printk(KERN_WARNING "start_kernel(): bug: interrupts were "
        "enabled *very* early, fixing it\n");
      local_irq_disable();
     }
     idr_init_cache();
     perf_event_init();
     rcu_init();
     radix_tree_init();
     /* init some links before init_ISA_irqs() */
     early_irq_init();
     init_IRQ();
     prio_tree_init();
     init_timers();
     hrtimers_init();
     softirq_init();
     timekeeping_init();
     time_init();
     profile_init();
     if (!irqs_disabled())
      printk(KERN_CRIT "start_kernel(): bug: interrupts were "
         "enabled early\n");
     early_boot_irqs_disabled = false;
     local_irq_enable();
    
     /* Interrupts are enabled now so all GFP allocations are safe. */
     gfp_allowed_mask = __GFP_BITS_MASK;
    
     kmem_cache_init_late();
    
     /*
      * HACK ALERT! This is early. We're enabling the console before
      * we've done PCI setups etc, and console_init() must be aware of
      * this. But we do want output early, in case something goes wrong.
      */
     console_init();
     if (panic_later)
      panic(panic_later, panic_param);
    
     lockdep_info();
    
     /*
      * Need to run this when irqs are enabled, because it wants
      * to self-test [hard/soft]-irqs on/off lock inversion bugs
      * too:
      */
     locking_selftest();
    
    #ifdef CONFIG_BLK_DEV_INITRD
     if (initrd_start && !initrd_below_start_ok &&
         page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
      printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
          "disabling it.\n",
          page_to_pfn(virt_to_page((void *)initrd_start)),
          min_low_pfn);
      initrd_start = 0;
     }
    #endif
     page_cgroup_init();
     enable_debug_pagealloc();
     kmemleak_init();
     debug_objects_mem_init();
     setup_per_cpu_pageset();
     numa_policy_init();
     if (late_time_init)
      late_time_init();
     sched_clock_init();
     calibrate_delay();
     pidmap_init();
     anon_vma_init();
    #ifdef CONFIG_X86
     if (efi_enabled)
      efi_enter_virtual_mode();
    #endif
     thread_info_cache_init();
     cred_init();
     fork_init(totalram_pages);
     proc_caches_init();
     buffer_init();
     key_init();
     security_init();
     dbg_late_init();
     vfs_caches_init(totalram_pages);
     signals_init();
     /* rootfs populating might need page-writeback */
     page_writeback_init();
    #ifdef CONFIG_PROC_FS
     proc_root_init();
    #endif
     cgroup_init();
     cpuset_init();
     taskstats_init_early();
     delayacct_init();
    
     check_bugs();
    
     acpi_early_init(); /* before LAPIC and SMP init */
     sfi_init_late();
    
     ftrace_init();
    
     /* Do the rest non-__init'ed, we're now alive */
     rest_init();
    }
    
    .........
    
    static noinline void __init_refok rest_init(void)
     {
      int pid;
     
      rcu_scheduler_starting();
      /*
       * We need to spawn init first so that it obtains pid 1, however
       * the init task will end up wanting to create kthreads, which, if
       * we schedule it before we create kthreadd, will OOPS.
       */
      kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);  //启动线程,调用kernel_init函数
      numa_default_policy();
      pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
      rcu_read_lock();
      kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
      rcu_read_unlock();
      complete(&kthreadd_done);
     
      /*
       * The boot idle thread must execute schedule()
       * at least once to get things moving:
       */
      init_idle_bootup_task(current);
      preempt_enable_no_resched();
      schedule();
      preempt_disable();
     
      /* Call into cpu_idle with preempt disabled */
      cpu_idle();
     }
     
     
     .........
     static int __init kernel_init(void * unused)
     {
      /*
       * Wait until kthreadd is all set-up.
       */
      wait_for_completion(&kthreadd_done);
      /*
       * init can allocate pages on any node
       */
      set_mems_allowed(node_states[N_HIGH_MEMORY]);
      /*
       * init can run on any cpu.
       */
      set_cpus_allowed_ptr(current, cpu_all_mask);
      /*
       * Tell the world that we're going to be the grim
       * reaper of innocent orphaned children.
       *
       * We don't want people to have to make incorrect
       * assumptions about where in the task array this
       * can be found.
       */
      init_pid_ns.child_reaper = current;
     
      cad_pid = task_pid(current);
     
      smp_prepare_cpus(setup_max_cpus);
     
      do_pre_smp_initcalls();
      lockup_detector_init();
     
      smp_init();
      sched_init_smp();
     
      do_basic_setup();
     
      /* Open the /dev/console on the rootfs, this should never fail */
      if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
       printk(KERN_WARNING "Warning: unable to open an initial console.\n");
     
      (void) sys_dup(0);
      (void) sys_dup(0);
      /*
       * check if there is an early userspace init.  If yes, let it do all
       * the work
       */
     
      if (!ramdisk_execute_command)
       ramdisk_execute_command = "/init";
     
      if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
       ramdisk_execute_command = NULL;
       prepare_namespace();  //定义在/init/D0_mounts.c文件中
      }
     
      /*
       * Ok, we have completed the initial bootup, and
       * we're essentially up and running. Get rid of the
       * initmem segments and start the user-mode stuff..
       */
     
      init_post(); //根文件系统挂载完成
      return 0;
     }
==========================================================================
             init/Do_mounts.c
==========================================================================
部分代码如下:
    void __init prepare_namespace(void)
    {
     int is_floppy;
    
     if (root_delay) {
      printk(KERN_INFO "Waiting %dsec before mounting root device...\n",
             root_delay);
      ssleep(root_delay);
     }
    
     /*
      * wait for the known devices to complete their probing
      *
      * Note: this is a potential source of long boot delays.
      * For example, it is not atypical to wait 5 seconds here
      * for the touchpad of a laptop to initialize.
      */
     wait_for_device_probe();
    
     md_run_setup();
    
     if (saved_root_name[0]) {
      root_device_name = saved_root_name;
      if (!strncmp(root_device_name, "mtd", 3) ||
          !strncmp(root_device_name, "ubi", 3)) {
       mount_block_root(root_device_name, root_mountflags);
       goto out;
      }
      ROOT_DEV = name_to_dev_t(root_device_name);
      if (strncmp(root_device_name, "/dev/", 5) == 0)
       root_device_name += 5;
     }
    
     if (initrd_load())
      goto out;
    
     /* wait for any asynchronous scanning to complete */
     if ((ROOT_DEV == 0) && root_wait) {
      printk(KERN_INFO "Waiting for root device %s...\n",
       saved_root_name);
      while (driver_probe_done() != 0 ||
       (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
       msleep(100);
      async_synchronize_full();
     }
    
     is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
    
     if (is_floppy && rd_doload && rd_load_disk(0))
      ROOT_DEV = Root_RAM0;
    
     mount_root();   //挂载根文件系统
    out:
     devtmpfs_mount("dev");
     sys_mount(".", "/", NULL, MS_MOVE, NULL);
     sys_chroot((const char __user __force *)".");
    }
    
    .........
    
    static noinline int init_post(void)
     {
      /* need to finish all async __init code before freeing the memory */
      async_synchronize_full();
      free_initmem();
      mark_rodata_ro();
      system_state = SYSTEM_RUNNING;
      numa_default_policy();
     
     
      current->signal->flags |= SIGNAL_UNKILLABLE;
     
      if (ramdisk_execute_command) {
       run_init_process(ramdisk_execute_command);
       printk(KERN_WARNING "Failed to execute %s\n",
         ramdisk_execute_command);
      }
     
      /* 执行应用程序
       * We try each of these until one succeeds.
       *
       * The Bourne shell can be used instead of init if we are
       * trying to recover a really broken machine.
       */
      if (execute_command) {
       run_init_process(execute_command);
       printk(KERN_WARNING "Failed to execute %s.  Attempting "
          "defaults...\n", execute_command);
      }
      run_init_process("/sbin/init");
      run_init_process("/etc/init");
      run_init_process("/bin/init");
      run_init_process("/bin/sh");
     
      panic("No init found.  Try passing init= option to kernel. "
            "See Linux Documentation/init.txt for guidance.");
     }


=========================================================================
        arch/arm/kernel/setup.c
=========================================================================
部分代码如下:(取得启动参数)
   void __init setup_arch(char **cmdline_p)
   {
    struct tag *tags = (struct tag *)&init_tags;
    struct machine_desc *mdesc;
    char *from = default_command_line;  //默认的命令行参数
   
    unwind_init();
   
    setup_processor();
    mdesc = setup_machine(machine_arch_type);
    machine_desc = mdesc;
    machine_name = mdesc->name;
   
    if (mdesc->soft_reboot)
     reboot_setup("s");
   
    if (__atags_pointer)
     tags = phys_to_virt(__atags_pointer);
    else if (mdesc->boot_params)
     tags = phys_to_virt(mdesc->boot_params);
   
   #if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
    /*
     * If we have the old style parameters, convert them to
     * a tag list.
     */
    if (tags->hdr.tag != ATAG_CORE)
     convert_to_tag_list(tags);
   #endif
    if (tags->hdr.tag != ATAG_CORE)
     tags = (struct tag *)&init_tags;
   
    if (mdesc->fixup)
     mdesc->fixup(mdesc, tags, &from, &meminfo);
   
    if (tags->hdr.tag == ATAG_CORE) {
     if (meminfo.nr_banks != 0)
      squash_mem_tags(tags);
     save_atags(tags);
     parse_tags(tags);
    }
   
    init_mm.start_code = (unsigned long) _text;
    init_mm.end_code   = (unsigned long) _etext;
    init_mm.end_data   = (unsigned long) _edata;
    init_mm.brk    = (unsigned long) _end;
   
    /* parse_early_param needs a boot_command_line */
    strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
   
    /* populate cmd_line too for later use, preserving boot_command_line */
    strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
    *cmdline_p = cmd_line;
   
    parse_early_param();
   
    arm_memblock_init(&meminfo, mdesc);
   
    paging_init(mdesc);
    request_standard_resources(mdesc);
   
   #ifdef CONFIG_SMP
    if (is_smp())
     smp_init_cpus();
   #endif
    reserve_crashkernel();
   
    cpu_init();
    tcm_init();
   
   #ifdef CONFIG_MULTI_IRQ_HANDLER
    handle_arch_irq = mdesc->handle_irq;
   #endif
   
   #ifdef CONFIG_VT
   #if defined(CONFIG_VGA_CONSOLE)
    conswitchp = &vga_con;
   #elif defined(CONFIG_DUMMY_CONSOLE)
    conswitchp = &dummy_con;
   #endif
   #endif
    early_trap_init();
   
    if (mdesc->init_early)
     mdesc->init_early();
   }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值