Android brings up flow

First, Boot ROM  - Loads the BootLoader into RAM and starts executing. Second, BootLoader  - Start up and find the system kernel.  - Bootloader is a place where manufacturers put their locks   and restrictions.  - Detects external RAM  - Setups the network, memory … etc, which requires to run Kernel. Third, Kernel  - Setup cache   Protected memory   Scheduling   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 Forth, Init (user space)  - Mounts directories like /sys, /dev or/proc  - Runs /init.rc script. The init.rc is responsible for the initial set up of the system. Fifth, (Android) Zygote:  - VM process that starts as the system boots  - app_process launces Zygote Sixth, SystemServer  - Load a native library called android_servers
--------------------------------------------------------------------------------
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).
2. kernel self decompress
kernel/lib/inflate.c ref.​
3. Until calling start_kernel(), it is really to begin the initialization of the kernel.
  Scheduling   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 !!! 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…
Learn device tree from bootlin https://bootlin.com/blog/device-tree-101-webinar-slides-and-videos/
​