写在前头
*.版权声明:本篇文章为原创,可随意转载,转载请注明出处,谢谢!另我创建一个QQ群82642304,欢迎加入!
*.目的:整理一下RIotBoard开发板的启动流程,对自己的所学做一个整理总结,本系列Uboot代码基于2009.08版。
*.备注:整个系列只是对我所学进行总结,记录我认为是关键的点,另我能力有限,难免出现疏漏错误,如果读者有发现请多指正,以免我误导他人!
原理
在《IMX6Solo启动流程-从Uboot到kernel 上》中我们讲到了Uboot命令是通过宏U_BOOT_CMD来定义的.所以新建一条命令实际上就是定义一个宏的过程.
在新建一条命令之前,我们先要区分一下新建的命令是属于通用的还是专用的,所谓通用就是你的命令与具体的硬件无关,例如tftp命令,bootm命令等,专用的就是你的命令只能在你的硬件上面跑.
Uboot的命令一般在两个地方定义,一个是common目录下,在这里面定义的一般都是通用的命令,另外一个就是在board/xxx目录下,定义的就是专用的,当然这不是强制要求,只是一个建议,这样定义命令能够让逻辑分开.
我在每次更新Uboot或者内核的时候都要执行
tftp u-boot.bin
mmc dev 3
mmc erase 0 400
mmc write ${loadaddr} 0 400
对于我这种懒人来说,复制粘帖这几行都是一件繁琐的事情.所以就想新建一条命令,能够一条命令执行以上四个步骤.
在RIotBoard开发板上,存储设备是emmc,即port 3,下载烧写地址和长度都是固定的,所以这是一条专用的命令.当然你也可以将这些数据以参数的形式传给命令,将命令拓展成通用的.其实都一样.
首先在board/freescale/mx6q_riot/目录下新建cmd_update.c,定义命令
U_BOOT_CMD(
uduboot,
2,
1,
do_uduboot,
"download uboot from tftp,then update it",
"uduboot [uboot file name]"
);
U_BOOT_CMD(
udkernel,
2,
1,
do_udkernel,
"download kernel from tftp,then update it",
"udkernel [kernel file name]"
);
然后实现函数do_uduboot和do_uduboot.接下来修改board/freescale/mx6q_riot/Makefile,将cmd_update.c编译进去.
COBJS := $(BOARD).o cmd_update.o
在COBJS中增加 cmd_update.o
重新编译Uboot并下载烧写,在Uboot的命令交互模式下就可以看到新加两条命令uduboot和udkernel.
另:使用tftp需要配置Uboot的环境变量ipaddr和serverip,每次烧写Uboot,之前配置的环境变量值都会被恢复默认值,所以可以通过修改ipaddr和serverip的默认值,后续烧写Uboot就无需修改IP地址.
源码
#include <common.h>
#include <command.h>
#include <asm/io.h>
#include <mmc.h>
#include <net.h>
#include <malloc.h>
#define UBOOT_FILE_OFFSET 1024
#define MMC_PAGE_SIZE 512
extern ulong TftpRRQTimeoutMSecs;
extern int TftpRRQTimeoutCountMax;
extern ulong load_addr;
extern enum boot_device get_boot_device(void);
extern int get_mmc_env_devno(void);
/* follow function copy from $(TOPDIR)/common/update.c */
static int update_load(char *filename, ulong msec_max, int cnt_max, ulong addr)
{
int size, rv;
ulong saved_timeout_msecs;
int saved_timeout_count;
char *saved_netretry, *saved_bootfile;
rv = 0;
/* save used globals and env variable */
saved_timeout_msecs = TftpRRQTimeoutMSecs;
saved_timeout_count = TftpRRQTimeoutCountMax;
saved_netretry = strdup(getenv("netretry"));
saved_bootfile = strdup(BootFile);
/* set timeouts for auto-update */
TftpRRQTimeoutMSecs = msec_max;
TftpRRQTimeoutCountMax = cnt_max;
/* we don't want to retry the connection if errors occur */
setenv("netretry", "no");
/* download the update file */
load_addr = addr;
copy_filename(BootFile, filename, sizeof(BootFile));
size = NetLoop(TFTP);
if (size < 0)
rv = 1;
else if (size > 0)
flush_cache(addr, size);
/* restore changed globals and env variable */
TftpRRQTimeoutMSecs = saved_timeout_msecs;
TftpRRQTimeoutCountMax = saved_timeout_count;
setenv("netretry", saved_netretry);
if (saved_netretry != NULL)
free(saved_netretry);
if (saved_bootfile != NULL) {
copy_filename(BootFile, saved_bootfile, sizeof(BootFile));
free(saved_bootfile);
}
return rv;
}
int DownloadAndUpdate(char * filename,unsigned int maxsize,
unsigned int startblk,unsigned int fileoffset){
char * serverIP = getenv("serverip");
struct mmc * mmc = NULL;
block_dev_desc_t * mmc_dev = NULL;
char * s;
ulong addr;
uint boot_devno;
unsigned int filesize;
unsigned int block;
if (NULL == serverIP) {
printf("error: not found tftp server IP\n");
return -1;
}
/* get load address of downloaded update file */
if ((s = getenv("loadaddr")) != NULL){
addr = simple_strtoul(s, NULL, 16);
}else{
printf("error: not found $(loadaddr)\n");
return -1;
}
if (MMC_BOOT != get_boot_device()) {
printf("error: only support MMC boot dev\n");
return -1;
}
printf("download %s from %s\n",filename,serverIP);
if(update_load(filename,100,10,addr)){
printf("error: download failure\n");
return -1;
}else if (NULL == (s = getenv("filesize"))) {
printf("error: download failure,not found $(filesize)\n");
return -1;
}else{
filesize = simple_strtol(s,NULL,16);
if (filesize < maxsize) {
printf("download success,file length %d(0x%x)\n", filesize, filesize);
}else{
printf("error: download failure,$(filesize) bigger then 1M\n");
return -1;
}
}
block = (filesize + MMC_PAGE_SIZE - 1)/MMC_PAGE_SIZE;
boot_devno = readl(SRC_BASE_ADDR + 0x4);
/* BOOT_CFG2[3] and BOOT_CFG2[4] */
boot_devno = (boot_devno & 0x00001800) >> 11;
if (boot_devno >= CONFIG_SYS_FSL_USDHC_NUM) {
printf("error: not such mmc dev %d\n",boot_devno);
return -1;
}else{
printf("boot dev is MMC%d\n",boot_devno);
}
if(NULL == (mmc = find_mmc_device(boot_devno))){
printf("Can't find mmc devno %d\n",boot_devno);
return -1;
}
mmc_init(mmc);
if (NULL == (mmc_dev = mmc_get_dev(boot_devno))) {
printf("mmc init failure %d\n",boot_devno);
return -1;
}
if (block != mmc_dev->block_erase(boot_devno,startblk,block)) {
printf("error: erase %d,%d failure\n",startblk,block);
return -1;
}else{
printf("erase %d,%d\n",startblk,block);
}
if (block != mmc_dev->block_write(boot_devno,startblk,block,(void *)(addr + fileoffset))) {
printf("error: write %d,%d failure\n",startblk,block);
return -1;
}else{
printf("write %d,%d,0x%x\n",startblk,block,addr + fileoffset);
}
printf("update %s success\n",filename);
return 0;
}
int do_uduboot ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){
char * filename = "u-boot.bin";
if (argc > 1) {
filename = argv[1];
}
return DownloadAndUpdate(filename,0x100000,UBOOT_FILE_OFFSET/MMC_PAGE_SIZE,UBOOT_FILE_OFFSET);
};
int do_udkernel ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){
char * filename = "uImage";
if (argc > 1) {
filename = argv[1];
}
return DownloadAndUpdate(filename,0x800000,0x800,0);
};
U_BOOT_CMD(
uduboot,
2,
1,
do_uduboot,
"download uboot from tftp,then update it",
"uduboot [uboot file name]"
);
U_BOOT_CMD(
udkernel,
2,
1,
do_udkernel,
"download kernel from tftp,then update it",
"udkernel [kernel file name]"
);
总结
只要了解U_BOOT_CMD宏的原理之后,新建一条命令就十分简单。
另:上述的功能也可以通过将多条命令用分号隔开,保存到一个环境变量中,然后通过run命令来执行,达到一样的效果。类似bootcmd的做法,这里就不再赘述.
参考
暂无