1、uboot 的作用。
相比于 linux 操作系统,uboot 本身不大,能够自启动,作为嵌入式设备的引导
启动,是个好的选择。此外,它具有源码开放、支持多种嵌入式操作系统、丰富
的设备驱动源码等特点。
作用:
1)、为系统启动之前初始化硬件设备、为操作系统准备软件环境。
2)、引导操作系统内核启动。
2、uboot 如何启动内核、如何传参给内核?
2.1、如何启动内核?
在嵌入式设备没有上电运行前,操作系统是放在外存中的(硬盘、外部 flash、
服务器等)。设备一开始上电首先执行的是 uboot(BootLoader),uboot 的运行
有两个阶段:
第一个阶段: uboot 对硬件的初始化,为第二阶段准备运行环境。
1.建立异常向量表。
2.设置 CPU 的模式。禁止中断、cpu 设为 SVC 模式。
3.关开门狗。
4.CPU 时钟初始化。
5.内存初始化。
6.复制第二阶段的代码到内存中。
7.建立映射表并开启 MMU。
8.跳转到指定的内存执行第二阶段。
第二阶段: 初始化串口、网络硬件等,为内核设置启动参数,然后调用内核。
1.初始化 IRQ/FIQ 模式的栈。
2.设置系统时钟、初始化定时器,初始化串口。
3.检查环境参数是否有效,将环境参数读入指定的内存。
4.初始化网络设备。
5.调用内核、启动内核。
第一个阶段启动代码分析:
uboot 的启动入口在 uboot/cpu/xxx/start.S 文件中.
1. reset:
2. @;mrs
3. @;bic
4. @;orr
5. @;msr
6. msr cpsr_c, #0xd3 @ I & F disable, Mode: 0x13 - SVC
r0,cpsr
r0,r0,#0x1f
r0,r0,#0xd3
cpsr,r0
第 2-6 行禁止中断(IRQs 和 FIQs),cpu 设置成 SVC 模式。
_TEXT_BASE:
7.
8. .word TEXT_BASE
第 7-8 行的_TEXT_BASE 就是在 Makefile 中指定的 uboot 链接地址,makefile
1
中指定的 TEXT_BASE 如下:
x210_sd_config : unconfig
@$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk
9. _TEXT_PHY_BASE:
10. .word CFG_PHY_UBOOT_BASE
11. bl disable_l2cache
12. bl set_l2cache_auxctrl_cycle
13. bl enable_l2cache
14. bl lowlevel_init
/* go setup pll,mux,memory */
15 ldr r0, =PRO_ID_BASE
16 ldr r1, [r0,#OMR_OFFSET]
15. bic r2, r1, #0xffffffc1
第 9-10 行_TEXT_PHY_BASE 是 uboot 的物理地址。
第 11-14 的目的就是刷新并打开 icache,然后调用 lowlevel_init。
第 15-16 行将 r2 寄存器存储一个特定值,用于指定 uboot 以何种方式来启动。
17 lowlevel_init:
......
......
......
/* r0 <- current base addr of code */
/* r1 <- original base addr in ram */
/* r0 <- current base addr of code */
/* compare r0, r1
/* r0 == r1 then skip sdram init
*/
*/
18 ldr r0, =0xff000fff
19 bic r1, pc, r0
20 ldr r2, _TEXT_BASE
21 bic r2, r2, r0
r1, r2
22 cmp
23 beq
1f
24
25 /* init system clock */
26 bl system_clock_init
27
28 /* Memory initialize */
29 bl mem_ctrl_asm_init
30
31 /* get ready to call C functions */
32 ldr sp, _TEXT_PHY_BASE
33 sub sp, sp, #12
34 mov fp, #0
35
36 ldr r0, =0xff000fff
2
/* setup temp stack pointer */
/* no previous frame, so fp=0 */
/* r0 <- current base addr of code */
/* r1 <- original base addr in ram */
37 bic r1, pc, r0
38 ldr r2, _TEXT_BASE
39 bic r2, r2, r0
40 cmp
r1, r2
after_copy
41 beq
/* r0 <- current base addr of code */
/* compare r0, r1
/* r0 == r1 then skip flash copy
*/
*/
第 18-23 行判断当前执行的代码是在 SRAM 还是在 DDR 中,如果在 SRAM 中,
那就要进行初始化 DDR,进行时钟初始化、代码的复制等,如果在 DDR 中,就可
以直接启动。
第 26 行进行时钟的初始化。
第 29 行初始化内存 DDR,在 x210_sd.h 中,起始地址的定义如下:
42. #define MEMORY_BASE_ADDRESS
43. #define MEMORY_BASE_ADDRESS2
44. #define CFG_PHY_UBOOT_BASE
0x30000000
0x40000000
MEMORY_BASE_ADDRESS + 0x3e00000
由 此 可 知 , uboot 的 可 用 物 理 地 址 为 30000000-4FFFFFFF , 共 有 512M ,
30000000-3FFFFFFF 为 DMC0,40000000-4FFFFFFF 为 DMC1。
第 32-33 行设置栈,之前已经设置过一次了,那时是在 SRAM 中,SRAM 内存
空间小,现在 DDR 内存可以用了,重新分配大一些的栈空间,为执行第二阶段做
好运行环境。
第 37-41 行判断是否需要重定位,运行地址是在 SRAM 还是 DDR 中,如果在
SRAM 中,要把 uboot 的第二部分加载到 DDR 中链接地址_TEXT_BASE 处。Uboot
刚开始运行的时候,将第一阶段的代码从 SD 卡复制到 SRAM 中运行,第一阶段进
行各种初始化,给第二阶段准备好软件的运行空间后,将第二阶段复制到链接地
址执行,这过程就是重定位。通过调用 movi_bl2_copy 函数复制第二阶段的代
码,,之后建立转换表,也就是作地址映射,代码如下第 45-51 行,映射完后调
用 enable_mmu 使能 MMU。
45. /* Set the TTB register */
46. ldr r0, _mmu_table_base
47. ldr r1, =CFG_PHY_UBOOT_BASE
48. ldr r2, =0xfff00000
49. bic r0, r0, r2
50. orr r1, r0, r1
51. mcr p15, 0, r1, c2, c0, 0
52. ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)
第 52 行是再次设置栈,这次也是在 DDR 中,只是这次放的地方比较规范。
设置栈好后,进行清理 bss 等,然后执行 start_armboot 进入第二阶段。
3
//typedef int (init_fnc_t) (void);
第二阶段:
53. _start_armboot:
54. .word start_armboot
55.
56. init_fnc_t **init_fnc_ptr;
57.
58. #ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
59. ulong gd_base;
60.
61. gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN -
62. CFG_STACK_SIZE - sizeof(gd_t);
63. #ifdef CONFIG_USE_IRQ
64. gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
65. #endif
66. gd = (gd_t*)gd_base;
67. #else
68. gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
69. #endif
70. /* compiler optimization barrier needed for GCC >= 3.4 */
71. __asm__ __volatile__("": : :"memory");
72.
73. memset ((void*)gd, 0, sizeof (gd_t));
74. gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
75. memset (gd->bd, 0, sizeof (bd_t));
76.
77. monitor_flash_len = _bss_start - _armboot_start;
78.
79. for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr)
80. {
81. if((*init_fnc_ptr)() !=0){
82.
83.
84. }
hang ();
}
第 66 行的 gd 变量定义在 DECLARE_GLOBAL_DATA_PTR 宏中,#define DECLARE
register volatile gd_t *gd asm ("r8"),gd 是一个 gd_t
_GLOBAL_DATA_PTR
指针类型,放在 r8 寄存器中。gd_t 的结构体定义在 include/asm-arm/global
_data.h 文件中。Uboot 使用它来存放很多的信息,第 86-110 行就是定义了 gd_t
结构体,它还指向了 bd_t 结构体,其定义在第 111-127 行,它有波特率参数、
DDR 内存大小、IP 地址、机器码、环境变量等。都是一些为启动内核准备内核的
参数。
第 73-75 行为结构体分配内存空间,此分配的空间是有讲究的,此指向的起
始空间通过第 68 行计算得到的。
第 79-84 行 是 进 行 一 些 列 的 初 始 化 , 波 特 率 、 串 口 、 RAM 、 中 断 等 。
4
init_sequence 是一个函数指针数组,里面存放有个初始化函数的函数名。其定
义在 uboot/lib_arm/board.c 文件中
*bd;
86. Typedef struct global_data
87. {
88. bd_t
89.
90. unsigned long flags;
91. unsigned long baudrate;
92. unsigned long have_console; /* serial_init() was called
93. unsigned long reloc_off; /* Relocation Offset
*/
94. unsigned long env_addr; /*Address
95. unsigned long env_valid; /* Checksum of Environment valid? */
96. unsigned long fb_base;
97. #ifdef
98. unsigned char vfd_type; /* display type */
102.
103.
104.
105.
106.
107.
108.
109.
110.
#endif
#if 0
unsigned long cpu_clk;/* CPU clock in Hz!
unsigned long bus_clk;
phys_size_t
unsigned long reset_status;/* reset status register at boot
#endif
/* base address of frame buffer */
ram_size; /* RAM size */
of Environment struct*/
**jt;
/* jump table */
void
} gd_t;
CONFIG_VFD
*/
*/
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
typedef struct bd_info {
*bi_env;
/* serial console baudrate */
/* IP Address */
int
bi_baudrate;
unsigned long bi_ip_addr;
unsigned char bi_enetaddr[6]; /* Ethernet adress */
struct environment_s
ulong
ulong
struct
{
ulong start;
ulong size;
}bi_dram[CONFIG_NR_DRAM_BANKS];
bi_arch_number;/* unique id for this board */
bi_boot_params;/* where this board expects
/* RAM configuration */
#ifdef
/*
unsigned char
CONFIG_HAS_ETH1
second onboard ethernet port */
#endif
} bd_t;
bi_enet1addr[6];
5
mmc_exist = mmc_initialize(gd->bd);
/* initialize environment */
env_relocate ();
/* IP Address
gd->bd->bi_ip_addr =
*/
getenv_IPaddr ("ipaddr");
/****************lxg added**************/
#ifdef CONFIG_MPAD
extern int x210_preboot_init(void);
x210_preboot_init();
#endif
/****************end**********************/
for (;;) {
main_loop ();
}
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
第 128 行进行初始化 uboot 的堆管理器,这样 uboot 中也可以 malloc、free
这套机制来申请内存和释放内存。
第 130 行是将环境变量从 SD 卡中读到 DDR 中,第一次运行时 SD 卡中是没有
环境变量的,uboot 是使用自定义默认的环境变量,然后将其写入 SD 中,下一
次启动时,就可以直接从 SD 卡读取环境变量了。
第 32 行获取 IP 地址,第 135-138 行进行一些初始化,以及开始显示的 LOGO。
第 141 执行 main_loop 判断用户是否执行下载模式,在 bootdelay 秒内,用
户是否有数据输入,如果没有就执行 bootcmd 命令,然后执行 do_bootm ,
->do_bootm_linux->theKernel ,执行 theKernel 就启动内核了。
144.
145.
146.
147.
148.
149.
150.
151.
152.
s = getenv ("bootcmd");
debug ("### main_loop: bootcmd=\"%s\"\n", s ? s :
"");
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc,
char *argv[])
do_bootm_linux (cmdtp, flag, argc, argv, &images);
theKernel (0, machid, bd->bi_boot_params);
第 151 行的 theKernel 函数参数,各参数对应着在寄存器 R0、R1、R2 中存
放的,R0 = 0,R1 = 机器的 ID,R2 = 启动参数标志列表在 RAM 中的启始基地
址。
给内核传哪些参数? 在 board_init 函数中,有这样两行代码,
gd->bd->bi_arch_number = MACH_TYPE;
gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);
MACH_TYPE 是板子的机器码,和内核一样。bi_boot_params 就是给内核传参
的存放地址。 setup_start_tag,setup_memory_tags,setup_commandline_tag,
setup_initrd_tag,setup_serial_tag,setup_revision_tag,这几个函数都会
6
标有存放的标志,告诉内核当前位置存放的是什么内容。然后调用 tag_next 函
数指向下一个内容。
#define tag_next(t)
((struct tag *)((u32 *)(t) + (t)->hdr.size))
ATAG_CORE:代码开始标志。ATAG_MEM:内存标志。ATAG_CMDLINE:命令行
标志。ATAG_NONE:结束标志。
3、uboot 的移植。
拿到源码包尝试进行编译,make_name_config,由 Readme 文档知道,根据
使用的开发板,就要执行 make_boadr_name_config 命令进行配置,对应的配置
信息在 include/configs/boadr_name.h 中,提示配置成功后,在 make 编译一
下,就会生成 3 个文件:
u-boot.bin:二进制可执行文件,可以直接烧入 ROM、NOR Flash。
u-boot:ELF 格式的可执行文件
u-boot.srec:Motorola S-Record 格式的可执行文件。
一般是将 u-boot.bin 烧录 SD 卡,方便调试。进入 sd_fusing 目录下执行
sd_fusing.sh 脚本,里面可能需要给一些东西,如烧录 sd 的哪个扇区,要烧录
哪个文件名等,都是在该脚本下更改。执行./sd_fusing.sh + 设备名,就可以
烧录。
---编译之前的工作是确保安装正确的交叉编译链工具,这个在 Makefile 中
更改,如下的第 159 行。
153.
154.
155.
156.
157.
158.
159.
160.
ifeq ($(ARCH),arm)
#CROSS_COMPILE = arm-linux-
#CROSS_COMPILE=
/usr/local/arm/4.4.1-eabi-cortex-a8/usr/bin/arm-linux-
#CROSS_COMPILE=
CROSS_COMPILE=
/usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
Endif
/usr/local/arm/4.2.2-eabi/usr/bin/arm-linux-
161.
162.
163.
164.
165.
smdkv210single_config :
unconfig
@$(MKCONFIG)$(@:_config=)arm s5pc11x smdkc110 samsung s5pc110
@echo "TEXT_BASE = 0xc3e00000" >
$(obj)board/samsung/smdkc110/config.mk
make 配 置 的 过 程 , Makefile 中 的 代 码 161-165 行 , 配 置 命 令 “ make
smdkv210single_config”,实际的作用就是执行“./mkconfig smdkv210single
arm s5pc11x smdkc110 samsung s5pc110”命令,相当于执行./mkconfig $1 $2 $3
$4 $5 $6(这几个变量是在 mkconfig 文件中使用到),几个变量被赋值为:
($1=smdkv210single,$2=arm,$3=s5pc11x,$4= smdkc110,$5= samsung,
$4 = s5pc110)
BOARD_NAME = $1,ARCH = $2,CPU = $3,BOARD = $4,VENDOR = $5, SOC = $6。
Mkconfig 文件中的代码:
166.
APPEND=no # Default: Create new config file
7
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
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 ;;
*)
Esac
break ;;
Done
[ "${BOARD_NAME}" ] || BOARD_NAME="$1"
第 176 行执行后,BOARD_NAME 的值等于第一个参数,即“smdkv210single”。
这几个变量都在 mkconfig 文件中用到,就是 Makefile 那边传过来的,用来创建
板子相关的头文件等文件。
在移植的时候要根据串口打印的内容进行修改调试,哪里出错,哪里可以正
常,uboot 初始化的硬件正确,传递的参数正确的情况下,就可以正常启动内核
了。Uboot 的配置一般在 include/configs/.h 文件中,修改相关
的配置信息。有关“CONFIG_”开头是设置一些参数,设置 uboot 的功能,选用
文件中的哪一部分,而“CFG_”用来设置更为细节的参数。以下是移植三星
s5pv210 开发板的例子。
1、修改内存相关信息。
177.
178.
#define SDRAM_BANK_SIZE
#define MEMORY_BASE_ADDRESS
0x10000000
/* 256 MB*/ 内存大小
0x30000000
内存地
在 lowlevel_init.S (board\samsung\smdkc110)文件中,将.set __base,
0x200 修改为.set __base,0x300,此外还要修改 smdkc110 的函数,修改虚拟地
址映射表的基地址 virt_to_phy_smdkc110(ulong addr)
return (addr - 0xc0000000 + 0x30000000)。
2、修改 inand 驱动问题
uboot 启动后,出现 unrecognised EXT_CSD structure version 7 的提示
错误。此提示版本错误。解决办法:在 MMC.C 文件中将 ext_csd_struct > 8(大
于 7 就行)
179.
180.
181.
182.
183.
184.
185.
ext_csd_struct = ext_csd[EXT_CSD_REV];
if (ext_csd_struct > 8) {
printf("unrecognised EXT_CSD structure "
"version %d\n", ext_csd_struct);
err = -1;
goto out;
}
3、串口问题
串口输出的 SD checksum error。初始化串口控制器的代码在
lowlevel_init.S 中的 uart_asm_init 中,其中初始化串口的寄存器用
ELFIN_UART_CONSOLE_BASE 宏作为串口 n 的寄存器的基地址,结合偏移量对寄存
8