catalogue
2.Linux kernel startup process analysis
1. Link script vmlinux lds
To analyze the Linux startup process, you also need to compile the Linux source code first, because many files need to be compiled before they are generated
Yes. First, analyze the connection script file arch / arm / kernel / vmlinux LDS, which can be found through the link script
Where is the first line of the Linux kernel executed. vmlinux.lds has the following codes:
492 OUTPUT_ARCH(arm) 493 ENTRY(stext) 494 jiffies = jiffies_64; 495 SECTIONS 496 { 497 /* 498 * XXX: The linker does not define how output sections are 499 * assigned to input sections when there are multiple statements 500 * matching the same input section name. There is no documented 501 * order of matching. 502 * 503 * unwind exit sections must be discarded before the rest of the 504 * unwind sections get included. 505 */ 506 /DISCARD/ : { 507 *(.ARM.exidx.exit.text) 508 *(.ARM.extab.exit.text) 509 ...... 645 }
ENTRY in line 493 indicates the Linux kernel ENTRY. The ENTRY is stext, which is defined in the file
arch/arm/kernel/head.S, so to analyze the startup process of Linux kernel, you have to start from the file
arch/arm/kernel/head. Start analysis at stext of S.
2.Linux kernel startup process analysis
1) Linux kernel entry stext
stext is the entry address of the Linux kernel, which is in the file arch / arm / kernel / head S contains the following prompts:
/* * 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 or dtb pointer. ..... */
According to the comments of the above code, the requirements before Linux kernel startup are as follows:
1. Turn off MMU.
2. Close D-cache.
3. I-Cache doesn't matter.
4,r0=0.
5. R1 = machinenr (i.e. machine ID).
6. r2=atags or the first address of the device tree (dtb).
The entry point stext of Linux kernel is actually equivalent to the entry function of the kernel. The contents of stext function are as follows:
80 ENTRY(stext) ...... 91 @ ensure svc mode and all interrupts masked 92 safe_svcmode_maskall r9 93 94 mrc p15, 0, r9, c0, c0 @ get processor id 95 bl __lookup_processor_type @ r5=procinfo r9=cpuid 96 movs r10, r5 @ invalid processor (r5=0)? 97 THUMB( it eq ) @ force fixup-able long branch encoding 98 beq __error_p @ yes, error 'p' 99 ...... 107 108 #ifndef CONFIG_XIP_KERNEL ...... 113 #else 114 ldr r8, =PLAT_PHYS_OFFSET @ always constant in this case 115 #endif 116 117 /* 118 * r1 = machine no, r2 = atags or dtb, 119 * r8 = phys_offset, r9 = cpuid, r10 = procinfo 120 */ 121 bl __vet_atags ...... 128 bl __create_page_tables 129 130 /* 131 * The following calls CPU specific code in a position independent 132 * manner. See arch/arm/mm/proc-*.S for details. r10 = base of 133 * xxx_proc_info structure selected by __lookup_processor_type 134 * above. On return, the CPU will be ready for the MMU to be 135 * turned on, and r0 will hold the CPU control register value. 136 */ 137 ldr r13, =__mmap_switched @ address to jump to after 138 @ mmu has been enabled 139 adr lr, BSYM(1f) @ return (PIC) address 140 mov r8, r4 @ set TTBR1 to swapper_pg_dir 141 ldr r12, [r10, #PROCINFO_INITFUNC] 142 add r12, r12, r10 143 ret r12 144 1: b __enable_mmu 145 ENDPROC(stext)
Line 92, call the function safe_svcmode_maskall ensures that the CPU is in SVC mode and that all the are turned off
Break. safe_svcmode_maskall is defined in the file arch / arm / include / ASM / assembly H medium.
Line 94 reads the processor ID, and the ID value is stored in the r9 register.
Line 95, call the function__ lookup_processor_type check whether the current system supports this CPU. If it supports
Get procinfo information. Procinfo is proc_info_list structure, proc_info_list in file
arch/arm/include/asm/procinfo.h is defined as follows:
struct proc_info_list { unsigned int cpu_val; unsigned int cpu_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 int elf_hwcap; const char *cpu_name; struct processor *proc; struct cpu_tlb_fns *tlb; struct cpu_user_fns *user; struct cpu_cache_fns *cache; };
The Linux kernel abstracts each processor as a proc_info_list structure, one for each processor
procinfo. Therefore, the corresponding procinfo structure can be found through the processor ID__ lookup_processor_type
The function finds the procinfo of the corresponding processor and saves it in the r5 register.
Continue back to the above code, line 121, calling the function__ vet_atags verifies the validity of atags or device tree (dtb)
Sex. Function__ vet_atags is defined in the file arch / arm / kernel / head common S.
Line 128, call the function__ create_page_tables creates a page table.
Line 137, change the function__ mmap_ The switched address is saved in the r13 register__ mmap_switched setting
The meaning is in the file arch / arm / kernel / head common S,__ mmap_switched will eventually call start_kernel
Function.
Line 144, call__ enable_mmu function enables MMU__ enable_mmu is defined in the file
arch/arm/kernel/head.S__ enable_mmu will eventually call__ turn_mmu_on to open
MMU,__ turn_mmu_on will finally execute the saved in r13__ mmap_switched function.
2)__ mmap_switched function
__ mmap_ The switched function is defined in the file arch / arm / kernel / head common S, the function code is as follows:
81 __mmap_switched: 82 adr r3, __mmap_switched_data 83 84 ldmia r3!, {r4, r5, r6, r7} 85 cmp r4, r5 @ Copy data segment if needed 86 1: cmpne r5, r6 87 ldrne fp, [r4], #4 88 strne fp, [r5], #4 89 bne 1b 90 91 mov fp, #0 @ Clear BSS (and zero fp) 92 1: cmp r6, r7 93 strcc fp, [r6],#4 94 bcc 1b 95 96 ARM( ldmia r3, {r4, r5, r6, r7, sp}) 97 THUMB( ldmia r3, {r4, r5, r6, r7} ) 98 THUMB( ldr sp, [r3, #16] ) 99 str r9, [r4] @ Save processor ID 100 str r1, [r5] @ Save machine type 101 str r2, [r6] @ Save atags pointer 102 cmp r7, #0 103 strne r0, [r7] @ Save control register values 104 b start_kernel 105 ENDPROC(__mmap_switched)
Line 104 finally calls start_kernel to start the Linux kernel, start_ The kernel function is defined in the file init / main c
Yes.
3)start_kernel function
start_ The kernel completes some initialization before Linux startup by calling many sub functions
start_ There are too many sub functions called in the kernel function, and these sub functions are very complex, so let's take a simple look
Let's talk about some important subfunctions.
start_ A large number of functions are called in the kernel. Each function is a huge knowledge point. If you want to learn
Linux kernel, then these functions need to be studied in detail. This tutorial focuses on getting started with embedded Linux, so it doesn't
I will talk too much about the Linux kernel. start_ The kernel function finally calls rest_. Init, let's look at it briefly
Take a rest_init function.
4)rest_init function
rest_ The init function is defined in the file init / main In C, the function is as follows:
383 static noinline void __init_refok rest_init(void) 384 { 385 int pid; 386 387 rcu_scheduler_starting(); 388 smpboot_thread_init(); 389 /* 390 * We need to spawn init first so that it obtains pid 1, however 391 * the init task will end up wanting to create kthreads, which, 392 * if we schedule it before we create kthreadd, will OOPS. 393 */ 394 kernel_thread(kernel_init, NULL, CLONE_FS); 395 numa_default_policy(); 396 pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); 397 rcu_read_lock(); 398 kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); 399 rcu_read_unlock(); 400 complete(&kthreadd_done); 401 402 /* 403 * The boot idle thread must execute schedule() 404 * at least once to get things moving: 405 */ 406 init_idle_bootup_task(current); 407 schedule_preempt_disabled(); 408 /* Call into cpu_idle with preempt disabled */ 409 cpu_startup_entry(CPUHP_ONLINE); 410 }
Line 387, call the function rcu_scheduler_starting: start the RCU lock scheduler.
Line 394, call the kernel function_ Thread create kernel_init process, the famous init kernel
Cheng. The PID of init process is 1. The init process starts with the kernel process (that is, running in the kernel state), followed by the init process
The program named "init" will be found in the root file system. The "init" program is in user state. Run this program
"Init" program, the init process will realize the transformation from kernel state to user state.
Line 396, call the kernel function_ Thread creates a kthreadd kernel process with a PID of 2.
kthreadd process is responsible for the scheduling and management of all kernel processes.
The 409th row, and finally call the function cpu_. startup_ Enter to enter the idle process, cpu_startup_entry will call
cpu_idle_loop,cpu_ idle_ Loop is a while loop, that is, idle process code. PID of idle process is
0. Idle processes are called idle processes. If you have studied FreeRTOS or UCOS, you should have heard of idle tasks.
Idle processes are the same as idle tasks. When the CPU has nothing to do, it "wanders around" in idle processes
"Swim", anyway, is to find something to do for the CPU. When other processes want to work, they will preempt the idle process and seize it
CPU usage rights. In fact, you should see that the idle process does not use the kernel_thread or fork function
Created because it evolved from the main process.
Enter "ps -A" in the Linux terminal to print out all processes in the current system, where you can see the init entry
Process and kthreadd process, as shown in Figure 1:
Figure 1 current process of Linux system
As can be seen from Figure 1, the PID of init process is 1 and that of kthreadd process is 2. The reason why PID is not shown in Figure 1
The idle process is 0 because the idle process is a kernel process. Let's focus on the init process,
kernel_init is the process function of init process.
5) init process
kernel_ The init function is the specific work done by the init process, which is defined in the file init / main In C, the function is as follows:
928 static int __ref kernel_init(void *unused) 929 { 930 int ret; 931 932 kernel_init_freeable(); /* init Some other initialization of the process */ 933 /* need to finish all async __init code before freeing the memory */ 934 async_synchronize_full(); /* Wait for all asynchronous calls to complete */ 935 free_initmem(); /* Release init segment memory */ 936 mark_rodata_ro(); 937 system_state = SYSTEM_RUNNING; /* Marking system is running */ 938 numa_default_policy(); 939 940 flush_delayed_fput(); 941 942 if (ramdisk_execute_command) { 943 ret = run_init_process(ramdisk_execute_command); 944 if (!ret) 945 return 0; 946 pr_err("Failed to execute %s (error %d)\n", 947 ramdisk_execute_command, ret); 948 } 949 950 /* 951 * We try each of these until one succeeds. 952 * 953 * The Bourne shell can be used instead of init if we are 954 * trying to recover a really broken machine. 955 */ 956 if (execute_command) { 957 ret = run_init_process(execute_command); 958 if (!ret) 959 return 0; 960 panic("Requested init %s failed (error %d).", 961 execute_command, ret); 962 } 963 if (!try_to_run_init_process("/sbin/init") || 964 !try_to_run_init_process("/etc/init") || 965 !try_to_run_init_process("/bin/init") || 966 !try_to_run_init_process("/bin/sh")) 967 return 0; 968 969 panic("No working init found. Try passing init= option to kernel. " 970 "See Linux Documentation/init.txt for guidance."); 971 }
Line 932, kernel_ init_ The freeable function is used to complete some other initialization of the init process, which will be implemented later
Take a look at this function.
Line 940, ramdisk_execute_command is a global char pointer variable whose value is "/ init",
That is, the init program in the root directory. ramdisk_ execute_ The command can also be passed through uboot. In
"rdinit=xxx" can be used in bootargs, and xxx is the specific init program name.
Line 943, if there is a "/ init" program, use the function run_init_process to run this program.
Line 956, if ramdisk_ execute_ If the command is empty, it depends on execute_ Whether command is empty,
Anyway, you must find a runnable init program in the root file system. execute_command
The value is passed through uboot. Just use "init=xxxx" in bootargs. For example, "init=/linuxrc" indicates the root text
linuxrc in the system is the user space init program to be executed.
Lines 963-966, if ramdisk_execute_command and execute_ If all commands are empty, then
Look for "/ sbin/init", "etc/init", "bin/init" and "/ bin/sh" successively, which are equivalent to standby init programs. If
If these four don't exist, then Linux startup fails! Line 969, if the user space is not found in the above steps
Init program, an error will be prompted! Finally, let's take a brief look at the kernel_init_freeable function, as mentioned earlier
Okay, kernel_init will call this function to initialize the init process. kernel_init_freeable is defined in the text
Init / main In C, the reduced function is as follows:
973 static noinline void __init kernel_init_freeable(void) 974 { 975 /* 976 * Wait until kthreadd is all set-up. 977 */ 978 wait_for_completion(&kthreadd_done);/* Wait for the kthreadd process to be ready */ ...... 998 999 smp_init(); /* SMP initialization */ 1000 sched_init_smp(); /* Multi core (SMP) scheduling initialization */ 1001 1002 do_basic_setup(); /* Device initialization is completed in this function */ 1003 1004 /* Open the /dev/console on the rootfs, this should never fail */ 1005 if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) 1006 pr_err("Warning: unable to open an initial console.\n"); 1007 1008 (void) sys_dup(0); 1009 (void) sys_dup(0); 1010 /* 1011 * check if there is an early userspace init. If yes, let it do 1012 * all the work 1013 */ 1014 1015 if (!ramdisk_execute_command) 1016 ramdisk_execute_command = "/init"; 1017 1018 if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { 1019 ramdisk_execute_command = NULL; 1020 prepare_namespace(); 1021 } 1022 1023 /* 1024 * Ok, we have completed the initial bootup, and 1025 * we're essentially up and running. Get rid of the 1026 * initmem segments and start the user-mode stuff.. 1027 * 1028 * rootfs is available now, try loading the public keys 1029 * and default modules 1030 */ 1031 1032 integrity_load_keys(); 1033 load_default_modules(); 1034 }
Line 1002, do_ basic_ The setup function is used to initialize the device driver under Linux! Very important.
do_basic_setup calls the driver_ The init function completes the initialization of the driver model subsystem under Linux.
In line 1005, open the device "/ dev/console". In Linux, everything is a file! So is "/ dev/console"
A file that is a console device. Each file has a file descriptor, which is opened here
"/ dev/console" file descriptor is 0 as standard input (0).
Lines 1008 and 1009, Sys_ The DUP function copies the file descriptor of the standard input (0) twice, one as the standard
Output (1), one as standard error (2). In this way, the standard input, output and error are all / dev/console.
Console is set through the bootargs environment variable of uboot, "console = ttymxc0115200" indicates
Set / dev/ttymxc0 to console, that is, serial port 1 of I.MX6U. Of course, other devices can also be set up
Set tty1 to console, such as virtual console tty1. Set tty1 to console, you can see the system status on the LCD screen
Display information.
Line 1020, call the function prepare_namespace to mount the root file system. The file system is also controlled by the command line
Parameter, that is, the bootargs environment variable of uboot. For example, "root=/dev/mmcblk1p2rootwaitrw"
It means that the root file system is in / dev/mmcblk1p2, that is, partition 2 of EMMC.
The Linux kernel startup process is analyzed here. Finally, the Linux kernel needs to deal with the root file system
Mount the root file system and execute the init program in the root file system to enter the user state. Here's the official citation
Out of the root file system, the root file system is also the last piece of puzzle of our system transplantation. Linux porting big three:
uboot, Linux kernel, rootfs (root file system).
The root file system will be explained in detail in the following chapters. Here, we only need to know that we need to wait until the Linux kernel is transplanted
You need to build a root file system.