__atags_pointer

本文深入探讨了ARM架构下Linux内核的启动流程,详细分析了ATAGS和设备树的验证与处理机制,以及MMU开启前后的内存映射转换和初始化过程。文章还介绍了如何设置和保存关键寄存器值,以及如何根据ATAGS或设备树选择正确的机器描述。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

arch/arm/kernel/head-common.S

/* 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.  If CONFIG_OF_FLATTREE
 * is selected, then it will also accept a dtb pointer.  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.
 *
 * Returns:
 *  r2 either valid atags pointer, valid dtb pointer, or zero
 *  r5, r6 corrupted
 */
__vet_atags:
        tst     r2, #0x3                        @ aligned?
        bne     1f
        ldr     r5, [r2, #0]
#ifdef CONFIG_OF_FLATTREE
        ldr     r6, =OF_DT_MAGIC                @ is it a DTB?
        cmp     r5, r6
        beq     2f
#endif
        cmp     r5, #ATAG_CORE_SIZE             @ is first tag ATAG_CORE?
        cmpne   r5, #ATAG_CORE_SIZE_EMPTY
        bne     1f
        ldr     r5, [r2, #4]
        ldr     r6, =ATAG_CORE
        cmp     r5, r6
        bne     1f

2:      ret     lr                              @ atag/dtb pointer is ok

1:      mov     r2, #0
        ret     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/dtb pointer
 *  r9  = processor ID
 */
        __INIT
__mmap_switched:
        adr     r3, __mmap_switched_data

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

        mov     fp, #0                          @ Clear BSS (and zero fp)
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
        cmp     r7, #0
        strne   r0, [r7]                        @ Save control register values
        b       start_kernel
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
#ifdef CONFIG_CPU_CP15
        .long   cr_alignment                    @ r7
#else
        .long   0                               @ r7
#endif
        .long   init_thread_union + THREAD_START_SP @ sp
        .size   __mmap_switched_data, . - __mmap_switched_data

arch/arm/kernel/setup.c

unsigned int __atags_pointer __initdata;
 
static char __initdata cmd_line[COMMAND_LINE_SIZE];    
       
void __init setup_arch(char **cmdline_p)
{
        const struct machine_desc *mdesc;
        setup_processor();
        mdesc = setup_machine_fdt(__atags_pointer);
        if (!mdesc)
                mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
        machine_desc = mdesc;
        machine_name = mdesc->name;
        dump_stack_set_arch_desc("%s", mdesc->name);

	    if (mdesc->reboot_mode != REBOOT_HARD)
	            reboot_mode = mdesc->reboot_mode;

	    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;
        /* populate cmd_line too for later use, preserving boot_command_line */
        strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
        *cmdline_p = cmd_line;

	    early_fixmap_init();
	    early_ioremap_init();
	    
	    parse_early_param();
	    
	    #ifdef CONFIG_MMU
	    early_mm_init(mdesc);
	    #endif
	    setup_dma_zone(mdesc);
	    xen_early_init();
	    efi_init();
	    /*
	     * Make sure the calculation for lowmem/highmem is set appropriately
	     * before reserving/allocating any mmeory
	     */
	    adjust_lowmem_bounds();
	    arm_memblock_init(mdesc);
	    /* Memory may have been removed so recalculate the bounds. */    

        adjust_lowmem_bounds();
    
        early_ioremap_reset();
    
        paging_init(mdesc);
        request_standard_resources(mdesc);
    
        if (mdesc->restart)
                arm_pm_restart = mdesc->restart;
    
        unflatten_device_tree();

        arm_dt_init_cpu_maps();
        psci_dt_init();
	    #ifdef CONFIG_SMP
	        if (is_smp()) {
	                if (!mdesc->smp_init || !mdesc->smp_init()) {
	                        if (psci_smp_available())
	                                smp_set_ops(&psci_smp_ops);
	                        else if (mdesc->smp)
	                                smp_set_ops(mdesc->smp);
	                }
	                smp_init_cpus();
	                smp_build_mpidr_hash();
	        }
	    #endif

       if (!is_smp())
                hyp_mode_check();

        reserve_crashkernel();
	    
	    #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
	
	    if (mdesc->init_early)
	            mdesc->init_early();
}

arch/arm/kernel/devtree.c

/**
* setup_machine_fdt - Machine setup when an dtb was passed to the kernel
* @dt_phys: physical address of dt blob
*
* If a dtb was passed to the kernel in r2, then use it to choose the
* correct machine_desc and to setup the system.
*/
const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
{
	const struct machine_desc *mdesc, *mdesc_best = NULL;

    #if defined(CONFIG_ARCH_MULTIPLATFORM) || defined(CONFIG_ARM_SINGLE_ARMV7M)
    DT_MACHINE_START(GENERIC_DT, "Generic DT based system")
            .l2c_aux_val = 0x0,
            .l2c_aux_mask = ~0x0,
    MACHINE_END
    
    mdesc_best = &__mach_desc_GENERIC_DT;
    #endif
    
    if (!dt_phys || !early_init_dt_verify(phys_to_virt(dt_phys)))
            return NULL;
    
    mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);

    if (!mdesc) {
            const char *prop;
            int size;
            unsigned long dt_root;

        early_print("\nError: unrecognized/unsupported "
                    "device tree compatible list:\n[ ");

        dt_root = of_get_flat_dt_root();
        prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
        while (size > 0) {
                early_print("'%s' ", prop);
                size -= strlen(prop) + 1;
                prop += strlen(prop) + 1;
        }
        early_print("]\n\n");

        dump_machine_table(); /* does not return */
    }

    /* We really don't want to do this, but sometimes firmware provides buggy data */
    if (mdesc->dt_fixup)
            mdesc->dt_fixup();
    
    early_init_dt_scan_nodes();
    
    /* Change machine number to match the mdesc we're using */
    __machine_arch_type = mdesc->nr;
    
    return mdesc;
}

bool __init early_init_dt_verify(void *params)
{
    if (!params)
            return false;
            
    /* check device tree validity */
    if (fdt_check_header(params))
            return false;
    
    /* Setup flat device-tree pointer */
    initial_boot_params = params;
    of_fdt_crc32 = crc32_be(~0, initial_boot_params,
                            fdt_totalsize(initial_boot_params));
    return true;
}
            
int fdt_check_header(const void *fdt)
{
    if (fdt_magic(fdt) == FDT_MAGIC) {
            /* Complete tree */
            if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
                    return -FDT_ERR_BADVERSION;
            if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
                    return -FDT_ERR_BADVERSION;
    } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
            /* Unfinished sequential-write blob */
            if (fdt_size_dt_struct(fdt) == 0)
                    return -FDT_ERR_BADSTATE;
    } else {
            return -FDT_ERR_BADMAGIC;
    }
    
    return 0;
}

