学:关于嵌入式Linux内核Boot的启动流程的总结(一)

0x00 概述

最近在看关于嵌入式Linux关于Boot的一些资料,准备了一些资料。现在在这里将最近学习到的一些知识做个总结。
这次的总结主要关于Boot的一些参数解析与相应的启动流程总结。
使用了飞凌科技的IMXQ6的开发板2016.06的BOOT适配板搭配的源码进行分析。
因为Markdown插件对于&符号判断的问题,我将代码中所有的&替换成了,修改回来即可。

文章目录

  • 0x00 概述
  • 0x10 make编译参数与流程解析
    • 0x11 Boot的Make参数
    • 0x12 Boot的Makefile相关解析
  • 0x20 启动前准备
  • 0x30 启动时操作
  • 0x40 跳转方式与内存管理
  • 0x50总结
  • 更多

0x10 make编译参数与流程解析

makefile文件在现在是一个软件编译系统的核心,根据makefile可以了解当前的文件是否被包括在项目中,可以有效的排除相应的干扰项。
当前BOOT项目的makefile应该是autoconf进行生成的,所以文件中带有很多autoconf的痕迹,对一个大型项目来说,使用autoconf生成makefile是常见的。

0x11 Boot的Make参数

飞凌科技的boot使用了脚本的方式进行编译,编译包括其公司各种产品的UBootImage,而我只需要一个需要的内核bin即可。

#!/bin/bash

make distclean                    //清除了相应的数据缓存
make mx6q-c-sabresd_defconfig     //根据我的开发板进行编译数据配置
make -j5                          //j5为5线程编译流程
cp u-boot.imx uboot-6q.tran       //编译完成后的文件拷贝


这段代码就是相应的编译指令。而make mx6q-c-sabresd_defconfig则是主要的make配置。

0x12 Boot的Makefile相关解析

分析一个复杂软件的源代码可以直接使用其Makefile看出现在编译的文件,进而可以确定现在软件包括的源代码与数据。
打开makefile文件即可看到当前的数据标志:

#  版本相关
VERSION = 2016      //主要版本
PATCHLEVEL = 03     //次要版本
SUBLEVEL =          //分支版本
EXTRAVERSION =      //额外版本号
NAME =              //开发代号


上述的数据主要是当前的版本号与版本的标志。主要版本号、次要版本号都是经常改变的。而分支版本号、开发代号主要是设备分支的版本代号,而额外版本号主要是当前的额外版本补丁号。一般只有在需要更新之后才会进行修改编译。

##############################正式编译###################################
#########################################################################
#  相应的数据定义,主要是选择当前需要的编译信息
HOSTARCH :=(shell uname -m | \
    sed -e s/i.86/x86/ \
        -e s/sun4u/sparc64/ \
        -e s/arm.*/arm/ \                #核心点
        -e s/sa110/arm/ \
        -e s/ppc64/powerpc/ \
        -e s/ppc/powerpc/ \
        -e s/macppc/powerpc/\
        -e s/sh.*/sh/)

HOSTOS :=(shell uname -s | tr '[:upper:]' '[:lower:]' | \
        sed -e 's/\(cygwin\).*/cygwin/')

export  HOSTARCH HOSTOS


跳过一般Makefile的一些常见递归编译代码与编译目录选择相关代码之后,可以看到这段代码。这段代码主要是定义当前的设备编译架构与设备系统类型。因为IMXQ6为ARM A9内核,所以当前选择的是arm架构Linux.
随后就是大量的编译环境配置(因为这个makefile是由autoconf定制生成)。与相应的数据定义。
而选择编译的配置文件主要在makefile的编译之前:

#  正式编译脚本
#  选择编译配置脚本或者是手动编译配置
ifeq ((mixed-targets),1)
# ===========================================================================
# We're called with mixed targets (*config and build targets).
# Handle them one by one.
#  一个一个选择(手动编译配置)
PHONY +=(MAKECMDGOALS) __build_one_by_one

¥(filter-out __build_one_by_one, ¥(MAKECMDGOALS)): __build_one_by_one
    @:

__build_one_by_one:
    ¥(Q)set -e; \
    for i in(MAKECMDGOALS); do \
        ¥(MAKE) -f ¥(srctree)/Makefile ¥¥i; \
    done

else
#  选择编译配置文件进行编译配置文件
ifeq ((config-targets),1)
# ===========================================================================
# *config targets only - make sure prerequisites are updated, and descend
# in scripts/kconfig to make the *config target

KBUILD_DEFCONFIG := sandbox_defconfig
export KBUILD_DEFCONFIG KBUILD_KCONFIG

