Preface:
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-2.6.22.6 \ 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 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-2.6.22.6 \ 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:
linux-2.6.22.6\arch\arm\kernel\head.S ENTRY(stext) 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 linux-2.6.22.6\arch\arm\kernel\head-common.S __switch_data: .type __mmap_switched, %function __mmap_switched: 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 __lookup_machine_type: 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-2.6.22.6 \ 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
#linux-2.6.22.6\arch\arm\kernel\vmlinux.lds SECTIONS { . = (0xc0000000) + 0x00008000; ... __arch_info_begin = .; *(.arch.info.init) __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 "arch.info.init" -nR ./ ./include/asm/mach/arch.h:53: __attribute__((__section__(".arch.info.init"))) = { \ ./include/asm-arm/mach/arch.h:53: __attribute__((__section__(".arch.info.init"))) = { \ 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/vmlinux.lds.S:39: *(.arch.info.init) ./arch/arm/kernel/vmlinux.lds:306: *(.arch.info.init) 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
#linux-2.6.22.6\include\asm-arm\mach\arch.h /* * 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__(".arch.info.init"))) = { \ .nr = MACH_TYPE_##_type, \ .name = _name, #define MACHINE_END \ };
We use S3C2440 to search for linux-2.6.22.6 \ arch \ arm \ mach-s3c2440 \ mach-smdk2440 Machine in C file_ Start definition
#linux-2.6.22.6\arch\arm\mach-s3c2440\mach-smdk2440.c MACHINE_START(S3C2440, "SMDK2440") /* Maintainer: Ben Dooks <ben@fluff.org> */ .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, MACHINE_END
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__(".arch.info.init"))) = { .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 arch.info.init
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...)