arch/arm/kernel/atags.h

#ifdef CONFIG_ATAGS
const struct machine_desc *setup_machine_tags(phys_addr_t __atags_pointer,
        unsigned int machine_nr);
#else   
static inline const struct machine_desc *
setup_machine_tags(phys_addr_t __atags_pointer, unsigned int machine_nr)
{               
        early_print("no ATAGS support: can't continue\n");
        while (true);
        unreachable();
}       
#endif

arch/arm/kernel/atags_parse.c

static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;

static int __init parse_tag_cmdline(const struct tag *tag)
{
#if defined(CONFIG_CMDLINE_EXTEND)
        strlcat(default_command_line, " ", COMMAND_LINE_SIZE);
        strlcat(default_command_line, tag->u.cmdline.cmdline,
                COMMAND_LINE_SIZE);
#elif defined(CONFIG_CMDLINE_FORCE)
        pr_warn("Ignoring tag cmdline (using the default kernel command line)\n");
#else
        strlcpy(default_command_line, tag->u.cmdline.cmdline,
                COMMAND_LINE_SIZE);
#endif
        return 0;
}

__tagtable(ATAG_CMDLINE, parse_tag_cmdline);    

static struct {
        struct tag_header hdr1;
        struct tag_core   core;
        struct tag_header hdr2;
        struct tag_mem32  mem;
        struct tag_header hdr3;
} default_tags __initdata = {
        { tag_size(tag_core), ATAG_CORE },
        { 1, PAGE_SIZE, 0xff },
        { tag_size(tag_mem32), ATAG_MEM },
        { MEM_SIZE },
        { 0, ATAG_NONE }
};    
    
