S3C6410开发全纪录(一)-----还原SD卡启动的真相

来源:互联网 发布:c语言能做什么 编辑:程序博客网 时间:2024/05/16 02:52

   转自:http://blog.csdn.net/turui/article/details/6634461


    前章我们也大致分析了SD卡的启动过程,在具体进行问题的定位及解决的过程中,发现还是有很多不明确的地方,网上的文章也多是人云亦云让我们来一步一步搞清楚S3C6410 SD卡启动的步骤及过程(我这里的开发板为OK6410,256M+1G的配置,SD卡为2G,MMC)


一、开发板跳线,从SD卡启动

view plaincopy to clipboard
  1. 查看《OK6410开发板LINUX2.6用户手册.pdf》 将跳线设置为 11111000 (从左到右为 pin8 到 pin1的设置,别搞反了)  
  2. 这些设置可以在s3c6410的datasheet中查到,打开《s3c6410_rev12.pdf》125页  
  3. Table 3-1. Device operating mode selection at boot-up   
  4. 从这里可以看到,最后3个位为0,表示从SD/MMC(CH0)启动  

view plaincopy to clipboard
  1. 修改UBOOT,让它支持从SD卡读取数据,并将自己自举到内存  
  2. 当我们设置完CPU的跳线,CPU已经清楚要从哪里去取第一条指,S3C6410到底是怎么工作的呢  
  3. 之前找到过一篇参考文章 《S3C2450_IROM_ApplicationNote_Rev003.pdf》  
  4. 这次找到了官方的文档更清楚的说明了这一切《S3C6410_Internal_ROM_Booting.pdf》  
  5. 在文档中我们看到CPU上电之后的启动过程如下  

①  iROM supports initial boot up,initialize system clock,D-TCM,device specific controller and bootin device.   ②  iROM boot codes can load 4KB of bootloader to stepping stone. The 8KB boot loader is called BL1  ③  BL1: BL1 can initialize system clock, UART, and SDRAM for user. After initializing, BL1 will load remaining boot loader which is called BL2 on the SDRAM   ④  Finally, jump to start address of BL2. That will make good environment to use system.   

按照这个启动过程,我们必须准备好8K的引导代码在BL1,  用来初始化系统,始终,串口,SDRAM等,并且将完整的BootLoader放在BL2上    2.7 Boot Block Assignment Guide 中有详细的描述    2G以下 SD/MMC 的卡的存储结构  SD/MMC 1Block = 512 Byte  =========================================================================================  |                                    SD/MMC Device                                      |  =========================================================================================  |                  | Recommendation |                    Mandatory                      |  | User File System |=====================================================================  |                  | Kernel |  BL2  | BL1(8K)  | Signature(512Byte) | Reserved(512Byte) |  |                  |        |       | 16 Block |       1 Block      |     1 Block       |  =========================================================================================    有了这个大致的印象,我们先放一下,下面我们来修改UBOOT的代码  



二、修改UBOOT代码

s3c6410的uboot源码见最后的参考资料  修改makefile 交叉工具链的绝对路径(嘿嘿,这个交叉工具链当然是俺自己做的交叉工具链了,参考前面篇BLOG)    ifeq ($(ARCH),arm)  CROSS_COMPILE = arm-none-linux-gnueabi-  endif    CROSS_COMPILE = /opt/cross_toolchains/arm/4.6.1/bin/arm-none-linux-gnueabi-  

修改 include/configs/smdk6410.h 打开 #define CONFIG_BOOT_MOVINAND 这个宏,并屏蔽其他的启动选项  在UBOOT的代码中,将IROM的启动模式称之为MOVINAND  


这个UBOOT就已经可以从SD卡启动了,S3C6410具体又是怎么工作的呢?


view plaincopy to clipboard
  1. 在 cpu/s3c64xx/start.S 中我们看到如下这段  
  2.   
  3. #ifdef CONFIG_BOOT_MOVINAND   
  4.         ldr     sp, _TEXT_PHY_BASE  
  5.         bl      movi_bl2_copy  
  6.         b       after_copy  
  7. #endif  

分析 movi_bl2_copy 函数


cpu/s3c64xx/movi.c 中 void movi_bl2_copy(void) 中定义如下  