config: scripts_basic outputmakefile FORCE
    ¥(Q)(MAKE)(build)=scripts/kconfig ¥@

%config: scripts_basic outputmakefile FORCE
    ¥(Q)(MAKE)(build)=scripts/kconfig ¥@

else
# ===========================================================================
# Build targets only - this includes vmlinux, arch specific targets, clean
# targets and others. In general all targets except *config targets.
#  单独编译
ifeq ((dot-config),1)
# Read in config
-include include/config/auto.conf


这样就可以进行选择,自动配置完成的脚本与单独配置。
相比Linux的配置繁琐,Uboot的配置较为简单。所以使用defconfig文件或者是手动配置方式都是可以的。但是如果是Linux内核编译则最好使用defconfig文件就可以完成,这样可以避免内核配置出现的不必要的错误。
下面就是包含config.mk文件,这个文件包括了编译CPU相关的参数。

#
# (C) Copyright 2000-2013
# Wolfgang Denk, DENX Software Engineering, [email protected].
#
# SPDX-License-Identifier:  GPL-2.0+
#
#########################################################################

# This file is included from ./Makefile and spl/Makefile.
# Clean the state to avoid the same flags added twice.
#
# (Tegra needs different flags for SPL.
#  That's the reason why this file must be included from spl/Makefile too.
#  If we did not have Tegra SoCs, build system would be much simpler...)
#  相应的标志位
PLATFORM_RELFLAGS :=
PLATFORM_CPPFLAGS :=
PLATFORM_LDFLAGS :=
LDFLAGS :=
LDFLAGS_FINAL :=
OBJCOPYFLAGS :=
# clear VENDOR for tcsh
VENDOR :=
#########################################################################
#  相应的编译属性
ARCH :=(CONFIG_SYS_ARCH:"%"=%)
CPU :=(CONFIG_SYS_CPU:"%"=%)
ifdef CONFIG_SPL_BUILD
ifdef CONFIG_TEGRA
CPU := arm720t
endif
endif
BOARD :=(CONFIG_SYS_BOARD:"%"=%)
ifneq ((CONFIG_SYS_VENDOR),)
VENDOR :=(CONFIG_SYS_VENDOR:"%"=%)
endif
ifneq ((CONFIG_SYS_SOC),)
SOC :=(CONFIG_SYS_SOC:"%"=%)
endif

# Some architecture config.mk files need to know what CPUDIR is set to,
# so calculate CPUDIR before including ARCH/SOC/CPU config.mk files.
# Check if arch/¥ARCH/cpu/¥CPU exists, otherwise assume arch/¥ARCH/cpu contains
# CPU-specific code.
#  CPU配置
CPUDIR=arch/¥(ARCH)/cpu¥(if ¥(CPU),/¥(CPU),)

sinclude ¥(srctree)/arch/¥(ARCH)/config.mk  # include architecture dependend rules
sinclude ¥(srctree)/¥(CPUDIR)/config.mk     # include  CPU  specific rules

ifdef   SOC
sinclude ¥(srctree)/¥(CPUDIR)/¥(SOC)/config.mk  # include  SoC  specific rules
endif
ifneq ((BOARD),)
ifdef   VENDOR
BOARDDIR =(VENDOR)/¥(BOARD)
else
BOARDDIR =(BOARD)
endif
endif
ifdef   BOARD
sinclude ¥(srctree)/board/¥(BOARDDIR)/config.mk # include board specific rules
endif

ifdef FTRACE
PLATFORM_CPPFLAGS += -finstrument-functions -DFTRACE
endif

# Allow use of stdint.h if available
ifneq ((USE_STDINT),)
PLATFORM_CPPFLAGS += -DCONFIG_USE_STDINT
endif

#########################################################################

RELFLAGS :=(PLATFORM_RELFLAGS)

PLATFORM_CPPFLAGS +=(RELFLAGS)
PLATFORM_CPPFLAGS += -pipe

LDFLAGS +=(PLATFORM_LDFLAGS)
LDFLAGS_FINAL += -Bstatic

export PLATFORM_CPPFLAGS
export RELFLAGS
export LDFLAGS_FINAL
export CONFIG_STANDALONE_LOAD_ADDR


做完相应的配置后,就开始相应的编译,主要流程为:

  1. 相应的数据标志位配置与转换
# Read in dependencies to all Kconfig* files, make sure to run
# oldconfig if changes are detected.
#    配置相关的config.mk余项
-include include/config/auto.conf.cmd

# To avoid any implicit rule to kick in, define an empty command(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;

