sequence

When it comes to Android phones, the first thing you see is the boot process, and Android is built on the Linux kernel. So how do you boot up when you start up? What happened? In this article, I will study with you.

A, an overview of

On Android phones, the startup of the entire system can be divided into three processes, as follows:

  • BIOS and BootLoader phases
  • Linux kernel startup
  • Android Startup

The first stage is mainly done by hardware and assembly language, the second part is mainly done by C language, and the third part is mainly done by Java. Many documents only explain the tasks of the third stage, so we have to go back to the source and look at the boot process from power on.

Second, the startup process

2.1 BIOS and BootLoader Startup

This part is generally designed and implemented by hardware manufacturers. Take x86 as an example. After power-on, the CPU works in real mode, where the ADDRESSING space of the CPU is 1M and the registers are reset to the default value, where the CS:IP reset value is FFFF:0000, which means that the instructions are executed from here. The motherboard design value must ensure that the address is mapped to the program address of the BIOS chip, not RAM. When running the BIOS above the program, the physical address of the first 1KB of memory will be established in real mode interrupt vector table, followed by a part of the BIOS startup detected hardware information. Finally, the BIOS loads the BootLoader to the physical address 0x07c00 based on the configuration information, and then jumps to this address to start the BootLoader. On ARM, the biOS-like operation is performed by a Boot program solidified on ROM, which loads the BootLoader into RAM and then jumps to execution. Header.s is the important assembly code executed at this stage, consisting of two 512 bytes (Boot Sector and setup, loaded to memory addresses 0X00090000 and 0X0009200, respectively). Load the Linux small kernel image to memory address 0X00010000 or the Linux large kernel image to memory address 0X00100000 and jump to the setup code of header.S code. The first 512-byte content was created to be compatible with the floppy drive era. It is placed within a disk sector. The actual kernel entry starts with the second 512 byte, and today’s bootloaders give control to this entry. Header.s stars complete the following missions:

  • Setting up the real-mode stack (ready to run the program)
  • Check the labels in setup
  • Removal of BSS
  • Call C entry main (boot/main.c)

The key codes are as follows:

# We will have entered with %cs = %ds+0x20, normalize %cs so
# it is on par with the other segments.
        pushw   %ds
        pushw   $6f
        lretw
6:

# Check signature at end of setup
        cmpl    $0x5a5aaa55, setup_sig
        jne     setup_bad

# Zero the bss
        movw    $__bss_start, %di
        movw    $_end+3, %cx
        xorl    %eax, %eax
        subw    %di, %cx
        shrw    $2, %cx
        rep; stosl

# Jump to C code (should not return)
        calll   main
Copy the code

2.2 the kernel boot

The main.c function is actually part of the kernel, but there is still a lot of work to be done. Main () handles some registration, copying parameters, setting up memory maps, etc., and then switches to protected mode via go_to_protected_mode(). Go_to_protected_mode () needs to set temporary IDT (interrupt descriptor table) and GDT (global descriptor table), because the IDT and GDT of protected mode and real mode are different, so you must set them in advance. The important code is as follows:

void main(void)
{
	/* First, copy the boot header into the "zeropage" */
	copy_boot_params();

	/* End of heap check */
	init_heap();

	/* Make sure we have all the proper CPU support */
	if (validate_cpu()) {
		puts("Unable to boot - please use a kernel appropriate "
		     "for your CPU.\n");
		die();
	}

	/* Tell the BIOS what CPU mode we intend to run in. */
	set_bios_mode();

	/* Detect memory layout */
	detect_memory();

	/* Set keyboard repeat rate (why?) * /
	keyboard_set_repeat();

	/* Query MCA information */
	query_mca();

	/* Voyager */
#ifdef CONFIG_X86_VOYAGER
	query_voyager();
#endif

	/* Query Intel SpeedStep (IST) information */
	query_ist();

	/* Query APM information */
#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
	query_apm_bios();
#endif

	/* Query EDD information */
#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
	query_edd();
#endif

	/* Set the video mode */
	set_video();

	/* Do the last things and invoke protected mode */
	go_to_protected_mode();
}