#if defined(CONFIG_S3C6400)           CopyMovitoMem(MOVI_BL2_POS, MOVI_BL2_BLKCNT, (uint *)BL2_BASE, CONFIG_SYS_CLK_FREQ, MOVI_INIT_REQUIRED);  #else           writel(readl(HM_CONTROL4) | (0x3 << 16), HM_CONTROL4);          CopyMovitoMem(HSMMC_CHANNEL, MOVI_BL2_POS, MOVI_BL2_BLKCNT, (uint *)BL2_BASE, MOVI_INIT_REQUIRED);  #endif  


确定MOVI_BL2_POS的值


./include/movi.h:#define MOVI_BL2_POS     (MOVI_LAST_BLKPOS - MOVI_BL1_BLKCNT - MOVI_BL2_BLKCNT - MOVI_ENV_BLKCNT)    ./include/movi.h:#define MOVI_LAST_BLKPOS (MOVI_TOTAL_BLKCNT - (eFUSE_SIZE / MOVI_BLKSIZE))    ./include/movi.h:#define MOVI_BL1_BLKCNT  (SS_SIZE / MOVI_BLKSIZE)    ./include/movi.h:#define MOVI_BL2_BLKCNT  (((PART_ZIMAGE_OFFSET - PART_UBOOT_OFFSET) / MOVI_BLKSIZE) - MOVI_ENV_BLKCNT)    ./include/movi.h:#define MOVI_ENV_BLKCNT  (CFG_ENV_SIZE / MOVI_BLKSIZE)    ./include/movi.h  #ifdef CONFIG_BOOT_MOVINAND   #define MOVI_TOTAL_BLKCNT       *((volatile unsigned int*)(TCM_BASE - 0x4))   #define MOVI_HIGH_CAPACITY      *((volatile unsigned int*)(TCM_BASE - 0x8))   #else   #define MOVI_TOTAL_BLKCNT       7864320 // 7864320 // 3995648 // 1003520 /* static movinand total block count: for writing to movinand when nand boot */   #define MOVI_HIGH_CAPACITY      0   #endif     ./include/movi.h:#define MOVI_BLKSIZE           512    ./include/movi.h  #if defined(CONFIG_S3C6400) || defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430)   #define TCM_BASE                0x0C004000   #define BL2_BASE                0x57E00000   #elif defined(CONFIG_S3C2450) || defined(CONFIG_S3C2416)   #define TCM_BASE                0x40004000   #define BL2_BASE                0x33E00000   #else   # error TCM_BASE or BL2_BASE is not defined   #endif     ./include/movi.h  #if defined(CONFIG_S3C6400)   #define SS_SIZE                 (4 * 1024)   #define eFUSE_SIZE              (2 * 1024)      // 1.5k eFuse, 0.5k reserved   #else   #define SS_SIZE                 (8 * 1024)   #define eFUSE_SIZE              (1 * 1024)      // 0.5k eFuse, 0.5k reserved`   #endif     ./include/movi.h:#define PART_ZIMAGE_OFFSET     0x40000    ./include/movi.h:#define PART_UBOOT_OFFSET      0x0    ./include/configs/smdk6410.h:#define CFG_ENV_SIZE               0x4000  /* Total Size of Environment Sector */  


确定BL2_BASE的值


./include/movi.h  #if defined(CONFIG_S3C6400) || defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430)   #define TCM_BASE                0x0C004000   #define BL2_BASE                0x57E00000   #elif defined(CONFIG_S3C2450) || defined(CONFIG_S3C2416)   #define TCM_BASE                0x40004000   #define BL2_BASE                0x33E00000   #else   # error TCM_BASE or BL2_BASE is not defined   #endif  


经过计算我们可以看出,这里决定CopyMovitoMem的功能,是TCM_BASE - 0x4,这个地址的寄存器的值(隐含的也告诉我们UBOOT编译出来之后不得大于256k)

《S3C6410_Internal_ROM_Booting.pdf》中看到

0x0C003FFC globalBlockSizeHide Total block count of the MMC device

这个值,具体的为,256K(根据打印可以得出,没找到具体的说明)

MOVI_BL2_BLKCNT 的值为 ( (0x40000-0)/512 - (0x4000/512) ) = 512 -32 (个扇区)

网上大多数资料写到CopyMovitoMem是将256k的数据搬运到内存中,从这个计算上我们可以看出实际这个结论是错的

在这里只搬运了 256k-16k的数据到内存

