1. An overview of the

I have written several articles on Linux kernel startup, such as:Decompress the kernel image””Call the start_kernel”Are written in assembly language, the role of these code is just to put the kernel image to a specific location, at the same time to configure the RUNNING environment of C language, there is a simple kernel image in the area of the page table Settings, after opening MMU formally began the execution of C language code, The entry point of the C code is the start_kernel function. This article will introduce the set_arch function, which is used to find the data structure information of the given machine ID, configure the memory information, and parse the command line parameters passed by the bootloader. Then, according to the information recorded by the machine_desc structure, some necessary Settings are carried out on the machine. Finally, the complete page table is formally established. The general process is shown in the figure below.

2. set_processor

This function first calls assembly code to find the proc_info data for a given machine ID, and then extracts the Processor structure that contains many of the underlying functions related to task switching.

/* arch/arm/kernel/setup.c */
list = lookup_processor_type(read_cpuid_id());
/* arch/arm/kernel/head-common.S */ENTRY(lookup_processor_type) stmfd sp! , {r4 - r6, r9, lr} mov r9, r0 bl __lookup_processor_type mov r0, r5 ldmfd sp! , {r4 - r6, r9, pc} ENDPROC(lookup_processor_type)Copy the code

The cacheid_init function sets the cacheer-related flag bit based on the CPU ID; Cpu_init calls the processor._proc_init function in the processor you just found, but it does nothing.

/* arch/arm/mm/proc-v7.S */
ENTRY(cpu_v7_proc_init)
 mov pc, lr
ENDPROC(cpu_v7_proc_init)
Copy the code

Sets the stack pointer for different exception modes on the CPU where the kernel is booted.

/* arch/arm/kernel/setup.c::cpu_init */
 __asm__ (
 "msr cpsr_c, %1\n\t"
 "add r14, %0, %2\n\t"
 "mov sp, r14\n\t"
 "msr cpsr_c, %3\n\t"
 "add r14, %0, %4\n\t"
 "mov sp, r14\n\t"
 "msr cpsr_c, %5\n\t"
 "add r14, %0, %6\n\t"
 "mov sp, r14\n\t"
 "msr cpsr_c, %7"
     :
     : "r" (stk),
       PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
       "I" (offsetof(struct stack, irq[0])),
       PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
       "I" (offsetof(struct stack, abt[0])),
       PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE),
       "I" (offsetof(struct stack, und[0])),
       PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
     : "r14");
Copy the code

3. setup_machine_tags

Look for the machine_desc structure based on the machine ID, print a message if it doesn’t find it, and then go down. If early_print is used, according to PrinTK Flow Analysis, the console driver is not registered at this point, so if early_printk is not enabled, the system will silently crash.

/* arch/arm/kernel/setup.c */
for_each_machine_desc(p)
  if (nr == p->nr) {
   printk("Machine: %s\n", p->name);
   mdesc = p;
   break;
  }
Copy the code

After mdesc is found, execute mdesc->fixup(). This call actually executes the cpu_fixup function defined in cpu.c, which sets the number of memory and the corresponding physical start address and size.

/* arch/arm/kernel/setup.c */
if (mdesc->fixup)
 mdesc->fixup(tags, &from, &meminfo);
/* arch/arm/mach-s5p4418/cpu.c */
MACHINE_START(S5P4418, NXP_MACH_NAME)
 .atag_offset =  0x00000100,
 .fixup   =  cpu_fixup,
 .map_io   =  cpu_map_io,
 .init_irq  =  nxp_cpu_init_irq,
#ifdef CONFIG_ARM_GIC
 .handle_irq  =  gic_handle_irq,
#else
 .handle_irq  =  vic_handle_irq,
#endif
 .timer   = &nxp_cpu_sys_timer,
 .init_machine =  cpu_init_machine,
#if defined CONFIG_CMA && defined CONFIG_ION
 .reserve  =  cpu_mem_reserve,
#endif
MACHINE_END
static void __init cpu_fixup(...)
{
 mi->nr_banks      = 1;
 mi->bank[0].start = CFG_MEM_PHY_SYSTEM_BASE;
#if! defined(CFG_MEM_PHY_DMAZONE_SIZE)
 mi->bank[0].size  = CFG_MEM_PHY_SYSTEM_SIZE;
#else
 mi->bank[0].size  = CFG_MEM_PHY_SYSTEM_SIZE + CFG_MEM_PHY_DMAZONE_SIZE;
#endif
}
Copy the code

Tag ->hdr.tag to find the preset parsing function of the corresponding type of tag in the kernel, and then call the parse function of the corresponding type of tag to achieve the parsing of the parameters.

/* arch/arm/kernel/setup.c */
static int __init parse_tag(const struct tag *tag)
{
 extern struct tagtable __tagtable_begin, __tagtable_end;
 struct tagtable *t;

 for (t = &__tagtable_begin; t < &__tagtable_end; t++)
  if (tag->hdr.tag == t->tag) {
   t->parse(tag);
   break;
  }

 return t < &__tagtable_end;
}
/* arch/arm/kernel/setup.h */
struct tagtable {
 __u32 tag;
 int (*parse)(const struct tag *);
};
Copy the code

4. To summarize

In order to avoid a long article, it will be divided into three or four parts. The following is the summary of this article:

  • Setup_processor: searches for machine description information based on the given machine ID, sets the flag bits related to the cache based on the CPU ID, runs the processor._proc_init command to initialize the processor, and sets the stack pointer in different CPU exception modes.
  • Setup_machine_tags: Looks up the machine_desc structure based on the machine ID, then executes the cpu_fixup function to configure the memory information, and finally parses the command-line arguments passed by the bootloader.