andy 发表于 2013-1-28 22:01:59

U-boot的SD启动卡写入工具

本帖最后由 andy 于 2013-1-28 22:06 编辑

原文在我博客:http://blog.csdn.net/andy_wsj/article/details/8550247

在制作U-boot的SD启动卡时,由于每次编译完都要输一串命令写入.bin文件,为了方便行事,故写了一个小工具,专为写U-boot使用
该工具只适用于Cubieboard,若要用于其他开发板,请自行修改代码

源代码:   
开发环境:RHEL5
开发工具:VIM
编译器 : GCC-----版本没注意看,应该2.6内核以上使用的GCC都可以吧

代码+说明:
#define _LARGEFILE64_SOURCE
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#define STAGE1_OFFSET      (8* 1024)               //Cubieboard在启动时,第一阶段bootloader在SD卡8K位置开始
#define STAGE2_OFFSET      (32 * 1024)            //Cubieboard在启动时,第二阶段bootloader在SD卡32K位置开始
#define STAGE1_SIZE_LIMIT   (24* 1024)            //由上面可知,第一阶段的长度限制在8K到32K之间,因此是24K
#define STAGE2_SIZE_LIMIT   (992 * 1024)         //由上面可知,第二阶段的长度限制在32K到1M之间,因此是992K
#define STAGE1_FLAG             0                           //写第一阶段标志
#define STAGE2_FLAG             1                           //写第二阶段标志
#define UNDEFINE_FLAG      2                               //用户没有输入写第几段时,未定义标志

int main(int argc, char *argv[])
{
   unsigned int file_size;             //写入文件的size
   off64_t sd_size;                     //SD卡的size
   int flag    = STAGE1_FLAG;    //写入标志
   int temp = 0;                              
   int ret      = -1;                           
   int ifd;                                          //输入文件描述符 ,编译好的U-boot文件
   int ofd;                                        //输出文件描述符,就是SD卡
   char input_file = {0};   //输入文件缓冲区,由于最大的输入文件是第二阶段,因此限制为992K
   
    if((argc < 3) ||(strncmp(argv, "/dev/", 5))){ //命令输入参数少于3个或者输出文件不是SD所在目录,提示用户操作
         fprintf(stderr, "Usage: sd_write<Input file><Output file>\n");
         return -1;
   }
   ofd = open(argv, O_WRONLY | O_LARGEFILE);   //打开SD卡所在的文件
   if(ofd == -1){
         perror(argv);
         goto err0;
   }

   ifd = open(argv, O_RDONLY);//打开输入文件,就是bootloader
   if(ifd == -1){
         perror(argv);
         goto err1;
   }

   if(argc == 4){   //如果用户输入了4个参数,则最后一个参数为1表示是第一阶段,为2是第二阶段,否则是未定义,
          temp = atoi(argv);       //程序将自行决定写在什么位置
          if( 1 == temp ){
                flag = STAGE1_FLAG;
          }
          else if( 2 == temp ){
                flag = STAGE2_FLAG;
          }
          else{
                flag = UNDEFINE_FLAG;
          }
   }
   else{//如果输入参数不是4个,也是未定义,,程序将执行决定写在什么位置
          flag = UNDEFINE_FLAG;
   }

/*************************************************************************/
   if((file_size = lseek(ifd, 0, SEEK_END)) == -1){//获取输入文件长度
          perror("lseek()");
          goto err2;
      }
      if(file_size == 0){//   输入文件为空,直接退出
          fprintf(stderr, "Error: File size == 0\n");
          goto err2;
      }
      if(lseek(ifd, 0, SEEK_SET) == -1){//将文件偏移量移到开始位置,准备读取
          perror("lseek()");
          goto err2;
      }
      if( flag == STAGE1_FLAG ){      //输入文件长度不能超过设置好的限制值
          if( file_size > STAGE1_SIZE_LIMIT ){
               fprintf(stderr, "Error: File size > %uK.\n", STAGE1_SIZE_LIMIT / 1024);
               goto err2;
            }
       }
       else if( flag == STAGE2_FLAG ){
         if( file_size > STAGE2_SIZE_LIMIT ){
                fprintf(stderr, "Error: File size > %uK.\n", STAGE2_SIZE_LIMIT / 1024);
                goto err2;
            }
       }
       else{//若没有输入写第几阶段,则当输入文件小于24K时写在8K开始的地方,大于24K小于992K时,写在32K开始的地方
                if( file_size < STAGE1_SIZE_LIMIT ){
                     flag = STAGE1_FLAG;   
               }
               else if( file_size < STAGE2_SIZE_LIMIT ){
                     flag = STAGE2_FLAG;
               }
               else{    //这是我刚加的,原来没写这里,有这句保险一点
                        fprintf(stderr, "Error: File size > %uK.\n", STAGE2_SIZE_LIMIT / 1024);
                        goto err2;
               }
               fprintf(stdout, "file_size = %uK flag = %u .\n", file_size /1024, flag);
      }

      if(read( ifd, input_file, file_size ) == -1){   // 读取整个输入文件
               perror("read()");
               goto err2;
         }
/*************************************************************************/
      if((sd_size = lseek64(ofd, 0, SEEK_END)) == -1){   //读取SD卡size
                perror("lseek64()");
                goto err2;
      }
      if( sd_size < 0x100000 ){//如果SD卡小于1M,则不写
                fprintf(stderr, "Error: SD card < 1M.\n");
                goto err2;
      }
//上面这里应该可以写的更好一点,可以将SD卡的分区表读出来,等写完了在写到U-boot后面,
//这样不需要对SD卡做命令处理就可以直接使用这个工具了
//不同的SD卡,2G、4G、8G.....分区表大小可能不同,因此还要考虑程序如何支持不同的SD卡....等等,有兴趣的同学可以试试
/*************************************************************************/

       if(lseek64(ofd, 0, SEEK_SET) == -1){   //将SD卡文件偏移量复位,准备开始写
            perror("lseek64()");
            goto err2;
       }
      if(flag == STAGE1_FLAG){
            if( lseek64(ofd, STAGE1_OFFSET, SEEK_SET) == -1){    //写第一阶段,将SD卡的文件偏移量移动到8K,从8K往后写
                  perror("lseek64()");
                  goto err2;
            }
            fprintf(stdout, "write in stage1, offset = %uK.\n", STAGE1_OFFSET / 1024);
      }
   else{
         if(lseek64(ofd, STAGE2_OFFSET, SEEK_SET) == -1){      //写第二阶段,将SD卡的文件偏移量移动到32K,从32K往后写
               perror("lseek64()");
               goto err2;
         }
         fprintf(stdout, "write in stage2, offset = %uK.\n", STAGE2_OFFSET / 1024);
       }

      if(write(ofd, input_file, file_size) != file_size){    //U-boot写入SD卡
         fprintf(stderr, "write failed.\n");
         goto err2;
      }

/*************************************************************************/
      fsync(ofd);   //写完收工,记得SD卡是外部慢速设备,同步一下,等同于shell的sync命令
      printf(" Done.\n");
      ret = 0;
err2:
   close(ifd);
err1:
   close(ofd);
err0:
   return ret;
}