const struct machine_desc * __init
 setup_machine_tags(phys_addr_t __atags_pointer, unsigned int machine_nr)
 {
         struct tag *tags = (struct tag *)&default_tags;
         const struct machine_desc *mdesc = NULL, *p;
         char *from = default_command_line;
 
         default_tags.mem.start = PHYS_OFFSET;
 
         /*
          * locate machine in the list of supported machines.
          */
         for_each_machine_desc(p)
                 if (machine_nr == p->nr) {
                         pr_info("Machine: %s\n", p->name);
                         mdesc = p;
                         break;
                 }
 
         if (!mdesc) {
                 early_print("\nError: unrecognized/unsupported machine ID"
                             " (r1 = 0x%08x).\n\n", machine_nr);
                 dump_machine_table(); /* does not return */
         }
 
         if (__atags_pointer)
                 tags = phys_to_virt(__atags_pointer);
         else if (mdesc->atag_offset)
                 tags = (void *)(PAGE_OFFSET + mdesc->atag_offset);
 
 #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) {
                 early_print("Warning: Neither atags nor dtb found\n");
                 tags = (struct tag *)&default_tags;
         }
 
         if (mdesc->fixup)
                 mdesc->fixup(tags, &from);
 
         if (tags->hdr.tag == ATAG_CORE) {
                 if (memblock_phys_mem_size())
                         squash_mem_tags(tags);
                 save_atags(tags);
                 parse_tags(tags);
         }
 
         /* parse_early_param needs a boot_command_line */
         strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
 
         return mdesc;
 }

arch/arm/mach-s3c24xx/mach-smdk2440.c

MACHINE_START(S3C2440, "SMDK2440")
        /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
        .atag_offset    = 0x100,
        .init_irq       = s3c2440_init_irq,
        .map_io         = smdk2440_map_io,
        .init_machine   = smdk2440_machine_init,
        .init_time      = smdk2440_init_time,
MACHINE_END

init/main.c

/* Untouched command line saved by arch-specific code. */
char __initdata boot_command_line[COMMAND_LINE_SIZE];

/* Untouched saved command line (eg. for /proc) */
char *saved_command_line;

/* Command line for parameter parsing */
static char *static_command_line;

/* Command line for per-initcall parameter parsing */
static char *initcall_command_line;

static char *execute_command;
static char *ramdisk_execute_command;

/*
 * We need to store the untouched command line for future reference.
 * We also need to store the touched command line since the parameter
 * parsing is performed in place, and we should allow a component to
 * store reference of name/value for future reference.
 */
static void __init setup_command_line(char *command_line)
{
        saved_command_line =
                memblock_virt_alloc(strlen(boot_command_line) + 1, 0);
        initcall_command_line =
                memblock_virt_alloc(strlen(boot_command_line) + 1, 0);
        static_command_line = memblock_virt_alloc(strlen(command_line) + 1, 0);
        strcpy(saved_command_line, boot_command_line);
        strcpy(static_command_line, command_line);
}

/* Arch code calls this early on, or if not, just before other parsing. */
void __init parse_early_param(void)
{
        static int done __initdata;
        static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata;

        if (done)
                return;

        /* All fall through to do_early_param. */
        strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
        parse_early_options(tmp_cmdline);
        done = 1;
}

asmlinkage __visible void __init start_kernel(void)
{
	char *command_line;
	...
	setup_arch(&command_line);
	...
	setup_command_line(command_line);
	...
	pr_notice("Kernel command line: %s\n", boot_command_line);
   parse_early_param();
   after_dashes = parse_args("Booting kernel",
                             static_command_line, __start___param,
                             __stop___param - __start___param,
                             -1, -1, NULL, &unknown_bootoption);
   if (!IS_ERR_OR_NULL(after_dashes))
           parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
                      NULL, set_init_arg);
	
	...
}

extern initcall_t __initcall_start[];
extern initcall_t __initcall0_start[];
extern initcall_t __initcall1_start[];
extern initcall_t __initcall2_start[];
extern initcall_t __initcall3_start[];
extern initcall_t __initcall4_start[];
extern initcall_t __initcall5_start[];
extern initcall_t __initcall6_start[];
extern initcall_t __initcall7_start[];
extern initcall_t __initcall_end[];

static initcall_t *initcall_levels[] __initdata = {
        __initcall0_start,
        __initcall1_start,
        __initcall2_start,
        __initcall3_start,
        __initcall4_start,
        __initcall5_start,
        __initcall6_start,
        __initcall7_start,
        __initcall_end,
};

/* Keep these in sync with initcalls in include/linux/init.h */
static char *initcall_level_names[] __initdata = {
        "early",
        "core",
        "postcore",
        "arch",
        "subsys",
        "fs",
        "device",
        "late",
};

static void __init do_initcall_level(int level)
{
        initcall_t *fn;

        strcpy(initcall_command_line, saved_command_line);
        parse_args(initcall_level_names[level],
                   initcall_command_line, __start___param,
                   __stop___param - __start___param,
                   level, level,
                   NULL, &repair_env_string);

        for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
                do_one_initcall(*fn);
}

static void __init do_initcalls(void)
{
        int level;

        for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
                do_initcall_level(level);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值