# If .config is newer than include/config/auto.conf, someone tinkered
# with it and forgot to run make oldconfig.
# if auto.conf.cmd is missing then we are probably in a cleaned tree so
# we execute the config step to be sure to catch updated Kconfig files
include/config/%.conf: ¥(KCONFIG_CONFIG) include/config/auto.conf.cmd
    ¥(Q)(MAKE) -f ¥(srctree)/Makefile silentoldconfig
    @# If the following part fails, include/config/auto.conf should be
    @# deleted so "make silentoldconfig" will be re-run on the next build.(Q)(MAKE) -f ¥(srctree)/scripts/Makefile.autoconf || \
        { rm -f include/config/auto.conf; false; }
    @# include/config.h has been updated after "make silentoldconfig".
    @# We need to touch include/config/auto.conf so it gets newer
    @# than include/config.h.
    @# Otherwise, 'make silentoldconfig' would be invoked twice.(Q)touch include/config/auto.conf

-include include/autoconf.mk
-include include/autoconf.mk.dep

# We want to include arch/¥(ARCH)/config.mk only when include/config/auto.conf
# is up-to-date. When we switch to a different board configuration, old CONFIG
# macros are still remaining in include/config/auto.conf. Without the following
# gimmick, wrong config.mk would be included leading nasty warnings/errors.
ifneq ((wildcard ¥(KCONFIG_CONFIG)),)
ifneq ((wildcard include/config/auto.conf),)
autoconf_is_old :=(shell find . -path ./¥(KCONFIG_CONFIG) -newer \
                        include/config/auto.conf)
ifeq ((autoconf_is_old),)
#  当前配置需要重新生成
include config.mk
include arch/¥(ARCH)/Makefile
endif
endif
endif

# If board code explicitly specified LDSCRIPT or CONFIG_SYS_LDSCRIPT, use
# that (or fail if absent).  Otherwise, search for a linker script in a
# standard location.
#  建立链接文件树
ifndef LDSCRIPT
    #LDSCRIPT := ¥(srctree)/board/¥(BOARDDIR)/u-boot.lds.debug
    ifdef CONFIG_SYS_LDSCRIPT
        # need to strip off double quotes
        LDSCRIPT :=(srctree)/¥(CONFIG_SYS_LDSCRIPT:"%"=%)
    endif
endif

# If there is no specified link script, we look in a number of places for it
ifndef LDSCRIPT
    ifeq ((wildcard ¥(LDSCRIPT)),)
        LDSCRIPT :=(srctree)/board/¥(BOARDDIR)/u-boot.lds
    endif
    ifeq ((wildcard ¥(LDSCRIPT)),)
        LDSCRIPT :=(srctree)/¥(CPUDIR)/u-boot.lds
    endif
    ifeq ((wildcard ¥(LDSCRIPT)),)
        LDSCRIPT :=(srctree)/arch/¥(ARCH)/cpu/u-boot.lds
    endif
endif

else
#  无需重新配置
# Dummy target needed, because used as prerequisite
include/config/auto.conf: ;
endif # ¥(dot-config)
#  根据相应的config.mk进行相应的标志位配置
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS   += -Os
else
KBUILD_CFLAGS   += -O2
endif

KBUILD_CFLAGS +=(call cc-option,-fno-stack-protector)
KBUILD_CFLAGS +=(call cc-option,-fno-delete-null-pointer-checks)

KBUILD_CFLAGS   += -g
# ¥(KBUILD_AFLAGS) sets -g, which causes gcc to pass a suitable -g
# option to the assembler.
KBUILD_AFLAGS   += -g

# Report stack usage if supported
ifeq ((shell ¥(CONFIG_SHELL)(srctree)/scripts/gcc-stack-usage.sh ¥(CC)),y)
    KBUILD_CFLAGS += -fstack-usage
endif

KBUILD_CFLAGS +=(call cc-option,-Wno-format-nonliteral)

# turn jbsr into jsr for m68k
ifeq ((ARCH),m68k)
ifeq ((findstring 3.4,¥(shell ¥(CC) --version)),3.4)
KBUILD_AFLAGS += -Wa,-gstabs,-S
endif
endif

# Prohibit date/time macros, which would make the build non-deterministic
KBUILD_CFLAGS   +=(call cc-option,-Werror=date-time)

include scripts/Makefile.extrawarn

# Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments
KBUILD_CPPFLAGS +=(KCPPFLAGS)
KBUILD_AFLAGS +=(KAFLAGS)
KBUILD_CFLAGS +=(KCFLAGS)
# Use UBOOTINCLUDE when you must reference the include/ directory.
# Needed to be compatible with the O= option
#  包含相应的数据包与配置项
UBOOTINCLUDE    := \
        -Iinclude \
        ¥(if ¥(KBUILD_SRC), -I¥(srctree)/include) \
        ¥(if ¥(CONFIG_SYS_THUMB_BUILD), ¥(if ¥(CONFIG_HAS_THUMB2),, \
            -I¥(srctree)/arch/¥(ARCH)/thumb1/include),) \
        -I¥(srctree)/arch/¥(ARCH)/include \
        -include ¥(srctree)/include/linux/kconfig.h

