Based on MIPS architecture, this paper analyzes the linux-3.3.8
I. Kernel entrance
First, find the portal of kernel through a link script.
1. The kernel is compressed
/arch/mips/boot/compressed/makefile $ (obj)/head.o $ (obj)/decompress.o $ (obj)/dbg.o $ (obj)/piggy.o link to Vmlinuz, Link script for Ld.script vmlinuz: $ (SRC)/ld.script $ (vmlinuzobjs-y) $ (obj)/calc_vmlinuz_load_addr $ (call Cmd,zld) $ (call cmd , strip)
The vmlinux.bin.z is used as the Objcopy tool, packaged as data for PIGGY.O targets + = piggy.o objcopyflags_piggy.o: =--add-section=.image=$ (obj)/ VMLINUX.BIN.Z \--set-section-flags=.image=contents,alloc,load,readonly,data $ (obj)/piggy.o: $ (obj )/dummy.o $ (obj)/vmlinux.bin.z FORCE $ (call if_changed,objcopy)
Compress vmlinux.bin using compression tool to Vmlinux.bin.z tool_$ (config_kernel_gzip) = GZIP tool_$ (config_kernel_bzip2) = bzip2 tool_$ (CONFI G_kernel_lzma) = Lzma tool_$ (config_kernel_lzo) = Lzo
Targets + + vmlinux.bin.z $ (obj)/vmlinux.bin.z: $ (obj)/vmlinux.bin FORCE $ (call if_changed,$ (tool_y))
Visible in compression is the entrance for/arch/mips/boot/compressed/head.s
2. The kernel is uncompressed
The definition makefile Specifies the vmlinux link script:
Vmlinux-lds: = arch/$ (Srcarch)/kernel/vmlinux.lds, compiled VMLINUX.LDS.S by Vmlinux.lds:OUTPUT_ARCH (MIPS) ENTRY (kernel_ Entry) Phdrs {text Pt_load flags (7);/* RWX * * Pt_note flags (4);/* r__/} jiffies = jiffies_64; SECTIONS {. = 0x80001000/* read-only/_text =./* Text and read-only data/. Text: {. = ALIGN (8); * (. text.hot) * (. Text) * (. Ref.text) * (. Devinit.text) * (. Devexit.text) * (. Cpuinit.text) * (. Cpuexit.text) * (. text.unlikely). = ALIGN (8); __sched_text_start =.; * (. sched.text) __sched_text_end =.; . = ALIGN (8); __lock_text_start =.; * (. spinlock.text) __lock_text_end =.; . = ALIGN (8); __kprobes_text_start =.; * (. kprobes.text) __kprobes_text_end =.; * (. text.*) * (. Fixup) * (. gnu.warning)}: Text = 0 _etext =.; /* End of text section * *
Visible Entrance for Kernel_entry
This only describes the uncompressed situation, because the compression is uncompressed after the jump to the kernel execution process is the same.
(ARCH/MIPS/KERNEL/HEAD.S) NESTED (Kernel_entry, SP) # Kernel entry point
Kernel_entry_setup # CPU Specific setup
Setup_c0_status_pri
/* We might not get launched in the address the kernel are linked to, so We jump there. * * Ptr_la t0, 0f Jr t0 0:ptr_la t0, __bss_start # Clear. BSS//Will kernel BSS Chingqing zero long_s Zero, (t0) Ptr_la T1, __bss_stop-longsize 1:ptr_addiu t0, longsize long_s Zero, (t 0) bne t0, T1, 1b
long_s a0, fw_arg0 # Firmware arguments long_s A1, Fw_arg1 long_s A2, Fw_arg2 LONG _s A3, Fw_arg3
mtc0 zero, CP0_CONTEXT # Clear Context Register ptr_la $28, Init_thread_union //union thread_union init_thread_union, $28 Register pointing init_ Thread_union /* Set The SP after a empty pt_regs. */ ptr_li         SP, _thread_size-32-pt_size ptr_addu     SP, $28   //SP registers pointing to the stack space in init_thread_union back_to_back_c0_hazard //sync C0 to prevent c0 adventures, see below for explanations set_saved_sp   SP, t0, T1 //Save the value of the SP register to the KERNELSP variable, and save the process kernel state stack pointer in the KERNELSP variable ptr_ SUBU    SP, 4 * szreg # init stack pointer
J Start_kernel/Start C code, INIT/MAIN.C end (Kernel_entry)
The definition of Kernel_entry_setup is located at:
(arch/mips/include/asm/mach-brcmstb/kernel-entry-init.h). Macro Kernel_entry_setup # Save arguments for CFE Callba CK SW A0, Cfe_handle//Saves the contents of the A0 registers to Cfe_handle, in order to kernel the functions in CFE to obtain kernel boot functions and other hardware configuration parameters, which are stored in the CFE environment variable SW A2, Cfe_entry//Save the contents of the A2 registers to cfe_entry SW A3 cfe_seal//Save the contents of the A3 registers to Cfe_seal JAL Nable_xks01. ENDM. Macro Smp_slave_setup. endm
(ARCH/MIPS/KERNEL/BMIPS_VEC.S)/*********************************************************************** * XKS01 Support * Certain CPUs support extending kseg0 to 1024MB. /LEAF (BMIPS_ENABLE_XKS01)
#if defined (config_xks01) mfc0 t0, $22, 3 li T1, 0x1ff0 Li T2, (1 << 12) | (1 << 9) or t0 T1 xor t0, T1 or t0, T2 mtc0 t0, $22, 3 BARRIER #endif/* Defined (CO NFIG_XKS01) * * *
JR RA
End (BMIPS_ENABLE_XKS01)
The definition of Setup_c0_status_pri is located at:
(ARCH/MIPS/KERNEL/HEAD.S). Macro Setup_c0_status_pri #ifdef config_64bit setup_c0_status st0_kx 0 #else Set Up_c0_status 0 0//will c0 status register clear 0 #endif. endm
Arch/mips/include/asm/hazards.h: _EHB assembly instruction, for synchronous c0, to prevent C0 Adventures Asmmacro (Back_to_back_c0_hazard, _EHB)
Current represents the process, let's look at its definition: #define Get_current () (Current_thread_info ()->task) #define Present get_current ()
Register struct Thread_info *__current_thread_info __asm__ ("$28"); #define CURRENT_THREAD_INFO () __current_thread_info
__current_thread_info is a register variable, $28 represents the GP register
So here Ptr_la $28, Init_thread_union is actually set to process No. 0,
Ptr_li sp, _thread_size-32-pt_size
Ptr_addu sp, $28
sp= $28 + _thread_size-32-pt_size=$28 +8192-32-sizeof (struct pt_regs)
Union thread_union {
#ifndef config_x86
struct Thread_info thread_info;
#endif
unsigned long stack[thread_size/sizeof (long)];
};
struct Thread_info {
struct Task_struct *task; /* Main task structure/*
struct Exec_domain *exec_domain; * Execution Domain * *
unsigned long flags; /* Low Level flags */
unsigned long tp_value; /* Thread pointer * *
__U32 CPU; /* Current CPU */
int preempt_count; /* 0 => preemptable, <0 => BUG * *
mm_segment_t Addr_limit; /* Thread Address spaces:
0-0XBFFFFFFF for User-thead
0-0XFFFFFFFF for Kernel-thread
*/
struct Restart_block restart_block;
struct Pt_regs *regs;
};
$28---------→+----------------+ low Address
| *task |
| ... |
| *regs | ----------------------------------+
grow↑| xxxxxxxxx | |
| | xxxxxxxxx | |
SP---------> | xxxxxxxxx | |
| Byte (MB) | |
| sizeof (Pt_regs) | ←------------------------------+
This allows the SP to switch to the kernel stack on the No. 0 process, and after setting the stack, you can perform the C-defined function.
two. Enter Start_kernel
asmlinkage void __init start_kernel (void)
{
Boot_init_stack_canary ();
Local_irq_disable ();
Tick_init ();
Setup_arch (&command_line);
Build_all_zonelists (NULL);
Page_alloc_init ();
Trap_init ();
Mm_init ();
Sched_init ();
INIT_IRQ ();
Prio_tree_init ();
Init_timers ();
Hrtimers_init ();
Softirq_init ();
Timekeeping_init ();
Time_init ();
Local_irq_enable ();
...
/* Do the rest non-__init ' Ed, we ' re now alive * *
Rest_init ();
}
static noinline void __init_refok rest_init (void)
{
int pid;
/*
* We need to spawn Init, it obtains PID 1, however
* The INIT task would end up wanting to create kthreads, which, if
* We schedule it before we create Kthreadd, would OOPS.
*/
Kernel_thread (Kernel_init, NULL, CLONE_FS | Clone_sighand); Create the Init process, that is, the number 1th process
Rcu_read_lock ();
Kthreadd_task = Find_task_by_pid_ns (PID, &init_pid_ns);
Rcu_read_unlock ();
Complete (&kthreadd_done);
/*
* The boot idle thread must execute schedule ()
* At least once to get things moving:
*/
Init_idle_bootup_task (current);
Preempt_enable_no_resched ();
Schedule ();
/* Call to Cpu_idle with preempt disabled * *
Preempt_disable ();
Cpu_idle (); Process # No. 0
}
static int __init kernel_init (void * unused)
{