linux开发环境搭建(5)-使用tftp下载uboot
来源:互联网 发布:智能大厦网络设计方案 编辑:程序博客网 时间:2024/05/16 03:25
前面已经移植好了uboot可以从sd卡启动了,也可以通过tftp下载代码到机器内存上了。有人会说这不是很简单吗,通过tftp下载uboot到内存的某一个位置,然后用nand write命令写入到nand flash中,这样拨码开关拨到nand flash启动的时候uboot就能跑起来了。一开始我也是这么想的,后来发现并没有想的这么简单。
1、尝试验证
因为之前编译出来的uboot是从sd卡启动的,所以我们还需要编译一个从nand flash启动的uboot。
进入到uboot目录,执行
#make tiny6410_config#make ARCH=arm CROSS_COMPILE=arm-linux-
编译好后将uboot.bin放到tftp下载目录,然后开发板拨码开关拨到sd卡启动,这是会出现之前移植好的uboot菜单,选择q退回原始shell命令行,然后输入tftp c000000 u-boot.bin,将编译好的从nand flash启动的uboot下载到内存c0008000的位置。
#tftp c000000 u-boot.bin
然后执行下面命令,表示把内存地址为0xc000000位置处开始,大小为0x4000大小的数据,写入到nand flash的0地址处,uboot默认放在uboot的0地址开始的位置。
nand write c0000000 0 40000
烧写完后,将开关拨到nand flash启动,重新上电,发现串口没有输出。奇怪,不是已经把uboot下载到内存,然后写入到nand flash里面去了吗?怎么起不来了。
2、猜测分析
第一种猜测是编译生成的从nand flash启动的uboot.bin代码有问题,然后尝试使用dnw通过usb下载uboot到nand flash,烧写完后重新上电发现串口有打印,uboot也能正常跑起来,说明编译生成的uboot是没问题的。
第二种猜测就是tftp下载方式有问题,因为使用dnw下载的代码可以跑起来,使用tftp下载的代码却跑步起来。然后开始分析dnw下载uboot的代码。首先找到uboot源码里面的下载菜单的位置,在/uboot/common/main.c中有一个FriendlyARMMenu函数,这个就是uboot起来后打印出来的菜单。
void FriendlyARMMenu(void){ while(1) { int c; printf("##### FriendlyARM U-Boot(" RELEASE_MARK ", " BOOT_MEDIA ") for 6410 #####\n"); printf("[f] Format the nand flash\n"); printf("[v] Download u-boot.bin\n"); printf("[k] Download Linux/Android kernel\n"); printf("[y] Download root yaffs2 image\n"); printf("[u] Download root ubifs image\n"); printf("[a] Download Absolute User Application\n"); printf("[n] Download Nboot.nb0 for WinCE\n"); printf("[w] Download WinCE NK.nb0\n"); printf("[s] Set the boot parameter of Linux\n"); printf("[b] Boot Linux\n"); printf("[q] Quit to shell\n"); printf("NAND(%s): %u MiB, RAM: %u MiB\n", NandIsMlc2() ? "MLC2" : (NandIsMlc1()? "MLC1" : "SLC"), FriendlyARMGetNandSizeInMB(), PHYS_SDRAM_1_SIZE >> 20); if (Lcd != 0) { printf("LCD type, firmware version: %u %u\n", Lcd, FirmwareVer); } printf("Enter your Selection:"); c = getc(); printf("%c\n", c >= ' ' && c <= 127 ? c : ' '); switch(c) { unsigned max_size, pos, len; case 'F': case 'f': FriendlyARMFormatFrom(0, 1); break; case 'V': case 'v': pos = 0; max_size = 256 K; len = 256 K; FriendlyARMGetDataFromUsbAndWriteNand(max_size, pos, len, "U-Boot.bin"); SetLinuxCommandLine(NULL); break; case 'K': case 'k': if (NandIsMlc()) { pos = 4 M; max_size = 5 M - 128 K; len = 8 M; NAND_EraseBlock(1 M / NandBlockSizeInByte); } else { pos = 4 * 128 K; max_size = 5 M - 128 K; len = 5 M; } FriendlyARMGetDataFromUsbAndWriteNand(max_size, pos, len, "Linux/Android Kernel"); break; case 'Y': case 'y': if (NandIsMlc()) { printf("Yaffs is not support yet for MLC2 NAND\n"); } else { FriendlyARMGetDataFromUsbAndWriteNand(126 M, 5 M + 4 * 128 K, (unsigned)-1, "yaffs2-image"); SetLinuxCommandLine("root=/dev/mtdblock2 console=ttySAC0,115200"); } break; case 'U': case 'u': max_size = 126 M; len = (unsigned) -2; if (NandIsMlc()) { pos = 12 M; } else { pos = 5 M + 4 * 128 K; } FriendlyARMGetDataFromUsbAndWriteNand(max_size, pos, len, "ubifs-image"); SetLinuxCommandLine("init=/linuxrc rootfstype=ubifs root=ubi0:FriendlyARM-root ubi.mtd=2 console=ttySAC0,115200"); break; case 'A': case 'a': FriendlyARMGetDataFromUsbAndWriteNand(64 M, 0, 128 M, "User-Bin"); break; case 'N': case 'n': FriendlyARMGetDataFromUsbAndWriteNand(128 K, 0, 128 K, "nboot.nb0"); break; case 'W': case 'w': if (NandIsMlc()) { max_size = 63 M; pos = 8 M; len = 72 M; FriendlyARMFormatFrom( pos / NandBlockSizeInByte, 0); } else { max_size = 63 M; pos = 2 M + 4 * 128 K; len = 64 M; ExecuteCmd("nand erase 4280000"); } FriendlyARMGetDataFromUsbAndWriteNand(max_size, pos, len, "NK.nb0"); // Mark the indicators of NK Magic Number and Image Size { unsigned char *p = (unsigned char *)0xC0000000; memset(p, 0, 128 K); ((unsigned *)p)[0] = 0xCEFA4146U; ((unsigned *)p)[1] = 63 M; if (NandIsMlc()) { pos = 1 M; len = 2 M; } else { pos = 2 * NandBlockSizeInByte; len = 1 * NandBlockSizeInByte; } FriendlyARMWriteNand(p, 128 K, pos, len); } break; case 'S': case 's': { int r; r = readline("Linux cmd line: "); if (r > 0 && r < 1000) { SetLinuxCommandLine(console_buffer); } else { printf("Linux command line not changed\n"); } } break; case 'B': case 'b': if (NandIsMlc()) { ExecuteCmd("nand read.i c0008000 400000 500000;bootm c0008000"); } else { ExecuteCmd(CONFIG_BOOTCOMMAND); } while(1); case 'Q': case 'q': if (NandIsMlc()) { //printf("Caution: any nand write command may damage your data. DON'T use them\n"); } return; default: ; } }}
其中case ‘V’: case ‘v’:为下载uboot.bin,可以看到下载的位置为0地址处,下载的长度为0x4000也就是256K。继续跟进到FriendlyARMGetDataFromUsbAndWriteNand这个函数,这个函数是从usb接受数据,并且写入到nand flash中。
int FriendlyARMGetDataFromUsbAndWriteNand(unsigned max_len, unsigned offset, unsigned MaxNandSize, const char *Name){ int ret; unsigned char *RevPtr; unsigned RevLen; printf("Downloading %s from USB...\n", Name); ret = FriendlyARMGetDataFromUSB(max_len, &RevPtr, &RevLen); printf("Downloading %s %s\n", Name, ret >= 0 ? "successed" : "failed"); if (ret < 0) { return ret; } ret = FriendlyARMCheckData(RevPtr, RevLen, offset, MaxNandSize); if (ret < 0) { return ret; } printf("Writing %s into NAND...\n", Name); if (NandIsMlc() && offset == 0) { ret = FriendlyARMWriteNandMlcBoot(RevPtr, RevLen, 1); printf("FriendlyARMWriteNandMlcBoot\n"); } else { ret = FriendlyARMWriteNand(RevPtr, RevLen, offset, MaxNandSize); printf("FriendlyARMWriteNand\n"); } printf("Writing %s %s\n", Name, ret >= 0 ? "successed" : "failed"); return ret;}
函数一开始先调用FriendlyARMGetDataFromUSB函数接收usb数据,这个函数需要三个参数,一个是数据的最大长度,一个是数据在内存中存放的位置的指针,最后一个是实际接收到的数据长度。
int FriendlyARMGetDataFromUSB (unsigned max_len, unsigned char **data_ptr, unsigned *received_len){ s3c_usbd_dn_addr = USBD_DOWN_ADDR; /* Default Address */ s3c_receive_done = 0; //memset( (unsigned char *)USBD_DOWN_ADDR, 0xFF, max_len); s3c_usbctl_init(); s3c_usbc_activate(); printf("Download address 0x%08x\n", s3c_usbd_dn_addr); while (1) { if (S3C_USBD_DETECT_IRQ()) { s3c_udc_int_hndlr(); S3C_USBD_CLEAR_IRQ(); } if (s3c_receive_done) break; } s3c_usb_stop(); *received_len = UsbDownloadFileSize; *data_ptr = (unsigned char *)USBD_DOWN_ADDR; printf("received_len :0x%x\n", UsbDownloadFileSize); if (UsbDownloadFileSize > max_len) { printf("DNW download Data size is too big\n"); return -1; } if (!UsbDownloadChecksumOK) { printf("DNW download Data Error\n"); return -2; } { //padlen为从USBD_DOWN_ADDR + UsbDownloadFileSize到USBD_DOWN_ADD+256k剩余的内存,剩余内存没有数据全部填0xFF //当UsbDownloadFileSize大于128时,除以128k取余数,得到的是比128k多出来的,再用128k去减多出来的,得到剩余的 //当UsbDownloadFileSize小于128时,除以128取余数,得到的是比128k少的,再用128k减去少的,得到剩余的 int PadLen = 128 * 1024 - UsbDownloadFileSize % (128 * 1024); PadLen %= 128 * 1024; if (PadLen != 0 && PadLen + UsbDownloadFileSize <= max_len) { printf("memset 0xFF addr:0x%x, size:0x%x\n", USBD_DOWN_ADDR + UsbDownloadFileSize, PadLen); memset((unsigned char *)USBD_DOWN_ADDR + UsbDownloadFileSize, 0xFF, PadLen); } } return 0;}
函数先确定usb接收的数据要存放在内存中的位置,这个位置为
#define USBD_DOWN_ADDR 0xc0000000
也就是之前使用tftp c0000000 uboot.bin命令里的那个位置,可以看到使用dnw下载和使用tftp下载数据在内存中的位置是对的上的。
然后是初始化usb并且循环接收数据,接收到的数据放到了USBD_DOWN_ADDR这个地址上面,接收到的数据长度为UsbDownloadFileSize。接收完数据后进行检验,并且在最后做了一步我们在使用tftp下载时候没有做的操作。
dnw下载的代码里面,在把uboot下载到内存的0xc0000000位置后,会计算实际数据大小和最大数据长度256K之间还剩有多少内存没有填充数据的,然后把这部分剩余的内存全部用0xFF填充。所以这里猜测是否是我们使用tftp下载的时候,剩余的内存空间没有填充0xff,因为我们把内存中的数据写到nand flash里面的时候一次写256K,实际的长度并没有256K,这剩余的部分里面如果不填充0xff的话,可能会有一些碎片。
3、填充剩余内存数据为0xff
uboot里面操作内存的话,提供了mw命令对内存进行写操作
mw [.b, .w, .l] address value [count]
先把从0xc0000000开始大小为0x4000的内存全部写0xff
然后可以使用md命令读一下内存地址的值看是不是0xff,可以看到已经全部变成了0xff了。
然后再使用之前的命令下载uboot,这个时候剩余的内存应该已经全部填充为0xff了,所以写入到nand flash里面的也是按照这样的格式填充的了。
tftp c0000000 u-boot.binnand write c0000000 0 40000
如果猜想没错,这个时候uboot应该能跑起来了,然而uboot还是没有起来,看来不是这个原因。
4、nand write命令猜测
既然使用usb下载和用tftp下载的数据,下载的到内存的地址一样,在内存中的填充格式也一样,那还有一种可能就是将数据写入nand flash的时候,nand write命令写入的时候出了问题。
先分析看使用dnw的时候是怎么写数据到,可以看到写数据进入nand flash的时候先进行了判断,如果偏移offset为0,说明写入的数据是uboot,则调用FriendlyARMWriteNandMlcBoot。如果不为0,说明写入的可能是kernel或者filesystem,则调用FriendlyARMWriteNand来写。
if (NandIsMlc() && offset == 0) { ret = FriendlyARMWriteNandMlcBoot(RevPtr, RevLen, 1); printf("FriendlyARMWriteNandMlcBoot\n"); } else { ret = FriendlyARMWriteNand(RevPtr, RevLen, offset, MaxNandSize); printf("FriendlyARMWriteNand\n"); }
因为我们下载的是uboot,所以偏移offset为0,则调用FriendlyARMWriteNandMlcBoot来写,具体是怎么写的呢,遗憾的是友善并没有公开这个函数具体是如何把uboot的数据写入到nand flash中的,搜索FriendlyARMWriteNandMlcBoot,可看到这个函数是在libcommon.a这个库里面的。
没有源码没关系,我们虽然不知道他是怎么写的,但是我们可以调用他这个函数啊。我们在使用nand write命令的时候加一个判断,判断当前写的是不是uboot,如果是就调用FriendlyARMWriteNandMlcBoot这个函数来写,否则的话就按照正常的nand write流程来写。
5、修改nand write命令
打开uboot/common/cmd_nand.c文件,找到do_nand函数,这个函数就是输入nand xx的时候会调用到的函数。这个函数很长,都是在判断是nand 命令的具体类型,然后执行相应的代码,我们这就看write时候的代码
/* read write */ if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { int read; if (argc < 4) goto usage; addr = (ulong)simple_strtoul(argv[2], NULL, 16); read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ printf("\nNAND %s: ", read ? "read" : "write"); if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) return 1; s = strchr(cmd, '.'); if (s != NULL && (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) { printf("nand read/write step1\n"); if (read) { ret = FriendlyARMReadNand( (u_char*)addr, size, off); if (ret == -1) { puts("offset should be multiple of page size\n"); } } else { /* write */ nand_write_options_t opts; memset(&opts, 0, sizeof(opts)); opts.buffer = (u_char*) addr; opts.length = size; opts.offset = off; /* opts.forcejffs2 = 1; */ opts.pad = 1; opts.blockalign = 1; opts.quiet = quiet; if (NandIsMlc()) { ret = -1; puts("write.jffs2/write.e/write.i is not supported\n"); } else ret = nand_write_opts(nand, &opts); }#ifdef CFG_NAND_YAFFS_WRITE } else if (!read && s != NULL && + (!strcmp(s, ".yaffs") || !strcmp(s, ".yaffs1"))) { printf("nand read/write step2\n"); nand_write_options_t opts; memset(&opts, 0, sizeof(opts)); opts.buffer = (u_char*) addr; opts.length = size; opts.offset = off; opts.pad = 0; opts.blockalign = 1; opts.quiet = quiet; opts.writeoob = 1; opts.autoplace = 1; /* jsgood */ /* if (s[6] == '1') opts.forceyaffs = 1; */ ret = nand_write_opts(nand, &opts);#endif } else { printf("nand read/write step3\n"); if (read) { if (!NandIsMlc()) { ret = nand_read(nand, off, &size, (u_char *)addr); } else { ret = FriendlyARMReadNand( (u_char*)addr, size, off); if (ret == -1) { puts("offset should be multiple of page size\n"); } } } else { if (NandIsMlc()) { if (off % NandBlockSizeInByte != 0) { puts("offset should be multiple of block size\n"); ret = -1; } else { unsigned int i; ret = 0; //add by CHB,if write uboot.bin,use FriendlyARMWriteNandMlcBoot if(off == 0){ printf("FriendlyARMWriteNandMlcBoot\n"); ret = FriendlyARMWriteNandMlcBoot((u_char *)addr, size, 1); }else{ for (i = 0; i < size; i += NandBlockSizeInByte) { int len = size - i; if (len > NandBlockSizeInByte) { len = NandBlockSizeInByte; } ret = FriendlyARMWriteNand(((u_char *)addr) + i, len, off + i, NandBlockSizeInByte); } } } } else { ret = nand_write(nand, off, &size, (u_char *)addr); if (ret == 0) { uint *magic = (uint*)(PHYS_SDRAM_1); if ((0x24564236 == magic[0]) && (0x20764316 == magic[1])) magic[0] = 0x27051956; } } } } printf(" %d bytes %s: %s\n", size, read ? "read" : "written", ret ? "ERROR" : "OK"); return ret == 0 ? 0 : 1; }
前面一些if主要是判断write的类型,比如write.yaffs2,write.i ,write.e等,我们这边只有一个write,后面没有加.参数,所以可以忽略前面的代码,直接看最后的写部分。
if (NandIsMlc()) { if (off % NandBlockSizeInByte != 0) { puts("offset should be multiple of block size\n"); ret = -1; } else { unsigned int i; ret = 0; //add by CHB,if write uboot.bin,use FriendlyARMWriteNandMlcBoot if(off == 0){ printf("FriendlyARMWriteNandMlcBoot\n"); ret = FriendlyARMWriteNandMlcBoot((u_char *)addr, size, 1); }else{ for (i = 0; i < size; i += NandBlockSizeInByte) { int len = size - i; if (len > NandBlockSizeInByte) { len = NandBlockSizeInByte; } ret = FriendlyARMWriteNand(((u_char *)addr) + i, len, off + i, NandBlockSizeInByte); } } } } else { ret = nand_write(nand, off, &size, (u_char *)addr); if (ret == 0) { uint *magic = (uint*)(PHYS_SDRAM_1); if ((0x24564236 == magic[0]) && (0x20764316 == magic[1])) magic[0] = 0x27051956; } }
最后写部分先判断nand flash的类型,我使用的nand flash的类型为MLC,所以执行if语句为真。然后添加了对偏移off进行判断,如果off为0,说明要写的数据是uboot,则调用FriendlyARMWriteNandMlcBoot函数写uboot,否则的话就按照正常的写操作。
加入这一段修改后,需要先把uboot编译成从sd卡启动的,然后再更新sd卡上面的uboot,这样子修改才能生效。把修改完的uboot烧到sd卡里面去以后,从sd卡启动,再次通过tftp命令下载uboot.bin。然后使用nand write c0000000 0 4000把uboot写到nand flash里面去,因为这里的偏移为0,所以会调用FriendlyARMWriteNandMlcBoot这个函数来写。写完以后将开关拨到nand flash启动,可以看到这次串口有log打印出来了,这次uboot是正常跑起来了。从##### FriendlyARM U-Boot(2011-10, NAND) for 6410 #####可以知道是从nand flash启动的
之前的都是##### FriendlyARM U-Boot(2011-10, SD) for 6410 #####。
然后测试下载kernel看能否启动
先将kernel下载到内存,然后再写入到nand flash 0x400000的位置处,大小为0x500000。然后重启,选择b会从nand flash的0x400000位置处读取大小为0x500000大小的内核到内存,然后启动内核,可以看到kernel也能正常跑起来,说明下载的数据是正确的。
6、总结
到这里使用tftp下载程序的功能也移植完了,虽然最后还是不知道FriendlyARMWriteNandMlcBoot这个函数是怎么把uboot写到nand flash里面去的,以后有时间会去研究。但是现在大体上的功能已经可以使用了,uboot可以从sd卡启动,也可以从nand flash启动,可以使用uboot通过tftp下载程序,可以使用nfs挂载根文件系统,也使用busybox制作了一个根文件系统。所有这一切看似都完成了,但是实质上uboot和kernel的代码都是友善提供好的,并不是我们自己移植的,所以我只是把这个移植好的代码当成一个参考对象和工具来使用,在之后的文章会介绍如何自己来移植我们需要的uboot和kernel。但是前面这些操作又必须先有一个移植好的代码作为测试才能进行,所以后面我们会使用自己移植的代码来重新测试这些操作。
- linux开发环境搭建(5)-使用tftp下载uboot
- linux开发环境搭建(2)-tftp下载内核
- tftp搭建安装 & uboot使用tftp下载内核
- 开发板s5pc100搭建环境(串口发送命令,tftp下载)
- uboot中使用tftp命令下载
- 嵌入式linux开发环境搭建(三)——TFTP服务器的搭建
- 搭建开发环境----安装tftp
- 虚拟机开发环境搭建(tftp,nfs,samba,arm-linux-gcc)
- linux开发环境搭建tftp与nfs以及配置
- tftp下载uboot命令
- uboot tftp下载功能
- DM3730开发板使用uboot通过网络下载内核和文件系统 ubuntu下配置 TFTP
- linux开发环境搭建(4)-从SD卡启动uboot
- (韦)dnw无法使用,uboot+ 通过tftp ,nfs 下载之nand flash
- uboot的使用:tftp下载内核,直接…
- Android 搭建tftp-nfs开发环境文档
- 关于嵌入式linux开发环境搭建-TFTP,SAMBA,NFS服务器的搭建
- UBOOT 通过 TFTP 下载 uImage
- PHP配置文件的说明(2)
- Java数组的相关练习
- 二维数组中的查找
- 配置Server Side TAF
- Shell语法错误----变量与空格[: too many arguments
- linux开发环境搭建(5)-使用tftp下载uboot
- HBase编程api介绍(转)
- 常用css样式
- hbase的查询scan功能注意点(setStartRow, setStopRow)
- Notification中显示进度条
- 安装redis和phpredis模块
- 103. Binary Tree Zigzag Level Order Traversal
- java位移运算导致数值为负数
- 编译OpenCV 3.2