NOSTDINC_FLAGS += -nostdinc -isystem ¥(shell ¥(CC) -print-file-name=include)
CHECKFLAGS     +=(NOSTDINC_FLAGS)

# FIX ME
cpp_flags :=(KBUILD_CPPFLAGS)(PLATFORM_CPPFLAGS)(UBOOTINCLUDE) \
                            ¥(NOSTDINC_FLAGS)
c_flags :=(KBUILD_CFLAGS)(cpp_flags)

  1. 包含相应的项目库
#########################################################################
# U-Boot objects....order is important (i.e. start must be first)
#  包含各种库文件
HAVE_VENDOR_COMMON_LIB =(if ¥(wildcard ¥(srctree)/board/¥(VENDOR)/common/Makefile),y,n)

libs-y += lib/
libs-¥(HAVE_VENDOR_COMMON_LIB) += board/¥(VENDOR)/common/
libs-¥(CONFIG_OF_EMBED) += dts/
libs-y += fs/
libs-y += net/
libs-y += disk/
libs-y += drivers/
libs-y += drivers/dma/
libs-y += drivers/gpio/
libs-y += drivers/i2c/
libs-y += drivers/mmc/
libs-y += drivers/mtd/
libs-¥(CONFIG_CMD_NAND) += drivers/mtd/nand/
libs-y += drivers/mtd/onenand/
libs-¥(CONFIG_CMD_UBI) += drivers/mtd/ubi/
libs-y += drivers/mtd/spi/
libs-y += drivers/net/
libs-y += drivers/net/phy/
libs-y += drivers/pci/
libs-y += drivers/power/ \
    drivers/power/fuel_gauge/ \
    drivers/power/mfd/ \
    drivers/power/pmic/ \
    drivers/power/battery/ \
    drivers/power/regulator/
