RK Android系统开机启动流程

一、芯片上电启动流程

芯片上电解复位后执行第一段程序的执行地址指向0x00000000或0xffff0000,这段程序被称为Bootrom loader,该段程序在芯片制造过程中固化到其内部的ROM空间,只读不可修改

1、启动片内bootrom程序后,根据设置的启动模式,决定从什么介质启动,sd-card或emmc;所有的存储设备的bootloader都无法校验通过,则会自动进入MaskRom模式

2、初始化ddr

3、bootloader完整代码到DDR内存中并运行

b506992c2e1172dfbb1b4009b2790145.png

二、U-BOOT启动流程

RK平台的U-Boot 启动流程如下,仅列出一些重要步骤,参考rk文档

start.s
    // 汇编环境
    => IRQ/FIQ/lowlevel/vbar/errata/cp15/gic 
    => _main
    => stack 
    // ARM架构相关的lowlevel初始化
    // 准备好C环境需要的栈    
    //【第一阶段】C环境初始化,发起一系列的函数调用
    => board_init_f: init_sequence_f[]
        initf_malloc
        arch_cpu_init                                    // SoC的lowlevel初始化
        serial_init                                          //   串口初始化
        dram_init                                          //   获取ddr容量信息
        reserve_mmu                                   // 从ddr末尾开始往低地址reserve内存
        reserve_video
        reserve_uboot
        reserve_malloc
        reserve_global_data
        reserve_fdt
        reserve_stacks  
        dram_init_banksize                           
        sysmem_init
        setup_reloc                                       // 确定U-Boot自身要reloc的地址
    // 汇编环境
    => relocate_code                                  // 汇编实现U-Boot代码的relocation
    // 第二阶段:C环境初始化,发起一系列的函数调用
    => board_init_r: init_sequence_r[]
        initr_caches                                       // 使能MMU和I/Dcache
        initr_malloc
        bidram_initr
        sysmem_initr
        initr_of_live                                        // 初始化of_live
        initr_dm                                             // 初始化dm框架
        board_init                                          // 平台初始化,最核心部分
            board_debug_uart_init                  // 串口iomux、clk配置
            init_kernel_dtb                               // 初始化dtb
            clks_probe                                     // 初始化系统频率
            regulators_enable_boot_on           // 初始化系统电源
            io_domain_init                                //io-domain初始化
            set_armclk_rate
            dvfs_init 
            rk_board_init 
        console_init_r
        board_late_init                                    // 平台late初始化
            rockchip_set_ethaddr                     // 设置mac地址
            rockchip_set_serialno                    // 设置serialno 
            setup_boot_mode                          // 解析reboot XX命令
            charge_display                               
            rockchip_show_logo                       // 显示开机logo
            soc_clk_dump                                 // 打印clk tree
            rk_board_late_init
                boot_jump_linux                          // uboot 跳转到linux内核
        run_main_loop                                     // 进入命令行模式,或执行启动命令

uboot的启动过程分为BL1和BL2两个阶段,BL1阶段通常是开发板的配置等设备初始化代码,需要依赖依赖于SoC体系结构,通常用汇编语言来实现;BL2阶段主要是对外部设备如网卡、Flash等的初始化以及uboot命令集等的自身实现,通常用C语言来实现。

1、BL1阶段

uboot的BL1阶段代码通常放在start.s文件中,用汇编语言实现,其主要代码功能如下:

(1) 指定uboot的入口。在链接脚本uboot.lds中指定uboot的入口为start.S中的_start。

(2)设置异常向量(exception vector)

(3)关闭IRQ、FIQ,设置SVC模式

(4)关闭L1 cache、设置L2 cache、关闭MMU

(5)根据OM引脚确定启动方式

(6)在SoC内部SRAM中设置栈

(7)lowlevel_init(主要初始化系统时钟、SDRAM初始化、串口初始化等)

(8)设置开发板供电锁存

(9)设置SDRAM中的栈

(10)将uboot从SD卡拷贝到SDRAM中

(11)设置并开启MMU

(12)通过对SDRAM整体使用规划,在SDRAM中合适的地方设置栈

(13)清除bss段,远跳转到start_armboot执行,BL1阶段执行完

2、BL2阶段

start_armboot函数位于lib_arm/board.c中,是C语言开始的函数,也是BL2阶段代码中C语言的主函数,同时还是整个u-boot(armboot)的主函数,BL2阶段的主要功能如下:

(1)规划uboot的内存使用

(2)遍历调用函数指针数组init_sequence中的初始化函数

(3)初始化uboot的堆管理器mem_malloc_init

(4)初始化SD/MMC控制器mmc_initialize

(5)环境变量重定位env_relocate

