|Android| Android system powers up flow from kernel to user space

It is more detail the last version.

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

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
ref-2.

2. kernel self decompress

kernel/lib/inflate.c ref.

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

// 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 !!!


Note:
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:
    */
   lockdep_init();
   set_task_stack_end_magic(&init_task);
   smp_setup_processor_id();
   debug_objects_early_init();
 
   cgroup_init_early();
 
   local_irq_disable();
   early_boot_irqs_disabled = true;
 
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
   boot_cpu_init();
   page_address_init();
   pr_notice("%s", linux_banner);
   setup_arch(&command_line);
   /*
    * Set up the the initial canary ASAP:
    */
   boot_init_stack_canary();
   mm_init_cpumask(&init_mm);
   setup_command_line(command_line);
   setup_nr_cpu_ids();
   setup_per_cpu_areas();
   smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
 
   build_all_zonelists(NULL, NULL);
   page_alloc_init();
 
   pr_notice("Kernel command line: %s\n", boot_command_line);
   /* parameters may set static keys */
   jump_label_init();
   parse_early_param();
   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()
    */
   setup_log_buf(0);
   pidhash_init();
   vfs_caches_init_early();
   sort_main_extable();
   trap_init();
   mm_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.
    */
   sched_init();
   /*
    * Disable preemption - early bootup scheduling is extremely
    * fragile until we cpu_idle() for the first time.
    */
   preempt_disable();
   if (WARN(!irqs_disabled(),
        "Interrupts were enabled *very* early, fixing it\n"))
       local_irq_disable();
   idr_init_cache();
   rcu_init();
 
   /* trace_printk() and trace points may be used after this */
   trace_init();
 
   context_tracking_init();
   radix_tree_init();
   /* init some links before init_ISA_irqs() */
   early_irq_init();
   init_IRQ();
   tick_init();
   rcu_init_nohz();
   init_timers();
   hrtimers_init();
   softirq_init();
   timekeeping_init();
   time_init();
   sched_clock_postinit();
   perf_event_init();
   profile_init();
   call_function_init();
   WARN(!irqs_disabled(), "Interrupts were enabled early\n");
   early_boot_irqs_disabled = false;
   local_irq_enable();
 
   kmem_cache_init_late();
 
   /*
    * 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.
    */
   console_init();
   if (panic_later)
       panic("Too many boot %s vars at `%s'", panic_later,
             panic_param);
 
   lockdep_info();
 
   /*
    * Need to run this when irqs are enabled, because it wants
    * to self-test [hard/soft]-irqs on/off lock inversion bugs
    * too:
    */
   locking_selftest();
 
#ifdef CONFIG_BLK_DEV_INITRD
   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)),
           min_low_pfn);
       initrd_start = 0;
   }
#endif
   page_ext_init();
   debug_objects_mem_init();
   kmemleak_init();
   setup_per_cpu_pageset();
   numa_policy_init();
   if (late_time_init)
       late_time_init();
   sched_clock_init();
   calibrate_delay();
   pidmap_init();
   anon_vma_init();
   acpi_early_init();
#ifdef CONFIG_X86
   if (efi_enabled(EFI_RUNTIME_SERVICES))
       efi_enter_virtual_mode();
#endif
#ifdef CONFIG_X86_ESPFIX64
   /* Should be run before the first non-init thread is created */
   init_espfix_bsp();
#endif
   thread_stack_cache_init();
   cred_init();
   fork_init();
   proc_caches_init();
   buffer_init();
   key_init();
   security_init();
   dbg_late_init();
   vfs_caches_init();
   signals_init();
   /* rootfs populating might need page-writeback */
   page_writeback_init();
   proc_root_init();
   nsfs_init();
   cpuset_init();
   cgroup_init();
   taskstats_init_early();
   delayacct_init();
 
   check_bugs();
 
   acpi_subsystem_init();
   sfi_init_late();
 
   if (efi_enabled(EFI_RUNTIME_SERVICES)) {
       efi_late_init();
       efi_free_boot_services();
   }
 
   ftrace_init();
 
   /* Do the rest non-__init'ed, we're now alive */
   rest_init();
}


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_mss_driver_probe:
       pil_subsys_init -> (load modem image)
               modem_powerup
               -> pil_boot
   —> 2. open /dev/console 
   —> 3. to find “init” program

kernel_init:


kernel_init_freeable()

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

system/core/init/main.cpp

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…