Linux kernel Boot

Source: Internet
Author: User

Linux Kernel Boot process overview

Linux startup code is really big, from assembler to C, from makefile to LDS files, there are a lot of things to understand. After all, the Linux kernel is written by a lot of people, spending a great deal of time and effort. And until now, there are still thousands of programmers in the world who are constantly perfecting the code of the Linux kernel. Today we are mainly about Linux-2.6.22.6 this kernel version. To be honest, the blogger is not sure that he can speak the topic today, because the topic is too big and too difficult. But bloggers have confidence that they will learn the content clearly to tell everyone, I hope we can also have some gains.

1. Start the file head. S and HEAD-COMMON.S

First, we have to make clear "Why do we start the Linux kernel". Yes, of course because we want to use Linux systems, to make sure that our ultimate goal is to use applications on Linux. These applications can be purely software or hardware-related. Bloggers are doing embedded development, then of course I want to use the Linux kernel to better control my hardware. Whether it's robots, drones or other smart hardware, this is an inevitable trend. First we look at the boot file head of the kernel. S

    . section ". Text.head", "Ax"    . Type    stext,%functionentry (stext)    msr    cpsr_c, #PSR_F_BIT | Psr_i_bit | Svc_mode @ Ensure svc MODE                        @ and IRQs disabled    MRC    P15, 0, R9, C0, C0        @ Get processor ID    BL    __loo Kup_processor_type        @ r5=procinfo r9=cpuid    movs    R10, R5                @ Invalid processor (r5=0)?    BEQ    __error_p            @ Yes, error ' P '    bl    __lookup_machine_type        @ r5=machinfo    movs    R8, R5                @ Invalid machine (r5=0)?    BEQ    __error_a            @ Yes, error ' a '    bl    __create_page_tables    ldr    R13, __switch_data        @ Address to jump to after                        @ MMU have been enabled    ADR    LR, __enable_mmu        @ return (PIC) address    add
   PC, R10, #PROCINFO_INITFUNC

