Kernel boot phase 1

Posted by SlyOne on Sat, 19 Feb 2022 15:25:19 +0100


This post is a rough summary of the class notes and my own insights from Mr. Wei Dongshan's linux kernel tutorial. If you want to know more about linux kernel, please detour.

There are many contents in the linux startup process. This paper only briefly analyzes the kernel startup process. The assembly code initialization process is called the first stage; The C code is called the second stage.

meet Above Analysis shows that the first file of kernel startup is: arch / arm / kernel / head S,

The link script is: linux- \ arch \ arm \ kernel \ vmlinux lds

This paper focuses on how to find the corresponding information in the code according to the segment attribute content of the link script.

The segment attribute of link script is actually that some structure data of C code is forced to be set as this segment attribute, and the data with the same segment attribute is placed together.

Related posts:

Simple analysis of kernel Makefile file

Kernel configuration file analysis -- with CONFIG_DM9000 as an example

Initial experience of kernel: compiling and downloading

uboot starts the second phase

uboot starts the first phase

UBOOT initial experience: compiling and downloading


1 - preliminary analysis head S

from Simple analysis of kernel Makefile file Analysis shows that the first file of kernel startup is: arch / arm / kernel / head S

head.S is mainly used to verify relevant parameters, establish page table, enable MMU, etc., and then enter C code start_kernel.

Reasons for creating page table and enabling MMU:

Kernel link script linux- \ arch \ arm \ kernel \ vmlinux The starting address of virtual operation defined by LDS is 0xc0008000, but the starting address of physical address of SDRAM is 0x3000 0000 (the physical address of kernel operation is 0x3000 8000) The purpose of creating page table and enabling mmu is to establish a mapping relationship between the virtual address of the kernel and the physical address of SDRAM.

head.S code is listed as follows:

    bl	__lookup_processor_type		@ r5=procinfo r9=cpuid Check processor type
    bl	__lookup_machine_type		@ r5=machinfo Check machine type
    bl	__create_page_tables        @ Create page table
	 * The following calls CPU specific code in a position independent
	 * manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of
	 * 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  Enable mmu Jump to after__switch_data
	adr	lr, __enable_mmu		@ return (PIC) address  Enable mmu

    .type	__mmap_switched, %function
    b	start_kernel

Next, analyze the interface__ lookup_machine_type details.

2 - how does the kernel compare the machine ID passed in by uboot__ lookup_machine_type

uboot starts the second phase Finally, the kernel will be started and the startup parameters will be passed to the kernel

theKernel (0, bd->bi_arch_number, bd->bi_boot_params)

Where BD - > Bi_ arch_ Number is the machine ID information passed in by uboot, which is in the kernel head S will check whether the machine ID passed in by uboot matches that defined by the kernel.

uboot incoming BD - > Bi_ arch_ The number parameter is placed in the r1 register.

How does the kernel match the machine ID parameter passed in by uboot

The machine ID information that the kernel can support is saved in__ arch_info_begin to__ arch_info_end address range, which stores the information of multiple devices that the kernel can support. Whether the ID of these devices matches the machine ID passed in by uboot.

The detailed matching process is as follows:

 * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for
 * more information about the __proc_info and __arch_info structures.
	.long	__proc_info_begin
	.long	__proc_info_end
3:	.long	.
	.long	__arch_info_begin
	.long	__arch_info_end

 * Lookup machine architecture in the linker-build list of architectures.
 * Note that we can't use the absolute addresses for the __arch_info
 * lists since we aren't running with the MMU on (and therefore, we are
 * not in the correct address space).  We have to calculate the offset.
 *  r1 = machine architecture number
 * Returns:
 *  r3, r4, r6 corrupted
 *  r5 = mach_info pointer in physical address space
	.type	__lookup_machine_type, %function
	adr	r3, 3b               @r3=3b Address. The address here is the physical address
	ldmia	r3, {r4, r5, r6} @r4=.; r5=__arch_info_begin; r6=__arch_info_end there r4-r6 All virtual addresses
	sub	r3, r3, r4			@ get offset between virt&phys Calculation 3 b The deviation between physical address and virtual address is saved in r3
	add	r5, r5, r3			@ convert virt addresses to calculation r5 Physical address of
	add	r6, r6, r3			@ physical address space    calculation r6 Physical address of
