10 key steps in linux boot to init (arm64) https://elixir.bootlin.com/linux/v5.10.208/source 0-kernel program starts arch/arm64/kernel/head.S efi -> primary_entry -> __create_page_tables, __primary_switch - 70,82: magic numbers - 107: el2_setup -- privilege levels - 276: set up the basic page table 1-configure hardware, including virtual addressing __primary_switch -> __enable_mmu 2-data structure for 0th process __primary_switch -> __primary_switched -> init_cpu_task, start_kernel init_task statically defined init/init_task.c - adrp: address of 4KB page at a PC-relative offset 3-initialize scheduler start_kernel -> sched_init in kernel/sched/core.c initialize scheduler on boot cpu (1 cpu), initializes cpu's rq, etc. sched_init -> init_idle in kernel/sched/core.c init_idle sets current task (ie init_task) to idle task for boot cpu 4-0th process "forks" 1st process to run kernel_init 1st process put in runqueue init/main.c start_kernel -> arch_call_rest_init -> rest_init starts init kernel_thread(kernel_init, NULL, CLONE_FS) - Kernel threads are not children of init because they can be started before all the userspace processes They are typically used to manage hardware that's why they are directly handled by the kernel and have high priority. For a process to be child of init it needs to be cloned from init and Kthreads aren't that's why their parent PID is 2 meaning kthreadd. 5-0th process idle task, calls schedule init/main.c rest_init -> schedule_preempt_disabled, cpu_startup_entry kernel/sched/core.c schedule_preempt_disabled -> schedule cpu_startup_entry -> while(1) do_idle() do_idle loops if nothing to run, otherwise calls schedule_idle -> schedule 6-schedule context switches to some other process from runqueue kernel/sched/core.c schedule -> __schedule -> context_switch -> switch_to include/asm-generic/switch_to.h switch_to -> __switch_to arch/arm64/kernel/process.c __switch_to -> cpu_switch_to arch/arm64/kernel/asm-offsets.c THREAD_CPU_CONTEXT thread.cpu_context arch/arm64/kernel/entry.S cpu_switch_to add x8, x1, x10 (next task_struct cpu_context) ldp instructions load values from cpu_context and update x8 ldr lr, [x8] loads pc from cpu_context into linker register msr sp_el0, x1 (current to next task) ret returns to pc in linker register 7-1st process will run and do ret_from_fork arch/arm64/kernel/entry.S ret_from_fork -> schedule_tail, x19 (kernel_init) kernel_init does kernel_init_freeable and run_init_process kernel_init_freeable -> prepare_namespace -> mount_root run_init_process -> kernel_execve using "/init" or "/sbin/init" (process 1) - how does "/init" or "/sbin/init" exist? 8-init (systemd) regular program, reads config file, starts other processes 9-at some point, will return to kernel code when back in kernel code, kernel regains control, can schedule something else, etc. for example, system call causes function in kernel to run Credits to Jason Nieh