Android system powers up flow from kernel to user space

Main reference.
There is a beautiful kernel power up figure.

0. Boot ROM
 - Loads the BootLoader into RAM and starts executing.

1. Bootloader: ({android}/bootable/bootloader)

In Qualcomm, its bootloader called as little kernel.

It takes responsibilities for closing interrupting, initializing hardwares, load linux kernel and ramdisk to RAM.
Setting for initial registers and core command parameters, then transfer to kernel (do_bootm_linux).

– about devices trees
Qualcomm security boot
Qualcomm bootloader

2. kernel self decompress

kernel/lib/inflate.c ref.

3. Until calling  start_kernel(), it is really to begin the initialization of the kernel.

  Loads drivers
  Starts kernel daemons
  Mounts root file system
  Initializing Input/Output
  Starts interrupts
  Initializes process table ……
 - Looks for “init” in system files
 - Launch root process

// kernel/msm-4.4/init/main.c :
key functions in __init start_kernel(void):
—> vfs_caches_init(totalram_pages);  // file system, including kernfs, sysfs, rootfs, mount tree
—> proc_root_init();  // /proc, /proc/fs, /proc/driver, …
  —> cpu_startup_entry 
    —> cpu_idle_loop   —> pid = 0 idle process
  —>kernel_thread(kernel_init, —> pid = 1
  —>kernel_thread(kthreadd,   —> pid = 2

kernel_init will bring up user space !!!

pid = 0: scheduler
pid = 1: init/systemd (user thread’s ancestor)
pid = 2: kthreadd (kernel thread’s father)

asmlinkage __visible void __init start_kernel(void){
   char *command_line;
   char *after_dashes;
    * Need to run as early as possible, to initialize the
    * lockdep hash:
   early_boot_irqs_disabled = true;
* Interrupts are still disabled. Do necessary setups, then
* enable them
   pr_notice("%s", linux_banner);
    * Set up the the initial canary ASAP:
   smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
   build_all_zonelists(NULL, NULL);
   pr_notice("Kernel command line: %s\n", boot_command_line);
   /* parameters may set static keys */
   after_dashes = parse_args("Booting kernel",
                 static_command_line, __start___param,
                 __stop___param - __start___param,
                 -1, -1, NULL, &unknown_bootoption);
   if (!IS_ERR_OR_NULL(after_dashes))
       parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
              NULL, set_init_arg);
    * These use large bootmem allocations and must precede
    * kmem_cache_init()
    * Set up the scheduler prior starting any interrupts (such as the
    * timer interrupt). Full topology setup happens at smp_init()
    * time - but meanwhile we still have a functioning scheduler.
    * Disable preemption - early bootup scheduling is extremely
    * fragile until we cpu_idle() for the first time.
   if (WARN(!irqs_disabled(),
        "Interrupts were enabled *very* early, fixing it\n"))
   /* trace_printk() and trace points may be used after this */
   /* init some links before init_ISA_irqs() */
   WARN(!irqs_disabled(), "Interrupts were enabled early\n");
   early_boot_irqs_disabled = false;
    * HACK ALERT! This is early. We're enabling the console before
    * we've done PCI setups etc, and console_init() must be aware of
    * this. But we do want output early, in case something goes wrong.
   if (panic_later)
       panic("Too many boot %s vars at `%s'", panic_later,
    * Need to run this when irqs are enabled, because it wants
    * to self-test [hard/soft]-irqs on/off lock inversion bugs
    * too:
   if (initrd_start && !initrd_below_start_ok &&
       page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
       pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",
           page_to_pfn(virt_to_page((void *)initrd_start)),
       initrd_start = 0;
   if (late_time_init)
#ifdef CONFIG_X86
   if (efi_enabled(EFI_RUNTIME_SERVICES))
#ifdef CONFIG_X86_ESPFIX64
   /* Should be run before the first non-init thread is created */
   /* rootfs populating might need page-writeback */
   if (efi_enabled(EFI_RUNTIME_SERVICES)) {
   /* Do the rest non-__init'ed, we're now alive */

4. for bring up user space, consider pid = 1:

Key functions in kernel_init:
 —> kernel_init_freeable()
   —> 1. do_basic_setup
      initialize device, driver, rootfs
      mount /dev, /sys … virtual file systems
      —> peripheral image loader (PIL) driver
       PIL: in qualcomm design, it used for loading QDSP6v5 (Hexagon) firmware images
       for modem subsystems into memory and
       preparing the subsystem’s processor to execute code.)
       pil_subsys_init -> (load modem image)
               -> pil_boot
   —> 2. open /dev/console 
   —> 3. to find “init” program



5. If find the init success, then running Android init process


There are 3 stages. (since Android Q)
Second: setting selinux
Third: PropertyInit and load init.rc (LoadBootScripts)

init.cpp : creating adb, rild, …

init.rc will bring the Zygote, which creates the SystemServer and all Android services.

picture source
– about property selinux

6. After systemserver …

  • Entropy Service
  • Power Manager
  • Activity Manager
  • Telephony Registry
  • Package Manager
  • Battery Service
  • Lights Service
  • Vibrator Service
  • Alarm Manager
  • Window Manager
  • Bluetooth Service
  • and many more…

Addition refs.

Learn device tree from bootlin