Copy the code
void go_to_protected_mode(void)
{
	/* Hook before leaving real mode, also disables interrupts */
	realmode_switch_hook();

	/* Move the kernel/setup to their final resting places */
	move_kernel_around();

	/* Enable the A20 gate */
	if (enable_a20()) {
		puts("A20 gate not responding, unable to boot... \n");
		die();
	}

	/* Reset coprocessor (IGNNE#) */
	reset_coprocessor();

	/* Mask all interrupts in the PIC */
	mask_all_interrupts();

	/* Actual transition to protected mode... * /
	setup_idt();// Interrupt descriptor table
	setup_gdt();// Global descriptor table
	protected_mode_jump(boot_params.hdr.code32_start,
			    (u32)&boot_params + (ds() << 4));
}

Copy the code

The protected_mode_jump() of the last step jumps to pmjump.s, which goes into protected mode by setting cr0, and then goes to code32_start specified in setup header, which is in the second part of header.s, Head_32.s of the compressed image. The code for head_32.s is as follows:

__HEAD ENTRY(startup_32) cld /* * Test KEEP_SEGMENTS flag to see if the bootloader is asking * us to not reload segments  */ testb $(1<<6), BP_loadflags(%esi) jnz 1f cli movl $__BOOT_DS, %eax movl %eax, %ds movl %eax, %es movl %eax, %fs movl %eax, %gs movl %eax, %ssCopy the code

This code calls startup_32 and decompress_kernel() to decompress the Linux kernel image and jump to kernel. The key code is as follows:

 * Do the decompression, and jump to the new kernel..
 */
	movl output_len(%ebx), %eax
	pushl %eax
	pushl %ebp	# output address
	movl input_len(%ebx), %eax
	pushl %eax	# input_len
	leal input_data(%ebx), %eax
	pushl %eax	# input_data
	leal boot_heap(%ebx), %eax
	pushl %eax	# heap area as third argument
	pushl %esi	# real mode pointer as second arg
	call decompress_kernel
	addl $20, %esp
	popl %ecx

/*
 * Jump to the decompressed kernel.
 */
	xorl %ebx,%ebx
	jmp *%ebp
Copy the code

The location of start_kernel is defined in vmlinux.lds, which is called eventuallystart_kernel()Function. The start_kernel function initializes the entire Linux kernel, including process scheduling, memory management, system time, etc., and finally calls kernel_thread() to create the init process. The flow to this point can look like this:At this point, the Linux kernel is basically booted, but there are three special processes in Linux: Idle (swapper) (PID = 0), init (PID = 1), and kthreadd (PID = 2). Its functions and features are as follows:

  • The Idle (Swapper) process is automatically created by the system and runs in kernel mode

The idle process, whose PID is 0, is the first process created in the system and the only one that is not generated through fork or kernel_thread. After loading the system, it evolves into process scheduling and switching, often referred to as switching process.

  • The init process is created by Idle using kernel_thread. After the initialization of the kernel space, the init program is loaded, and the init process is changed into the user space

Created by process 0 to complete system initialization, it is the ancestor of all other user processes in the system. All processes in Linux are created and run by the init process. First the Linux kernel starts, then the init process starts in user space, and then the other system processes. After the system is started, init becomes a daemon to monitor other system processes.

  • The kthreadd process is created by Idle through kernel_thread and always runs in kernel space, responsible for scheduling and management of all kernel threads

Its job is to manage and schedule other kernel_threads. It loops through a kthreadd function that runs the kthreads maintained in the global kthread_create_list. The kernel threads we create by calling kernel_thread are added to the list, so all kernel threads are directly or indirectly parent to KthreadD.

Third, summary

At this point, the Linux kernel has been booted up, and now the Android system will be booted up. I will study with you in the next article, and I hope you can correct the loose points in my article.