我机器上SD卡文件是/dev/sdc
SD卡处理,只需要处理一次,正确后以后就不用再输入这些啦

ddif=/dev/zero   of=/dev/sdc bs=1M count=1//将前面1M写0
cat <<EOT | sfdisk-uM/dev/sdc    //剩下的作为一个分区,如果不行拔出SD卡再插入
2,,L             //必须是大于1,表示从2M开始到SD结束作为一个区使用1时分区表会被弄丢了,
                   //弄丢了没关系,再用这几条命令在linux下可修复
EOT

拔出SD卡,再插入,继续处理,这时候SDka变成了/dev/sdc和/dev/sdc1
mkfs.vfat/dev/sdc1    //将余下的部分格式化,可以当U盘使用

编译SD卡工具:
gcc   sd_write.c   -o   sd_write
mv   sd_write   /work/uu-boot-sunxi-sunxi/    放到u-boot目录备用

效果演示:   //以后可以就只用使用这个来写入
# ./sd_write spl/sunxi-spl.bin /dev/sdc
file_size = 20K flag = 0 .
write in stage1, offset = 8K.
Done.

# ./sd_write u-boot.bin /dev/sdc
file_size = 171K flag = 1 .
write in stage2, offset = 32K.
Done.


取出SD卡,先插入XP台式机试试,确认没问题可当U盘用,将SD卡插入Cubieboard,上电,minicom打印U-boot正常驱动....
工具做好啦,工欲善其事,必先利其器-----就是这道理

Earthman 发表于 2013-3-9 03:37:38

本帖最后由 Earthman 于 2013-3-9 03:40 编辑

脚本版#!/bin/sh

device=null

test -b /dev/$1 && device=$1 || (echo Error && exit)

echo

echo write u-boot to /dev/$device : Y/N

echo

read yesno

(test $yesno = y || test $yesno = Y ) && \

dd if=/dev/zero of=/dev/$device bs=1M count=1 && \

dd if=spl/sunxi-spl.bin of=/dev/$device bs=1024 seek=8 && \

dd if=u-boot.bin of=/dev/$device bs=1024 seek=32 && \

echo Success!保存文件为write_uboot,添加可执行权限,当然必须在u-boot-sunxi-sunxi,即是编译根文件夹下
我的sd卡是/dev/sdc,所以敲 sudo ./write_uboot sdc,然后会要求确认,按y,就行了

andy 发表于 2013-3-23 10:55:31

Earthman 发表于 2013-3-9 03:37 static/image/common/back.gif
脚本版保存文件为write_uboot,添加可执行权限,当然必须在u-boot-sunxi-sunxi,即是编译根文件夹下
我的sd ...

这个太好啦,我不会写脚本,只会C.....憋了好久才写出来的
这个短小精湛,很好

sun_richard 发表于 2013-5-30 20:47:34

楼主还知道u-boot-spl.bin 这个.bin是干什么用的?

andy 发表于 2013-5-30 21:33:57

sun_richard 发表于 2013-5-30 20:47 static/image/common/back.gif
楼主还知道u-boot-spl.bin 这个.bin是干什么用的?

试过,不能从SD启动,我也不知道用来做什么的,使用sunxi-spl.bin吧

andy 发表于 2013-5-30 21:35:04

sun_richard 发表于 2013-5-30 20:47 static/image/common/back.gif
楼主还知道u-boot-spl.bin 这个.bin是干什么用的?

试过,不能从SD启动,我也不知道用来做什么的,使用sunxi-spl.bin吧

ahtwbg 发表于 2015-7-20 22:30:37

感謝樓主分享

leebosstw007 发表于 2015-10-13 12:38:51

sun_richard 发表于 2013-5-30 20:47 static/image/common/back.gif
楼主还知道u-boot-spl.bin 这个.bin是干什么用的?

u-boot-spl.bin 應該是boot0,是要為了將Boot1載入的小程式。
页: [1]
查看完整版本: U-boot的SD启动卡写入工具