原创 ARM linux引导过程

2010-12-27 15:47 4781 9 9 分类: MCU/ 嵌入式

简单的说,引导ARM linux我们需要一个Bootloader.这是一个很小的,并先于linux kernel运行之前被运行的程序。这个Bootloader主要作用是初始化各种设备,然后调用linux kernel镜像,并传递相关信息给 kernel.


Essentially, the boot loader should provide (as a minimum) the following:



  1. Setup and initialise the RAM.
  2. Initialise one serial port.
  3. Detect the machine type.
  4. Setup the kernel tagged list.
  5. Call the kernel image.

1. Setup and initialise RAM


Existing boot loaders: MANDATORY
New boot loaders: MANDATORY


The boot loader is expected to find and initialise all RAM that the kernel will use for volatile data storage in the system. It performs this in a machine dependent manner. (It may use internal algorithms to automatically locate and size all RAM, or it may use knowledge of the RAM in the machine, or any other method the boot loader designer sees fit.)


2. Initialise one serial port


Existing boot loaders: OPTIONAL, RECOMMENDED
New boot loaders: OPTIONAL, RECOMMENDED


The boot loader should initialise and enable one serial port on the target. This allows the kernel serial driver to automatically detect which serial port it should use for the kernel console (generally used for debugging purposes, or communication with the target.)


As an alternative, the boot loader can pass the relevant 'console=' option to the kernel via the tagged lists specifing the port, and serial format options as described in


linux/Documentation/kernel-parameters.txt.


3. Detect the machine type


Existing boot loaders: OPTIONAL
New boot loaders: MANDATORY


The boot loader should detect the machine type its running on by some method. Whether this is a hard coded value or some algorithm that looks at the connected hardware is beyond the scope of this document. The boot loader must ultimately be able to provide a MACH_TYPE_xxx value to the kernel. (see linux/arch/arm/tools/mach-types).


4. Setup the kernel tagged list


Existing boot loaders: OPTIONAL, HIGHLY RECOMMENDED
New boot loaders: MANDATORY


The boot loader must create and initialise the kernel tagged list. A valid tagged list starts with ATAG_CORE and ends with ATAG_NONE. The ATAG_CORE tag may or may not be empty. An empty ATAG_CORE tag has the size field set to '2' (0x00000002). The ATAG_NONE must set the size field to zero.


Any number of tags can be placed in the list. It is undefined whether a repeated tag appends to the information carried by the previous tag, or whether it replaces the information in its entirety; some tags behave as the former, others the latter.


The boot loader must pass at a minimum the size and location of the system memory, and root filesystem location. Therefore, the minimum tagged list should look:

        +-----------+
base -> | ATAG_CORE |  |
        +-----------+  |
        | ATAG_MEM  |  | increasing address
        +-----------+  |
        | ATAG_NONE |  |
        +-----------+  v

The tagged list should be stored in system RAM.


The tagged list must be placed in a region of memory where neither the kernel decompressor nor initrd 'bootp' program will overwrite it. The recommended placement is in the first 16KiB of RAM.


5. Calling the kernel image


Existing boot loaders: MANDATORY
New boot loaders: MANDATORY


There are two options for calling the kernel zImage. If the zImage is stored in flash, and is linked correctly to be run from flash, then it is legal for the boot loader to call the zImage in flash directly.


The zImage may also be placed in system RAM (at any location) and called there. Note that the kernel uses 16K of RAM below the image to store page tables. The recommended placement is 32KiB into RAM.


In either case, the following conditions must be met:



  • CPU register settings

    • r0 = 0.
    • r1 = machine type number discovered in (3) above.
    • r2 = physical address of tagged list in system RAM.

  • CPU mode

    • All forms of interrupts must be disabled (IRQs and FIQs.)
    • The CPU must be in SVC mode. (A special exception exists for Angel.)

  • Caches, MMUs

    • The MMU must be off.
    • Instruction cache may be on or off.
    • Data cache must be off and must not contain any stale data.

  • Devices

    • DMA to/from devices should be quiesced.

  • The boot loader is expected to call the kernel image by jumping directly to the first instruction of the kernel image.

=====================================================================


ARM Linux Boot Sequence


 


The following traces the Linux boot sequence for ARM-based systems in the 2.6.18 kernel. It looks at just the earliest stages of the boot process, until the generic non-processor-specific start_kernel function is called. The line numbers of each statement are in parenthese at the end of the line.


zImage decompression



  • arch/arm/boot/compressed/head.S: start (108)

    • First code executed, jumped to by the bootloader, at label "start" (108)
    • save contents of registers r1 and r2 in r7 and r8 to save off architecture ID and atags pointer passed in by bootloader (118)
    • execute arch-specific code (inserted at 146)

      • arch/arm/boot/compressed/head-xscale.S or other arch-specific code file
      • added to build in arch/arm/boot/compressed/Makefile
      • linked into head.S by linker section declaration:  .section “start”
      • flush cache, turn off cache and MMU

    • load registers with stored parameters (152)

      • sp = stack pointer for decompression code (152)
      • r4 = zreladdr = kernel entry point physical address

    • check if running at link address, and fix up global offset table if not (196)
    • zero decompression bss (205)
    • call cache_on to turn on cache (218)

      • defined at arch/arm/boot/compressed/head.S (320)
      • call call_cache_fn to turn on cache as appropriate for processor variant

        • defined at arch/arm/boot/compressed/head.S (505)
        • walk through proc_types list (530) until find corresponding processor
        • call cache-on function in list item corresponding to processor (511)

          • for ARMv5tej core, cache_on function is __armv4_mmu_cache_on (417)

            • call setup_mmu to set up initial page tables since MMU must be on for cache to be on (419)
            • turn on cache and MMU (426)

    • check to make sure won't overwrite image during decompression; assume not for this trace (232)
    • call decompress_kernel to decompress kernel to RAM (277)
    • branch to call_kernel (278)

      • call cache_clean_flush to flush cache contents to RAM (484)
      • call cache_off to turn cache off as expected by kernel initialization routines (485)
      • jump to start of kernel in RAM (489)

        • jump to address in r4 = zreladdr from previous load

          • zreladdr = ZRELADDR = zreladdr-y
          • zreladdr-y specified in arch/arm/mach-vx115/Makefile.boot

 