First look at this assembly code, it is mainly used to do some kernel pre-boot detection: __lookup_processor_type detect whether the kernel supports the current CPU, __lookup_machine_type detect whether the current board is supported, and __create_ Page_tables Create page table, __enable_mmu enable MMU. If no support is found after a series of self-test procedures, skip to __error_p or __error_a. Here we first open the __lookup_machine_type.

    . Type    __lookup_machine_type,%function__lookup_machine_type:    ADR    R3, 3b    Ldmia    R3, {r4, R5, R6}    Sub    R3, R3, R4            @ get offset between Virt&phys    add    R5, R5, R3            @ convert virt addresses To
   add    R6, R6, R3            @ Physical Address Space1:    LDR    R3, [R5, #MACHINFO_TYPE]    @ Get machine type< C18/>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 machine2:    mov    pc, LR3:    . Long    . Long    __arch_info_begin    . Long    __arch_info_end

We found __lookup_machine_type in Arch\arm\kernel was defined in Head-common. S file. Start parsing code: First, read out the address of the 3b to R3, where 3b is the following 3: the corresponding virtual address. Then use the ldmia instruction to store the virtual address r3 stored in R4,R5,R6 respectively. So now

r4=. ; r5=__arch_info_begin; r6=__arch_info_end.

Then use the R3-R4 to find the offset address, and then use this offset address to find out the actual physical address of R5 and R6. Where __arch_info_begin and __arch_info_end are defined in the kernel directory Arch\arm\kernel vmlinux.lds file, through the starting virtual address = (0xc0000000) + The 0x00008000 is superimposed by layers.

sections{. = (0xc0000000) + 0x00008000;. Text.head: {  _stext =.;  _sinittext =.;  * (. text.head)}. init: {/* init code and Data *   /* (. init.text)  _einittext =.;  __proc_info_begin =.;   * (. proc.info.init)  __proc_info_end =.;  __arch_info_begin =.;   * (. arch.info.init)  __arch_info_end =.;

The __arch_info_begin and __arch_info_end are stored in the middle of the structure of the Segment property. Arch.info.init. Here we can query the kernel directly under Linux that contains the. arch.info.init file.

Direction:include/asm-arm/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 };
direction:arch/arm/mach-s3c2440
Machine_start (s3c2440, "SMDK2440")/ * Maintainer:ben dooks <[email protected]> */ . Phys_io = S3c2410_pa_uart, . Io_pg_offst = (((u32) S3c24xx_va_uart >>) & 0XFFFC, . boot_params = S3c2410_sdram_pa + 0x100, . Init_irq = S3c24xx_init_irq, . Map_io = Smdk2440_map_io, . Init_machine = smdk2440_machine_init, . Timer = &s3c24xx_timer,machine_end

, the defined struct type Machine_desc is found in Include/asm-arm/arch.h, and its segment property is forced to be defined as the. Arch.info.init in the code. The purpose of this is to unify the structure with the. Arch.info.init segment attribute between __arch_info_begin and __arch_info_end in the Vmlinux.lds link script file that we have just seen. Very easy to handle. So now we're going to expand this structure and see what it's about. That is, the arguments in the arch/arm/mach-s3c2440 are passed in. Expand as follows:

#define Machine_start (_type,_name)            static const struct MACHINE_DESC __mach_desc_s3c2440     __used                             __ ATTRIBUTE__ ((__section__ (". Arch.info.init"))) = {        . nr        = mach_type_s3c2440,            . Name        = "SMDK2440",/* Maintainer:ben dooks <[email protected]> */    . Phys_io    = S3c2410_pa_uart    ,. Io_pg_offst = (((    (u32 ) S3c24xx_va_uart >>) & 0XFFFC,    . Boot_params    = S3c2410_sdram_pa + 0x100,                    //0x30000100    . Init_irq    = S3c24xx_init_irq,    . Map_io        = Smdk2440_map_io,    . Init_machine    = smdk2440_ Machine_init,    . Timer        = &s3c24xx_timer,};

Now we see that the defined struct type Machine_desc, the content is. nr to. Timer. We can see that this structure is probably storage hardware information. NR Storage Machine Id,name Storage board name, phys_io storage input output, IO_PG_OFFST storage io offset address, boot_params storage uboot to the kernel boot parameters (TAG), INIT_IRQ storage is interrupt initialization information, Map_io is the IO mapping table, Init_machine is the initialization information of the single board, the timer is stored in a single board timer information.

struct MACHINE_DESC {/* * note! The first four elements is used * by assembler code in HEAD-ARMV.        S */unsigned int nr;    /* Architecture number */unsigned int phys_io;    /* Start of physical IO */unsigned int io_pg_offst;        /* byte offset for IO * Page TABE entry */const char *name;    /* Architecture name */unsigned long boot_params;    /* Tagged list */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 (*map_io) (void);/* IO mapping function */void (*INIT_IRQ) (void);        struct Sys_timer *timer; /* System TICK Timer */void (*init_machine) (void);};

We open the Arch.h file and see that the definition of the Machine_desc struct is exactly the same as what we just said. and back to Head-common. s file, defined here for Mmap_switch:

    . 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, 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    ldmia    R3, {r4, R5, R6, SP}    str    r9, [R4]            @ Save Processor ID    str    R1, [R5]            @ Save machine type    bic    r4, R0, #CR_A            @ Clear ' A ' bit    stmia    R6, {r0, R4}            @ Save control register values    b    start_kernel

Mmap_switch do a lot of work, here we see a copy data segment, clear BSS section, save the ID of the CPU, save the machine ID, clear ' A ' bit, save the value of the control register, and then to the C segment--start_kernel function.

2.C Language Segment--start_kernel

  

asmlinkage void __init start_kernel (void)
{ local_irq_disable (); Early_boot_irqs_off (); Early_init_irq_lock_class ();/* * interrupts is still disabled. Do necessary setups, then * enable them * /Lock_kernel (); Tick_init (); Boot_cpu_init (); Page_address_init (); PRINTK (kern_notice); PRINTK (Linux_banner); Setup_arch (&command_line); Setup_command_line (command_line); 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); INIT_IRQ (); Profile_init (); if (!irqs_disabled ()) PRINTK ("Start_kernel (): bug:interrupts were enabled early\n"); Early_boot_irqs_on (); Local_irq_enable (); Console_init (); Rest_init ();}

Next enter the C function of the Start_kernel boot kernel. The above is part of the code for Start_kernel. The main purpose of this part of the code is to handle the parameters passed by Uboot, set up an architecture-related environment, initialize the console, and finally execute the application to implement the function. Here I put the start_kernel function of the functions of the sub-function of a number of layers, to help you understand the functional structure of Start_kernel.

Start_kernel    Setup_arch (&command_line);    Setup_command_line (command_line);    Unknown_bootoption        obsolete_checksetup        parse_early_param        do_early_param            rest_init;        Kernel_init            prepare_namespace                mount_root            init_post

Each of these backspace (TAB) means that this function is called by the previous function (for example, Obsolete_checksetup is a function called by unknown_bootoption). Setup_arch (&command_line) and Setup_command_line (command_line) are used to handle the boot parameters that the Uboot passes in (processing tag). Obsolete_checksetup from __setup_start to __setup_end, calling functions with non-early identities; Do_early_param from __setup_start to __setup_end, Call the function identified with early (but because __setup_param (str, FN, FN, 0) is assigned a value of 0, so it is not called here), so we mainly use Obsolete_checksetup. That's what we'll mention later. Mount_root is to mount the root file system because the application on Linux will eventually run on the root file system. Finally, run the application in Init_post. So now there's a question, how does the Linux kernel receive the root filesystem information from Uboot?

Bootcmd=nand READ.JFFS2 0x30007fc0 kernel; Bootm 0x30007fc0bootargs=noinitrd Root=/dev/mtdblock3 INIT=/LINUXRC console=ttysac0

Above is the environment variable that is printed at uboot startup. Where we can see the root file system mounted to the 4th partition: Root=/dev/mtdblock3 (starting with 0 partitions). As we mentioned above, Setup_arch (&command_line) and Setup_command_line (command_line) are used to handle the boot parameters that Uboot passes in (processing tag). But this processing is just a simple copy and paste, these two functions will save the tag, but did not do real processing. So what are the functions that really tell the kernel where to mount? We can see a saved_root_name by looking at Prepare_namespace. Find Saved_root_name and find a call to it in the DO_MOUNTS.C file:

static int __init Root_dev_setup (char *line) {    strlcpy (saved_root_name, line, sizeof (Saved_root_name));    return 1;} __setup ("root=", root_dev_setup);   Pass in a string, a function

Based on our previous experience, we can guess the __setup macro and define a struct. By looking for __setup we found its macro definition:

Dir:init.h#define __setup (str, FN)                        __setup_param (str, FN, FN, 0) #define __setup_param (str, unique_id, FN, early)                static char __setup_str_# #unique_id [] __initdata = str;        The static struct Obs_kernel_param __setup_# #unique_id            __attribute_used__                        __attribute__ ((__section__ (". Init.setup "))            __attribute__ ((Aligned (sizeof (long))))            = {__setup_str_# #unique_id, FN, early}

In the init.h file, the definition __setup equals __setup_param. So in __setup_param's macro definition, we can tell that it defines a string and then defines a struct type Obs_kernel_param __setup. The segment property of this struct is. Init.setup, which contains a string, a function, and a early. The struct with this attribute is put together by a linked script file, from __setup_start to __setup_end search call. In the Vmlinux.lds
__setup_start =.;
* (. Init.setup)
__setup_end =.;

But there is no partition in Flash, just like Uboot, the partition in the code to write dead. Linux automatically prints out partition information when Linux is started. Here my partition is this:

Creating 4 MTD partitions on "NAND 256MiB 3,3v 8-bit": 0x00000000-0x00040000: "Bootloader" 0x00040000-0x00060000: "params" 0x00060000-0x00260000: "Kernel" 0x00260000-0x10000000: "Root"

We search for this partition name grep "\" bootloader\ "*-nr. Locate the partition code in the ARCH/ARM/PLAT-S3C24XX:

static struct Mtd_partition smdk_default_nand_part[] = {    [0] = {        . Name   = "Bootloader",        . Size   = 0x00040000,        . Offset    = 0,    },    [1] = {        . Name   = "params",        . Offset = mtdpart_ofs_append,        . Size   = 0x00020000,    },    [2] = {        . Name   = "Kernel",        . Offset = mtdpart_ofs_append,        . Size   = 0x00200000,    },    [3] = {        . name   = "Root",        . Offset = mtdpart_ofs_append,        . Size   = Mtdpart_siz_full,    }};

That is, after processing the parameters of the Uboot pass, performing CPU and board verification, mounting a series of operations such as the root filesystem, the final kernel executes the application in Init_post (). The kernel boot process is complete ^_^

Aside: Recently, bloggers are learning to learn Linux kernel and Linux device driver, which makes them feel difficult. But it is very meaningful, because you can see the code of the predecessors, the heart is really happy. I would like to be able to modify the Linux source code, write a suitable for their own hardware Linux system. Not only that, I also want to be able to open up their own code to share with more people. Perfecting the Linux kernel, making it faster and more convenient is the ultimate goal of bloggers. Bloggers will continue to learn, and then share the knowledge better to everyone!

Linux kernel Boot

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.