(6)将环境变量中网卡地址赋值给全局变量的开发板变量

(7)开发板硬件设备的初始化devices_init

(8)跳转表jumptable_init

(9)控制台初始化console_init_r

(10)网卡芯片初始化eth_initialize

(11)uboot进入主循环main_loop

三、kernel启动流程

启动代码位置:init/main.c

asmlinkage __visible void __init start_kernel(void){
	char *command_line;
	char *after_dashes;
        // 设置任务栈结束魔术数,用于栈溢出检测
	set_task_stack_end_magic(&init_task);   
        //设置处理器 ID
	smp_setup_processor_id();
        //debug 初始化
	debug_objects_early_init();
        //cgroup 初始化, cgroup 用于控制 Linux 系统资源
	cgroup_init_early();
        //关闭当前 CPU 中断
	local_irq_disable();
	early_boot_irqs_disabled = true;
        //CPU 初始化
	boot_cpu_init();
        //页地址相关的初始化
	page_address_init();
	pr_notice("%s", linux_banner);
        // 架构相关的初始化,此函数会解析uboot传递进来的参数,读取并解析dtb内容,初始化内存等
        setup_arch(&command_line);
        //初始化内存相关
	mm_init_cpumask(&init_mm);
	setup_command_line(command_line);
        //如果只是 SMP(多核 CPU)的话,此函数用于获取 * CPU 核心数量, CPU 数量保存在变量
	setup_nr_cpu_ids();
	setup_per_cpu_areas();
	smp_prepare_boot_cpu();	/* arch-specific boot-cpu hooks */
	boot_cpu_hotplug_init();

	build_all_zonelists(NULL);    //建立系统内存页区(zone)链表
	page_alloc_init();
        ......
    	trap_init();    //完成对系统保留中断向量的初始化
	mm_init();    //内存管理初始化 
        ......
	init_IRQ();    //中断初始化
	tick_init();    //tick初始化
	rcu_init_nohz();
	init_timers();    //初始化定时器
	hrtimers_init();    
	softirq_init();    //软中断初始化
	timekeeping_init();
        .......
	early_boot_irqs_disabled = false;
	local_irq_enable();    //中断使能

	kmem_cache_init_late();    //slab 初始化, slab 是 Linux 内存分配器
	console_init();    //console init
        .......
	kmemleak_init();    //kmemleak 初始化, kmemleak 用于检查内存泄漏
	proc_caches_init();
	uts_ns_init();
	buffer_init();    //初始化缓冲区
        .......
	rest_init();    //初始化第一个init进程

	prevent_tail_call_optimization();
}

start kernel具体工作:

  1) 调用setup_arch()函数进行与体系结构相关的第一个初始化工作;对不同的体系结构来说该函数有不同的定义。

  2) 创建异常向量表和初始化中断处理函数;

  3) 初始化系统核心进程调度器和时钟中断处理机制;

  4) 初始化串口控制台(console_init);

  5) 创建和初始化系统cache,为各种内存调用机制提供缓存,包括;动态内存分配,虚拟文件系统(VirtualFile System)及页缓存。

  6) 初始化内存管理,检测内存大小及被内核占用的内存情况;

  7) 初始化系统的进程间通信机制(IPC); 当以上所有的初始化工作结束后,start_kernel()函数会调用rest_init()函数来进行最后的初始化,包括创建系统的第一个进程-init进程来结束内核的启动。

8)挂载根文件系统并启动initLinux内核启动的下一过程是启动第一个进程init,但必须以根文件系统为载体,所以在启动init之前,还要挂载根文件系统

  根文件系统至少包括以下目录:

  /etc/:存储重要的配置文件。

  /bin/:存储常用且开机时必须用到的执行文件。

  /sbin/:存储着开机过程中所需的系统执行文件。

  /lib/:存储/bin/及/sbin/的执行文件所需的链接库,以及Linux的内核模块。

  /dev/:存储设备文件。

  注:五大目录必须存储在根文件系统上,缺一不可。

四、android init进程

init进程是android系统中第一个进程,它完成下面这些事情:

1、设置和挂载文件系统

2、启动和管理属性服务property service

3、解析init.rc脚本,根据解析内容启动系统需要的基础服务

第一阶段:

0f88e069aefa6e39d3ba159b69e71733.png

Android根文件系统的镜像中不存在“/dev”目录,该目录是init进程启动后动态创建的,因此建立Android中设备节点文件的重任,也落在了init进程身上。为此,init进程创建子进程ueventd,并将创建设备节点文件的工作托付给ueventd。

第二阶段:

android的启动流程可以概括如下图

猜你喜欢

转载自blog.csdn.net/QQ135102692/article/details/125729225