6.3.3
U-Boot
启动过程
尽管有了调试跟踪手段,甚至也可以通过串口打印信息了,但是不一定能够判断出错原因。如果能够充分理解代码的启动流程,那么对准确地解决和分析问题很有帮助。
开发板上电后,执行
U-Boot
的第一条指令,然后顺序执行
U-Boot
启动函数。函数调用顺序如图
6.3
所示。
看一下
board/smsk2410/u-boot.lds
这个链接脚本,可以知道目标程序的各部分链接顺序。第一个要链接的是
cpu/arm920t/start.o
,那么
U-Boot
的入口指令一定位于这个程序中。下面详细分析一下程序跳转和函数的调用关系以及函数实现。
1
.
cpu/arm920t/start.S
这个汇编程序是
U-Boot
的入口程序,开头就是复位向量的代码。
图
6.3 U-Boot
启动代码流程图
_start: b 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 //
中断向量
…
/* the actual reset code */
reset: //
复位启动子程序
/*
设置
CPU
为
SVC32
模式
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
/*
关闭看门狗
*/
/*
这些初始化代码在系统重起的时候执行,运行时热复位从
RAM
中启动不执行
*/
#ifdef CONFIG_INIT_CRITICAL
bl cpu_init_crit
#endif
relocate
: /*
把
U-Boot
重新定位到
RAM */
adr r0, _start /* r0
是代码的当前位置
*/
ldr r1, _TEXT_BASE /*
测试判断是从
Flash
启动,还是
RAM */
cmp r0, r1 /*
比较
r0
和
r1
,调试的时候不要执行重定位
*/
beq stack_setup /*
如果
r0
等于
r1
,跳过重定位代码
*/
/*
准备重新定位代码
*/
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2
得到
armboot
的大小
*/
add r2, r0, r2 /* r2
得到要复制代码的末尾地址
*/
copy_loop: /*
重新定位代码
*/
ldmia r0!, {r3-r10} /*
从源地址
[r0]
复制
*/
stmia r1!, {r3-r10} /*
复制到目的地址
[r1] */
cmp r0, r2 /*
复制数据块直到源数据末尾地址
[r2] */
ble copy_loop
/*
初始化堆栈等
*/
stack_setup:
ldr r0, _TEXT_BASE /*
上面是
128 KiB
重定位的
u-boot */
sub r0, r0, #CFG_MALLOC_LEN /*
向下是内存分配空间
*/
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 /*
为
abort-stack
预留
3
个字
*/
clear_bss:
ldr r0, _bss_start /*
找到
bss
段起始地址
*/
ldr r1, _bss_end /* bss
段末尾地址
*/
mov r2, #0x00000000 /*
清零
*/
clbss_l:str r2, [r0] /* bss
段地址空间清零循环
... */
add r0, r0, #4
cmp r0, r1
bne clbss_l
/*
跳转到
start_armboot
函数入口,
_start_armboot
字保存函数入口指针
*/
ldr pc, _start_armboot
_start_armboot: .word start_armboot //start_armboot
函数在
lib_arm/board.c
中实现
/*
关键的初始化子程序
*/
cpu_init_crit:
…… //
初始化
CACHE
,关闭
MMU
等操作指令
/*
初始化
RAM
时钟。
*
因为内存时钟是依赖开发板硬件的,所以在
board
的相应目录下可以找到
memsetup.S
文件。
*/
mov ip, lr
bl memsetup //memsetup
子程序在
board/smdk2410/memsetup.S
中实现
mov lr, ip
mov pc, lr
2
.
lib_arm/board.c
start_armboot
是
U-Boot
执行的第一个
C
语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。
void start_armboot (void)
{
DECLARE_GLOBAL_DATA_PTR;
ulong size;
init_fnc_t **init_fnc_ptr;
char *s;
/* Pointer is writable since we allocated a register for it */
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
monitor_flash_len = _bss_start - _armboot_start;
/*
顺序执行
init_sequence
数组中的初始化函数
*/
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/*
配置可用的
Flash */
size = flash_init ();
display_flash_config (size);
/* _armboot_start
在
u-boot.lds
链接脚本中定义
*/
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
/*
配置环境变量,重新定位
*/
env_relocate ();
/*
从环境变量中获取
IP
地址
*/
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/*
以太网接口
MAC
地址
*/
……
devices_init (); /*
获取列表中的设备
*/
jumptable_init ();
console_init_r (); /*
完整地初始化控制台设备
*/
enable_interrupts (); /*
使能例外处理
*/
/*
通过环境变量初始化
*/
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
/* main_loop()
总是试图自动启动,循环不断执行
*/
for (;;) {
main_loop (); /*
主循环函数处理执行用户命令
-- common/main.c */
}
/* NOTREACHED - no way out of command loop except booting */
}
3
.
init_sequence[]
init_sequence[]
数组保存着基本的初始化函数指针。这些函数名称和实现的程序文件在下列注释中。
init_fnc_t *init_sequence[] = {
cpu_init, /*
基本的处理器相关配置
-- cpu/arm920t/cpu.c */
board_init, /*
基本的板级相关配置
-- board/smdk2410/smdk2410.c */
interrupt_init, /*
初始化例外处理
-- cpu/arm920t/s3c24x0/interrupt.c */
env_init, /*
初始化环境变量
-- common/cmd_flash.c */
init_baudrate, /*
初始化波特率设置
-- lib_arm/board.c */
serial_init, /*
串口通讯设置
-- cpu/arm920t/s3c24x0/serial.c */
console_init_f, /*
控制台初始化阶段
1 -- common/console.c */
display_banner, /*
打印
u-boot
信息
-- lib_arm/board.c */
dram_init, /*
配置可用的
RAM -- board/smdk2410/smdk2410.c */
display_dram_config, /*
显示
RAM
的配置大小
-- lib_arm/board.c */
NULL,
};
6.3.4
U-Boot
与内核的关系
U-Boot
作为
Bootloader
,具备多种引导内核启动的方式。常用的
go
和
bootm
命令可以直接引导内核映像启动。
U-Boot
与内核的关系主要是内核启动过程中参数的传递。
1
.
go
命令的实现
/* common/cmd_boot.c */
int do_go (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong addr, rc;
int rcode = 0;
if (argc < 2) {
printf ("Usage:\n%s\n", cmdtp->usage);
return 1;
}
addr = simple_strtoul(argv[1], NULL, 16);
printf ("## Starting application at 0x%08lX ...\n", addr);
/*
* pass address parameter as argv[0] (aka command name),
* and all remaining args
*/
rc = ((ulong (*)(int, char *[]))addr) (--argc, &argv[1]);
if (rc != 0) rcode = 1;
printf ("## Application terminated, rc = 0x%lX\n", rc);
return rcode;
}
go
命令调用
do_go()
函数,跳转到某个地址执行的。如果在这个地址准备好了自引导的内核映像,就可以启动了。尽管
go
命令可以带变参,实际使用时一般不用来传递参数。
2
.
bootm
命令的实现
/* common/cmd_bootm.c */
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong iflag;
ulong addr;
ulong data, len, checksum;
ulong *len_ptr;
uint unc_len = 0x400000;
int i, verify;
char *name, *s;
int (*appl)(int, char *[]);
image_header_t *hdr = &header;
s = getenv ("verify");
verify = (s && (*s == 'n')) ? 0 : 1;
if (argc < 2) {
addr = load_addr;
} else {
addr = simple_strtoul(argv[1], NULL, 16);
}
SHOW_BOOT_PROGRESS (1);
printf ("## Booting image at %08lx ...\n", addr);
/* Copy header so we can blank CRC field for re-calculation */
memmove (&header, (char *)addr, sizeof(image_header_t));
if (ntohl(hdr->ih_magic) != IH_MAGIC)
{
puts ("Bad Magic Number\n");
SHOW_BOOT_PROGRESS (-1);
return 1;
}
SHOW_BOOT_PROGRESS (2);
data = (ulong)&header;
len = sizeof(image_header_t);
checksum = ntohl(hdr->ih_hcrc);
hdr->ih_hcrc = 0;
if(crc32 (0, (char *)data, len) != checksum) {
puts ("Bad Header Checksum\n");
SHOW_BOOT_PROGRESS (-2);
return 1;
}
SHOW_BOOT_PROGRESS (3);
/* for multi-file images we need the data part, too */
print_image_hdr ((image_header_t *)addr);
data = addr + sizeof(image_header_t);
len = ntohl(hdr->ih_size);
if(verify) {
puts (" Verifying Checksum ... ");
if(crc32 (0, (char *)data, len) != ntohl(hdr->ih_dcrc)) {
printf ("Bad Data CRC\n");
SHOW_BOOT_PROGRESS (-3);
return 1;
}
puts ("OK\n");
}
SHOW_BOOT_PROGRESS (4);
len_ptr = (ulong *)data;
……
switch (hdr->ih_os) {
default: /* handled by (original) Linux case */
case IH_OS_LINUX:
do_bootm_linux (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
……
}
bootm
命令调用
do_bootm
函数。这个函数专门用来引导各种操作系统映像,可以支持引导
Linux
、
vxWorks
、
QNX
等操作系统。引导
Linux
的时候,调用
do_bootm_linux()
函数。
3
.
do_bootm_linux
函数的实现
/* lib_arm/armlinux.c */
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
ulong addr, ulong *len_ptr, int verify)
{
DECLARE_GLOBAL_DATA_PTR;
ulong len = 0, checksum;
ulong initrd_start, initrd_end;
ulong data;
void (*theKernel)(int zero, int arch, uint params);
image_header_t *hdr = &header;
bd_t *bd = gd->bd;
#ifdef CONFIG_CMDLINE_TAG
char *commandline = getenv ("bootargs");
#endif
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
/* Check if there is an initrd image */
if(argc >= 3) {
SHOW_BOOT_PROGRESS (9);
addr = simple_strtoul (argv[2], NULL, 16);
printf ("## Loading Ramdisk Image at %08lx ...\n", addr);
/* Copy header so we can blank CRC field for re-calculation */
memcpy (&header, (char *) addr, sizeof (image_header_t));
if (ntohl (hdr->ih_magic) != IH_MAGIC) {
printf ("Bad Magic Number\n");
SHOW_BOOT_PROGRESS (-10);
do_reset (cmdtp, flag, argc, argv);
}
data = (ulong) & header;
len = sizeof (image_header_t);
checksum = ntohl (hdr->ih_hcrc);
hdr->ih_hcrc = 0;
if(crc32 (0, (char *) data, len) != checksum) {
printf ("Bad Header Checksum\n");
SHOW_BOOT_PROGRESS (-11);
do_reset (cmdtp, flag, argc, argv);
}
SHOW_BOOT_PROGRESS (10);
print_image_hdr (hdr);
data = addr + sizeof (image_header_t);
len = ntohl (hdr->ih_size);
if(verify) {
ulong csum = 0;
printf (" Verifying Checksum ... ");
csum = crc32 (0, (char *) data, len);
if (csum != ntohl (hdr->ih_dcrc)) {
printf ("Bad Data CRC\n");
SHOW_BOOT_PROGRESS (-12);
do_reset (cmdtp, flag, argc, argv);
}
printf ("OK\n");
}
SHOW_BOOT_PROGRESS (11);
if ((hdr->ih_os != IH_OS_LINUX) ||
(hdr->ih_arch != IH_CPU_ARM) ||
(hdr->ih_type != IH_TYPE_RAMDISK)) {
printf ("No Linux ARM Ramdisk Image\n");
SHOW_BOOT_PROGRESS (-13);
do_reset (cmdtp, flag, argc, argv);
}
/* Now check if we have a multifile image */
} else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {
ulong tail = ntohl (len_ptr[0]) % 4;
int i;
SHOW_BOOT_PROGRESS (13);
/* skip kernel length and terminator */
data = (ulong) (&len_ptr[2]);
/* skip any additional image length fields */
for (i = 1; len_ptr[i]; ++i)
data += 4;
/* add kernel length, and align */
data += ntohl (len_ptr[0]);
if (tail) {
data += 4 - tail;
}
len = ntohl (len_ptr[1]);
} else {
/* no initrd image */
SHOW_BOOT_PROGRESS (14);
len = data = 0;
}
if (data) {
initrd_start = data;
initrd_end = initrd_start + len;
} else {
initrd_start = 0;
initrd_end = 0;
}
SHOW_BOOT_PROGRESS (15);
debug ("## Transferring control to Linux (at address %08lx) ...\n",
(ulong) theKernel);
#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
defined (CONFIG_CMDLINE_TAG) || \
defined (CONFIG_INITRD_TAG) || \
defined (CONFIG_SERIAL_TAG) || \
defined (CONFIG_REVISION_TAG) || \
defined (CONFIG_LCD) || \
defined (CONFIG_VFD)
setup_start_tag (bd);
#ifdef CONFIG_SERIAL_TAG
setup_serial_tag (¶ms);
#endif
#ifdef CONFIG_REVISION_TAG
setup_revision_tag (¶ms);
#endif
#ifdef CONFIG_SETUP_MEMORY_TAGS
setup_memory_tags (bd);
#endif
#ifdef CONFIG_CMDLINE_TAG
setup_commandline_tag (bd, commandline);
#endif
#ifdef CONFIG_INITRD_TAG
if (initrd_start && initrd_end)
setup_initrd_tag (bd, initrd_start, initrd_end);
#endif
setup_end_tag (bd);
#endif
/* we assume that the kernel is in place */
printf ("\nStarting kernel ...\n\n");
cleanup_before_linux ();
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
}
do_bootm_linux()
函数是专门引导
Linux
映像的函数,它还可以处理
ramdisk
文件系统的映像。这里引导的内核映像和
ramdisk
映像,必须是
U-Boot
格式的。
U-Boot
格式的映像可以通过
mkimage
工具来转换,其中包含了
U-Boot
可以识别的符号。
6.4
使用
U-Boot
U-Boot
是“
Monitor
”。除了
Bootloader
的系统引导功能,它还有用户命令接口,提供了一些复杂的调试、读写内存、烧写
Flash
、配置环境变量等功能。掌握
U-Boot
的使用,将极大地方便嵌入式系统的开发。
6.4.1
烧写
U-Boot
到
Flash
新开发的电路板没有任何程序可以执行,也就不能启动,需要先将
U-Boot
烧写到
Flash
中。
如果主板上的
EPROM
或者
Flash
能够取下来,就可以通过编程器烧写。例如:计算机
BIOS
就存储在一块
256KB
的
Flash
上,通过插座与主板连接。
但是多数嵌入式单板使用贴片的
Flash
,不能取下来烧写。这种情况可以通过处理器的调试接口,直接对板上的
Flash
编程。
处理器调试接口是为处理器芯片设计的标准调试接口,包含
BDM
、
JTAG
和
EJTAG 3
种接口标准。
JTAG
接口在第
4
章已经介绍过;
BDM
(
Background Debug Mode
)主要应用在
PowerPC8xx
系列处理器上;
EJTAG
主要应用在
MIPS
处理器上。这
3
种硬件接口标准定义有所不同,但是功能基本相同,下面都统称为
JTAG
接口。
JTAG
接口需要专用的硬件工具来连接。无论从功能、性能角度,还是从价格角度,这些工具都有很大差异。关于这些工具的选择,将在第
6.4.1
节详细介绍。
最简单方式就是通过
JTAG
电缆,转接到计算机并口连接。这需要在主机端开发烧写程序,还需要有并口设备驱动程序。开发板上电或者复位的时候,烧写程序探测到处理器并且开始通信,然后把
Bootloader
下载并烧写到
Flash
中。这种方式速率很慢,可是价格非常便宜。一般来说,平均每秒钟可以烧写
100
~
200
个字节。
烧写完成后,复位实验板,串口终端应该显示
U-Boot
的启动信息。
6.4.2
U-Boot
的常用命令
U-Boot
上电启动后,敲任意键可以退出自动启动状态,进入命令行。
U-Boot 1.1.2 (Apr 26 2005 - 12:27:13)
U-Boot code: 11080000 -> 1109614C BSS: -> 1109A91C
RAM Configuration:
Bank #0: 10000000 32 MB
Micron StrataFlash MT28F128J3 device initialized
Flash: 32 MB
In: serial
Out: serial
Err: serial
Hit any key to stop autoboot: 0
U-Boot>
在命令行提示符下,可以输入
U-Boot
的命令并执行。
U-Boot
可以支持几十个常用命令,通过这些命令,可以对开发板进行调试,可以引导
Linux
内核,还可以擦写
Flash
完成系统部署等功能。掌握这些命令的使用,才能够顺利地进行嵌入式系统的开发。
输入
help
命令,可以得到当前
U-Boot
的所有命令列表。每一条命令后面是简单的命令说明。
=> 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'
bootm - boot application image from memory
bootp - boot image via network using BootP/TFTP protocol
cmp - memory compare
coninfo - print console devices and information
cp - memory copy
crc32 - checksum calculation
dhcp - invoke DHCP client to obtain IP/boot params
echo - echo args to console
erase - erase FLASH memory
flinfo - print FLASH memory information
go - start application at address 'addr'
help - print online help
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
loop - infinite loop on address range
md - memory display
mm - memory modify (auto-incrementing)
mtest - simple RAM test
mw - memory write (fill)
nfs - boot image via network using NFS protocol
nm - memory modify (constant address)
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
tftpboot - boot image via network using TFTP protocol
version - print monitor version
=>
U-Boot
还提供了更加详细的命令帮助,通过
help
命令还可以查看每个命令的参数说明。由于开发过程的需要,有必要先把
U-Boot
命令的用法弄清楚。接下来,根据每一条命令的帮助信息,解释一下这些命令的功能和参数。
=> help bootm
bootm [addr [arg ...]]
- boot application image stored in memory
passing arguments 'arg ...'; when booting a Linux kernel,
'arg' can be the address of an initrd image
bootm
命令可以引导启动存储在内存中的程序映像。这些内存包括
RAM
和可以永久保存的
Flash
。
第
1
个参数
addr
是程序映像的地址,这个程序映像必须转换成
U-Boot
的格式。
第
2
个参数对于引导
Linux
内核有用,通常作为
U-Boot
格式的
RAMDISK
映像存储地址;也可以是传递给
Linux
内核的参数(缺省情况下传递
bootargs
环境变量给内核)。
=> help bootp
bootp [loadAddress] [bootfilename]
bootp
命令通过
bootp
请求,要求
DHCP
服务器分配
IP
地址,然后通过
TFTP
协议下载指定的文件到内存。
第
1
个参数是下载文件存放的内存地址。
第
2
个参数是要下载的文件名称,这个文件应该在开发主机上准备好。
=> help cmp
cmp [.b, .w, .l] addr1 addr2 count
- compare memory
cmp
命令可以比较
2
块内存中的内容。
.b
以字节为单位;
.w
以字为单位;
.l
以长字为单位。注意:
cmp.b
中间不能保留空格,需要连续敲入命令。
第
1
个参数
addr1
是第一块内存的起始地址。
第
2
个参数
addr2
是第二块内存的起始地址。
第
3
个参数
count
是要比较的数目,单位按照字节、字或者长字。
=> help cp
cp [.b, .w, .l] source target count
- copy memory
cp
命令可以在内存中复制数据块,包括对
Flash
的读写操作。
第
1
个参数
source
是要复制的数据块起始地址。
第
2
个参数
target
是数据块要复制到的地址。这个地址如果在
Flash
中,那么会直接调用写
Flash
的函数操作。所以
U-Boot
写
Flash
就使用这个命令,当然需要先把对应
Flash
区域擦干净。
第
3
个参数
count
是要复制的数目,根据
cp.b cp.w cp.l
分别以字节、字、长字为单位。
=> help crc32
crc32 address count [addr]
- compute CRC32 checksum [save at addr]
crc32
命令可以计算存储数据的校验和。
第
1
个参数
address
是需要校验的数据起始地址。
第
2
个参数
count
是要校验的数据字节数。
第
3
个参数
addr
用来指定保存结果的地址。
=> help echo
echo [args..]
- echo args to console; \c suppresses newline
echo
命令回显参数。
=> help erase
erase start end
- erase FLASH from addr 'start' to addr 'end'
erase N:SF[-SL]
- erase sectors SF-SL in FLASH bank # N
erase bank N
- erase FLASH bank # N
erase all
- erase all FLASH banks
erase
命令可以擦
Flash
。
参数必须指定
Flash
擦除的范围。
按照起始地址和结束地址,
start
必须是擦除块的起始地址;
end
必须是擦除末尾块的结束地址。这种方式最常用。举例说明:擦除
0x20000 – 0x3ffff
区域命令为
erase 20000 3ffff
。
按照组和扇区,
N
表示
Flash
的组号,
SF
表示擦除起始扇区号,
SL
表示擦除结束扇区号。另外,还可以擦除整个组,擦除组号为
N
的整个
Flash
组。擦除全部
Flash
只要给出一个
all
的参数即可。
=> help flinfo
flinfo
- print information for all FLASH memory banks
flinfo N
- print information for FLASH memory bank # N
flinfo
命令打印全部
Flash
组的信息,也可以只打印其中某个组。一般嵌入式系统的
Flash
只有一个组。
=> help go
go addr [arg ...]
- start application at address 'addr'
passing 'arg' as arguments
go
命令可以执行应用程序。
第
1
个参数是要执行程序的入口地址。
第
2
个可选参数是传递给程序的参数,可以不用。
=> help iminfo
iminfo addr [addr ...]
- print header information for application image starting at
address 'addr' in memory; this includes verification of the
image contents (magic number, header and payload checksums)
iminfo
可以打印程序映像的开头信息,包含了映像内容的校验(序列号、头和校验和)。
第
1
个参数指定映像的起始地址。
可选的参数是指定更多的映像地址。
=> help loadb
loadb [ off ] [ baud ]
- load binary file over serial line with offset 'off' and baudrate 'baud'
loadb
命令可以通过串口线下载二进制格式文件。
=> help loads
loads [ off ]
- load S-Record file over serial line with offset 'off'
loads
命令可以通过串口线下载
S-Record
格式文件。
=> help mw
mw [.b, .w, .l] address value [count]
- write memory
mw
命令可以按照字节、字、长字写内存,
.b .w .l
的用法与
cp
命令相同。
第
1
个参数
address
是要写的内存地址。
第
2
个参数
value
是要写的值。
第
3
个可选参数
count
是要写单位值的数目。
=> help nfs
nfs [loadAddress] [host ip addr:bootfilename]
nfs
命令可以使用
NFS
网络协议通过网络启动映像。
=> help nm
nm [.b, .w, .l] address
- memory modify, read and keep address
nm
命令可以修改内存,可以按照字节、字、长字操作。
参数
address
是要读出并且修改的内存地址。
=> help printenv
printenv
- print values of all environment variables
printenv name ...
- print value of environment variable 'name'
printenv
命令打印环境变量。
可以打印全部环境变量,也可以只打印参数中列出的环境变量。
=> help protect
protect on start end
- protect Flash from addr 'start' to addr 'end'
protect on N:SF[-SL]
- protect sectors SF-SL in Flash bank # N
protect on bank N
- protect Flash bank # N
protect on all
- protect all Flash banks
protect off start end
- make Flash from addr 'start' to addr 'end' writable
protect off N:SF[-SL]
- make sectors SF-SL writable in Flash bank # N
protect off bank N
- make Flash bank # N writable
protect off all
- make all Flash banks writable
protect
命令是对
Flash
写保护的操作,可以使能和解除写保护。
第
1
个参数
on
代表使能写保护;
off
代表解除写保护。
第
2
、
3
参数是指定
Flash
写保护操作范围,跟擦除的方式相同。
=> help rarpboot
rarpboot [loadAddress] [bootfilename]
rarboot
命令可以使用
TFTP
协议通过网络启动映像。也就是把指定的文件下载到指定地址,然后执行。
第
1
个参数是映像文件下载到的内存地址。
第
2
个参数是要下载执行的映像文件。
=> help run
run var [...]
- run the commands in the environment variable(s) 'var'
run
命令可以执行环境变量中的命令,后面参数可以跟几个环境变量名。
=> help setenv
setenv name value ...
- set environment variable 'name' to 'value ...'
setenv name
- delete environment variable 'name'
setenv
命令可以设置环境变量。
第
1
个参数是环境变量的名称。
第
2
个参数是要设置的值,如果没有第
2
个参数,表示删除这个环境变量。
=> help sleep
sleep N
- delay execution for N seconds (N is _decimal_ !!!)
sleep
命令可以延迟
N
秒钟执行,
N
为十进制数。
=> help tftpboot
tftpboot [loadAddress] [bootfilename]
tftpboot
命令可以使用
TFTP
协议通过网络下载文件。按照二进制文件格式下载。另外使用这个命令,必须配置好相关的环境变量。例如
serverip
和
ipaddr
。
第
1
个参数
loadAddress
是下载到的内存地址。
第
2
个参数是要下载的文件名称,必须放在
TFTP
服务器相应的目录下。
这些
U-Boot
命令为嵌入式系统提供了丰富的开发和调试功能。在
Linux
内核启动和调试过程中,都可以用到
U-Boot
的命令。但是一般情况下,不需要使用全部命令。比如已经支持以太网接口,可以通过
tftpboot
命令来下载文件,那么还有必要使用串口下载的
loadb
吗?反过来,如果开发板需要特殊的调试功能,也可以添加新的命令。
在建立交叉开发环境和调试
Linux
内核等章节时,在
ARM
平台上移植了
U-Boot
,并且提供了具体
U-Boot
的操作步骤。
6.4.3
U-Boot
的环境变量
有点类似
Shell
,
U-Boot
也使用环境变量。可以通过
printenv
命令查看环境变量的设置。
U-Boot> printenv
bootdelay=3
baudrate=115200
netmask=255.255.0.0
ethaddr=12:34:56:78:90:ab
bootfile=uImage
bootargs=console=ttyS0,115200 root=/dev/ram rw initrd=0x30800000,8M
bootcmd=tftp 0x30008000 zImage;go 0x30008000
serverip=192.168.1.1
ipaddr=192.168.1.100
stdin=serial
stdout=serial
stderr=serial
Environment size: 337/131068 bytes
U-Boot>
表
6.5
是常用环境变量的含义解释。通过
printenv
命令可以打印出这些变量的值。
表
6.5 U-Boot
环境变量的解释说明
环
境
变
量
|
解
释
说
明
|
bootdelay
|
定义执行自动启动的等候秒数
|
baudrate
|
定义串口控制台的波特率
|
netmask
|
定义以太网接口的掩码
|
ethaddr
|
定义以太网接口的
MAC
地址
|
bootfile
|
定义缺省的下载文件
|
bootargs
|
定义传递给
Linux
内核的命令行参数
|
bootcmd
|
定义自动启动时执行的几条命令
|
serverip
|
定义
tftp
服务器端的
IP
地址
|
ipaddr
|
定义本地的
IP
地址
|
stdin
|
定义标准输入设备,一般是串口
|
stdout
|
定义标准输出设备,一般是串口
|
stderr
|
定义标准出错信息输出设备,一般是串口
|
U-Boot
的环境变量都可以有缺省值,也可以修改并且保存在参数区。
U-Boot
的参数区一般有
EEPROM
和
Flash
两种设备。
环境变量的设置命令为
setenv
,在
6.2.2
节有命令的解释。
举例说明环境变量的使用。
=>setenv serverip 192.168.1.1
=>setenv ipaddr 192.168.1.100
=>setenv rootpath
"
/usr/local/arm/3.3.2/rootfs
"
=>setenv bootargs
"
root=/dev/nfs rw nfsroot=\$(serverip):\$(rootpath) ip=
\$(ipaddr)
"
=>setenv kernel_addr 30000000
=>setenv nfscmd
"
tftp \$(kernel_addr) uImage; bootm \$(kernel_addr)
"
=>run nfscmd
上面定义的环境变量有
serverip ipaddr rootpath bootargs kernel_addr
。环境变量
bootargs
中还使用了环境变量,
bootargs
定义命令行参数,通过
bootm
命令传递给内核。环境变量
nfscmd
中也使用了环境变量,功能是把
uImage
下载到指定的地址并且引导起来。可以通过
run
命令执行
nfscmd
脚本。