libs-y += drivers/spi/
libs-¥(CONFIG_FMAN_ENET) += drivers/net/fm/
libs-¥(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/
libs-¥(CONFIG_ALTERA_SDRAM) += drivers/ddr/altera/
libs-y += drivers/serial/
libs-y += drivers/usb/dwc3/
libs-y += drivers/usb/emul/
libs-y += drivers/usb/eth/
libs-y += drivers/usb/gadget/
libs-y += drivers/usb/gadget/udc/
libs-y += drivers/usb/host/
libs-y += drivers/usb/musb/
libs-y += drivers/usb/musb-new/
libs-y += drivers/usb/phy/
libs-y += drivers/usb/ulpi/
libs-y += cmd/
libs-y += common/
libs-¥(CONFIG_API) += api/
libs-¥(CONFIG_HAS_POST) += post/
libs-y += test/
libs-y += test/dm/
libs-¥(CONFIG_UT_ENV) += test/env/

libs-y +=(if ¥(BOARDDIR),board/¥(BOARDDIR)/)

libs-y :=(sort ¥(libs-y))

u-boot-dirs :=(patsubst %/,%,¥(filter %/, ¥(libs-y))) tools examples

u-boot-alldirs  :=(sort ¥(u-boot-dirs)(patsubst %/,%,¥(filter %/, ¥(libs-))))

libs-y      :=(patsubst %/, %/built-in.o, ¥(libs-y))

u-boot-init :=(head-y)
u-boot-main :=(libs-y)

  1. 包含相应的GCC库
# Add GCC lib
ifeq ((CONFIG_USE_PRIVATE_LIBGCC),y)
PLATFORM_LIBGCC = arch/¥(ARCH)/lib/lib.a
else
PLATFORM_LIBGCC := -L ¥(shell dirname `(CC)(c_flags) -print-libgcc-file-name`) -lgcc
endif
PLATFORM_LIBS +=(PLATFORM_LIBGCC)
export PLATFORM_LIBS
export PLATFORM_LIBGCC

# Special flags for CPP when processing the linker script.
# Pass the version down so we can handle backwards compatibility
# on the fly.
LDPPFLAGS += \
    -include ¥(srctree)/include/u-boot/u-boot.lds.h \
    -DCPUDIR=(CPUDIR) \
    ¥(shell ¥(LD) --version | \
      sed -ne 's/GNU ld version \([0-9][0-9]*\)\.\([0-9][0-9]*\).*/-DLD_MAJOR=\1 -DLD_MINOR=\2/p')

  1. 进行编译环境的配置
#  准备编译项
ifneq ((CONFIG_BOARD_SIZE_LIMIT),)
BOARD_SIZE_CHECK = \
    @actual=`wc -c ¥@ | awk '{print ¥¥1}'`; \
    limit=`printf "%d"(CONFIG_BOARD_SIZE_LIMIT)`; \
    if test ¥¥actual -gt ¥¥limit; then \
        echo "¥@ exceeds file size limit:" >&2 ; \
        echo "  limit:  ¥¥limit bytes" >&2 ; \
        echo "  actual: ¥¥actual bytes" >&2 ; \
        echo "  excess: ¥¥((actual - limit)) bytes" >&2; \
        exit 1; \
    fi
else
BOARD_SIZE_CHECK =
endif

# Statically apply RELA-style relocations (currently arm64 only)
ifneq ((CONFIG_STATIC_RELA),)
# ¥(1) is u-boot ELF, ¥(2) is u-boot bin, ¥(3) is text base
DO_STATIC_RELA = \
    start=¥¥((NM)(1) | grep __rel_dyn_start | cut -f 1 -d ' '); \
    end=¥¥((NM)(1) | grep __rel_dyn_end | cut -f 1 -d ' '); \
    tools/relocate-rela ¥(2)(3) ¥¥start ¥¥end
else
DO_STATIC_RELA =
endif

# Always append ALL so that arch config.mk's can add custom ones
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check

ALL-¥(CONFIG_ONENAND_U_BOOT) += u-boot-onenand.bin
ifeq ((CONFIG_SPL_FSL_PBL),y)
ALL-¥(CONFIG_RAMBOOT_PBL) += u-boot-with-spl-pbl.bin
else
ifneq ((CONFIG_SECURE_BOOT), y)
# For Secure Boot The Image needs to be signed and Header must also
# be included. So The image has to be built explicitly
ALL-¥(CONFIG_RAMBOOT_PBL) += u-boot.pbl
endif
endif
ALL-¥(CONFIG_SPL) += spl/u-boot-spl.bin
ALL-¥(CONFIG_SPL_FRAMEWORK) += u-boot.img
ALL-¥(CONFIG_TPL) += tpl/u-boot-tpl.bin
ALL-¥(CONFIG_OF_SEPARATE) += u-boot.dtb
ifeq ((CONFIG_SPL_FRAMEWORK),y)
ALL-¥(CONFIG_OF_SEPARATE) += u-boot-dtb.img
endif
ALL-¥(CONFIG_OF_HOSTFILE) += u-boot.dtb
ifneq ((CONFIG_SPL_TARGET),)
ALL-¥(CONFIG_SPL) +=(CONFIG_SPL_TARGET:"%"=%)
endif
ALL-¥(CONFIG_REMAKE_ELF) += u-boot.elf
ALL-¥(CONFIG_EFI_APP) += u-boot-app.efi
ALL-¥(CONFIG_EFI_STUB) += u-boot-payload.efi

ifneq ((BUILD_ROM),)
ALL-¥(CONFIG_X86_RESET_VECTOR) += u-boot.rom
endif

# enable combined SPL/u-boot/dtb rules for tegra
ifeq ((CONFIG_TEGRA)(CONFIG_SPL),yy)
ALL-y += u-boot-tegra.bin u-boot-nodtb-tegra.bin
ALL-¥(CONFIG_OF_SEPARATE) += u-boot-dtb-tegra.bin
endif

# Add optional build target if defined in board/cpu/soc headers
ifneq ((CONFIG_BUILD_TARGET),)
ALL-y +=(CONFIG_BUILD_TARGET:"%"=%)
endif

LDFLAGS_u-boot +=(LDFLAGS_FINAL)
ifneq ((CONFIG_SYS_TEXT_BASE),)
LDFLAGS_u-boot += -Ttext ¥(CONFIG_SYS_TEXT_BASE)
endif

# Normally we fill empty space with 0xff
quiet_cmd_objcopy = OBJCOPY ¥@
cmd_objcopy =(OBJCOPY) --gap-fill=0xff ¥(OBJCOPYFLAGS) \
    ¥(OBJCOPYFLAGS_¥(@F))< ¥@

# Provide a version which does not do this, for use by EFI
quiet_cmd_zobjcopy = OBJCOPY ¥@
cmd_zobjcopy =(OBJCOPY)(OBJCOPYFLAGS)(OBJCOPYFLAGS_¥(@F))< ¥@

quiet_cmd_efipayload = OBJCOPY ¥@
cmd_efipayload =(OBJCOPY) -I binary -O ¥(EFIPAYLOAD_BFDTARGET) -B ¥(EFIPAYLOAD_BFDARCH)< ¥@

quiet_cmd_mkimage = MKIMAGE ¥@
cmd_mkimage =(objtree)/tools/mkimage ¥(MKIMAGEFLAGS_¥(@F)) -d ¥< ¥@ \
    ¥(if ¥(KBUILD_VERBOSE:1=), >/dev/null)

quiet_cmd_cat = CAT     ¥@
cmd_cat = cat(filter-out ¥(PHONY), ¥^) > ¥@

append = cat(filter-out ¥<(PHONY), ¥^) >> ¥@

quiet_cmd_pad_cat = CAT     ¥@
cmd_pad_cat =(cmd_objcopy) &&(append) || rm -f ¥@


  1. 开始正式进行编译
#  开始正式编译
all:        ¥(ALL-y)
ifneq ((CONFIG_SYS_GENERIC_BOARD),y)
    @echo "===================== WARNING ======================"
    @echo "Please convert this board to generic board."
    @echo "Otherwise it will be removed by the end of 2014."
    @echo "See doc/README.generic-board for further information"
    @echo "===================================================="
endif
ifeq ((CONFIG_DM_I2C_COMPAT),y)
    @echo "===================== WARNING ======================"
    @echo "This board uses CONFIG_DM_I2C_COMPAT. Please remove"
    @echo "(possibly in a subsequent patch in your series)"
    @echo "before sending patches to the mailing list."
    @echo "===================================================="
endif

  1. 编译进行
#  实际的编译项
PHONY += dtbs
dtbs dts/dt.dtb: checkdtc u-boot
    ¥(Q)(MAKE)(build)=dts dtbs

quiet_cmd_copy = COPY    ¥@
      cmd_copy = cp< ¥@

ifeq ((CONFIG_OF_SEPARATE),y)
u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
    ¥(call if_changed,cat)

u-boot.bin: u-boot-dtb.bin FORCE
    ¥(call if_changed,copy)
else
u-boot.bin: u-boot-nodtb.bin FORCE
    ¥(call if_changed,copy)
endif

%.imx: %.bin
    ¥(Q)(MAKE)(build)=arch/arm/imx-common ¥@

u-boot.dtb: dts/dt.dtb
    ¥(call cmd,copy)

OBJCOPYFLAGS_u-boot.hex := -O ihex

OBJCOPYFLAGS_u-boot.srec := -O srec

u-boot.hex u-boot.srec: u-boot FORCE
    ¥(call if_changed,objcopy)

OBJCOPYFLAGS_u-boot-nodtb.bin := -O binary \
        ¥(if ¥(CONFIG_X86_RESET_VECTOR),-R .start16 -R .resetvec)

binary_size_check: u-boot-nodtb.bin FORCE
    @file_size=(shell wc -c u-boot-nodtb.bin | awk '{print ¥¥1}') ; \
    map_size=(shell cat u-boot.map | \
        awk '/_image_copy_start/ {start = ¥¥1} /_image_binary_end/ {end = ¥¥1} END {if (start != "" && end != "") print "ibase=16; " toupper(end) " - " toupper(start)}' \
        | sed 's/0X//g' \
        | bc); \
    if [ "" != "¥¥map_size" ]; then \
        if test ¥¥map_size -ne ¥¥file_size; then \
            echo "u-boot.map shows a binary size of ¥¥map_size" >&2 ; \
            echo "  but u-boot-nodtb.bin shows ¥¥file_size" >&2 ; \
            exit 1; \
        fi \
    fi

u-boot-nodtb.bin: u-boot FORCE
    ¥(call if_changed,objcopy)(call DO_STATIC_RELA,¥<,¥@,¥(CONFIG_SYS_TEXT_BASE))(BOARD_SIZE_CHECK)

u-boot.ldr: u-boot
        ¥(CREATE_LDR_ENV)(LDR) -T ¥(CONFIG_CPU) -c ¥@ ¥<(LDR_FLAGS)(BOARD_SIZE_CHECK)

OBJCOPYFLAGS_u-boot.ldr.hex := -I binary -O ihex

OBJCOPYFLAGS_u-boot.ldr.srec := -I binary -O srec

u-boot.ldr.hex u-boot.ldr.srec: u-boot.ldr FORCE
    ¥(call if_changed,objcopy)
#    ………………………………………………………………………………………………………………………………………………………………
#    大抵都是差不多的编译项和链接项,以及一些clean相关的项目
#  编译完成

  1. 编译完成
    最后编译完成的数据文件即为%PROJRCT%.imx%PROJRCT%为自定义项。

0x20 启动前准备

根据当前的配置项,可以发现当前的配置项主要是在board\freescale\mx6sabresd文件夹下,当前文件夹下的数据为当前CPU适配代码,而arch/arm/imx-common文件夹下主要是当前的开发板的配置。
相应的main函数与进入boot的函数都在这几个文件下。
启动时,指针会在数据0x00000000下读取指令进行运行:

init_fnc_t init_sequence_r[] = {
    initr_trace,
    initr_reloc,
    /* TODO: could x86/PPC have this also perhaps? */
#ifdef CONFIG_ARM
    initr_caches,
    /* Note: For Freescale LS2 SoCs, new MMU table is created in DDR.
     *   A temporary mapping of IFC high region is since removed,
     *   so environmental variables in NOR flash is not availble
     *   until board_init() is called below to remap IFC to high
     *   region.
     */
#endif
    initr_reloc_global_data,
#if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500)
    initr_unlock_ram_in_cache,
#endif
    initr_barrier,
    initr_malloc,
    initr_console_record,
#ifdef CONFIG_SYS_NONCACHED_MEMORY
    initr_noncached,
#endif
    bootstage_relocate,
#ifdef CONFIG_DM
    initr_dm,
#endif
    initr_bootstage,
#if defined(CONFIG_ARM) || defined(CONFIG_NDS32)
    board_init, /* Setup chipselects */
#endif
    /*
     * TODO: printing of the clock inforamtion of the board is now
     * implemented as part of bdinfo command. Currently only support for
     * davinci SOC's is added. Remove this check once all the board
     * implement this.
     */
#ifdef CONFIG_CLOCKS
    set_cpu_clk_info, /* Setup clock information */
#endif
    stdio_init_tables,
    initr_serial,
    initr_announce,
    INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_NEEDS_MANUAL_RELOC
    initr_manual_reloc_cmdtable,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_M68K)
    initr_trap,
#endif
#ifdef CONFIG_ADDR_MAP
    initr_addr_map,
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_R)
    board_early_init_r,