在MOVI_BL2_POS的定义中做了相应的处理,以保证位置的正确性

编译uboot,生成u-boot.bin


make smdk6410_config  make -j4  


三、将修改好的UBOOT烧录到SD卡中

为什么要用到下面的脚本,请参考:http://www.xxlinux.com/linux/article/development/embed/20100225/18059.html

生成的uboot-bin是不是直接可以烧录到SD卡中呢?

有现成的工具IROM_Fusing_Tool.exe(开源的,可以找到源代码),但这个工具烧录的是nb0文件

实际上nb0文件的结构是:256k+8k 这样的一个形式,在Linux系统可以通过下面这个脚本来生成nb0文件


#!/bin/sh   rm -rf temp x* u-boot_256k.bin u-boot_8k.bin u-boot_mmc.nb0  cat u-boot.bin >> temp  cat u-boot.bin >> temp  split -b 256k temp  mv xaa u-boot_256k.bin  split -b 8k u-boot.bin  mv xaa u-boot_8k.bin  cat u-boot_256k.bin > u-boot_mmc.nb0  cat u-boot_8k.bin >> u-boot_mmc.nb0  

我想直接将uboot-bin烧录到SD卡中,使用脚本太麻烦,于是我打算自己写一个烧录工具,关键代码如下

需要说明的是,我这里使用的是VC6,WINDOWS XP,VC6对磁盘IO的操作本身支持并不是非常好,有很多扩展的定义,功能都无法使用,这里很多都我手工添加进去的

如果使用的是VC2003及以上版本,会简单一些


DWORD CSC6410BootLoaderWriterDlg::BlockDataRead(char cPart,DWORD dwBlockIndex,DWORD dwReadCount,BYTE* pBuffer)  {      CString devName;      devName.Format("\\\\.\\%c:",cPart);      HANDLE hDevice = CreateFile(devName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);      DWORD dwFilePointer = SetFilePointer(hDevice,dwBlockIndex*512, 0,FILE_BEGIN);      DWORD dwBytesRead = 0;      BOOL bRead = ReadFile(hDevice,pBuffer,dwReadCount,&dwBytesRead,NULL);      if(bRead == FALSE)      {          DWORD dwError = GetLastError();      }      CloseHandle(hDevice);      return dwBytesRead;  }   

DWORD CSC6410BootLoaderWriterDlg::BlockDataWrite(ULONG nPhysicalDriveNumber,DWORD dwBlockIndex,BYTE* pData,DWORD dwDataLen)  {      if( (dwDataLen % 512) != 0 ) dwDataLen = ( (dwDataLen / 512) + 1 ) * 512;      CString devName;      devName.Format("\\\\.\\PhysicalDrive%d",nPhysicalDriveNumber);      HANDLE hDevice = CreateFile(devName,GENERIC_WRITE,FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);      DWORD dwFilePointer = SetFilePointer(hDevice,dwBlockIndex*512, 0,FILE_BEGIN);        DWORD dwBytesWrite = 0;      BOOL bWrite = WriteFile(hDevice,pData,dwDataLen,&dwBytesWrite,NULL);          if(bWrite == FALSE)      {          DWORD dwError = GetLastError();      }        CloseHandle(hDevice);      return dwBytesWrite;  }  

看到区别了吗?这里要非常非常注意!

Write函数中devName的构建形式与Read函数中的不一样,这个问题让我调试了很久,如果在Write函数中使用Read函数的构建形式,则会遇到WriteFile工作不正常的现象

具体为:在操作最后若干个扇区时,bWrite 等于 TRUE,dwBytesWrite却为0(为什么?还没能深究下去)

另外,如果要写入的数据长度不是512的整数倍,一定要进行处理,否则会引起GetLastError是87,参数错误

因为所有的FLASH,最小的扇区时512字节,则最少要以512个字节作为一次操作单位

(文件系统帮我们解决了这些问题,我们现在是对磁盘裸的操作,所以不能按照有文件系统的想法来考虑这个问题)

下面是Write函数构造devName时需要用到的函数


