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。但是前面这些操作又必须先有一个移植好的代码作为测试才能进行,所以后面我们会使用自己移植的代码来重新测试这些操作。

0 0
原创粉丝点击