#endif
    INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_LOGBUFFER
    initr_logbuffer,
#endif
#ifdef CONFIG_POST
    initr_post_backlog,
#endif
    INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_SYS_DELAYED_ICACHE
    initr_icache_enable,
#endif
#if defined(CONFIG_PCI) && defined(CONFIG_SYS_EARLY_PCI_INIT)
    /*
     * Do early PCI configuration _before_ the flash gets initialised,
     * because PCU ressources are crucial for flash access on some boards.
     */
    initr_pci,
#endif
#ifdef CONFIG_WINBOND_83C553
    initr_w83c553f,
#endif
#ifdef CONFIG_ARCH_EARLY_INIT_R
    arch_early_init_r,
#endif
    power_init_board,
#ifndef CONFIG_SYS_NO_FLASH
    initr_flash,
#endif
    INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_X86) || \
    defined(CONFIG_SPARC)
    /* initialize higher level parts of CPU like time base and timers */
    cpu_init_r,
#endif
#ifdef CONFIG_PPC
    initr_spi,
#endif
#ifdef CONFIG_CMD_NAND
    initr_nand,
#endif
#ifdef CONFIG_CMD_ONENAND
    initr_onenand,
#endif
#ifdef CONFIG_GENERIC_MMC
    initr_mmc,
