文章来源:卫东山老师视频: 006_u-boot_内核_根文件系统(新1期_2期间的衔接)(1)\视频\第001课_U-boot分析与使用,若有侵权,请联系作者,我会删除内容。
为了学习u-boot、内核的移植,所以回过头看看已经学过的内容,顺便做个笔记
一. 上电:
PC LINUX系统
BIOS BootLoader
引导操作系统windows linux内核
识别C盘,D盘 挂接跟文件系统
运行应用程序: QQ 运行应用程序
二. 对u-boot-1.1.6进行解压,打补丁,编译:
1. 解压
tar xjf u-boot-1.1.6.tar.bz2
cd u-boot-1.1.6/
2. 打补丁
打开补丁文件看看:
diff -urN u-boot-1.1.6/board/100ask24x0/100ask24x0.c u-boot-1.1.6_jz2440_20171103/board/100ask24x0/100ask24x0.c
--- u-boot-1.1.6/board/100ask24x0/100ask24x0.c 1970-01-01 08:00:00.000000000 +0800
+++ u-boot-1.1.6_jz2440_20171103/board/100ask24x0/100ask24x0.c 2017-11-03 14:19:59.249590991 +0800
其中---代表是原来的代码,+++代表是修改后的代码,随便找一行看看:
diff -urN u-boot-1.1.6/common/cmd_load.c u-boot-1.1.6_jz2440_20171103/common/cmd_load.c
--- u-boot-1.1.6/common/cmd_load.c 2006-11-02 22:15:01.000000000 +0800
+++ u-boot-1.1.6_jz2440_20171103/common/cmd_load.c 2017-11-03 14:19:59.253590991 +0800
@@ -34,6 +34,8 @@ //这句话的意思是修改前从34行开始,共6行,修改后是从34行开始,共8行
--- u-boot-1.1.6/common/cmd_load.c 2006-11-02 22:15:01.000000000 +0800
+++ u-boot-1.1.6_jz2440_20171103/common/cmd_load.c 2017-11-03 14:19:59.253590991 +0800
@@ -34,6 +34,8 @@
DECLARE_GLOBAL_DATA_PTR;
#if (CONFIG_COMMANDS & CFG_CMD_LOADB)
+/* support xmodem, www.100ask.net */
+static ulong load_serial_xmodem (ulong offset);
static ulong load_serial_ymodem (ulong offset);
#endif
看看这句话: diff -urN u-boot-1.1.6/board/100ask24x0/100ask24x0.c u-boot-1.1.6_jz2440_20171103/board/100ask24x0/100ask24x0.c
意思就是补丁达到u-boot-1.1.6/board....目录下面,现在的位置是:cd u-boot-1.1.6/下面,所以打补丁应该加上一个参数p1:
patch -p1 < ../u-boot-1.1.6_jz2440.patch
3. 配置
u-boot的目的是为了支持各种各样的单板和芯片,所以要是想让他支持某一种单板,就需要先配置
对于修改后的u-boot直接: make 100ask24x0_config,后面会讲如何自己配置。
4. 编译
make
三. 把编译好的u-boot.bin烧写到裸板上面:
cmd
cd e:
cd E:\BaiduNetdiskDownload\002_JZ2440资料光盘_20180516(免费)(1)\资料光盘\B盘\bin\uboot
oflash u-boot.bin
后面跟着操作就行。
烧写完成后上电重启:
##### 100ask Bootloader for OpenJTAG #####
[n] Download u-boot to Nand Flash
[c] Re-scan Nor Flash
[u] Copy bootloader from nand to nor
[v] Copy bootloader from nor to nand
[k] Download Linux kernel uImage
[j] Download root_jffs2 image
[y] Download root_yaffs image
[d] Download to SDRAM & Run
[z] Download zImage into RAM
[g] Boot linux from RAM
[f] Format the Nand Flash
[s] Set the boot parameters
[b] Boot the system
[r] Reboot u-boot
[q] Quit from menu
Enter your selection:
这个信息是韦东山老师自己写的,实际上正常的u-boot是没有这些命令的,要是想查看有哪些命令,可以用help看到很多命令:
OpenJTAG> help
? - alias for 'help'
autoscr - run script from memory
base - print or set address offset
bdinfo - print Board Info structure
boot - boot default, i.e., run 'bootcmd'
bootd - boot default, i.e., run 'bootcmd'
bootelf - Boot from an ELF image in memory
bootm - boot application image from memory
bootp - boot image via network using BootP/TFTP protocol
bootvx - Boot vxWorks from an ELF image
chpart - change active partition
cmp - memory compare
coninfo - print console devices and information
cp - memory copy
crc32 - checksum calculation
date - get/set/reset date & time
dcache - enable or disable data cache
echo - echo args to console
erase - erase FLASH memory
flinfo - print FLASH memory information
fsinfo - print information about filesystems
fsload - load binary file from a filesystem image
go - start application at address 'addr'
help - print online help
icache - enable or disable instruction cache
iminfo - print header information for application image
imls - list all images found in flash
itest - return true/false on integer compare
loadb - load binary file over serial line (kermit mode)
loads - load S-Record file over serial line
loadx - load binary file over serial line (xmodem mode)
loady - load binary file over serial line (ymodem mode)
loop - infinite loop on address range
ls - list files in a directory (default /)
md - memory display
menu - display a menu, to select the items to do something
mm - memory modify (auto-incrementing)
mtdparts- define flash/nand partitions
mtest - simple RAM test
mw - memory write (fill)
nand - NAND sub-system
nboot - boot from NAND device
nfs - boot image via network using NFS protocol
nm - memory modify (constant address)
ping - send ICMP ECHO_REQUEST to network host
printenv- print environment variables
protect - enable or disable FLASH write protection
rarpboot- boot image via network using RARP/TFTP protocol
reset - Perform RESET of the CPU
run - run commands in an environment variable
saveenv - save environment variables to persistent storage
setenv - set environment variables
sleep - delay execution for some time
suspend - suspend the board
tftpboot- boot image via network using TFTP protocol
usbslave - get file from host(PC)
version - print monitor version
OpenJTAG>
想看这些命令的具体用法,使用? 命令
OpenJTAG> ? md
md [.b, .w, .l] address [# of objects]
- memory display
OpenJTAG>
四. 结合Makefile分析u-boot的功能,结构,链接方式
对于u-boot来说,分析u-boot的最简单方式就是分析Makefile
1. 在Makefile中搜索100ask24x0_config,分析配置过程
100ask24x0_config : unconfig //在make这句话时候就相当于执行了下面这句话
@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0
在Makefile中搜索MKCONFIG,得到下面的信息:
Line 92: MKCONFIG := (SRCTREE)/mkconfig//源文件树下面应该有一个mkconfig,也就是说u−boot下面有一个mkconfig@(SRCTREE)/mkconfig //源文件树下面应该有一个mkconfig,也就是说u-boot下面有一个mkconfig
@(SRCTREE)/mkconfig//源文件树下面应该有一个mkconfig,也就是说u−boot下面有一个mkconfig@(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0
mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0 //也就是说,执行配置命令的时候就相当于执行了这个脚本
1. 分析: mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0:
代开mkconfig的脚本文件:
#!/bin/sh -e
# Script to create header files and links to configure
# U-Boot for a specific board.
#
# Parameters: Target Architecture CPU Board [VENDOR] [SOC]
#
# (C) 2002-2006 DENX Software Engineering, Wolfgang Denk <wd@denx.de>
#
#mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0
$0 $1 $2 $3 $4 $5 $6
APPEND=no # Default: Create new config file
BOARD_NAME="" # Name to print in make output
//while [ $# -gt 0 ] ; do
// case "$1" in
// --) shift ; break ;;
// -a) shift ; APPEND=yes ;;
// -n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
// *) break ;;
// esac
// done
传入的参数没有 -- -a -n ,所以上面一段话可以省略,省略用//表示
[ "${BOARD_NAME}" ] || BOARD_NAME="$1" 这句话的意思是如果定义了BOARD_NAME,就不执行BOARD_NAME="$1" ,否则就执行
因为上面有一句BOARD_NAME="",所以执行完这句话后BOARD_NAME="$1",在linux中
$1表示的是第一个参数 , BOARD_NAME="$1" = 100ask24x0
//[ $# -lt 4 ] && exit 1 $#表示的是参数的个数,小于4退出
//[ $# -gt 6 ] && exit 1 $#表示的是参数的个数,大于6退出,我们是6个参数,所以这两句话可以省略
echo "Configuring for ${BOARD_NAME} board..." 然后会打印这句话,在ubuntu里面执行这句话,make 100ask24x0_config,输出信息:
book@www.100ask.org:/work/system/u-boot-1.1.6$ make 100ask24x0_config
Configuring for 100ask24x0 board...
#
# Create link to architecture specific headers
#
if [ "$SRCTREE" != "$OBJTREE" ] ; then
在Makefile中搜索SRCTREE,得到的信息如下所示:
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR)) //这句话的意思,要是定义队了BUILD_DIR,就是BUILD_DIR,否则为CURDIR
SRCTREE := $(CURDIR)
然后搜索:BUILD_DIR
Line 71: BUILD_DIR := $(O) 所以得出结论就是"$SRCTREE" = "$OBJTREE",所以下面一大段话可以省略
// mkdir -p ${OBJTREE}/include
// mkdir -p ${OBJTREE}/include2
// cd ${OBJTREE}/include2
// rm -f asm
// ln -s ${SRCTREE}/include/asm-$2 asm
// LNPREFIX="../../include2/asm/"
// cd ../include
// rm -rf asm-$2
// rm -f asm
// mkdir asm-$2
// ln -s asm-$2 asm
else
cd ./include
rm -f asm
ln -s asm-$2 asm
上面这句话的意思就是 ln -s asm-arm asm 相当于建立一个链接文件asm,指向asm-arm
cd /work/system/u-boot-1.1.6/include/
ls -l asm
book@www.100ask.org:/work/system/u-boot-1.1.6/include$ ls -l asm
lrwxrwxrwx 1 book book 7 6月 16 11:33 asm -> asm-arm
为什么要这么做呢?
book@www.100ask.org:/work/system/u-boot-1.1.6/include$ ls asm //TAB键补全
asm/ asm-arm/ asm-avr32/ asm-blackfin/ asm-i386/
asm-m68k/ asm-microblaze/ asm-mips/ asm-nios/ asm-nios2/ asm-ppc/
这样做,就相当于包含头文件的时候,例如include "asm/type.h"就相当于include "asm-arm/type.h"
这样做是为了防止将代码改来改去,直接用asm就可以了。asm是配置的时候临时生成的指向某个架构
rm -f asm-$2/arch //rm -f asm-arm/arch 删除文件
//if [ -z "$6" -o "$6" = "NULL" ] ; then //如果第六个参数$6为空或者null,就执行下面这句话,但是我的$6 = s3c24x0
// ln -s ${LNPREFIX}arch-$3 asm-$2/arch
//else
ln -s ${LNPREFIX}arch-$6 asm-$2/arch //ln -s arch-s3c24x0 asm-arm/arch LNPREFIX没有人定义就是空的意思
这句话的意思就是在asm-arm的目录下建立链接arch,指向arch-s3c24x0
上面已经 cd include ,此时依旧在include目录下面
ls -l asm-arm/arch
book@www.100ask.org:/work/system/u-boot-1.1.6/include$ ls asm-arm/arch -l
lrwxrwxrwx 1 book book 12 6月 16 11:33 asm-arm/arch -> arch-s3c24x0
fi
if [ "$2" = "arm" ] ; then
rm -f asm-$2/proc //rm -f asm-arm/proc
ln -s ${LNPREFIX}proc-armv asm-$2/proc //ln -s proc-armv asm-arm/proc 建立链接
book@www.100ask.org:/work/system/u-boot-1.1.6/include$ ls -l asm-arm/proc
lrwxrwxrwx 1 book book 9 6月 16 11:33 asm-arm/proc -> proc-armv
fi
#
# Create include file for Make //生成配置文件
#
echo "ARCH = $2" > config.mk // >表示新建一个文件
echo "CPU = $3" >> config.mk // >>表示追加内容
echo "BOARD = $4" >> config.mk
内容 :
ARCH = arm
CPU = arm920t
BOARD = 100ask24x0
config.mk的目录就等于这个内容
这个可以进去看看,依旧在include目录下面, vi config.mk
//[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk //$5 = NULL
[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk //SOC = s3c24x0 >> config.mk
总结下 : config.mk 的内容就是:
ARCH = arm
CPU = arm920t
BOARD = 100ask24x0
SOC = s3c24x0
仅仅是查看内容,用cat config.mk更容易,用vi config.mk不太好用,还需要退出
#
# Create board specific header file //创建单板相关的头文件
#
//if [ "$APPEND" = "yes" ] # Append to existing config file //搜索APPEND,APPEND=no
//then
// echo >> config.h
//else
> config.h # Create new config file //创建新的文件config.h
fi
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h
上面两句话的意思是config.h里面有下面的两句话:
/* Automatically generated - do not edit */
#include <configs/$1.h>
以前Makefile感觉就行天书,现在看起来感觉好简单。
exit 0
上面讲解的都是配置的过程,接下来讲解的就是编译的过程 : make
2. 分析编译过程
include $(OBJTREE)/include/config.mk
Makefile将config.mk包含进去了,所谓的配置过程和编译过程就是这样链接的
看看config.mk的具体内容:
ARCH = arm
CPU = arm920t
BOARD = 100ask24x0
SOC = s3c24x0
ifndef CROSS_COMPILE
ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
endif
这句话的意思就是如果arch架构是arm的话,那么交叉工具链就是arm-linux-
OBJS = cpu/$(CPU)/start.o //这句话就不分析了,但是这个非常重要,后面会分析start.o里面的内容
//库
LIBS = lib_generic/libgeneric.a
LIBS += board/$(BOARDDIR)/lib$(BOARD).a //LIBS += board/100ask24x0/lib100ask24x0.a
LIBS += cpu/$(CPU)/lib$(CPU).a //LIBS += cpu/arm920t/libarm920t.a
ifdef SOC
LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
endif
LIBS += lib_$(ARCH)/lib$(ARCH).a
LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
LIBS += net/libnet.a
LIBS += disk/libdisk.a
LIBS += rtc/librtc.a
LIBS += dtt/libdtt.a
LIBS += drivers/libdrivers.a
LIBS += drivers/nand/libnand.a
LIBS += drivers/nand_legacy/libnand_legacy.a
LIBS += drivers/usb/libusb.a
LIBS += drivers/sk98lin/libsk98lin.a
LIBS += common/libcommon.a
LIBS += $(BOARDLIBS)
ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)
all: $(ALL)
$(obj)u-boot.hex: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@
$(obj)u-boot.srec: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
$(obj)u-boot.bin: $(obj)u-boot (elf,二进制格式) 然后去看看u-boot依赖于谁,可以从下面看到u-boot所依赖的
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(obj)u-boot.img: $(obj)u-boot.bin
./tools/mkimage -A $(ARCH) -T firmware -C none \
-a $(TEXT_BASE) -e 0 \
-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
sed -e 's/"[ ]*$$/ for $(BOARD) board"/') \
-d $< $@
$(obj)u-boot.dis: $(obj)u-boot
$(OBJDUMP) -d $< > $@
$(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
grep -nR "LDFLAGS" *
config.mk:45:PLATFORM_LDFLAGS =
config.mk:189:LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
注意一下,在上面提到了config.mk, 这里的config.mk和上面提到的一样,一个是主目录下面的,一个是include目录下面的
grep -nR "33F800000" *
board/100ask24x0/config.mk:25:TEXT_BASE = 0x33F80000
单板的的SDRAM的大小是64M,从0x30000000开始,顶端空出了512K去存放u-boot的内容,也就是从33F800000开始
所以要根据u-boot.bin的大小设置一下TEXT_BASE = 0x33F80000
看看u-boot.bin的大小: 193 KB,所以512K是足够的
这个一大堆东西看不懂,可以在u-boot目录下面执行make命令,在make的最后面可以看见这些内容:
UNDEF_SYM=`arm-linux-objdump -x lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a
cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a
fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a
drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a drivers/usb/libusb.a
drivers/sk98lin/libsk98lin.a common/libcommon.a |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
上面一大串不用管,然后进入某个目录
cd /work/system/u-boot-1.1.6 && //进入某个目录
arm-linux-ld -Bstatic -T /work/system/u-boot-1.1.6/board/100ask24x0/u-boot.lds //这个是链接,dis是反汇编文件
-Ttext 0x33F80000 //代码段,后面就是原材料了
$UNDEF_SYM cpu/arm920t/start.o \ //对应上面的OBJS = cpu/$(CPU)/start.o
--start-group lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a
cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a
fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a
dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a
drivers/usb/libusb.a drivers/sk98lin/libsk98lin.a common/libcommon.a --end-group \ //对应上面的各种库
-Map u-boot.map -o u-boot //最后输出u-boot文件
从上面的内容可以看见脚本文件的位置: /work/system/u-boot-1.1.6/board/100ask24x0/u-boot.lds
第一个执行的文件是: cpu/arm920t/start.o
链接地址是 : -Ttext 0x33F80000
可以在u-boot中搜索0x33F80000看看链接地址的来源: grep -nR "0x33F80000" *
从u-boot-1.1.6\board\100ask24x0去看看u-boot.lds文件的内容:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000; //运行的时候位于这个位置0x33F80000 + 0x00000000
. = ALIGN(4); //四字节对齐
.text :
{
cpu/arm920t/start.o (.text) //首先放入start.o文件代码段
board/100ask24x0/boot_init.o (.text) //再填充boot_init.o文件代码段
*(.text) //最后在填入所有的文件的代码段
}
. = ALIGN(4);
.rodata : { *(.rodata) } //所有文件的只读数据段
. = ALIGN(4);
.data : { *(.data) } //所有文件的数据段
. = ALIGN(4);
.got : { *(.got) }
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) } //所有文件的u-boot的命令段
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .; //当前地址
.bss : { *(.bss) } //bss段
_end = .;
}
make的时候要是不指定目标的情况下就会生成第一个目标: all
all依赖于ALL,而ALL的具体内容就是:
$(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)
3. 根据start.o开始分析u-boot的功能(第一阶段):
以前在裸板程序的时候已经对u-boot有了一定的了解,所以大致看看就行了,u-boot其实就是一个比较复杂的单片机程序而已。
下面是分析过程:
先建立souce insight工程,嵌入式linux完全开发应用手册上面对底层目录有详细的解释,只需要注意一下board目录,cpu目录,
include目录就可以了,其他的目录全部加入。source insight实际上是一个读写代码的工具,多家少加都没有什么问题,只是有些
不需要的文件如果加入的话会干扰我们读写代码。
以前学习裸板的时候,start.S已经了解不少了,随便看看:
.globl _start
_start: b reset //复位上电后跳转到reset,下面的是各种异常情况
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
//看看reset
reset:
/*
* set the cpu to SVC32 mode //cpu运行与SVC32模式
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
/* turn off the watchdog */ //关看门狗
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410)
# define pWTCON 0x53000000
# define INTMOD 0X4A000004
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR - default //屏蔽所有的中断
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
#if 0
/* FCLK:HCLK:PCLK = 1:2:4 */ //初始化时钟
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
#if 0
/* 这些代码会使用SP,在NAND启动时会破坏片内内存的部分代码
* 导致NAND启动时无法使用休眠-唤醒功能
*/
/* 设置SP指向片内内存 */
ldr sp, =4092
ldr r0, =0x12345678
str r0, [sp]
ldr r1, [sp]
cmp r0, r1
ldrne sp, =0x40000000+4096
bl clock_init
#else
/* 设置时钟, 使用汇编 */
#define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))
#define S3C2440_UPLL_48MHZ ((0x38<<12)|(0x02<<4)|(0x02))
#define S3C2440_CLKDIV (0x05) // | (1<<3)) /* FCLK:HCLK:PCLK = 1:4:8, UCLK = UPLL/2 */
ldr r1, =CLKDIVN
mov r2, #S3C2440_CLKDIV
str r2, [r1]
mrc p15, 0, r1, c1, c0, 0 // read ctrl register
orr r1, r1, #0xc0000000 // Asynchronous
mcr p15, 0, r1, c1, c0, 0 // write ctrl register
ldr r0,=LOCKTIME
ldr r1,=0xffffff
str r1,[r0]
// delay
mov r0, #0x200
1: subs r0, r0, #1
bne 1b
// Configure MPLL
ldr r0,=MPLLCON
ldr r1,=S3C2440_MPLL_400MHZ
str r1,[r0]
// delay
mov r0, #0x200
1: subs r0, r0, #1
bne 1b
//Configure UPLL
ldr r0, =UPLLCON
ldr r1, =S3C2440_UPLL_48MHZ
str r1, [r0]
// delay
mov r0, #0x200
1: subs r0, r0, #1
bne 1b
#endif
#endif
/* 2. 根据 GSTATUS2[1]判断是复位还是唤醒 */
ldr r0, =GSTATUS2
ldr r1, [r0]
tst r1, #(1<<1) /* r1 & (1<<1) */
bne wake_up
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */ //如果r0和r1不相等时,说明没有人初始化SDRAM
blne cpu_init_crit //搜索一下cpu_init_crit函数
#endif
//看看看cpu_init_crit
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
/*
* flush v4 I/D caches
*/
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
*/
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 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
/*
* 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 //进去看看lowlevel_init
mov lr, ip
mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
//看看lowlevel_init
_TEXT_BASE:
.word TEXT_BASE
.globl lowlevel_init
lowlevel_init:
/* memory control configuration */
/* make r0 relative the current location so that it */
/* reads SMRDATA out of FLASH rather than memory ! */
ldr r0, =SMRDATA
ldr r1, _TEXT_BASE
sub r0, r0, r1
ldr r1, =BWSCON /* Bus Width Status Controller */
add r2, r0, #13*4
0:
ldr r3, [r0], #4
str r3, [r1], #4
cmp r2, r0
bne 0b
/* everything is fine now */
mov pc, lr
.ltorg
/* the literal pools origin */
SMRDATA: //初始化各种内存
.word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
.word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
.word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
.word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
.word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
.word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
.word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
.word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
.word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
.word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
.word 0xb1
.word 0x30
.word 0x30
//在回到start.S继续看看内容,设置栈
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */ 设置栈,栈设置好了才可以使用C函数
/代码重定位/
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq clear_bss
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
#if 1
bl CopyCode2Ram /* r0: source, r1: dest, r2: size */
总结: 1. 设置svc模式
2. 关看门狗
3. 屏蔽中断
4. 设置SDRAM
5. 设置栈
6. 初始化时钟
7. 代码重定位
8. 清BSS段,就是那些初始值为0,或者是没有初始化的静态变量和全局变量
9. 调用C函数: _start_armboot
上面的8个内容是u-boot的第一阶段,从第九个开始是u-boot的第二阶段。
第二阶段例如网卡、串口、读写flash等等用C程序写的归为第二阶段,其实第一阶段也有c函数。这个c函数是韦东山老师添加进去的,
事实上,在网上下载的u-boot函数的第一阶段基本上都是汇编语言。
4. u-boot的第二阶段
u-boot的最终目的是从flash中读出内核,然后启动内核。根据这个目的去看u-boot的代码,对于其他的就忽略掉。
_start_armboot
init_sequence //这里面是一系列的初始化就不用管了
board_init
gd->bd->bi_arch_number = MACH_TYPE_S3C2440; //arch编号
gd->bd->bi_boot_params = 0x30000100; //启动内核参数
//一些列的初始化,就是上面的init_sequence
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
#endif
dram_init, /* configure available RAM banks */
display_dram_config,
NULL,
};
要是想读取内核以及开发的方便,u-boot需要具备两种能力: 读取flash和写flash
对于NOR flash要是想写需要先识别出是哪种类型的flash,NAND Flash也是一样,需要做一些初始化,以及识别是什么类型。
#ifndef CFG_NO_FLASH
/* configure available FLASH banks */
size = flash_init (); //对NOR flash的初始化
display_flash_config (size);
#endif /* CFG_NO_FLASH */
u-boot是一个单片机程序,分配内存、释放内存都需要自己去实现:
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
malloc是从内存中分配出一块空间,malloc称之为堆,对应192K那块区域,在这块空间上实现堆的释放和分配
nand_init(); //初始化NAND FLASH
env_relocate (); //环境变量初始化,在u-boot界面输入print就可以看见一堆的数据,那个就是环境变量,前面是环境变量,等号后面
的是环境变量的值
环境变量的值来源于两种:默认的(代码中写死的)、可以修改的存在于flash中的
/* MAC Address */ 这个不用管
devices_init (); /* get the devices list going. */ //这个也不用管
if (!PreLoadedONRAM) { //调试器相关的代码
/* enable exceptions */
enable_interrupts ();
/* add by www.100ask.net */
usb_init();
}
/* Perform network card initialisation if necessary */ 网卡相关的代码
for (;;) { //进入循环
main_loop ();
}
一直讲解到这里都还没有看到u-boot的最终的目的: 启动内核。进去看看main_loop()
start_armboot
main_loop
setenv ("bootcount", bcs_set);
s = getenv ("bootdelay");
s = getenv ("bootcmd"); //启动命令,这个参数非常重要
if (bootdelay >= 0 && s && !abortboot (bootdelay)) { //倒数计时
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(1); /* disable Control C checking */
# endif
# ifndef CFG_HUSH_PARSER
{
printf("Booting Linux ...\n");
run_command (s, 0);
}
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
# ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */
# endif
}
bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
这句话包含了两个命令:nand read.jffs2 0x30007FC0 kernel //用命令nand read.jffs2,读到0x30007FC0,从kernel读
main_loop是一个循环函数,在u-boot窗口一直等待我们输入命令,输入什么命令就解析什么命令
5. u-boot命令
后面老子听不懂了..................