1:	ldr	r3, [r5, #MACHINFO_TYPE] 	@  get machine type__ arch_ info_ Machinfo corresponding to begin_ Type information
	teq	r3, r1				@ matches loader number?  contrast uboot Incoming machine ID and__arch_info_begin in MACHINFO_TYPE Are the values the same
	beq	2f				@ found
	add	r5, r5, #SIZEOF_MACHINE_DESC 	@  next machine_desc does not match, calculate next__ arch_ info_ Machinfo in begin_ Is the type value the same as the machine ID passed in by uboot
	cmp	r5, r6
	blo	1b
	mov	r5, #0				@ unknown machine
2:	mov	pc, lr

__ arch_info_begin   __ arch_ info_ Where is end defined

Among them__ arch_info_begin,__ arch_info_end is defined in the connection script linux- \ arch \ arm \ kernel \ vmlinux In LDS. All are defined as arch info. The segment of the init attribute is placed in the_ arch_info_begin and__ arch_ info_ Between end addresses

 . = (0xc0000000) + 0x00008000;
  __arch_info_begin = .;
  __arch_info_end = .;

How to view those code defined as segment attribute arch info. init

Search the kernel source code for arch info. init

# grep "" -nR ./
./include/asm/mach/arch.h:53: __attribute__((__section__(""))) = {       \
./include/asm-arm/mach/arch.h:53: __attribute__((__section__(""))) = {   \
Binary file ./arch/arm/mach-s3c2410/mach-smdk2410.o matches
Binary file ./arch/arm/mach-s3c2410/mach-qt2410.o matches
Binary file ./arch/arm/mach-s3c2410/built-in.o matches
Binary file ./arch/arm/mach-s3c2412/mach-smdk2413.o matches
Binary file ./arch/arm/mach-s3c2412/mach-vstms.o matches
Binary file ./arch/arm/mach-s3c2412/built-in.o matches
./arch/arm/kernel/                     *(
./arch/arm/kernel/   *(
Binary file ./arch/arm/mach-s3c2443/mach-smdk2443.o matches
Binary file ./arch/arm/mach-s3c2443/built-in.o matches
Binary file ./arch/arm/mach-s3c2440/mach-smdk2440.o matches
Binary file ./arch/arm/mach-s3c2440/built-in.o matches

You know/ include/asm-arm/mach/arch. Line h 53 has arch info. Init definition

 * Set of macros to define architecture features.  This is built into
 * a table by the linker.
#define MACHINE_START(_type,_name)			\
static const struct machine_desc __mach_desc_##_type	\
 __used							\
 __attribute__((__section__(""))) = {	\
	.nr		= MACH_TYPE_##_type,		\
	.name		= _name,

#define MACHINE_END				\

We use S3C2440 to search for linux- \ arch \ arm \ mach-s3c2440 \ mach-smdk2440 Machine in C file_ Start definition

	/* Maintainer: Ben Dooks <> */
	.phys_io	= S3C2410_PA_UART,
	.io_pg_offst	= (((u32)S3C24XX_VA_UART) >> 18) & 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,

Set mach-smdk2440 Machine in C_ START,MACHINE_END uses arch The macro of H is expanded as follows:

static const struct machine_desc __mach_desc_S3C2440	
 __used							\
 __attribute__((__section__(""))) = {	
	.nr		= MACH_TYPE_S3C2440,		\
	.name		= "SMDK2440",
    .phys_io	= S3C2410_PA_UART,
	.io_pg_offst	= (((u32)S3C24XX_VA_UART) >> 18) & 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,

Struct machine_ The data of desc structure type is defined as segment attribute

Except 2440, mach-smdk2410 c,mach-qt2410. Struct machine defined in C and other files_ The desc structure is also placed in the segment. The attribute is arch. info. In the address of init.

In the kernel, ldr, #r3, [r5, #MACHINFO_TYPE] is to obtain struct machine_desc structure unsigned int nr member variable information, i.e. machine ID. (why? nr I don't know...)

Topics: Linux