#endif
#ifdef CONFIG_HAS_DATAFLASH
    initr_dataflash,
#endif
    initr_env,
#ifdef CONFIG_SYS_BOOTPARAMS_LEN
    initr_malloc_bootparams,
#endif
    INIT_FUNC_WATCHDOG_RESET
    initr_secondary_cpu,
#if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET)
    mac_read_from_eeprom,
#endif
    INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PCI) && !defined(CONFIG_SYS_EARLY_PCI_INIT)
    /*
     * Do pci configuration
     */
    initr_pci,
#endif
    stdio_add_devices,
    initr_jumptable,
#ifdef CONFIG_API
    initr_api,
#endif
    console_init_r,     /* fully init console as a device */
#ifdef CONFIG_DISPLAY_BOARDINFO_LATE
    show_board_info,
#endif
#ifdef CONFIG_ARCH_MISC_INIT
    arch_misc_init,     /* miscellaneous arch-dependent init */
#endif
#ifdef CONFIG_MISC_INIT_R
    misc_init_r,        /* miscellaneous platform-dependent init */
#endif
    INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_CMD_KGDB
    initr_kgdb,
#endif
    interrupt_init,
#if defined(CONFIG_ARM) || defined(CONFIG_AVR32)
    initr_enable_interrupts,
