This article analyzes the startup of the Linux-Android system. The following article focuses on the analysis of the functions before start_kernel () is called during Linux Startup, at the same time, I also gave an overview of the Code flow after the function start_kernel (). I hope that the topic on Linux-android startup can be continued. Haha. If there are any errors or imperfections, you are welcome to take a brick and leave a message or send an emailGuopeixin@126.comThank you for the discussion.
I. kernel self-guided Program
1. kernel zimage self-extracting
This part of code is in arch/$ {arch}/boot/compressed/head. S. The code of this file will be packaged into zimage during zimage generation.
Head. s will first initialize the self-extracting environment, such as the memory, and then call decompress_kernel to decompress the environment and call the call_kernel function to start vmlinux.
Let's just list the most important parts of the head. s file:
----------------------------------------------------------------
/*
* We're not in danger of overwriting ourselves. Do this the simple way.
*
* R4 = kernel execution address
* R7 = architecture ID
*/
Wont_overwrite: mov r0, r4
MoV R3, r7
BL decompress_kernel
B call_kernel
...
Call_kernel: BL cache_clean_flush
BL cache_off
MoV r0, #0 @ must be zero
MoV R1, R7 @ restore architecture number
MoV R2, R8 @ restore atags pointer
MoV PC, R4 @ call Kernel
----------------------------------------------------------------
The decompress_kernel function is implemented in arch/$ {arch}/boot/compressed/Misc. C. The function is to decompress the zimage image. Obviously, the self-extracting process is required.
To configure the corresponding decompression address, the Code is as follows:
----------------------------------------------------------------
Ulg
Decompress_kernel (ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p,
Int arch_id)
{
Output_data = (UCH *) output_start;/* points to kernel start */
Free_mem_ptr = free_mem_ptr_p;/* apparently, this address is transmitted from the register */
Free_mem_end_ptr = free_mem_ptr_end_p;
_ Machine_arch_type = arch_id;
Arch_decomp_setup ();
Makecrc ();
Putstr ("Uncompressing Linux ...");
Gunzip ();
Putstr ("done, booting the kernel./N ");
Return output_ptr;
}
----------------------------------------------------------------
After call_kernel is called, the cache is disabled first, and then the command is executed at the vmlinux entry and the control of the system is handed over to vmlinux.
2. kernel vmlinux entry
> Simple description of vmlinux Compilation
Because the two files head. s and head-nommu.S are involved here, the following is a brief description of the Generation Process of vmlinux. Let's take a look at/ARCH/$ {arch}/kernel/makefile.
, The script at the end of the file is as follows:
----------------------------------------------------------------
#
# Makefile for the Linux kernel.
#
Aflags_head.o: =-dtext_offset = $ (text_offset)
Ifdef config_dynamic_ftrace
Cflags_remove_ftrace.o =-PG
Endif
# Object file lists.
OBJ-Y: = compat. O elf. O entry-armv.o IRQ. O/
Process. O ptrace. O setup. O signal. O/
Sys_arm.o stacktrace. O time. O traps. o
OBJ-$ (config_isa_dma_api) + = DMA. o
OBJ-$ (config_arch_acorn) + = ecard. o
OBJ-$ (config_fiq) + = FIQ. o
OBJ-$ (config_modules) + = armksyms. O module. o
OBJ-$ (config_arthur) + = Arthur. o
OBJ-$ (config_isa_dma) + = dma-isa.o
OBJ-$ (config_pci) + = bios32.o Isa. o
OBJ-$ (config_smp) + = SMP. o
OBJ-$ (config_dynamic_ftrace) + = ftrace. o
OBJ-$ (config_kexec) + = machine_kexec.o relocate_kernel.o
OBJ-$ (config_kprobes) + = kprobes. O kprobes-decode.o
OBJ-$ (config_atags_proc) + = atags. o
OBJ-$ (config_oabi_compat) + = sys_oabi-compat.o
OBJ-$ (config_arm_thumbee) + = thumbee. o
OBJ-$ (config_kgdb) + = kgdb. o
OBJ-$ (config_crunch) + = crunch. O crunch-bits.o
AFLAGS_crunch-bits.o: =-wa,-mcpu = ep9312
OBJ-$ (config_cpu_xscale) + = xscale-cp0.o
OBJ-$ (config_cpu_xsc3) + = xscale-cp0.o
OBJ-$ (config_iwmmxt) + = iwmmxt. o
Aflags_iwmmxt.o: =-wa,-mcpu = iwmmxt
Ifneq ($ (config_arch_ebsa110), Y)
OBJ-y + = Io. o
Endif
Head-Y: = head $ (mmuext). o
OBJ-$ (config_debug_ll) + = Debug. o
Extra-Y: = $ (Head-y) init_task.o vmlinux. LDS
----------------------------------------------------------------
You can see that there is a line of code "head-Y: = head $ (mmuext). O" at the end of the file, where mmuext is in/ARCH/$ {arch}/makefile.
Definition: in fact, for a processor without MMU, mmuext is nommu, and for a processor containing MMU, its value is null, refer to the code of mmuext in/ARCH/$ {arch}/makefile.
As follows:
----------------------------------------------------------------
# Defines filename extension depending memory manement type.
Ifeq ($ (config_mmu ),)
Mmuext: =-nommu
Endif
----------------------------------------------------------------
Therefore, the final code at the beginning of vmlinux is/ARCH/$ {arch}/kernel/head. S.
> Head. s File Analysis
Note that for the description of this file, the general book may only analyze the Linux system of the old version, that is, the end position of the file is directly called.
Start_kernel function. Now the C code is executed. Actually, this is not the case.
Below is a simple list of head. s content:
----------------------------------------------------------------/*
* 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
* 0xc0008000, you call this AT _ Pa (0xc0008000 ).
*
* See Linux/ARCH/ARM/tools/Mach-types for the complete list of machines
* Numbers for R1.
*
* We're 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.
*/
. Section ". Text. Head", "ax"
Entry (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 _ lookup_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 _ vet_atags
BL _ create_page_tables
/*
* The following cils cpu specific code in a position independent
* Manner. See ARCH/ARM/MM/proc-*. s for details. R10 = base
* 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, _ switch_data @ address to jump to after
@ MMU has been enabled
Adr lr, _ enable_mmu @ return (PIC) Address
Add PC, R10, # procinfo_initfunc
Endproc (stext)
# If defined (config_smp)
Entry (secondary_startup)
/*
* Common entry point for secondary CPUs.
*
* Ensure that we're 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.
*/
MSR cpsr_c, # psr_f_bit | psr_ I _bit | svc_mode
MRC P15, 0, R9, C0, C0 @ get processor ID
BL _ lookup_processor_type
Movs R10, R5 @ invalid processor?
Moveq r0, # 'P' @ Yes, error 'P'
Beq _ Error
/*
* Use the page tables supplied from _ cpu_up.
*/
ADR R4, _ secondary_data
Ldmia R4, {R5, R7, R13} @ address to jump to after
Sub R4, R4, R5 @ MMU has been enabled
LDR R4, [R7, R4] @ get secondary_data.pgdir
Adr lr, _ enable_mmu @ return address
Add PC, R10, # procinfo_initfunc @ initialise Processor
@ (Return control REG)
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)
. 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. Essential tially
* This is just loading the page table pointer and domain access
* Registers.
*/
_ 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
* 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, r3
MoV PC, R13
Endproc (_ turn_mmu_on)
# Include "head-common.S"
----------------------------------------------------------------
It may be noted that the large text above is secondary_startup and config_smp. In fact, this is the code used by the SMP system. As we all know, SMP is
The abbreviation of Multi-Processing refers to a group of processors used in the system. Each CPU shares the memory subsystem and bus structure, corresponding to Asymmetric Multi-processing, which is not used on embedded devices.
SMP functions.
At first glance, in any case, the so-called start_kernel function on the Internet cannot be called. For details, see "LDR R13, _ switch_data". Here the function _ switch_data
Save the address to R13 and call _ switch_data in the "mov PC, R13" at the end of the _ enable_mmu -- >__ turn_mmu_on function. While function _ switch_data is real
Now there is a function in/ARCH/$ {arch}/kernel/head-common.S, And the start_kernel function is called by _ switch_data.
You must be wondering how the function _ enable_mmu is called. Haha, you are so intelligent and careful. Now let me tell you the code "add PC,
R10, # procinfo_initfunc "will jump to the initialization function _ arm926_setup in/ARCH/$ {arch}/MM/proc-arn-926.S, and at the end of the function at" mov PC,
LR "method call _ enable_mmu. Do not forget the value of _ enable_mmu mentioned earlier.
As to why the code "add PC, R10, # procinfo_initfunc" will jump to the initialization function _ arm926_setup in/ARCH/$ {arch}/MM/proc-arn-926.S
. You can refer to an article I reproduced later.
----------------------------------------------------------------
. Type _ arm926_setup, # Function
_ Arm926_setup:
MoV r0, #0
MCR P15, 0, R0, C7, C7 @ invalidate I, d caches on v4
MCR P15, 0, R0, C7, C10, 4 @ drain write buffer on v4
# Ifdef config_mmu
MCR P15, 0, R0, C8, C7 @ invalidate I, d tlbs on v4
# Endif
# Ifdef config_cpu_dcache_writethrough
MoV r0, #4 @ disable write-back on caches explicitly
MCR P15, 7, R0, C15, C0, 0
# Endif
ADR R5, arm926_crval
Ldmia R5, {R5, R6}
MRC P15, 0, R0, C1, C0 @ get control register v4
Bic r0, R0, R5
ORR r0, R0, R6
# Ifdef config_cpu_cache_round_robin
ORR r0, R0, #0X4000 @. 1 ..............
# Endif
MoV PC, LR
----------------------------------------------------------------
Now, we finally call start_kernel, which is a common initialization function in any version of Linux kernel.
3. Linux system initialization
As mentioned above, the start_kernel function is a common initialization function in any version of the Linux kernel and the first C function after the assembly code is executed. It is implemented in
Init/Main. C.
The code for start_kernel is very long and many things have been initialized, such as calling setup_arch (), timer_init (), init_irq, lele_init (),
Pgtable_cache_init (), security_init (), signals_init (), and rest_init (). Here, we only perform simple analysis on rest_init.
The following describes the code of rest_init:
----------------------------------------------------------------
/*
* We need to finalize in a non-_ init function or else race conditions
* Between the root thread and the init thread may cause start_kernel
* Be reaped by free_initmem before the root thread has proceeded
* Cpu_idle.
*
* Gcc-3.4 accidentally inlines this function, so use noinline.
*/
Static noinline void _ init_refok rest_init (void)
_ Releases (kernel_lock)
{
Int PID;
Kernel_thread (kernel_init, null, clone_fs | clone_sighand );
Numa_default_policy ();
PID = kernel_thread (kthreadd, null, clone_fs | clone_files );
Kthreadd_task = find_task_by_pid_ns (PID, & init_pid_ns );
Unlock_kernel ();
/*
* The Boot idle thread must execute schedule ()
* At least once to get things moving:
*/
Init_idle_bootup_task (current );
Rcu_scheduler_starting ();
Preempt_enable_no_resched ();
Schedule ();
Preempt_disable ();
/* Call into cpu_idle with preempt disabled */
Cpu_idle ();
}
----------------------------------------------------------------
As you can see, the rest_init () function will first create the thread kernel_init (Note: This is different from what is described on the Internet or in related books, and may be a problem with the Linux version)
Some documents describe that the init thread is created here. Although the names are inconsistent, the specific implementation is basically the same. Basically, it is used to mount the root file system and initialize all Linux
The device driver (that is, the Device Manager in CE/mobile calls the initialization function of the driver) and starts the user space INIT process.
Because the rest_init process in the hands is inconsistent with the descriptions on the Internet, the following code is also briefly listed:
----------------------------------------------------------------
Static int _ init kernel_init (void * unused)
{
Lock_kernel ();
/*
* Init can run on any CPU.
*/
Set_cpus_allowed_ptr (current, cpu_mask_all_ptr );
/*
* Tell the world that we're 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_initcils ();
Start_boot_trace ();
Smp_init ();
Sched_init_smp ();
Cpuset_init_smp ();
Do_basic_setup ();
/*
* 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 ();
}
/*
* OK, we have completed the initial bootup, and
* We're essential up and running. Get rid of
* Initmem segments and start the user-mode stuff ..
*/
Init_post ();
Return 0;
}
Static noinline int init_post (void)
{
/* Need to finish all async _ init code before freeing the memory */
Async_synchronize_full ();
Free_initmem ();
Unlock_kernel ();
Mark_rodata_ro ();
System_state = system_running;
Numa_default_policy ();
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 );
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 .");
}
----------------------------------------------------------------
As you can see, what is different from the network description is that the device driver will be initialized first, instead of loading the file system as described on the Internet or data.
Is there a need to access the file driver during initialization? Or is the previous approach purely a security concern?
These questions will be left for future consideration after a deep understanding of Linux and Android!
Okay, let's get it done !!!