ULONG CSC6410BootLoaderWriterDlg::GetPhysicalDriveNumber(char cPart)  {      typedef struct _DISK_EXTENT {        ULONG         DiskNumber;        LARGE_INTEGER StartingOffset;        LARGE_INTEGER ExtentLength;      } DISK_EXTENT, *PDISK_EXTENT;          typedef struct _VOLUME_DISK_EXTENTS {        ULONG       NumberOfDiskExtents;        DISK_EXTENT Extents[ANYSIZE_ARRAY];      } VOLUME_DISK_EXTENTS, *PVOLUME_DISK_EXTENTS;        #define VOLUMEDISKSIZE (sizeof(VOLUME_DISK_EXTENTS))         #define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 5636096         CString devName;      devName.Format("\\\\.\\%c:",cPart);      HANDLE hDevice = CreateFile(devName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );        VOLUME_DISK_EXTENTS volumeData;      DWORD dwOut = 0;      DeviceIoControl( hDevice, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,NULL, 0, &volumeData, VOLUMEDISKSIZE, &dwOut, NULL );      CloseHandle( hDevice );         return volumeData.Extents[0].DiskNumber;  }  

下面这段代码是用来获得,所有插入的U盘(可移动磁盘,注意下面的判断)的盘符

void CSC6410BootLoaderWriterDlg::OnButtonGetdriverinfo()   {      // TODO: Add your control notification handler code here       // 获取所有的盘符       m_cbList.ResetContent();      DWORD dwStrLen = GetLogicalDriveStrings(0,NULL);        char* pString = new char[dwStrLen+1];      memset(pString,0,dwStrLen+1);      GetLogicalDriveStrings(dwStrLen+1,pString);         char *pNow = pString;      while(TRUE)      {          if(pNow >= (pString+dwStrLen-1)) break;          DWORD dwRet = GetDriveType(pNow);          if(dwRet == DRIVE_REMOVABLE)           {              m_cbList.InsertString(0,pNow);          }          pNow += strlen(pNow);          pNow ++;      }        delete[] pString;        m_cbList.SetCurSel(0);      m_cbList.SetFocus();        WriteLog("U盘检查完成");  }  

最后是写入代码,在这里我们会要求SD卡首先会被各式化成FAT32各式,以便我们能去读取FTA32文件分配表中MBR的部分

void CSC6410BootLoaderWriterDlg::OnButtonOp()   {      // TODO: Add your control notification handler code here         int nCurSel = m_cbList.GetCurSel();      if(nCurSel == -1)       {          MessageBox("探测磁盘信息");             return;      }        CString strBootFilePath;      m_eBootFilePath.GetWindowText(strBootFilePath);      if(strBootFilePath == "")      {          MessageBox("请先选择需要烧录的引导文件");              return;       }        // 获得驱动器盘符       CString strText;      m_cbList.GetLBText(m_cbList.GetCurSel(),strText);      char cPart = strText[0];        // 获得 PhysicalDriveNumber       ULONG nPhysicalDriveNumber = GetPhysicalDriveNumber(cPart);        // 读取 0 扇区 mbr       BYTE szMbr[512];      memset(szMbr,0,sizeof(szMbr));      DWORD dwReturn = BlockDataRead(cPart,0,sizeof(szMbr),szMbr);      if(dwReturn != sizeof(szMbr))      {          MessageBox("Read MBR error.","错误",MB_OK|MB_ICONERROR);          return;      }      {          CString strLog;          strLog.Format("读取MBR成功");          WriteLog(strLog);         }        // 判断是否为 fat32       // FAT16 为 0x36 0x37 0x38 0x39 0x3a 0x3b       char szFs[6];      szFs[0] = szMbr[0x52];         szFs[1] = szMbr[0x53];         szFs[2] = szMbr[0x54];         szFs[3] = szMbr[0x55];         szFs[4] = szMbr[0x56];         szFs[5] = '\0';           if ( strcmp(szFs,"FAT32") != 0)      {             MessageBox("请将SD卡格式化为FAT32文件系统","错误",MB_OK|MB_ICONINFORMATION);             return;      }            // 获得磁盘的扇区总数       //1CH-1FH   4  本分区隐含扇区数        //20H-23H   4  该盘实际使用扇区数(不包括隐含扇区)            DWORD count_block_hidden = 0;      memcpy(&count_block_hidden,&szMbr[0x1c],sizeof(count_block_hidden));        DWORD count_block = 0;      memcpy(&count_block,&szMbr[0x20],sizeof(count_block));        DWORD count_block_total = count_block_hidden + count_block;        // 另一种方法 获得 扇区总数       // #define IOCTL_DISK_GET_DRIVE_GEOMETRY_EX 458912       //  struct _DISK_GEOMETRY_EX        // {    DISK_GEOMETRY  Geometry;         //  LARGE_INTEGER  DiskSize;         //  UCHAR  Data[1];       // } DiskEX;       // DeviceIoControl(hDevice,IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,NULL,0,(LPVOID)&DiskEX,sizeof(DiskEX),(LPDWORD)&bytesReturned,NULL);         // 读取 u-boot.bin 文件       // 最大 256k       #define MOVI_TOTAL_BLKCNT 0x40000       BYTE buffer_utoot[MOVI_TOTAL_BLKCNT];      memset(buffer_utoot,0,sizeof(buffer_utoot));            CFile cf;      cf.Open(strBootFilePath,CFile::modeRead);        DWORD uboot_size = min(MOVI_TOTAL_BLKCNT,cf.GetLength());            cf.Read(buffer_utoot,uboot_size);      cf.Close();            // 将引导程序 uboot-bin 写入相应的位置       DWORD bl2_begin = count_block_total-1-1-16-512;      DWORD bl1_begin = count_block_total-1-1-16;        dwReturn = BlockDataWrite(nPhysicalDriveNumber,bl1_begin,buffer_utoot,8192);      if(dwReturn != 8192)      {          MessageBox("写入引导8k字节失败");          return;      }      {          CString strLog;          strLog.Format("写入引导8k字节成功");          WriteLog(strLog);         }      dwReturn = BlockDataWrite(nPhysicalDriveNumber,bl2_begin,buffer_utoot,uboot_size);        if(dwReturn < uboot_size)      {          MessageBox("写入引导失败");          return;      }      {          CString strLog;          strLog.Format("引导文件写入完成,磁盘总扇区数:%d",count_block_total);          WriteLog(strLog);         }  }  

到这里,我们就可以完全分析清楚,并可以自主的制作一个S3C6410的启动SD卡了,附后为一些参考资料,这里没办法贴附件,问题给我留言吧

后面的内容中,我们将进入UBOOT,对UBOOT进行修改,请关注下一节《如何计算内存大小,并在UBOOT中调整内存大小》


四、参考资料

下载 uboot1.1.6 (支持 movinand)

http://www.rayfile.com/zh-cn/files/7ac4e133-0e58-11de-bd70-0014221b798a/

使uboot支持S3C6410的SD启动

http://blog.csdn.net/zwj0403/article/details/6420245

《S3C6410_Internal_ROM_Booting.pdf》

《s3c6410_rev12.pdf》

《smdk6410_users_manual_rev1.0.pdf》

《K4X51163PC.pdf》

《OK6410开发板LINUX2.6用户手册.pdf》

《OK6410开发板硬件手册2.1.pdf》




原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 u盘弹出要格式化怎么办 u盘无法格式化怎么办啊 大盘下跌下你该怎么办 股票想卖没人买怎么办 百变模板乱了怎么办 5个半月宝宝胀气怎么办 三岁宝宝胃胀怎么办 全民k歌不能用wf怎么办 雅乐之舞掉叶子怎么办 姬珊瑚发软歪了怎么办 胃胀气怎么办简单的方法 8个月的宝宝咳嗽怎么办 坐完月子掉头发怎么办 在香港买到假货怎么办 用气垫脸上浮粉怎么办 不够奶给宝宝吃怎么办 叶插发芽发根后怎么办 多肉植物摊大饼怎么办 英短蓝猫掉毛怎么办 英短蓝猫很凶怎么办 面部打伤怎么办了肿了 朋友欠钱一直拖怎么办 旧车三年不年检怎么办 遇到领导整你该怎么办 皮肤热了就瘙痒怎么办 猫爪子肉垫脏了怎么办 嘴周围干燥起皮怎么办 下嘴唇总是起皮怎么办 上嘴唇老是起皮怎么办 涂口红嘴唇起皮怎么办 上嘴唇干裂起皮怎么办 9岁儿童嘴唇干裂怎么办 上嘴唇干裂烂了怎么办 孕妇脚干的裂开怎么办 小腿皮肤干燥起皮怎么办 秋天脸干燥起皮怎么办 脚上干燥起皮怎么办 脚皮肤干燥起皮怎么办 脚有脚气脱皮又痒怎么办 脚特别干还脱皮怎么办 皮肤干燥起皮怎么办痒