ARM-specific kernel code



  • arch/arm/kernel/head.S: stext (72)

    • call __lookup_processor_type (76)

      • defined in arch/arm/kernel/head-common.S (146)
      • search list of supported processor types __proc_info_begin (176)

        • kernel may be built to support more than one processor type
        • list of proc_info_list structs 

          • defined in arch/arm/mm/proc-arm926.S (467) and other corresponding proc-*.S files
          • linked into list by section declaration:  .section ".proc.info.init"

      • return pointer to proc_info_list struct corresponding to processor if found, or loop in error if not

    • call __lookup_machine_type (79)

      • defined in arch/arm/kernel/head-common.S (194)
      • search list of supported machines (boards)

        • kernel may be built to support more than one board
        • list of machine_desc structs 

          • machine_desc struct for boards defined in board-specific file vx115_vep.c
          • linked into list by section declaration that's part of MACHINE_DESC macro

      • return pointer to machine_desc struct corresponding to machine (board)

    • call __create_page_tables to set up initial MMU tables (82)
    • set lr to __enable_mmu, r13 to address of __switch_data (91, 93)

      • lr and r13 used for jumps after the following calls
      • __switch_data defined in arch/arm/kernel/head-common.S (15)

    • call the __cpu_flush function pointer in the previously returned proc_info_list struct (94)

      • offset is #PROCINFO_INITFUNC into struct
      • this function is __arm926_setup for the ARM 926EJ-S, defined in arch/arm/mm/proc-arm926.S (392)

        • initialize caches, writebuffer
        • jump to lr, previously set to address of __enable_mmu

    • __enable_mmu (147)

      • set page table pointer (TTB) in MMU hardware so it knows where to start page-table walks (167)
      • enable MMU so running with virtual addresses (185)
      • jump to r13, previously set to address of __switch_data, whose first field is address of __mmap_switched

        • __switch_data defined in arch/arm/kernel/head-common.S (15)

 



  • arch/arm/kernel/head-common.S: __mmap_switched (35)

    • copy data segment to RAM (39)
    • zero BSS (45)
    • branch to start_kernel (55)

 


Processor-independent kernel code



  • init/main.c: start_kernel (456)

其中:


asmlinkage void __init start_kernel(void)


{


       char * command_line;


       extern 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_off();


       early_init_irq_lock_class();


 


/*


 * Interrupts are still disabled. Do necessary setups, then


 * enable them


 */


       lock_kernel();


       tick_init();


       boot_cpu_init();


       page_address_init();


       printk(KERN_NOTICE "%s", linux_banner);


       setup_arch(&command_line);


       mm_init_owner(&init_mm, &init_task);


       setup_command_line(command_line);


       setup_per_cpu_areas();


       setup_nr_cpu_ids();


       smp_prepare_boot_cpu();     /* arch-specific boot-cpu hooks */


 


       /*


        * 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();


       build_all_zonelists();


       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);


       if (!irqs_disabled()) {


              printk(KERN_WARNING "start_kernel(): bug: interrupts were "


                            "enabled *very* early, fixing it\n");


              local_irq_disable();


       }


       sort_main_extable();


       trap_init();


       rcu_init();


       /* init some links before init_ISA_irqs() */


       early_irq_init();


       init_IRQ();


       pidhash_init();


       init_timers();


       hrtimers_init();


       softirq_init();


       timekeeping_init();


       time_init();


       sched_clock_init();


       profile_init();


       if (!irqs_disabled())


              printk(KERN_CRIT "start_kernel(): bug: interrupts were "


                             "enabled early\n");


       early_boot_irqs_on();


       local_irq_enable();


 


       /*


        * 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


       vmalloc_init();


       vfs_caches_init_early();


       cpuset_init_early();


       page_cgroup_init();


       mem_init();


       enable_debug_pagealloc();


       cpu_hotplug_init();


       kmem_cache_init();


       kmemtrace_init();


       debug_objects_mem_init();


       idr_init_cache();


       setup_per_cpu_pageset();


       numa_policy_init();


       if (late_time_init)


              late_time_init();


       calibrate_delay();


       pidmap_init();


       pgtable_cache_init();


       prio_tree_init();


       anon_vma_init();


#ifdef CONFIG_X86


       if (efi_enabled)


              efi_enter_virtual_mode();


#endif


       thread_info_cache_init();


       cred_init();


       fork_init(num_physpages);


       proc_caches_init();


       buffer_init();


       key_init();


       security_init();


       vfs_caches_init(num_physpages);


       radix_tree_init();


       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 */


 


       ftrace_init();


 


       /* Do the rest non-__init'ed, we're now alive */


       rest_init();


}

文章评论0条评论)

登录后参与讨论
我要评论
0
9
关闭 站长推荐上一条 /2 下一条