uboot分析

本文详细解析了Bootloader的两个阶段,包括硬件初始化、加载内核映象等步骤。着重介绍了从Bootloader到Linux内核启动的过程,如设置启动参数、调用内核、内存管理等。同时,分析了配置文件、编译过程和内核启动的架构相关工作,涉及了ARM架构的特定操作,如页表设置和MMU启用。
摘要由CSDN通过智能技术生成

Bootloader的两个阶段

1.第一阶段
(1. 硬件设备初始化。(关闭WATCHDOG、关中断、设置CPU的速度和时钟频率(不是必须)、RAM初始化等)
(2. 为加载Bootloader的第二阶段代码准备RAM空间。(不是必须)
(3. 复制Bootloader的第二阶段代码到RAM空间中。
(4. 设置好栈。
(5. 跳转到第二阶段代码的C入口点。
2.第二阶段:
(1. 初始化本阶段要使用到的硬件设备。
(2. 检测系统内存映射。
(3. 将内核映象和根文件映象从Flash上读到RAM空间中。
(4. 为内核设置启动参数。
(5. 调用内核。
将内核存放在适当的位置后,直接跳到它的入口点即可调用内核。调用内核前,下列条件要满足:
(1. CPU寄存器的设置
  .R0=0
  .R1=机器类型ID;对于ARM结构的CPU,其机器类型ID可以参见linux/arch/arm/tools/mach-types.
  .R2=启动参数标记列表在RAM中起始基地址。
(2. CPU工作模式
  .必须禁止中断(IRQs和FIQs)
  .CPU必须为SVC模式。
(3. Cache和MMU的设置
  .MMU必须关闭
  .指令Cache可以打开也可以关闭。
  .数据Cache必须关闭。

Bootloader与内核的交互是单向的,Bootloader将各类参数传给内核。传递方法是:Bootloader将参数放在某个约定的地方后,再启动内核,内核启动后从这个地方获得参数。既要约定好参数存放的地址,还要规定参数的结构。Linux2.4.x以后的内核都期望以标记列表的形式来传递启动参数。标记,就是一种数据结构;标记列表,就是挨着存放的多个标记。标记列表以ATAG_CORE开始,以标记ATAG_NONE结束。标记的数据结构为tag,它由一个tag_header结构和一个联合组成。tag_header结构表示标记的类型及长度。
数据结构tag和tag_header定义在uboot/include/asm-arm/setup.h , kernel/arch/arm/include/asm/setup.h中。
设置标记的文件uboot/lib_arm/bootm.c

分析配置过程:

// uboot/Makefile:
mx6q_sabresd_config                     \
mx6q_sabresd_android_config             \
mx6q_sabresd_mfg_config                 \
mx6q_sabresd_iram_config        : unconfig
        @[ -z "$(findstring iram_,$@)" ] || \
                { echo "TEXT_BASE = 0x00907000" >$(obj)board/freescale/mx6q_sabresd/config.tmp ; \
                  echo "... with iram configuration" ; \
                }
        @$(MKCONFIG) $(@:_config=) arm arm_cortexa8 mx6q_sabresd freescale mx6

分析:

       mkconfig   mx6q_sabresd_android_config   arm arm_cortexa8  mx6q_sabresd  freescale  mx6

          $0                 $1                                         $2         $3                       $4               $5             $6

           

   分析文件uboot/mkconfig    最后生成include/config.mk, include/config.h文件。

<pre name="code" class="cpp">//  include/config.mk
ARCH   = arm
CPU    = arm_cortexa8
BOARD  = mx6q_sabresd
VENDOR = freescale
SOC    = mx6

 

分析编译过程:

  分析文件Makefile

  uboot/cpu/arm_cortexa8/start.S

  链接地址:board/freescale/mx6q_sabresd/u-boot.lds + TEXT_BASE(board/freescale/mx6q_sabresd/config.mk)

从链接文件board/freescale/mx6q_sabresd/u-boot.lds可以看出uboot的入口点是start.S

源码分析之第一阶段:

  uboot/cpu/arm_cortexa8/start.S

  uboot/lib_arm/board.c   (start_armboot (void))

源码分析第二阶段:(从FLASH读出内核,启动)

  uboot/lib_arm/board.c   (start_armboot (void))

  调用main_loop (common/main.c)

                s = getenv ("bootcmd");

         debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
 
         if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
 # ifdef CONFIG_AUTOBOOT_KEYED
                 int prev = disable_ctrlc(1);    /* disable Control C checki     ng */
 # endif
 
 # ifndef CONFIG_SYS_HUSH_PARSER
                 run_command (s, 0);


bootcmd ???

uboot命令

 uboot/common/main.c(int run_command (const char *cmd, int flag))

 比如bootm, common/cmd_bootm.c (U_BOOT_CMD)

 include/command.h  (#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help})

// board/freescale/mx6q_sabresd/u-boot.lds
        . = .;
        __u_boot_cmd_start = .;
        .u_boot_cmd : { *(.u_boot_cmd) }
        __u_boot_cmd_end = .;

uboot启动内核

     uboot/common/main.c    (main_loop (void))

     从哪读,读到哪?                     分区

    uImage   =   头部 + 真正的内核

    

   lib_arm/bootm.c      (int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images))

theKernel (0, machid, bd->bi_boot_params);

     

kernel 分析

Linux的启动过程可以分为两部分:架构/开发板相关的引导过程,后续的通用启动过程。
引导阶段通常使用汇编语言编写,它首先检查内核是否支持当前架构的处理器,然后检查是否支持当前开发板。通过检查后,就为调用下一阶段的start_kernel函数作准备了,主要分如下两个步骤。
1.连接内核时使用的虚拟地址,所以要设置页表,使能MMU。
2.调用C函数start_kernel之前的常规工作,包括复制数据段,清除BSS段,调用start_kernel函数。
第二阶段的关键代码主要使用C语言编写。它进行内核初始化的全部工作,最后调用rest_init函数启动init过程,创建系统第一个进程:init进程。第二阶段,仍有部分架构/开发板相关的代码,如setup_arch函数用于进行架构/开发板相关的设置。
ARM架构处理器上Linux内核vmlinux的启动过程。 

配置文件 arch/arm/configs/*_defconfig  ,      生成.config文件。

以CONFIG_TOUCHSCREEN_GOODIX为例:

<pre name="code" class="cpp"><pre name="code" class="cpp">

 
 
配置项: CONFIG_TOUCHSCREEN_GOODIX=y
1.Makefile       drivers/input/touchscreen/Makefile
obj-$(CONFIG_TOUCHSCREEN_GOODIX)	+= gt9xx.o gt9xx_update.o goodix_tool.o
2.autoconf.h     include/generated/autoconf.h
#define CONFIG_TOUCHSCREEN_GOODIX 1
3.auto.conf           include/config/auto.conf
4.include/config/tristate.conf           ??????????????
5.arch/arm/configs/imx6_android_defconfig


make uImage时,  .config文件自动生成auto.conf(用于源码)和autoconf.h(用于顶层的Makefile)

Makefile分析

 makefile文件说明手册(kernel/Documentation/kbuild/makefiles.txt)

1.子目录下的Makefile:     obj-y +=           obj-m +=

2.make uImage        =>  arch/arm/Makefile

 uImage依赖vmlinux

vmlinux-init := $(head-y) $(init-y)
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
vmlinux-all  := $(vmlinux-init) $(vmlinux-main)
vmlinux-lds  := arch/$(SRCARCH)/kernel/vmlinux.lds
init-y          := $(patsubst %/, %/built-in.o, $(init-y))         // =    init/built-in.o
core-y          := $(patsubst %/, %/built-in.o, $(core-y))    // =    usr/built-in.o
drivers-y       := $(patsubst %/, %/built-in.o, $(drivers-y))       // =   drivers/buit-in.o   sound/built-in.o     firmware/built-in.o
net-y           := $(patsubst %/, %/built-in.o, $(net-y))         //   =    net/built-in.o
head-y          := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
init-y          := init/
drivers-y       := drivers/ sound/ firmware/
net-y           := net/
libs-y          := lib/
core-y          := usr/
core-y          += kernel/ mm/ fs/ ipc/ security/ crypto/ block/

链接脚本:arch/arm/kernel/vmlinux.lds

第一个文件: arch/arm/kernel/head.S           arch/arm/kernel/head-common.S
(1.  __lookup_processor_type 确定内核是否支持该架构  
(2.  __create_page_tables 建立一级页表
(3.  __enable_mmu 使能MMU
(4.  __mmap_switched 调用start_kernel

init/main.c的start_kernel函数
(1. printk(KERN_NOTICE "%s", linux_banner); 输出linux版本信息
(2. setup_arch 设置与体系结构相关的环境 (arch/arm/kernel/setup.c)
(3. console_init 初始化控制台
(4. rest_init 启动init过程

配置文件arch/arm/configs/imx6_android_defconfig

内核中对于每种支持的开发板都会使用宏MACHINE_START、MACHINE_END来定义一个machine_desc结构,它定义了开发板相关的一些属性及函数,比如机器类型ID、起始I/O物理地址、Bootloader传入的参数的地址、中断初始化函数、I/O映射函数等。

// arch/arm/mach-mx6/board-mx6q_sabresd.c
MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board")
        /* Maintainer: Freescale Semiconductor, Inc. */
        .boot_params = MX6_PHYS_OFFSET + 0x100,
        .fixup = fixup_mxc_board,
        .map_io = mx6_map_io,
        .init_irq = mx6_init_irq,
        .init_machine = mx6_sabresd_board_init,
        .timer = &mx6_sabresd_timer,
        .reserve = mx6q_sabresd_reserve,
MACHINE_END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值