在前面的文章我们已经介绍了如何自己去实现一个BootLoader,今天我们来介绍u-boot的移植,u-boot是一种通用的BootLoader。
在嵌入式Linux之uboot源码make配置编译正向分析中我们已经介绍了如何通过Source Insight打开u-boot源码,以及uboot的编译过程。这一节我们将对u-boot源码进行分析,并且将uboot移植到Mini2440开发板。
在移植之前,我们首先需要明确Mini2440开发板具有的硬件资源:
- CPU:S3C2440;
- NAND:K9F2G08U0C;
- 网卡:DM9000A;
- SDRAM:HY57V561620FTP;
从网站上下载得到u-boot源码包,例如:u-boot-2016.05.tar.bz2 (最新的u-boot版本已经不支持s3c2440)。
一、uboot编译
在之前的文章中我们介绍过smdk2410的编译过程,这里在啰嗦一遍,uboot的编译分为两步:配置、编译:
1.1 配置
配置选择所要使用的board ,我调试使用的是S3C2440,但是./configs目录下没有smdk2440_defconfig这个文件,只有smdk2410_defconfig,因此执行如下命令,生成.config文件:
make smdk2410_defconfig
.config文件定义如下:
# # Automatically generated file; DO NOT EDIT. # U-Boot 2016.05 Configuration # CONFIG_CREATE_ARCH_SYMLINK=y CONFIG_HAVE_GENERIC_BOARD=y CONFIG_SYS_GENERIC_BOARD=y # CONFIG_ARC is not set CONFIG_ARM=y # CONFIG_AVR32 is not set # CONFIG_BLACKFIN is not set # CONFIG_M68K is not set # CONFIG_MICROBLAZE is not set # CONFIG_MIPS is not set # CONFIG_NDS32 is not set # CONFIG_NIOS2 is not set # CONFIG_OPENRISC is not set # CONFIG_PPC is not set # CONFIG_SANDBOX is not set # CONFIG_SH is not set # CONFIG_SPARC is not set # CONFIG_X86 is not set CONFIG_SYS_ARCH="arm" CONFIG_SYS_CPU="arm920t" CONFIG_SYS_SOC="s3c24x0" CONFIG_SYS_VENDOR="samsung" CONFIG_SYS_BOARD="smdk2410" CONFIG_SYS_CONFIG_NAME="smdk2410" # # ARM architecture # CONFIG_CPU_ARM920T=y # CONFIG_SEMIHOSTING is not set # CONFIG_SYS_L2CACHE_OFF is not set # CONFIG_ARCH_AT91 is not set # CONFIG_TARGET_EDB93XX is not set # CONFIG_TARGET_VCMA9 is not set CONFIG_TARGET_SMDK2410=y # CONFIG_TARGET_ASPENITE is not set # CONFIG_TARGET_GPLUGD is not set # CONFIG_ARCH_DAVINCI is not set # CONFIG_KIRKWOOD is not set # CONFIG_ARCH_MVEBU is not set # CONFIG_TARGET_DEVKIT3250 is not set # CONFIG_TARGET_WORK_92105 is not set # CONFIG_TARGET_MX25PDK is not set # CONFIG_TARGET_ZMX25 is not set # CONFIG_TARGET_APF27 is not set # CONFIG_TARGET_APX4DEVKIT is not set # CONFIG_TARGET_XFI3 is not set # CONFIG_TARGET_M28EVK is not set # CONFIG_TARGET_MX23EVK is not set # CONFIG_TARGET_MX28EVK is not set # CONFIG_TARGET_MX23_OLINUXINO is not set # CONFIG_TARGET_BG0900 is not set # CONFIG_TARGET_SANSA_FUZE_PLUS is not set # CONFIG_TARGET_SC_SPS_1 is not set # CONFIG_ORION5X is not set # CONFIG_TARGET_SPEAR300 is not set # CONFIG_TARGET_SPEAR310 is not set # CONFIG_TARGET_SPEAR320 is not set # CONFIG_TARGET_SPEAR600 is not set # CONFIG_TARGET_STV0991 is not set # CONFIG_TARGET_X600 is not set # CONFIG_TARGET_IMX31_PHYCORE is not set # CONFIG_TARGET_MX31ADS is not set # CONFIG_TARGET_MX31PDK is not set # CONFIG_TARGET_WOODBURN is not set # CONFIG_TARGET_WOODBURN_SD is not set # CONFIG_TARGET_FLEA3 is not set # CONFIG_TARGET_MX35PDK is not set # CONFIG_ARCH_BCM283X is not set # CONFIG_TARGET_VEXPRESS_CA15_TC2 is not set # CONFIG_TARGET_VEXPRESS_CA5X2 is not set # CONFIG_TARGET_VEXPRESS_CA9X4 is not set # CONFIG_TARGET_KWB is not set # CONFIG_TARGET_TSERIES is not set # CONFIG_TARGET_CM_T335 is not set # CONFIG_TARGET_PEPPER is not set # CONFIG_TARGET_AM335X_IGEP0033 is not set # CONFIG_TARGET_PCM051 is not set # CONFIG_TARGET_DRACO is not set # CONFIG_TARGET_THUBAN is not set # CONFIG_TARGET_RASTABAN is not set # CONFIG_TARGET_PXM2 is not set # CONFIG_TARGET_RUT is not set # CONFIG_TARGET_PENGWYN is not set # CONFIG_TARGET_AM335X_BALTOS is not set # CONFIG_TARGET_AM335X_EVM is not set # CONFIG_TARGET_AM335X_SL50 is not set # CONFIG_TARGET_AM43XX_EVM is not set # CONFIG_TARGET_BAV335X is not set # CONFIG_TARGET_TI814X_EVM is not set # CONFIG_TARGET_TI816X_EVM is not set # CONFIG_TARGET_BCM28155_AP is not set # CONFIG_TARGET_BCMCYGNUS is not set # CONFIG_TARGET_BCMNSP is not set # CONFIG_ARCH_EXYNOS is not set # CONFIG_ARCH_S5PC1XX is not set # CONFIG_ARCH_HIGHBANK is not set # CONFIG_ARCH_INTEGRATOR is not set # CONFIG_ARCH_KEYSTONE is not set # CONFIG_ARCH_MX7 is not set # CONFIG_ARCH_MX6 is not set # CONFIG_ARCH_MX5 is not set # CONFIG_TARGET_M53EVK is not set # CONFIG_TARGET_MX51EVK is not set # CONFIG_TARGET_MX53ARD is not set # CONFIG_TARGET_MX53EVK is not set # CONFIG_TARGET_MX53LOCO is not set # CONFIG_TARGET_MX53SMD is not set # CONFIG_OMAP34XX is not set # CONFIG_OMAP44XX is not set # CONFIG_OMAP54XX is not set # CONFIG_RMOBILE is not set # CONFIG_ARCH_SNAPDRAGON is not set # CONFIG_ARCH_SOCFPGA is not set # CONFIG_TARGET_CM_T43 is not set # CONFIG_ARCH_SUNXI is not set # CONFIG_TARGET_TS4800 is not set # CONFIG_TARGET_VF610TWR is not set # CONFIG_TARGET_COLIBRI_VF is not set # CONFIG_TARGET_PCM052 is not set # CONFIG_ARCH_ZYNQ is not set # CONFIG_ARCH_ZYNQMP is not set # CONFIG_TEGRA is not set # CONFIG_TARGET_VEXPRESS64_AEMV8A is not set # CONFIG_TARGET_VEXPRESS64_BASE_FVP is not set # CONFIG_TARGET_VEXPRESS64_BASE_FVP_DRAM is not set # CONFIG_TARGET_VEXPRESS64_JUNO is not set # CONFIG_TARGET_LS2080A_EMU is not set # CONFIG_TARGET_LS2080A_SIMU is not set # CONFIG_TARGET_LS2080AQDS is not set # CONFIG_TARGET_LS2080ARDB is not set # CONFIG_TARGET_HIKEY is not set # CONFIG_TARGET_LS1021AQDS is not set # CONFIG_TARGET_LS1021ATWR is not set # CONFIG_TARGET_LS1043AQDS is not set # CONFIG_TARGET_LS1043ARDB is not set # CONFIG_TARGET_H2200 is not set # CONFIG_TARGET_ZIPITZ2 is not set # CONFIG_TARGET_COLIBRI_PXA270 is not set # CONFIG_ARCH_UNIPHIER is not set # CONFIG_STM32 is not set # CONFIG_ARCH_ROCKCHIP is not set # CONFIG_TARGET_THUNDERX_88XX is not set # CONFIG_SYS_MALLOC_F is not set # # ARM debug # # CONFIG_DEBUG_LL is not set # # General setup # CONFIG_LOCALVERSION="" CONFIG_LOCALVERSION_AUTO=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_EXPERT=y CONFIG_SYS_MALLOC_CLEAR_ON_INIT=y # # Boot images # # CONFIG_FIT is not set CONFIG_SYS_EXTRA_OPTIONS="" # # Boot timing # # CONFIG_BOOTSTAGE is not set CONFIG_BOOTSTAGE_USER_COUNT=20 CONFIG_BOOTSTAGE_STASH_ADDR=0 CONFIG_BOOTSTAGE_STASH_SIZE=4096 # CONFIG_CONSOLE_RECORD is not set # # Command line interface # CONFIG_CMDLINE=y CONFIG_HUSH_PARSER=y CONFIG_SYS_HUSH_PARSER=y CONFIG_SYS_PROMPT="SMDK2410 # " # # Autoboot options # # CONFIG_AUTOBOOT_KEYED is not set # # Commands # # # Info commands # CONFIG_CMD_BDI=y CONFIG_CMD_CONSOLE=y # CONFIG_CMD_CPU is not set # CONFIG_CMD_LICENSE is not set # # Boot commands # CONFIG_CMD_BOOTD=y CONFIG_CMD_BOOTM=y # CONFIG_CMD_BOOTZ is not set CONFIG_CMD_ELF=y CONFIG_CMD_GO=y CONFIG_CMD_RUN=y CONFIG_CMD_IMI=y CONFIG_CMD_IMLS=y CONFIG_CMD_XIMG=y # # Environment commands # # CONFIG_CMD_ASKENV is not set CONFIG_CMD_EXPORTENV=y CONFIG_CMD_IMPORTENV=y CONFIG_CMD_EDITENV=y # CONFIG_CMD_GREPENV is not set CONFIG_CMD_SAVEENV=y CONFIG_CMD_ENV_EXISTS=y # # Memory commands # CONFIG_CMD_MEMORY=y CONFIG_CMD_CRC32=y # CONFIG_LOOPW is not set # CONFIG_CMD_MEMTEST is not set # CONFIG_CMD_MX_CYCLIC is not set # CONFIG_CMD_MEMINFO is not set # # Device access commands # CONFIG_CMD_LOADB=y CONFIG_CMD_LOADS=y CONFIG_CMD_FLASH=y # CONFIG_CMD_ARMFLASH is not set # CONFIG_CMD_MMC is not set # CONFIG_CMD_NAND is not set # CONFIG_CMD_SF is not set # CONFIG_CMD_SPI is not set # CONFIG_CMD_I2C is not set CONFIG_CMD_USB=y # CONFIG_CMD_DFU is not set # CONFIG_CMD_USB_MASS_STORAGE is not set CONFIG_CMD_FPGA=y # CONFIG_CMD_GPIO is not set # # Shell scripting commands # CONFIG_CMD_ECHO=y CONFIG_CMD_ITEST=y CONFIG_CMD_SOURCE=y # CONFIG_CMD_SETEXPR is not set # # Network commands # CONFIG_CMD_NET=y # CONFIG_CMD_TFTPPUT is not set # CONFIG_CMD_TFTPSRV is not set # CONFIG_CMD_RARP is not set CONFIG_CMD_DHCP=y CONFIG_CMD_NFS=y # CONFIG_CMD_MII is not set CONFIG_CMD_PING=y # CONFIG_CMD_CDP is not set # CONFIG_CMD_SNTP is not set # CONFIG_CMD_DNS is not set # CONFIG_CMD_LINK_LOCAL is not set # # Misc commands # CONFIG_CMD_CACHE=y # CONFIG_CMD_TIME is not set CONFIG_CMD_MISC=y # CONFIG_CMD_TIMER is not set # # Power commands # # # Security commands # # # Filesystem commands # CONFIG_CMD_EXT2=y # CONFIG_CMD_EXT4 is not set CONFIG_CMD_FAT=y # CONFIG_CMD_FS_GENERIC is not set CONFIG_SUPPORT_OF_CONTROL=y # # Device Tree Control # # CONFIG_OF_CONTROL is not set CONFIG_NET=y # CONFIG_NET_RANDOM_ETHADDR is not set # CONFIG_NETCONSOLE is not set CONFIG_NET_TFTP_VARS=y # # Device Drivers # # # Generic Driver Options # # CONFIG_DM is not set # CONFIG_ADC is not set # CONFIG_ADC_EXYNOS is not set # CONFIG_ADC_SANDBOX is not set # CONFIG_BLOCK_CACHE is not set # # Clock # # CONFIG_CPU is not set # # Hardware crypto devices # # CONFIG_FSL_CAAM is not set # # Demo for driver model # # # DFU support # # CONFIG_DFU_TFTP is not set # # DMA Support # # CONFIG_TI_EDMA3 is not set # # GPIO Support # # # I2C support # # CONFIG_CROS_EC_KEYB is not set # # LED Support # # # Memory Controller drivers # # # Multifunction device drivers # # CONFIG_CROS_EC is not set # CONFIG_FSL_SEC_MON is not set # CONFIG_MXC_OCOTP is not set # CONFIG_PCA9551_LED is not set # CONFIG_WINBOND_W83627 is not set # # MMC Host controller Support # # # MTD Support # # # NAND Device Support # # CONFIG_NAND_DENALI is not set # CONFIG_NAND_VF610_NFC is not set # CONFIG_NAND_PXA3XX is not set # CONFIG_NAND_ARASAN is not set # # Generic NAND options # # # SPI Flash Support # # CONFIG_SPI_FLASH is not set # CONFIG_PHYLIB is not set # CONFIG_NETDEVICES is not set # # PCI # # # Pin controllers # # # Power # # # Remote Processor drivers # # # Real Time Clock # # # Serial drivers # # CONFIG_DEBUG_UART is not set # CONFIG_DEBUG_UART_SKIP_INIT is not set # CONFIG_FSL_LPUART is not set # CONFIG_SYS_NS16550 is not set # # Sound support # # CONFIG_SOUND is not set # # SPI Support # # CONFIG_FSL_ESPI is not set # CONFIG_TI_QSPI is not set # # SPMI support # # CONFIG_DM_THERMAL is not set # # Timer Support # # # TPM support # # CONFIG_USB is not set # # Graphics support # # # TrueType Fonts # # CONFIG_VIDEO_VESA is not set # CONFIG_VIDEO_LCD_ANX9804 is not set # CONFIG_VIDEO_LCD_SSD2828 is not set # CONFIG_VIDEO_MVEBU is not set # CONFIG_PHYS_TO_BUS is not set # # File systems # # # Library routines # # CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED is not set CONFIG_HAVE_PRIVATE_LIBGCC=y # CONFIG_USE_PRIVATE_LIBGCC is not set CONFIG_SYS_HZ=1000 # CONFIG_USE_TINY_PRINTF is not set CONFIG_REGEX=y # CONFIG_LIB_RAND is not set # CONFIG_CMD_DHRYSTONE is not set # CONFIG_RSA is not set # # Hashing Support # # CONFIG_SHA1 is not set # CONFIG_SHA256 is not set # CONFIG_SHA_HW_ACCEL is not set # # Compression Support # # CONFIG_LZ4 is not set # CONFIG_ERRNO_STR is not set # CONFIG_OF_LIBFDT is not set # CONFIG_SPL_OF_LIBFDT is not set # CONFIG_UNIT_TEST is not set
这个文件和include/config/auto.conf文件内容大致差不多:
# # Automatically generated file; DO NOT EDIT. # U-Boot 2016.05 Configuration # CONFIG_CMD_BOOTM=y CONFIG_CMD_USB=y CONFIG_CMD_EDITENV=y CONFIG_SYS_GENERIC_BOARD=y CONFIG_CMD_CONSOLE=y CONFIG_CMD_BOOTD=y CONFIG_CMD_IMLS=y CONFIG_BOOTSTAGE_STASH_ADDR=0 CONFIG_HAVE_PRIVATE_LIBGCC=y CONFIG_CMD_BDI=y CONFIG_ARM=y CONFIG_CREATE_ARCH_SYMLINK=y CONFIG_SYS_VENDOR="samsung" CONFIG_CMD_PING=y CONFIG_CMD_SAVEENV=y CONFIG_CMD_MISC=y CONFIG_SYS_CPU="arm920t" CONFIG_BOOTSTAGE_USER_COUNT=20 CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_CMD_FLASH=y CONFIG_REGEX=y CONFIG_CMD_ENV_EXISTS=y CONFIG_HAVE_GENERIC_BOARD=y CONFIG_CMD_EXPORTENV=y CONFIG_CMD_DHCP=y CONFIG_SYS_EXTRA_OPTIONS="" CONFIG_CMD_CRC32=y CONFIG_SYS_BOARD="smdk2410" CONFIG_SYS_CONFIG_NAME="smdk2410" CONFIG_CMD_NFS=y CONFIG_NET=y CONFIG_TARGET_SMDK2410=y CONFIG_CMD_GO=y CONFIG_SYS_MALLOC_CLEAR_ON_INIT=y CONFIG_CMD_IMI=y CONFIG_SYS_HZ=1000 CONFIG_SUPPORT_OF_CONTROL=y CONFIG_CMD_EXT2=y CONFIG_LOCALVERSION="" CONFIG_CMDLINE=y CONFIG_CMD_LOADB=y CONFIG_CMD_RUN=y CONFIG_SYS_HUSH_PARSER=y CONFIG_SYS_PROMPT="SMDK2410 # " CONFIG_CMD_MEMORY=y CONFIG_HUSH_PARSER=y CONFIG_CMD_XIMG=y CONFIG_CMD_ECHO=y CONFIG_LOCALVERSION_AUTO=y CONFIG_SYS_ARCH="arm" CONFIG_EXPERT=y CONFIG_SYS_SOC="s3c24x0" CONFIG_CMD_ITEST=y CONFIG_CMD_CACHE=y CONFIG_BOOTSTAGE_STASH_SIZE=4096 CONFIG_CMD_LOADS=y CONFIG_CMD_FAT=y CONFIG_CMD_NET=y CONFIG_CMD_FPGA=y CONFIG_NET_TFTP_VARS=y CONFIG_CPU_ARM920T=y CONFIG_CMD_IMPORTENV=y CONFIG_CMD_ELF=y CONFIG_CMD_SOURCE=y
include/config/auto.conf是由fixdep在编译时生成的依赖文件。在顶层Makefile会引入auto.conf文件:
ifeq ($(dot-config),1) # Read in config -include include/config/auto.conf
1.2 编译
编译、执行make命令,生成u-boot:
make ARCH=arm CROSS_COMPILE=arm-linux-
如果需要输出u-boot反汇编代码,执行时加入-j4参数,会在u-boot根目录下生成u-boot.map文件,在map文件中,包含函数名以及函数所在文件。
make ARCH=arm CROSS_COMPILE=arm-linux- -j4
直接反汇编u-boot文件,可以得到反汇编代码:
arm-linux-objdump -D u-boot > u-boot.dis
CROSS-COPILE是在Makefile文件中定义的变量,是用来指定交叉工具链,ARCH用来指定处理器架构。此外,我们可以在u-boot的顶层Makefile中定义:
CROSS_COMPILE=arm-linux- ARCH=arm
这样就省去了每次编译都要在控制台输入的麻烦。
编译成功后会生成一个u-boot.bin,可以烧写到开发板上,不过一般是用不起来的,需要进一步修改。
我们在编译u-boot的时候加入V=1参数:
make ARCH=arm CROSS_COMPILE=arm-linux- V=1
将会输出u-boot编译的完整步骤:
set -e; : ' CHK include/config/uboot.release'; mkdir -p include/config/; echo "2016.05$(/bin/bash ./scriptfig/uboot.release.tmp; then rm -f include/config/uboot.release.tmp; else : ' UPD include/config/uboot.releas set -e; : ' CHK include/generated/version_autogenerated.h'; mkdir -p include/generated/; (echo #define PLVERSION_STRING "$(arm-linux-ld --version | head -n 1)"; ) < include/config/uboot.release > include/generated/vemp; then rm -f include/generated/version_autogenerated.h.tmp; else : ' UPD include/generated/version_autogen set -e; : ' CHK include/generated/timestamp_autogenerated.h'; mkdir -p include/generated/; (if test te}"; done; if test -n "${DATE}"; then LC_ALL=C ${DATE} -u -d "${SOURCE_DATE}" +'#define U_BOOT_DATE "%b %d %C%y"TE}" +'#define U_BOOT_DMI_DATE "%m/%d/%Y"'; else return 42; fi; else LC_ALL=C date +'#define U_BOOT_DATE "%b %d %ated/timestamp_autogenerated.h.tmp; if [ -r include/generated/timestamp_autogenerated.h ] && cmp -s include/generd/timestamp_autogenerated.h'; mv -f include/generated/timestamp_autogenerated.h.tmp include/generated/timestamp_a make -f ./scripts/Makefile.build obj=scripts/basic rm -f .tmp_quiet_recordmcount make -f ./scripts/Makefile.build obj=. mkdir -p lib/ set -e; : ' CHK include/generated/generic-asm-offsets.h'; mkdir -p include/generated/; (set -e; echo "#i" */"; echo ""; sed -ne "s:[[:space:]]*.ascii[[:space:]]*"(.*)":1:; /^->/{s:->#(.*):/* 1 */:; s:-offsets.s > include/generated/generic-asm-offsets.h.tmp; if [ -r include/generated/generic-asm-offsets.h ] && cmnerated/generic-asm-offsets.h'; mv -f include/generated/generic-asm-offsets.h.tmp include/generated/generic-asm-o mkdir -p arch/arm/lib/ set -e; : ' CHK include/generated/asm-offsets.h'; mkdir -p include/generated/; (set -e; echo "#ifndef __"s:[[:space:]]*.ascii[[:space:]]*"(.*)":1:; /^->/{s:->#(.*):/* 1 */:; s:^->([^ ]*) [$#]*([-0-9]*) erated/asm-offsets.h.tmp; if [ -r include/generated/asm-offsets.h ] && cmp -s include/generated/asm-offsets.h inmp include/generated/asm-offsets.h; fi make -f ./scripts/Makefile.build obj=tools cc -Wp,-MD,tools/.mkenvimage.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -include ./include/libfdtvimage.o tools/mkenvimage.c cc -o tools/mkenvimage tools/mkenvimage.o tools/os_support.o tools/lib/crc32.o cc -Wp,-MD,tools/.fit_image.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -include ./include/libfdt_mage.o tools/fit_image.c cc -Wp,-MD,tools/.image-host.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -include ./include/libfdte-host.o tools/image-host.c cc -Wp,-MD,tools/.dumpimage.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -include ./include/libfdt_mage.o tools/dumpimage.c cc -o tools/dumpimage tools/aisimage.o tools/atmelimage.o tools/common/bootm.o tools/lib/crc32.o tools/defaulttools/common/image.o tools/imagetool.o tools/imximage.o tools/kwbimage.o tools/lib/md5.o tools/lpc32xximage.o tooaimage.o tools/lib/sha1.o tools/lib/sha256.o tools/common/hash.o tools/ublimage.o tools/zynqimage.o tools/lib/lib.o tools/dumpimage.o cc -Wp,-MD,tools/.mkimage.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -include ./include/libfdt_en.o tools/mkimage.c cc -o tools/mkimage tools/aisimage.o tools/atmelimage.o tools/common/bootm.o tools/lib/crc32.o tools/default_iols/common/image.o tools/imagetool.o tools/imximage.o tools/kwbimage.o tools/lib/md5.o tools/lpc32xximage.o toolsmage.o tools/lib/sha1.o tools/lib/sha256.o tools/common/hash.o tools/ublimage.o tools/zynqimage.o tools/lib/libfd tools/mkimage.o make -f ./scripts/Makefile.build obj=arch/arm/cpu make -f ./scripts/Makefile.build obj=arch/arm/cpu/arm920t make -f ./scripts/Makefile.build obj=arch/arm/cpu/arm920t/s3c24x0 make -f ./scripts/Makefile.build obj=arch/arm/lib make -f ./scripts/Makefile.build obj=board/samsung/common make -f ./scripts/Makefile.build obj=board/samsung/smdk2410 make -f ./scripts/Makefile.build obj=cmd arm-linux-gcc -Wp,-MD,cmd/.version.o.d -nostdinc -isystem /usr/local/arm/4.3.2/bin/../lib/gcc/arm-none-linux-g-ffreestanding -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -Wno-format-nonliteral -D__ARM__ -marmTR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(version)" -D"KBUILD_MODNAME=KBUILD_STR(version)" -c -o cmd/version.o cmd arm-linux-ld -r -o cmd/built-in.o cmd/boot.o cmd/bootm.o cmd/help.o cmd/version.o cmd/source.o cmd/bdinfo.cmd/net.o cmd/pcmcia.o cmd/reginfo.o cmd/test.o cmd/ubi.o cmd/ubifs.o cmd/usb.o cmd/ximg.o cmd/yaffs2.o cmd/nvedi make -f ./scripts/Makefile.build obj=common make -f ./scripts/Makefile.build obj=common/init arm-linux-gcc -Wp,-MD,common/.main.o.d -nostdinc -isystem /usr/local/arm/4.3.2/bin/../lib/gcc/arm-none-linux-g-ffreestanding -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -Wno-format-nonliteral -D__ARM__ -marmTR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(main)" -D"KBUILD_MODNAME=KBUILD_STR(main)" -c -o common/main.o common/ma arm-linux-gcc -Wp,-MD,common/.board_f.o.d -nostdinc -isystem /usr/local/arm/4.3.2/bin/../lib/gcc/arm-none-linuin -ffreestanding -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -Wno-format-nonliteral -D__ARM__ -mD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(board_f)" -D"KBUILD_MODNAME=KBUILD_STR(board_f)" -c -o common/board_f arm-linux-ld -r -o common/built-in.o common/init/built-in.o common/main.o common/exports.o common/hash.o cags.o common/env_flash.o common/usb.o common/usb_hub.o common/usb_storage.o common/flash.o common/splash.o common/command.o common/s_record.o common/xyzModem.o make -f ./scripts/Makefile.build obj=disk make -f ./scripts/Makefile.build obj=drivers make -f ./scripts/Makefile.build obj=drivers/adc make -f ./scripts/Makefile.build obj=drivers/block make -f ./scripts/Makefile.build obj=drivers/crypto make -f ./scripts/Makefile.build obj=drivers/crypto/fsl make -f ./scripts/Makefile.build obj=drivers/crypto/rsa_mod_exp make -f ./scripts/Makefile.build obj=drivers/dfu make -f ./scripts/Makefile.build obj=drivers/hwmon make -f ./scripts/Makefile.build obj=drivers/input make -f ./scripts/Makefile.build obj=drivers/memory make -f ./scripts/Makefile.build obj=drivers/misc make -f ./scripts/Makefile.build obj=drivers/pcmcia make -f ./scripts/Makefile.build obj=drivers/pwm make -f ./scripts/Makefile.build obj=drivers/rtc make -f ./scripts/Makefile.build obj=drivers/soc make -f ./scripts/Makefile.build obj=drivers/sound make -f ./scripts/Makefile.build obj=drivers/spmi make -f ./scripts/Makefile.build obj=drivers/thermal make -f ./scripts/Makefile.build obj=drivers/timer make -f ./scripts/Makefile.build obj=drivers/tpm make -f ./scripts/Makefile.build obj=drivers/twserial make -f ./scripts/Makefile.build obj=drivers/video make -f ./scripts/Makefile.build obj=drivers/video/bridge make -f ./scripts/Makefile.build obj=drivers/watchdog make -f ./scripts/Makefile.build obj=drivers/dma make -f ./scripts/Makefile.build obj=drivers/gpio make -f ./scripts/Makefile.build obj=drivers/i2c make -f ./scripts/Makefile.build obj=drivers/mmc make -f ./scripts/Makefile.build obj=drivers/mtd make -f ./scripts/Makefile.build obj=drivers/mtd/nand make -f ./scripts/Makefile.build obj=drivers/mtd/onenand make -f ./scripts/Makefile.build obj=drivers/mtd/spi make -f ./scripts/Makefile.build obj=drivers/mtd/ubi make -f ./scripts/Makefile.build obj=drivers/net make -f ./scripts/Makefile.build obj=drivers/net/phy make -f ./scripts/Makefile.build obj=drivers/pci make -f ./scripts/Makefile.build obj=drivers/power make -f ./scripts/Makefile.build obj=drivers/power/battery make -f ./scripts/Makefile.build obj=drivers/power/fuel_gauge make -f ./scripts/Makefile.build obj=drivers/power/mfd make -f ./scripts/Makefile.build obj=drivers/power/pmic make -f ./scripts/Makefile.build obj=drivers/power/regulator make -f ./scripts/Makefile.build obj=drivers/serial make -f ./scripts/Makefile.build obj=drivers/spi make -f ./scripts/Makefile.build obj=drivers/usb/common make -f ./scripts/Makefile.build obj=drivers/usb/dwc3 make -f ./scripts/Makefile.build obj=drivers/usb/emul make -f ./scripts/Makefile.build obj=drivers/usb/eth make -f ./scripts/Makefile.build obj=drivers/usb/gadget make -f ./scripts/Makefile.build obj=drivers/usb/gadget/udc make -f ./scripts/Makefile.build obj=drivers/usb/host make -f ./scripts/Makefile.build obj=drivers/usb/musb-new make -f ./scripts/Makefile.build obj=drivers/usb/musb make -f ./scripts/Makefile.build obj=drivers/usb/phy make -f ./scripts/Makefile.build obj=drivers/usb/ulpi make -f ./scripts/Makefile.build obj=fs make -f ./scripts/Makefile.build obj=fs/ext4 make -f ./scripts/Makefile.build obj=fs/fat make -f ./scripts/Makefile.build obj=fs/ubifs make -f ./scripts/Makefile.build obj=fs/yaffs2 make -f ./scripts/Makefile.build obj=lib make -f ./scripts/Makefile.build obj=lib/bzip2 make -f ./scripts/Makefile.build obj=lib/lzma make -f ./scripts/Makefile.build obj=lib/lzo make -f ./scripts/Makefile.build obj=lib/zlib arm-linux-gcc -Wp,-MD,lib/.display_options.o.d -nostdinc -isystem /usr/local/arm/4.3.2/bin/../lib/gcc/arm-nonebuiltin -ffreestanding -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -Wno-format-nonliteral -D__ARMKBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(display_options)" -D"KBUILD_MODNAME=KBUILD_STR(display_options)" arm-linux-ld -r -o lib/built-in.o lib/lzma/built-in.o lib/lzo/built-in.o lib/zlib/built-in.o lib/bzip2/bui/hashtable.o lib/errno.o lib/display_options.o lib/crc32.o lib/ctype.o lib/div64.o lib/hang.o lib/linux_compat.o make -f ./scripts/Makefile.build obj=net make -f ./scripts/Makefile.build obj=test make -f ./scripts/Makefile.build obj=test/dm make -f ./scripts/Makefile.build obj=examples make -f ./scripts/Makefile.build obj=examples/standalone arm-linux-ld -pie --gc-sections -Bstatic -Ttext 0x0 -o u-boot -T u-boot.lds arch/arm/cpu/arm920t/start.o --sn.o common/built-in.o disk/built-in.o drivers/built-in.o drivers/dma/built-in.o drivers/gpio/built-in.o dribuilt-in.o drivers/net/built-in.o drivers/net/phy/built-in.o drivers/pci/built-in.o drivers/power/built-in.o rs/serial/built-in.o drivers/spi/built-in.o drivers/usb/common/built-in.o drivers/usb/dwc3/built-in.o drivers.o drivers/usb/musb/built-in.o drivers/usb/phy/built-in.o drivers/usb/ulpi/built-in.o fs/built-in.o lib/buil -lgcc -Map u-boot.map arm-linux-objcopy --gap-fill=0xff -j .text -j .secure_text -j .rodata -j .hash -j .data -j .got -j .got.plt -j arm-linux-objcopy --gap-fill=0xff -j .text -j .secure_text -j .rodata -j .hash -j .data -j .got -j .got.plt -j cp u-boot-nodtb.bin u-boot.bin arm-linux-objdump -t u-boot > u-boot.sym
我们大致看一下输出能容,主要包括以下两点:
-
先利用./scripts/Makefile.build编译各个built-in.o目标文件;
- 再将各个bult-in.o文件链接成u-boot;
二、 u-boot.lds
想要分析启动流程,第一步就是分析链接文件,弄清楚程序入口是哪里。在分析Makefile中,u-boot的依赖中可以看到链接脚本文件是u-boot.lds,u-boot.lds依赖于$(LDSCRIPT)和prepare。
通过对u-boot源代码编译会在顶层目录生成u-boot.lds,打开u-boot.lds:
$(LDSCRIPT)变量值为arch/arm/cpu/u-boot.lds,定位到该文件:
#include <config.h> OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { #ifndef CONFIG_CMDLINE /DISCARD/ : { *(.u_boot_list_2_cmd_*) } #endif #if defined(CONFIG_ARMV7_SECURE_BASE) && defined(CONFIG_ARMV7_NONSEC) /* * If CONFIG_ARMV7_SECURE_BASE is true, secure code will not * bundle with u-boot, and code offsets are fixed. Secure zone * only needs to be copied from the loading address to * CONFIG_ARMV7_SECURE_BASE, which is the linking and running * address for secure code. * * If CONFIG_ARMV7_SECURE_BASE is undefined, the secure zone will * be included in u-boot address space, and some absolute address * were used in secure code. The absolute addresses of the secure * code also needs to be relocated along with the accompanying u-boot * code. * * So DISCARD is only for CONFIG_ARMV7_SECURE_BASE. */ /DISCARD/ : { *(.rel._secure*) } #endif . = 0x00000000; . = ALIGN(4); .text : { *(.__image_copy_start) *(.vectors) CPUDIR/start.o (.text*) *(.text*) } #ifdef CONFIG_ARMV7_NONSEC #ifndef CONFIG_ARMV7_SECURE_BASE #define CONFIG_ARMV7_SECURE_BASE #endif .__secure_start : { . = ALIGN(0x1000); *(.__secure_start) } .secure_text CONFIG_ARMV7_SECURE_BASE : AT(ADDR(.__secure_start) + SIZEOF(.__secure_start)) { *(._secure.text) } . = LOADADDR(.__secure_start) + SIZEOF(.__secure_start) + SIZEOF(.secure_text); __secure_end_lma = .; .__secure_end : AT(__secure_end_lma) { *(.__secure_end) LONG(0x1d1071c); /* Must output something to reset LMA */ } #endif . = ALIGN(4); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } . = ALIGN(4); .data : { *(.data*) } . = ALIGN(4); . = .; . = ALIGN(4); .u_boot_list : { KEEP(*(SORT(.u_boot_list*))); } . = ALIGN(4); .__efi_runtime_start : { *(.__efi_runtime_start) } .efi_runtime : { *(efi_runtime_text) *(efi_runtime_data) } .__efi_runtime_stop : { *(.__efi_runtime_stop) } .efi_runtime_rel_start : { *(.__efi_runtime_rel_start) } .efi_runtime_rel : { *(.relefi_runtime_text) *(.relefi_runtime_data) } .efi_runtime_rel_stop : { *(.__efi_runtime_rel_stop) } . = ALIGN(4); .image_copy_end : { *(.__image_copy_end) } .rel_dyn_start : { *(.__rel_dyn_start) } .rel.dyn : { *(.rel*) } .rel_dyn_end : { *(.__rel_dyn_end) } .end : { *(.__end) } _image_binary_end = .; /* * Deprecated: this MMU section is used by pxa at present but * should not be used by new boards/CPUs. */ . = ALIGN(4096); .mmutable : { *(.mmutable) } /* * Compiler-generated __bss_start and __bss_end, see arch/arm/lib/bss.c * __bss_base and __bss_limit are for linker only (overlay ordering) */ .bss_start __rel_dyn_start (OVERLAY) : { KEEP(*(.__bss_start)); __bss_base = .; } .bss __bss_base (OVERLAY) : { *(.bss*) . = ALIGN(4); __bss_limit = .; } .bss_end __bss_limit (OVERLAY) : { KEEP(*(.__bss_end)); } .dynsym _image_binary_end : { *(.dynsym) } .dynbss : { *(.dynbss) } .dynstr : { *(.dynstr*) } .dynamic : { *(.dynamic*) } .plt : { *(.plt*) } .interp : { *(.interp*) } .gnu.hash : { *(.gnu.hash) } .gnu : { *(.gnu*) } .ARM.exidx : { *(.ARM.exidx*) } .gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) } }
- OUTPUT_FORMAT:
-
- *(.__image_copy_start):这里比较有意思的是section(.__image_copy_start),在arch/arm/lib/sections.c中有如下定义:
char __image_copy_start[0] __attribute__((section(".__image_copy_start")));
这是一个0长度的数组,GNU 对C的扩展,实际达到的目的就是section(.__image_copy_start)在链接时不占用链接地址空间,其后的section(.vectors)起始地址依然是0x00000000,然后在__image_copy_start存放了一个地址,也就是0x00000000,相当于创建一个位置标签;代表程序重定位(relocate)时拷贝的起始地址; - *(.vectors):字面上的意思就是向量段,在arch/arm/lib/vectors.S中定义了.verctos段:.section ".vectors", "ax";这是一个自定义的段标签的伪操作,含义是:定义一个段标签名为.vectors,且为允许、可执行段;
- CPUDIR/start.o (.text*):CPUDIR在根目录的Makefile中定义,根据芯片;选择不同的start.S编译,编译出的start.o中.text段,链接到此处;在这里CPUDIR=arch/arm/cpu/arm920t;
- *(.text*):剩余所有的代码都放到此段;
- *(.__image_copy_start):这里比较有意思的是section(.__image_copy_start),在arch/arm/lib/sections.c中有如下定义:
- .rodata{}:声明只读数据段,简称rodata段,存放常量,字符常量,const常量,据说还存放调试信息;
- .data{}:声明初始化数据段(Initialized data segment),简称data段,存放程序中已经初始化全局与初始化静态变量;
- .bss{}:声明未始化数据段(Uninitialized data segment),简称bss段,存放程序中未初始化全局与未初始化静态变量,该区域会在程序载入时由内核清零;
__image_copy_start、__image_copy_end用于u-boot搬移本身image到指定的ddr地址处;
__rel_dyn_start、__rel_dyn_end用于重定位代码;
__bss_start、__bss_end是bss段的开始、结束地址;
这几个变量在arch/arm/lib/sections.c中使用,定义一些全局变量作用于各个段的起始地址:
//C文件中利用这种方式把一个变量或者函数标记到指定段。 char __bss_start[0] __attribute__((section(".__bss_start"))); char __bss_end[0] __attribute__((section(".__bss_end"))); char __image_copy_start[0] __attribute__((section(".__image_copy_start"))); char __image_copy_end[0] __attribute__((section(".__image_copy_end"))); char __rel_dyn_start[0] __attribute__((section(".__rel_dyn_start"))); char __rel_dyn_end[0] __attribute__((section(".__rel_dyn_end"))); char __secure_start[0] __attribute__((section(".__secure_start"))); char __secure_end[0] __attribute__((section(".__secure_end")));
我们接下来分析start.S,程序的第一条指令就是在arch/arm/cpu/arm920t/start.S中。
三、分析start.S(arch/arm/cpu/arm920t/start.S)
/* * armboot - Startup Code for ARM920 CPU-core * * Copyright (c) 2001 Marius Gröger <mag@sysgo.de> * Copyright (c) 2002 Alex Züpke <azu@sysgo.de> * Copyright (c) 2002 Gary Jennejohn <garyj@denx.de> * * SPDX-License-Identifier: GPL-2.0+ */ #include <asm-offsets.h> #include <common.h> #include <config.h> /* ************************************************************************* * * Startup Code (called from the ARM reset exception vector) * * do important init only if we don't start from memory! * relocate armboot to ram * setup stack * jump to second stage * ************************************************************************* */ .globl reset reset: /* * set the cpu to SVC32 mode 1. 设置为SVC模式 */ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 msr cpsr, r0 #if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) /* * relocate exception table */ ldr r0, =_start ldr r1, =0x0 mov r2, #16 copyex: subs r2, r2, #1 ldr r3, [r0], #4 str r3, [r1], #4 bne copyex #endif #ifdef CONFIG_S3C24X0 /* turn off the watchdog */ # if defined(CONFIG_S3C2400) # define pWTCON 0x15300000 # define INTMSK 0x14400008 /* Interrupt-Controller base addresses */ # define CLKDIVN 0x14800014 /* clock divisor register */ #else # define pWTCON 0x53000000 /* 看门狗控制寄存器地址 */ # define INTMSK 0x4A000008 /* Interrupt-Controller base addresses */ # define INTSUBMSK 0x4A00001C # define CLKDIVN 0x4C000014 /* clock divisor register */ # endif ldr r0, =pWTCON //2. 关看门狗 mov r1, #0x0 str r1, [r0] /* * mask all IRQs by setting all bits in the INTMR - default */ mov r1, #0xffffffff //3. 屏蔽中断 ldr r0, =INTMSK str r1, [r0] # if defined(CONFIG_S3C2410) ldr r1, =0x3ff ldr r0, =INTSUBMSK str r1, [r0] # endif /* FCLK:HCLK:PCLK = 1:2:4 */ /* default FCLK is 120 MHz ! */ ldr r0, =CLKDIVN //4. 设置分频系数,未设置时钟频率 mov r1, #3 str r1, [r0] #endif /* CONFIG_S3C24X0 */ /* * we do sys-critical inits only at reboot, * not when booting from ram! */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit //5. 跳到cpu_init_crit函数 #endif bl _main //6. 进入arch/arm/lib/crt0.S的_main函数,进行其他初始化 /*------------------------------------------------------------------------------*/ .globl c_runtime_cpu_setup c_runtime_cpu_setup: mov pc, lr /* ************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * ************************************************************************* */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT cpu_init_crit: /* * flush v4 I/D caches [1]. 清除cache */ mov r0, #0 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ /* * disable MMU stuff and caches [2]. 禁止MMU */ mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM) orr r0, r0, #0x00000002 @ set bit 1 (A) Align orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache mcr p15, 0, r0, c1, c0, 0 #ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY /* * before relocating, we have to setup RAM timing * because memory timing is board-dependend, you will * find a lowlevel_init.S in your board directory. */ mov ip, lr bl lowlevel_init //[3]. 进入board/samsung/smdk2410/lowlevel_init.S执行, // 设置内存控制寄存器时序参数 mov lr, ip #endif mov pc, lr #endif /* CONFIG_SKIP_LOWLEVEL_INIT */
我们总结以下start.S文件主要做了哪些事情:
- 开启SVC模式,关闭fiq,irq中断;
- 关闭看门狗
-
屏蔽所有中断;
-
设置系统时钟;
-
执行cpu_init_crit函数;
- 关闭关闭MMU和cache;
- 执行lowlevel_init函数(board/samsung/smdk2410/lowlevel_init.S),初始化SDRAM;
- 执行_main(arch/arm/lib/crt0.S);
四、make编译过程
make在编译过程中,会根据不用的obj,编译生成相应的xxx/.../xxxx/built-in.o文件。
make -f ./scripts/Makefile.build obj=目标路径 #执行目标下的Makefile生成build-in.o
然后由这些文件最终链接连接成u-boot文件:
arm-linux-ld -pie --gc-sections -Bstatic -Ttext 0x0 -o u-boot -T u-boot.lds arch/arm/cpu/arm920t/start.o --sn.o common/built-in.o disk/built-in.o drivers/built-in.o drivers/dma/built-in.o drivers/gpio/built-in.o dribuilt-in.o drivers/net/built-in.o drivers/net/phy/built-in.o drivers/pci/built-in.o drivers/power/built-in.o rs/serial/built-in.o drivers/spi/built-in.o drivers/usb/common/built-in.o drivers/usb/dwc3/built-in.o drivers.o drivers/usb/musb/built-in.o drivers/usb/phy/built-in.o drivers/usb/ulpi/built-in.o fs/built-in.o lib/buil -lgcc -Map u-boot.map
不知道你们有没有留意到吗,在make的编译过程中,有以下几个步骤:
make -f ./scripts/Makefile.build obj=arch/arm/cpu make -f ./scripts/Makefile.build obj=arch/arm/cpu/arm920t make -f ./scripts/Makefile.build obj=arch/arm/cpu/arm920t/s3c24x0
我们从最后一个来说,这里将会跳转到./script/Makefile.build文件中去执行,然后include obj路径下的Makefile文件,即arch/arm/cpu/arm920t/s3c24x0/Makefile。
4.1 arch/arm/cpu/arm920t/s3c24x0/Makefile
arch/arm/cpu/arm920t/s3c24x0/Makefile:
# # (C) Copyright 2000-2006 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. # # SPDX-License-Identifier: GPL-2.0+ # obj-$(CONFIG_USE_IRQ) += interrupts.o obj-$(CONFIG_DISPLAY_CPUINFO) += cpu_info.o obj-y += speed.o obj-y += timer.o
obj-y是在./script/Makefile.build文件中定义:
obj-y :=
最终目标arch/arm/cpu/arm920t/s3c24x0/built-in.o依赖的obj-y如下:
- arch/arm/cpu/arm920t/s3c24x0/cpu_info.o
- arch/arm/cpu/arm920t/s3c24x0/speed.o
- arch/arm/cpu/arm920t/s3c24x0/timer.o
4.2 arch/arm/cpu/arm920t/Makefile
同理,arch/arm/cpu/arm920t/Makefile:
# # (C) Copyright 2000-2006 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. # # SPDX-License-Identifier: GPL-2.0+ # extra-y = start.o obj-y += cpu.o obj-$(CONFIG_USE_IRQ) += interrupts.o obj-$(CONFIG_EP93XX) += ep93xx/ obj-$(CONFIG_IMX) += imx/ obj-$(CONFIG_S3C24X0) += s3c24x0/ #以%/结尾最终会被替换成%/built-in.o # some files can only build in ARM mode ifdef CONFIG_SYS_THUMB_BUILD CFLAGS_cpu.o := -marm endif
最终目标arch/arm/cpu/arm920t/built-in.o以来的obj-y如下:
- arch/arm/cpu/arm920t/cpu.o
- arch/arm/cpu/arm920t/s3c24x0/built-in.o
4.3 arch/arm/cpu/Makefile
arch/arm/cpu/Makefile:
# # SPDX-License-Identifier: GPL-2.0+ # obj- += dummy.o
最终obj-y为空,所以生成的arch/arm/cpu/built-in.o文件实际是空的。
4.4 ./scripts/Makefile.build
我们在之前介绍过,u-boot目标依赖于$(u-boot-main)、$(u-boot-init)、u-boot.lds。
u-boot-init := $(head-y)
u-boot-main := $(libs-y)
而libs-y由若干个以built-in.o结尾的文件组成,lib-y大致如下:
libs-y = lib/built-in.o fs/built-in.o net/built-in.o disk/built-in.o drivers/built-in.o drivers/dma/built-in.o drivers/gpio/built-in.o ...
而libs-y中的各个built-in.o文件由./scripts/Makefile.build编译而成:
ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),) builtin-target := $(obj)/built-in.o endif $(builtin-target): $(obj-y) FORCE $(call if_changed,link_o_target)
比如make -f ./scripts/Makefile.build obj=arch/arm/cpu编译生成了arch/arm/cpu/build-in.o(builtin-target=arch/arm/cpu/built-in.o)。
而obj-y是由若干个.o目标文件组成,这里我将上面三条步骤中的obj-y输出,如下:
make -f ./scripts/Makefile.build obj=arch/arm/cpu make -f ./scripts/Makefile.build obj=arch/arm/cpu/arm920t make -f ./scripts/Makefile.build obj=arch/arm/cpu/arm920t/s3c24x0 --------------------------------------- arch/arm/cpu/arm920t/s3c24x0/cpu_info.o arch/arm/cpu/arm920t/s3c24x0/speed.o arch/arm/cpu/arm920t/s3c24x0/timer.o arch/arm/cpu/arm920t/cpu.o arch/arm/cpu/arm920t/s3c24x0/built-in.o
不存在
obj-y依赖的.o文件又是怎么生成的,生成规则也是在./scripts/Makefile.build有定义:
$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
$(call cmd,force_checksrc)
$(call if_changed_rule,cc_o_c)
这里src可以看做和obj相同:
- 当obj=arch/arm/cpu时,即定义了arch/arm/cpu/%.o文件的生成规则;
- 当obj=arch/arm/cpu/arm920t时,即定义了arch/arm/cpu/arm920t/%.o文件的生成规则;
- 当obj=arch/arm/cpu/arm920t/s3c24x0时,即定义了arch/arm/cpu/arm920t/s3c24x0/%.o文件的生成规则;
命令中的if_changed_rule函数定义在scripts/Kbuild.include文件中
# Usage: $(call if_changed_rule,foo) # Will check if $(cmd_foo) or any of the prerequisites changed, # and if so will execute $(rule_foo). if_changed_rule = $(if $(strip $(any-prereq) $(arg-check) ), @set -e; $(rule_$(1)))
在./scripts/Makefile.build有如下定义:
define rule_cc_o_c $(call echo-cmd,checksrc) $(cmd_checksrc) $(call echo-cmd,cc_o_c) $(cmd_cc_o_c); $(cmd_modversions) $(call echo-cmd,record_mcount) $(cmd_record_mcount) scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > $(dot-target).tmp; rm -f $(depfile); mv -f $(dot-target).tmp $(dot-target).cmd endef
cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
所以调用$(call if_changed_rule,cc_o_c)等价于执行cmd_cc_o_c。
这里我们重点介绍一下cmc_cc_o_c,因为这里在使用arm-linux-gcc编译时指定了c_flags参数(在顶层Makefile中定义):
ifeq ($(dot-config),1)
# Read in config
-include include/config/auto.conf
# 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)
include/config/auto.conf.cmd是由fixdep在编译时生成的依赖文件,主要定义了常量,包含CPU信息、系统配置参数:
这里我们重点关注UBOOTINCLUDE和PLATFORM_CPPFLAGS:
- PLATFORM_CPPFLAGS是干嘛用的呢,之前我们介绍过built-in.o在生成时,依赖若干个.o目标文件,这些.o文件在生成时也会引入一些头文件,这些头文件一般都位于built-in.o同路径下得include 文件下,通过-I参数指定搜索的头路径,从而将这些头文件包含进来,比如arcm/arm/Makefile文件中指定了(需要注意的是这个追加的参数只有在编译arcm/arm/built-in.o时有效):
PLATFORM_CPPFLAGS += $(patsubst %,-I$(srctree)/%include,$(machdirs))
-
UBOOTINCLUDE 引入了./include、./arch/arm/include目录下的头文件、以及./include/linux/kconfig.h文件。
4.5 arch/arm/Makefile
arch/arm/Makefile:
# # SPDX-License-Identifier: GPL-2.0+ # ifeq ($(CONFIG_SPL_BUILD)$(CONFIG_TEGRA),yy) CONFIG_CPU_V7= CONFIG_CPU_ARM720T=y endif # This selects which instruction set is used. arch-$(CONFIG_CPU_ARM720T) =-march=armv4 arch-$(CONFIG_CPU_ARM920T) =-march=armv4t arch-$(CONFIG_CPU_ARM926EJS) =-march=armv5te arch-$(CONFIG_CPU_ARM946ES) =-march=armv4 arch-$(CONFIG_CPU_SA1100) =-march=armv4 arch-$(CONFIG_CPU_PXA) = arch-$(CONFIG_CPU_ARM1136) =-march=armv5 arch-$(CONFIG_CPU_ARM1176) =-march=armv5t arch-$(CONFIG_CPU_V7) =$(call cc-option, -march=armv7-a, $(call cc-option, -march=armv7, -march=armv5)) arch-$(CONFIG_ARM64) =-march=armv8-a # Evaluate arch cc-option calls now arch-y := $(arch-y) # This selects how we optimise for the processor. tune-$(CONFIG_CPU_ARM720T) =-mtune=arm7tdmi tune-$(CONFIG_CPU_ARM920T) = tune-$(CONFIG_CPU_ARM926EJS) = tune-$(CONFIG_CPU_ARM946ES) = tune-$(CONFIG_CPU_SA1100) =-mtune=strongarm1100 tune-$(CONFIG_CPU_PXA) =-mcpu=xscale tune-$(CONFIG_CPU_ARM1136) = tune-$(CONFIG_CPU_ARM1176) = tune-$(CONFIG_CPU_V7) = tune-$(CONFIG_ARM64) = # Evaluate tune cc-option calls now tune-y := $(tune-y) PLATFORM_CPPFLAGS += $(arch-y) $(tune-y) # Machine directory name. This list is sorted alphanumerically # by CONFIG_* macro name. machine-$(CONFIG_ARCH_AT91) += at91 machine-$(CONFIG_ARCH_BCM283X) += bcm283x machine-$(CONFIG_ARCH_DAVINCI) += davinci machine-$(CONFIG_ARCH_EXYNOS) += exynos machine-$(CONFIG_ARCH_HIGHBANK) += highbank machine-$(CONFIG_ARCH_KEYSTONE) += keystone # TODO: rename CONFIG_KIRKWOOD -> CONFIG_ARCH_KIRKWOOD machine-$(CONFIG_KIRKWOOD) += kirkwood machine-$(CONFIG_ARCH_MVEBU) += mvebu # TODO: rename CONFIG_TEGRA -> CONFIG_ARCH_TEGRA # TODO: rename CONFIG_ORION5X -> CONFIG_ARCH_ORION5X machine-$(CONFIG_ORION5X) += orion5x machine-$(CONFIG_ARCH_S5PC1XX) += s5pc1xx machine-$(CONFIG_ARCH_SUNXI) += sunxi machine-$(CONFIG_ARCH_SNAPDRAGON) += snapdragon machine-$(CONFIG_ARCH_SOCFPGA) += socfpga machine-$(CONFIG_ARCH_ROCKCHIP) += rockchip machine-$(CONFIG_STM32) += stm32 machine-$(CONFIG_TEGRA) += tegra machine-$(CONFIG_ARCH_UNIPHIER) += uniphier machine-$(CONFIG_ARCH_ZYNQ) += zynq machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y)) #取出$(machine-y)所有值,并替换追加arch/arm/mach-xx/ 这里machdirs最终为空 PLATFORM_CPPFLAGS += $(patsubst %,-I$(srctree)/%include,$(machdirs)) #machdirs路径下的include文件 gcc编译时,头文件指定 libs-y += $(machdirs) head-y := arch/arm/cpu/$(CPU)/start.o ifeq ($(CONFIG_SPL_BUILD),y) ifneq ($(CONFIG_SPL_START_S_PATH),) head-y := $(CONFIG_SPL_START_S_PATH:"%"=%)/start.o endif endif libs-y += arch/arm/cpu/$(CPU)/ libs-y += arch/arm/cpu/ libs-y += arch/arm/lib/ ifeq ($(CONFIG_SPL_BUILD),y) ifneq (,$(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_MX35)$(filter $(SOC), mx25 mx27 mx5 mx6 mx7 mx31 mx35)) libs-y += arch/arm/imx-common/ endif else ifneq (,$(filter $(SOC), mx25 mx27 mx5 mx6 mx7 mx31 mx35 mxs vf610)) libs-y += arch/arm/imx-common/ endif endif ifneq (,$(filter $(SOC), kirkwood)) libs-y += arch/arm/mach-mvebu/ endif # deprecated -include $(machdirs)/config.mk
CONFIG_CPU_ARM920T=y在include/config/auto.conf文件中定义,而auto.conf文件是通过./srcipts/Makefile.build文件引入的:
[root@longmax2002 u-boot-2016.05]# grep "CONFIG_CPU_ARM920T" * -nR arch/arm/mach-at91/Makefile:19:obj-$(CONFIG_CPU_ARM920T) += arm920t/ arch/arm/Makefile:12:arch-$(CONFIG_CPU_ARM920T) =-march=armv4t arch/arm/Makefile:28:tune-$(CONFIG_CPU_ARM920T) = drivers/usb/host/ohci-hcd.c:53:#if defined(CONFIG_CPU_ARM920T) || include/config/auto.conf:65:CONFIG_CPU_ARM920T=y include/generated/autoconf.h:67:#define CONFIG_CPU_ARM920T 1 u-boot.cfg:144:#define CONFIG_CPU_ARM920T 1
-include include/config/auto.conf
如果想知道machdirs的值,修改该Makefile文件,在变量后面追加:
print_machdirs:: @echo "===================== WARNING ======================" @echo $(machdirs) @echo $(PLATFORM_CPPFLAGS)
在顶层执行make ARCH=arm CROSS_COMPILE=arm-linux- V=1 print_machdirs,可以获取变量的值:
其中machdirs为空,arm-linux-gcc编译参数PLATFORM_CPPFLAGS如下:
-D__ARM__ -marm -mno-thumb-interwork -mabi=aapcs-linux -mword-relocations -fno-pic -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -msoft-float -pipe -march=armv4t
五 、config.h(./include/config.h)
start.S文件的开始,#include <config.h>,config.h从哪里来的呢?在上一小节中我们已经介绍了Make在编译过程中,arm-linux-gcc指定了头文件路径:
- UBOOTINCLUDE:引入了./include、./arch/arm/include目录下的头文件、以及./include/linux/kconfig.h文件。
- PLATFORM_CPPFLAGS
至于这里写成#include<config.h>而不是#include<include/config.h>是因为arm-linux-gcc在编译的时候通过-I参数指定了搜索头文件的目录有./include:
include/config.h文件在make smdk2410_config时创建,主要包含以下信息:
/* Automatically generated - do not edit */ #define CONFIG_BOARDDIR board/samsung/smdk2410 #include <config_defaults.h> #include <config_uncmd_spl.h> #include <configs/smdk2410.h> #include <asm/config.h> #include <config_fallbacks.h>
#include <configs/smdk2410.h> 从这里可以看出在include/configs目录下,包含smdk2410.h文件,如果在board目录下新建一个开发板的目录,则在include/configs目录下要创建一个.h文件,里面存放的开发板配置信息。
#include <asm/config.h>来自arch/arm/include/asm目录:
/* * Copyright 2009 Freescale Semiconductor, Inc. * * SPDX-License-Identifier: GPL-2.0+ */ #ifndef _ASM_CONFIG_H_ #define _ASM_CONFIG_H_ #define CONFIG_LMB #define CONFIG_SYS_BOOT_RAMDISK_HIGH #ifdef CONFIG_ARM64 #define CONFIG_PHYS_64BIT #define CONFIG_STATIC_RELA #endif #if defined(CONFIG_LS102XA) || defined(CONFIG_CPU_PXA27X) || defined(CONFIG_CPU_MONAHANS) || defined(CONFIG_CPU_PXA25X) || defined(CONFIG_FSL_LAYERSCAPE) #include <asm/arch/config.h> #endif #endif
六、smdk2410.h(./include/configs/smdk2410.h)
这个文件很重要,这里主要用来配置编译u-boot时是否启用某些功能,比如网卡,LCD等,如果定义了,那么将会编译相关模块的代码。
/* * (C) Copyright 2002 * Sysgo Real-Time Solutions, GmbH <www.elinos.com> * Marius Groeger <mgroeger@sysgo.de> * Gary Jennejohn <garyj@denx.de> * David Mueller <d.mueller@elsoft.ch> * * Configuation settings for the SAMSUNG SMDK2410 board. * * SPDX-License-Identifier: GPL-2.0+ */ #ifndef __CONFIG_H #define __CONFIG_H /* * High Level Configuration Options * (easy to change) */ #define CONFIG_S3C24X0 /* This is a SAMSUNG S3C24x0-type SoC */ #define CONFIG_S3C2410 /* specifically a SAMSUNG S3C2410 SoC */ #define CONFIG_SMDK2410 /* on a SAMSUNG SMDK2410 Board */ #define CONFIG_SYS_TEXT_BASE 0x0 #define CONFIG_SYS_ARM_CACHE_WRITETHROUGH /* input clock of PLL (the SMDK2410 has 12MHz input clock) */ #define CONFIG_SYS_CLK_FREQ 12000000 #define CONFIG_CMDLINE_TAG /* enable passing of ATAGs */ #define CONFIG_SETUP_MEMORY_TAGS #define CONFIG_INITRD_TAG /* * Hardware drivers */ #define CONFIG_CS8900 /* we have a CS8900 on-board */ #define CONFIG_CS8900_BASE 0x19000300 #define CONFIG_CS8900_BUS16 /* the Linux driver does accesses as shorts */ /* * select serial console configuration */ #define CONFIG_S3C24X0_SERIAL #define CONFIG_SERIAL1 1 /* we use SERIAL 1 on SMDK2410 */ /************************************************************ * USB support (currently only works with D-cache off) ************************************************************/ #define CONFIG_USB_OHCI #define CONFIG_USB_OHCI_S3C24XX #define CONFIG_USB_KEYBOARD #define CONFIG_USB_STORAGE #define CONFIG_DOS_PARTITION /************************************************************ * RTC ************************************************************/ #define CONFIG_RTC_S3C24X0 #define CONFIG_BAUDRATE 115200 /* * BOOTP options */ #define CONFIG_BOOTP_BOOTFILESIZE #define CONFIG_BOOTP_BOOTPATH #define CONFIG_BOOTP_GATEWAY #define CONFIG_BOOTP_HOSTNAME /* * Command line configuration. */ #define CONFIG_CMD_BSP #define CONFIG_CMD_DATE #define CONFIG_CMD_NAND #define CONFIG_CMD_REGINFO #define CONFIG_CMDLINE_EDITING /* autoboot */ #define CONFIG_BOOTDELAY 5 #define CONFIG_BOOT_RETRY_TIME -1 #define CONFIG_RESET_TO_RETRY #define CONFIG_ZERO_BOOTDELAY_CHECK #define CONFIG_NETMASK 255.255.255.0 #define CONFIG_IPADDR 10.0.0.110 #define CONFIG_SERVERIP 10.0.0.1 #if defined(CONFIG_CMD_KGDB) #define CONFIG_KGDB_BAUDRATE 115200 /* speed to run kgdb serial port */ #endif /* * Miscellaneous configurable options */ #define CONFIG_SYS_LONGHELP /* undef to save memory */ #define CONFIG_SYS_CBSIZE 256 /* Print Buffer Size */ #define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + sizeof(CONFIG_SYS_PROMPT)+16) #define CONFIG_SYS_MAXARGS 16 #define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE #define CONFIG_DISPLAY_CPUINFO /* Display cpu info */ #define CONFIG_SYS_MEMTEST_START 0x30000000 /* memtest works on */ #define CONFIG_SYS_MEMTEST_END 0x33F00000 /* 63 MB in DRAM */ #define CONFIG_SYS_LOAD_ADDR 0x30800000 /* support additional compression methods */ #define CONFIG_BZIP2 #define CONFIG_LZO #define CONFIG_LZMA /*----------------------------------------------------------------------- * Physical Memory Map */ #define CONFIG_NR_DRAM_BANKS 1 /* we have 1 bank of DRAM */ #define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */ #define PHYS_SDRAM_1_SIZE 0x04000000 /* 64 MB */ #define PHYS_FLASH_1 0x00000000 /* Flash Bank #0 */ #define CONFIG_SYS_FLASH_BASE PHYS_FLASH_1 /*----------------------------------------------------------------------- * FLASH and environment organization */ #define CONFIG_SYS_FLASH_CFI #define CONFIG_FLASH_CFI_DRIVER #define CONFIG_FLASH_CFI_LEGACY #define CONFIG_SYS_FLASH_LEGACY_512Kx16 #define CONFIG_FLASH_SHOW_PROGRESS 45 #define CONFIG_SYS_MAX_FLASH_BANKS 1 #define CONFIG_SYS_FLASH_BANKS_LIST { CONFIG_SYS_FLASH_BASE } #define CONFIG_SYS_MAX_FLASH_SECT (19) #define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x070000) #define CONFIG_ENV_IS_IN_FLASH #define CONFIG_ENV_SIZE 0x10000 /* allow to overwrite serial and ethaddr */ #define CONFIG_ENV_OVERWRITE /* * Size of malloc() pool * BZIP2 / LZO / LZMA need a lot of RAM */ #define CONFIG_SYS_MALLOC_LEN (4 * 1024 * 1024) #define CONFIG_SYS_MONITOR_LEN (448 * 1024) #define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_FLASH_BASE /* * NAND configuration */ #ifdef CONFIG_CMD_NAND #define CONFIG_NAND_S3C2410 #define CONFIG_SYS_S3C2410_NAND_HWECC #define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_NAND_BASE 0x4E000000 #endif /* * File system */ #define CONFIG_CMD_UBI #define CONFIG_CMD_UBIFS #define CONFIG_CMD_MTDPARTS #define CONFIG_MTD_DEVICE #define CONFIG_MTD_PARTITIONS #define CONFIG_YAFFS2 #define CONFIG_RBTREE /* additions for new relocation code, must be added to all boards */ #define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x1000 - GENERATED_GBL_DATA_SIZE) #define CONFIG_BOARD_EARLY_INIT_F #endif /* __CONFIG_H */
七、分析crt0.S(arch/arm/lib/crt0.S)
这块代码也比较多,这里就一点一点的展示,并进行讲解。
7.1 引入头文件
这里首先是引入头文件:
#include <config.h> #include <asm-offsets.h> #include <linux/linkage.h> #ifdef CONFIG_CPU_V7M #include <asm/armv7m.h> #endif
然后接着,是作者对这个文件的一大堆介绍:
/* * This file handles the target-independent stages of the U-Boot * start-up where a C runtime environment is needed. Its entry point * is _main and is branched into from the target's start.S file. * * _main execution sequence is: * * 1. Set up initial environment for calling board_init_f(). * This environment only provides a stack and a place to store * the GD ('global data') structure, both located in some readily * available RAM (SRAM, locked cache...). In this context, VARIABLE * global data, initialized or not (BSS), are UNAVAILABLE; only * CONSTANT initialized data are available. GD should be zeroed * before board_init_f() is called. * * 2. Call board_init_f(). This function prepares the hardware for * execution from system RAM (DRAM, DDR...) As system RAM may not * be available yet, , board_init_f() must use the current GD to * store any data which must be passed on to later stages. These * data include the relocation destination, the future stack, and * the future GD location. * * 3. Set up intermediate environment where the stack and GD are the * ones allocated by board_init_f() in system RAM, but BSS and * initialized non-const data are still not available. * * 4a.For U-Boot proper (not SPL), call relocate_code(). This function * relocates U-Boot from its current location into the relocation * destination computed by board_init_f(). * * 4b.For SPL, board_init_f() just returns (to crt0). There is no * code relocation in SPL. * * 5. Set up final environment for calling board_init_r(). This * environment has BSS (initialized to 0), initialized non-const * data (initialized to their intended value), and stack in system * RAM (for SPL moving the stack and GD into RAM is optional - see * CONFIG_SPL_STACK_R). GD has retained values set by board_init_f(). * * 6. For U-Boot proper (not SPL), some CPUs have some work left to do * at this point regarding memory, so call c_runtime_cpu_setup. * * 7. Branch to board_init_r(). * * For more information see 'Board Initialisation Flow in README. */
这里作者罗列了七条内容,这里我大致翻译一下:
- 函数首先设置栈,然后在栈底预留一定的内存空间给gd_t结构体并清零,用来存放全局参数, 然后调用board_init_f();这里作者提到在当前上下文,已经初始化的全局变量(位于.data段)或者是未初始化的全局变量(位于.bss段)是无法访问到的,这主要是因为还没将代码从NAND或者NOR拷贝到SDRAM。
- 调用board_init_f(),这个函数调用一系列函数来初始化硬件以使程序能够在SDRAM中执行,且这个函数需要使用设置好的gd_t结构体来存放初始化中的各个参数以备后用,比如:重定位地址,重定位后的栈地址、重定位后的gd_t的地址;
- 由于bss段还没初始化,board_init_f()设置gd_t时不能使用全局变量、静态变量等, 所以设置的环境参数不是最终的;
- smdk2410.h没有定义SPL,所以会在_main()函数中根据board_init_f()设置的重定位参数进行代码重定位;
- 重定位后,bss和non-const data都被初始化了,在进入board_init_r()函数之前应先设置最终的环境参数;
- 调用board_init_r()函数;
7.2 初始化栈空间,栈底为gd_t全局变量预留内存空间
/* * entry point of crt0 sequence */ ENTRY(_main) /* * Set up initial C runtime environment and call board_init_f(0). */ #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK) ldr sp, =(CONFIG_SPL_STACK) #else ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) //1. 设置栈 0x30000f50 #endif #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */ mov r3, sp bic r3, r3, #7 mov sp, r3 #else bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ #endif mov r0, sp bl board_init_f_alloc_reserve //2. 为gd_t结构体保留空间 mov sp, r0 /* set up gd here, outside any C code */ mov r9, r0 bl board_init_f_init_reserve //3. 初始化gd_t(清零) //gd_t的地址存在r9寄存器中,结构体中存放的是全局参数 mov r0, #0 bl board_init_f //4. 进入board_init_f进行各种初始化,分配SDRAM内存空间,填充进gd_t结构体中
smdk2410.h文件中没有定义SPL,所以sp设置为CONFIG_SYS_INIT_SP_ADDR:
#define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */ #define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x1000 - GENERATED_GBL_DATA_SIZE)
查找board_init_f_alloc_reserve函数,位于common/init/board_ibnit.c文件中:
这里top传入的为sp的值,并在栈顶底预留了global_data结构体大小的数据,然后设置栈底为16字节对齐,同时将gd_t结构体的起始地址保存到r9寄存器。
DECLARE_GLOBAL_DATA_PTR定义了一个gd_t全局数据结构的指针,这个指针存放在指定的寄存器r9中。这个声明也避免了编译器把r9分配给其他的变量,任何想要访问访问全局数据区的代码,只要代码开头加DECLARE_GLOBAL_DATA_PTR一行代码,然后就可以使用gd指针来访问全局数据区了。
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r9")
u-boot启动内核时要给内核传递参数,这时就要使用gd_t,结构体中的信息来设置标记列表。
查找board_init_f_init_reserve函数,该函数用于初始化gd_t内存空间为0。
void board_init_f_init_reserve(ulong base) { struct global_data *gd_ptr; #ifndef _USE_MEMCPY int *ptr; #endif /* * clear GD entirely and set it up. * Use gd_ptr, as gd may not be properly set yet. */ gd_ptr = (struct global_data *)base; /* zero the area */ #ifdef _USE_MEMCPY memset(gd_ptr, ' ', sizeof(*gd)); #*gd大小0xa8 #else for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); ) *ptr++ = 0; #endif /* set GD unless architecture did it already */ #if !defined(CONFIG_ARM) arch_setup_gd(gd_ptr); #endif /* next alloc will be higher by one GD plus 16-byte alignment */ base += roundup(sizeof(struct global_data), 16); /* * record early malloc arena start. * Use gd as it is now properly set for all architectures. */ #if defined(CONFIG_SYS_MALLOC_F) /* go down one 'early malloc arena' */ gd->malloc_base = base; /* next alloc will be higher by one 'early malloc arena' size */ base += CONFIG_SYS_MALLOC_F_LEN; #endif }
指定位置base作结构体gd的存放地。
7.3 执行board_init_f()
该函数位于common/board.c文件:
void board_init_f(ulong boot_flags) { #ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA /* * For some archtectures, global data is initialized and used before * calling this function. The data should be preserved. For others, * CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack * here to host global data until relocation. */ gd_t data; gd = &data; /* * Clear global data before it is accessed at debug print * in initcall_run_list. Otherwise the debug print probably * get the wrong vaule of gd->have_console. */ zero_global_data(); #endif gd->flags = boot_flags; #0x00 gd->have_console = 0; #0x00 if (initcall_run_list(init_sequence_f)) hang(); #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && !defined(CONFIG_EFI_APP) /* NOTREACHED - jump_to_copy() does not return */ hang(); #endif }
填充了flags和have_console字段后就执行一个初始化列表循环,这个循环里面有很多的函数,只要其中一个出错,u-boot启动就会停止。
initcall_run_list位于lib/initcall.c文件中:
int initcall_run_list(const init_fnc_t init_sequence[]) { const init_fnc_t *init_fnc_ptr; for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { unsigned long reloc_ofs = 0; int ret; if (gd->flags & GD_FLG_RELOC) reloc_ofs = gd->reloc_off; #ifdef CONFIG_EFI_APP reloc_ofs = (unsigned long)image_base; #endif debug("initcall: %p", (char *)*init_fnc_ptr - reloc_ofs); if (gd->flags & GD_FLG_RELOC) debug(" (relocated to %p) ", (char *)*init_fnc_ptr); else debug(" "); ret = (*init_fnc_ptr)(); if (ret) { printf("initcall sequence %p failed at call %p (err=%d) ", init_sequence, (char *)*init_fnc_ptr - reloc_ofs, ret); return -1; } } return 0; }
board_init_f(),这个函数调用一系列函数来初始化硬件以使程序能够在SDRAM中执行,且这个函数需要使用设置好的gd结构体来存放初始化中的各个参数以备后用,比如:重定位地址,重定位后的栈地址、重定位后的gd_t的地址。
这里我们记录gd_t这个数据结构:
typedef struct global_data { bd_t *bd; unsigned long flags; unsigned int baudrate; unsigned long cpu_clk; /* CPU clock in Hz! */ unsigned long bus_clk; /* We cannot bracket this with CONFIG_PCI due to mpc5xxx */ unsigned long pci_clk; unsigned long mem_clk; #if defined(CONFIG_LCD) || defined(CONFIG_VIDEO) unsigned long fb_base; /* Base address of framebuffer mem */ #endif #if defined(CONFIG_POST) || defined(CONFIG_LOGBUFFER) unsigned long post_log_word; /* Record POST activities */ unsigned long post_log_res; /* success of POST test */ unsigned long post_init_f_time; /* When post_init_f started */ #endif #ifdef CONFIG_BOARD_TYPES unsigned long board_type; #endif unsigned long have_console; /* serial_init() was called */ #ifdef CONFIG_PRE_CONSOLE_BUFFER unsigned long precon_buf_idx; /* Pre-Console buffer index */ #endif unsigned long env_addr; /* Address of Environment struct */ unsigned long env_valid; /* Checksum of Environment valid? */ unsigned long ram_top; /* Top address of RAM used by U-Boot */ unsigned long relocaddr; /* Start address of U-Boot in RAM */ phys_size_t ram_size; /* RAM size */ #ifdef CONFIG_SYS_MEM_RESERVE_SECURE #define MEM_RESERVE_SECURE_SECURED 0x1 #define MEM_RESERVE_SECURE_MAINTAINED 0x2 #define MEM_RESERVE_SECURE_ADDR_MASK (~0x3) /* * Secure memory addr * This variable needs maintenance if the RAM base is not zero, * or if RAM splits into non-consecutive banks. It also has a * flag indicating the secure memory is marked as secure by MMU. * Flags used: 0x1 secured * 0x2 maintained */ phys_addr_t secure_ram; #endif unsigned long mon_len; /* monitor len */ unsigned long irq_sp; /* irq stack pointer */ unsigned long start_addr_sp; /* start_addr_stackpointer */ unsigned long reloc_off; struct global_data *new_gd; /* relocated global data */ #ifdef CONFIG_DM struct udevice *dm_root; /* Root instance for Driver Model */ struct udevice *dm_root_f; /* Pre-relocation root instance */ struct list_head uclass_root; /* Head of core tree */ #endif #ifdef CONFIG_TIMER struct udevice *timer; /* Timer instance for Driver Model */ #endif const void *fdt_blob; /* Our device tree, NULL if none */ void *new_fdt; /* Relocated FDT */ unsigned long fdt_size; /* Space reserved for relocated FDT */ struct jt_funcs *jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ #ifdef CONFIG_TRACE void *trace_buff; /* The trace buffer */ #endif #if defined(CONFIG_SYS_I2C) int cur_i2c_bus; /* current used i2c bus */ #endif #ifdef CONFIG_SYS_I2C_MXC void *srdata[10]; #endif unsigned long timebase_h; unsigned long timebase_l; #ifdef CONFIG_SYS_MALLOC_F_LEN unsigned long malloc_base; /* base address of early malloc() */ unsigned long malloc_limit; /* limit address */ unsigned long malloc_ptr; /* current address */ #endif #ifdef CONFIG_PCI struct pci_controller *hose; /* PCI hose for early use */ phys_addr_t pci_ram_top; /* top of region accessible to PCI */ #endif #ifdef CONFIG_PCI_BOOTDELAY int pcidelay_done; #endif struct udevice *cur_serial_dev; /* current serial device */ struct arch_global_data arch; /* architecture-specific data */ #ifdef CONFIG_CONSOLE_RECORD struct membuff console_out; /* console output */ struct membuff console_in; /* console input */ #endif #ifdef CONFIG_DM_VIDEO ulong video_top; /* Top of video frame buffer area */ ulong video_bottom; /* Bottom of video frame buffer area */ #endif } gd_t; #endif /* * Global Data Flags - the top 16 bits are reserved for arch-specific flags */ #define GD_FLG_RELOC 0x00001 /* Code was relocated to RAM */ #define GD_FLG_DEVINIT 0x00002 /* Devices have been initialized */ #define GD_FLG_SILENT 0x00004 /* Silent mode */ #define GD_FLG_POSTFAIL 0x00008 /* Critical POST test failed */ #define GD_FLG_POSTSTOP 0x00010 /* POST seqeunce aborted */ #define GD_FLG_LOGINIT 0x00020 /* Log Buffer has been initialized */ #define GD_FLG_DISABLE_CONSOLE 0x00040 /* Disable console (in & out) */ #define GD_FLG_ENV_READY 0x00080 /* Env. imported into hash table */ #define GD_FLG_SERIAL_READY 0x00100 /* Pre-reloc serial console ready */ #define GD_FLG_FULL_MALLOC_INIT 0x00200 /* Full malloc() is ready */ #define GD_FLG_SPL_INIT 0x00400 /* spl_init() has been called */ #define GD_FLG_SKIP_RELOC 0x00800 /* Don't relocate */ #define GD_FLG_RECORD 0x01000 /* Record console */ #endif /* __ASM_GENERIC_GBL_DATA_H */
7.4 init_sequence_f
init_sequence_f是一个函数指针数组,里面存放着各种初始化的函数指针,其中大部分函数只有定义了指定了宏才会生效,后面会针对其中比较重要的函数进行一一介绍。
static init_fnc_t init_sequence_f[] = { #ifdef CONFIG_SANDBOX setup_ram_buf, #endif setup_mon_len, #ifdef CONFIG_OF_CONTROL fdtdec_setup, #endif #ifdef CONFIG_TRACE trace_early_init, #endif initf_malloc, initf_console_record, #if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx) /* TODO: can this go into arch_cpu_init()? */ probecpu, #endif #if defined(CONFIG_X86) && defined(CONFIG_HAVE_FSP) x86_fsp_init, #endif arch_cpu_init, /* basic arch cpu dependent setup */ initf_dm, arch_cpu_init_dm, mark_bootstage, /* need timer, go after init dm */ #if defined(CONFIG_BOARD_EARLY_INIT_F) board_early_init_f, #endif /* TODO: can any of this go into arch_cpu_init()? */ #if defined(CONFIG_PPC) && !defined(CONFIG_8xx_CPUCLK_DEFAULT) get_clocks, /* get CPU and bus clocks (etc.) */ #if defined(CONFIG_TQM8xxL) && !defined(CONFIG_TQM866M) && !defined(CONFIG_TQM885D) adjust_sdram_tbs_8xx, #endif /* TODO: can we rename this to timer_init()? */ init_timebase, #endif #if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || defined(CONFIG_BLACKFIN) || defined(CONFIG_NDS32) || defined(CONFIG_SPARC) timer_init, /* initialize timer */ #endif #ifdef CONFIG_SYS_ALLOC_DPRAM #if !defined(CONFIG_CPM2) dpram_init, #endif #endif #if defined(CONFIG_BOARD_POSTCLK_INIT) board_postclk_init, #endif #if defined(CONFIG_SYS_FSL_CLK) || defined(CONFIG_M68K) get_clocks, #endif env_init, /* initialize environment */ #if defined(CONFIG_8xx_CPUCLK_DEFAULT) /* get CPU and bus clocks according to the environment variable */ get_clocks_866, /* adjust sdram refresh rate according to the new clock */ sdram_adjust_866, init_timebase, #endif init_baud_rate, /* initialze baudrate settings */ serial_init, /* serial communications setup */ console_init_f, /* stage 1 init of console */ #ifdef CONFIG_SANDBOX sandbox_early_getopt_check, #endif #ifdef CONFIG_OF_CONTROL fdtdec_prepare_fdt, #endif display_options, /* say that we are here */ display_text_info, /* show debugging info if required */ #if defined(CONFIG_MPC8260) prt_8260_rsr, prt_8260_clks, #endif /* CONFIG_MPC8260 */ #if defined(CONFIG_MPC83xx) prt_83xx_rsr, #endif #if defined(CONFIG_PPC) || defined(CONFIG_M68K) checkcpu, #endif print_cpuinfo, /* display cpu info (and speed) */ #if defined(CONFIG_MPC5xxx) prt_mpc5xxx_clks, #endif /* CONFIG_MPC5xxx */ #if defined(CONFIG_DISPLAY_BOARDINFO) show_board_info, #endif INIT_FUNC_WATCHDOG_INIT #if defined(CONFIG_MISC_INIT_F) misc_init_f, #endif INIT_FUNC_WATCHDOG_RESET #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C) init_func_i2c, #endif #if defined(CONFIG_HARD_SPI) init_func_spi, #endif announce_dram_init, /* TODO: unify all these dram functions? */ #if defined(CONFIG_ARM) || defined(CONFIG_X86) || defined(CONFIG_NDS32) || defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32) dram_init, /* configure available RAM banks */ #endif #if defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_M68K) init_func_ram, #endif #ifdef CONFIG_POST post_init_f, #endif INIT_FUNC_WATCHDOG_RESET #if defined(CONFIG_SYS_DRAM_TEST) testdram, #endif /* CONFIG_SYS_DRAM_TEST */ INIT_FUNC_WATCHDOG_RESET #ifdef CONFIG_POST init_post, #endif INIT_FUNC_WATCHDOG_RESET /* * Now that we have DRAM mapped and working, we can * relocate the code and continue running from DRAM. * * Reserve memory at end of RAM for (top down in that order): * - area that won't get touched by U-Boot and Linux (optional) * - kernel log buffer * - protected RAM * - LCD framebuffer * - monitor code * - board info struct */ setup_dest_addr, #if defined(CONFIG_BLACKFIN) /* Blackfin u-boot monitor should be on top of the ram */ reserve_uboot, #endif #if defined(CONFIG_SPARC) reserve_prom, #endif #if defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_ADDR) reserve_logbuffer, #endif #ifdef CONFIG_PRAM reserve_pram, #endif reserve_round_4k, #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && defined(CONFIG_ARM) reserve_mmu, #endif #ifdef CONFIG_DM_VIDEO reserve_video, #else # ifdef CONFIG_LCD reserve_lcd, # endif /* TODO: Why the dependency on CONFIG_8xx? */ # if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && !defined(CONFIG_ARM) && !defined(CONFIG_X86) && !defined(CONFIG_BLACKFIN) && !defined(CONFIG_M68K) reserve_legacy_video, # endif #endif /* CONFIG_DM_VIDEO */ reserve_trace, #if !defined(CONFIG_BLACKFIN) reserve_uboot, #endif #ifndef CONFIG_SPL_BUILD reserve_malloc, reserve_board, #endif setup_machine, reserve_global_data, reserve_fdt, reserve_arch, reserve_stacks, setup_dram_config, show_dram_config, #if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_MIPS) setup_board_part1, #endif #if defined(CONFIG_PPC) || defined(CONFIG_M68K) INIT_FUNC_WATCHDOG_RESET setup_board_part2, #endif display_new_sp, #ifdef CONFIG_SYS_EXTBDINFO setup_board_extra, #endif INIT_FUNC_WATCHDOG_RESET reloc_fdt, setup_reloc, #if defined(CONFIG_X86) || defined(CONFIG_ARC) copy_uboot_to_ram, clear_bss, do_elf_reloc_fixups, #endif #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) jump_to_copy, #endif NULL, };
函数指针类型:
typedef int (*init_fnc_t)(void);
由于这个数组太长了,下面列出定义函数(如何知道哪些函数被定义了呢,查看反汇编代码u-boot.map和u-boot.dis)
然后我们打开u-boot.dis文件,找到init_sequence_f定义:
0007217c <init_sequence_f>: 7217c: 0000e70c .word 0x0000e70c 72180: 00014964 .word 0x00014964 72184: 0000e894 .word 0x0000e894 72188: 0000e72c .word 0x0000e72c 7218c: 0000e89c .word 0x0000e89c 72190: 0000e8ac .word 0x0000e8ac 72194: 0000ea78 .word 0x0000ea78 72198: 00000fd8 .word 0x00000fd8 7219c: 00000634 .word 0x00000634 721a0: 00010610 .word 0x00010610 721a4: 0000ea4c .word 0x0000ea4c 721a8: 0002ea38 .word 0x0002ea38 721ac: 000144d0 .word 0x000144d0 721b0: 0005a428 .word 0x0005a428 721b4: 0000e704 .word 0x0000e704 721b8: 000003b0 .word 0x000003b0 721bc: 0000ea34 .word 0x0000ea34 721c0: 000010e4 .word 0x000010e4 721c4: 0000e9b4 .word 0x0000e9b4 721c8: 0000e750 .word 0x0000e750 721cc: 0000e768 .word 0x0000e768 721d0: 0000e7a0 .word 0x0000e7a0 721d4: 0000e7a8 .word 0x0000e7a8 721d8: 0000e7d8 .word 0x0000e7d8 721dc: 0000e97c .word 0x0000e97c 721e0: 0000e7ec .word 0x0000e7ec 721e4: 0000e7f4 .word 0x0000e7f4 721e8: 0000e810 .word 0x0000e810 721ec: 0000e8a4 .word 0x0000e8a4 721f0: 0000e870 .word 0x0000e870 721f4: 0000ea24 .word 0x0000ea24 721f8: 0000e944 .word 0x0000e944 721fc: 0000e88c .word 0x0000e88c 72200: 0000e908 .word 0x0000e908 72204: 0000e8dc .word 0x0000e8dc 72208: 00000000 .word 0x00000000
然后我们找到每个函数指针地址,就可以看到对应的函数,最终得到如下函数:
/*虽然未定义的都删除了,但是还是有这么多*/ static const init_fnc_t init_sequence_f[] = { setup_mon_len, initf_malloc, initf_console_record, arch_cpu_init, /* basic arch cpu dependent setup */ initf_dm, arch_cpu_init_dm,
mark_bootstage,
board_early_init_f, timer_init, /* initialize timer */ env_init, /* initialize environment */ init_baud_rate, /* initialze baudrate settings */ serial_init, /* serial communications setup */ console_init_f, display_options, display_text_info, print_cpuinfo, announce_dram_init, dram_init, /* configure available RAM banks *//* * Now that we have DRAM mapped and working, we can * relocate the code and continue running from DRAM. * * Reserve memory at end of RAM for (top down in that order): * - area that won't get touched by U-Boot and Linux (optional) * - kernel log buffer * - protected RAM * - LCD framebuffer * - monitor code * - board info struct */ setup_dest_addr, reserve_round_4k, reserve_mmu, reserve_trace, reserve_uboot, reserve_malloc, reserve_board, setup_machine, reserve_global_data, reserve_fdt, reserve_arch, reserve_stacks, setup_dram_config, show_dram_config, display_new_sp, reloc_fdt, setup_reloc, NULL, };
参考文章:
[1]U-BOOT-2016.07移植 (第一篇) 初步分析
[3]从零开始之uboot、移植uboot2017.01(五、board_init_f分析)