#endif
#if defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32) || defined(CONFIG_M68K)
    timer_init,     /* initialize timer */
#endif
#if defined(CONFIG_STATUS_LED)
    initr_status_led,
#endif
    /* PPC has a udelay(20) here dating from 2002. Why? */
#ifdef CONFIG_CMD_NET
    initr_ethaddr,
#endif
#ifdef CONFIG_BOARD_LATE_INIT
    board_late_init,
#endif
#ifdef CONFIG_FSL_FASTBOOT
    initr_fastboot_setup,
#endif
#if defined(CONFIG_CMD_AMBAPP)
    ambapp_init_reloc,
#if defined(CONFIG_SYS_AMBAPP_PRINT_ON_STARTUP)
    initr_ambapp_print,
#endif
#endif
#ifdef CONFIG_CMD_SCSI
    INIT_FUNC_WATCHDOG_RESET
    initr_scsi,
#endif
#ifdef CONFIG_CMD_DOC
    INIT_FUNC_WATCHDOG_RESET
    initr_doc,
#endif
#ifdef CONFIG_BITBANGMII
    initr_bbmii,
#endif
#ifdef CONFIG_CMD_NET
    INIT_FUNC_WATCHDOG_RESET
    initr_net,
#endif
#ifdef CONFIG_POST
    initr_post,
#endif
#if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_CMD_IDE)
    initr_pcmcia,
#endif
#if defined(CONFIG_CMD_IDE)
    initr_ide,
#endif
#ifdef CONFIG_LAST_STAGE_INIT
    INIT_FUNC_WATCHDOG_RESET
    /*
     * Some parts can be only initialized if all others (like
     * Interrupts) are up and running (i.e. the PC-style ISA
     * keyboard).
     */
    last_stage_init,
#endif
#ifdef CONFIG_CMD_BEDBUG
    INIT_FUNC_WATCHDOG_RESET
    initr_bedbug,
#endif
#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)
    initr_mem,
#endif
#ifdef CONFIG_PS2KBD
    initr_kbd,
#endif
#if defined(CONFIG_SPARC)
    prom_init,
#endif
#ifdef CONFIG_FSL_FASTBOOT
    initr_check_fastboot,
#endif
  /* 初始化完成 */
    run_main_loop,
}

0x30 启动时操作

随后在进行了硬件初始化完成之后,运行了run_main_loop(),这个函数主要作用为进入main_loop()。

/* We come here after U-Boot is initialised and ready to process commands */
void main_loop(void)
{
    const char *s;

    bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");

#ifndef CONFIG_SYS_GENERIC_BOARD
    puts("Warning: Your board does not use generic board. Please read\n");
    puts("doc/README.generic-board and take action. Boards not\n");
    puts("upgraded by the late 2014 may break or be removed.\n");
#endif

#ifdef CONFIG_VERSION_VARIABLE
    setenv("ver", version_string);  /* set version variable */
#endif /* CONFIG_VERSION_VARIABLE */

    cli_init();

    run_preboot_environment_command();

#if defined(CONFIG_UPDATE_TFTP)
    update_tftp(0UL, NULL, NULL);
#endif /* CONFIG_UPDATE_TFTP */
    /* BOOT等待一定时间,未出现键盘输入则自动跳走 */
    s = bootdelay_process();
    if (cli_process_fdt(&s))
        cli_secure_boot_cmd(s);

    autoboot_command(s);
    run_command("scr_menu", 0);
    cli_loop();
}

而如果这一段时间之后没有出现键盘输入则会跳入下方的程序,进入正式的Linux内核,而将当前的控制权交予Linux内核。

0x40 跳转方式与内存管理

跳转函数为hang()(不知道为啥叫了这么个奇奇怪怪的名字),是一个跳转指令,可以将CPU指令跳转进入Linux内核部分,随后进入了Linux自己的内核。
在之前,BOOT会清除Cache相应的数据,并且将相应的TAG数据存入内存中,并且修改了相应的中断相关入口。这些关键的数据都会在Linux进行使用。

0x50总结

  1. 因为当前BOOT的内核相应的代码,实在是太多了,分析起来不是很容易。所以还有很多没有分析到的逻辑,这个会在之后的文章上补全。

  2. 启动流程主要分为四步:

    1. 上电进入。

    2. 硬件初始化。

    3. 启动前数据清除与跳转后数据交互。

    4. 跳转到指定位置,并移交CPU控制权。

更多

本文首发自 学:关于嵌入式Linux内核Boot的启动流程的总结(一)-我的博客,更多文章可进入我的博客详查。

你可能感兴趣的:(Linux)