GEC2410开发板自带的Eboot需要通过自带的Bootloader下载后才能运行,这个Eboot只起到下载内核镜像的作用,重启后是通过Nboot启动内核而不是Eboot。而且也不支持KITL,BSP的结构也完全是4.2下的,非常凌乱。不知道开发板商为什么不能按照5.0的架构做一个支持KITL内核调试的Eboot。
因此产生了移植一个上电后能直接运行的Eboot的想法。由于S3C2410支持4K的Steppintstone,可以从Nand Flash启动,我把Eboot放在Nand Flash中,用Nboot进行引导。NBoot位于NandFlash的Block 0,Eboot位于Block 2。上电后,位于前4K的Nboot被复制到RAM中运行,然后Nboot复制Eboot到RAM中,最后跳转到Eboot,由Eboot进行下一步的工作。
或者把Eboot放到Nor Flash中,也不需要Nboot,之后将介绍基于Nor Flash的移植
整个工程位于:http://download.csdn.net/source/620556中的NBoot_2410(Eboot)目录下。
一、开发环境
编译器: ADS1.2
目标板: GEC2410 S3C2410A,NAND Flash:64M K9F1208,NOR Flash:2M SST39VF1601 SDRAM 64M,CS8900
二、开始移植
(1)一般Nboot的结构
板子自带的Nboot是用来直接引导CE内核的,它需要一个toc结构的变量,保存了被引导镜像的地址大小等信息。typedef struct _TOC { DWORD dwSignature; BYTE udid[8]; // How to boot the images in this TOC. // This could be moved into the image descriptor if desired, // but I prefer to conserve space. BOOT_CFG BootCfg; // Array of Image Descriptors. IMAGE_DESCRIPTOR id[MAX_TOC_DESCRIPTORS]; CHAININFO chainInfo; } TOC, *PTOC; // 512 bytes
开发板自带的Eoot被下载后将在Block 1写入这个toc结构,然后Nboot在读取信息来引导CE Image。
同理如果用来启动Eboot,只需要把Eboot的信息写入这个结构即可。我们不把toc放入Block 1然后再去读,而是直接在程序中定义好,因为Eboot大小位置都是固定的。
(2)修改Nboot
我在网上找到一个最近似我需求的Nboot,已经把toc结构成员进行了初始化,只需要修改相应的参数即可。
(3)串口驱动
我使用UART1 38400kbps,用来输出Nboot的启动信息。#define BAUD_RATE 38400 void Uart_Init(void) { int i; rUFCON1 = 0x0; // FIFO disable rUMCON1 = 0x0; // AFC disable rULCON1 = 0x3; // Normal,No parity,1 stop,8 bits rUCON1 = 0x245; rUBRDIV1=( (int)(PCLK/16./BAUD_RATE) -1 ); for(i=0;i<100;i++); }
其他的初始化部分我参考我的资源中的工程
(4)ReadEbootFromNand函数
ReadEbootFromNand这个函数是这个工程中的核心,用来读取NandFlash起始位置为Block 2的Eboot到指定的SDRAM地址中,然后跳转到Eboot运行。
几个重要的参数为
#define EBOOT_RAM_IMAGE_SIZE 0x00040000 //256K Eboot #define EBOOT_BLOCK 2 //Eboot起始Block #define EBOOT_RAM_IMAGE_BASE 0x8c038000 //Eboot在SDRAM的地址 DWORD ReadEbootFromNand() { DWORD dwSectorsNeeded; DWORD dwSector, dwLength; // Start Sector & Length DWORD dwRAM, i; //Init Toc toc.id[0].dwVersion = (EBOOT_VERSION_MAJOR << 16) | EBOOT_VERSION_MINOR; toc.id[0].dwSignature = IMAGE_EBOOT_SIG; toc.id[0].dwImageType = IMAGE_TYPE_RAMIMAGE; //以上三个参数其实实际并未用到 toc.id[0].dwLoadAddress = EBOOT_RAM_IMAGE_BASE;//Eboot在SDRAM的地址 toc.id[0].dwJumpAddress = EBOOT_RAM_IMAGE_BASE; toc.id[0].dwTtlSectors = FILE_TO_SECTOR_SIZE(EBOOT_RAM_IMAGE_SIZE);//Eboot所占Sectors数目 // 1 contigious segment toc.id[0].sgList[0].dwSector = BLOCK_TO_SECTOR(EBOOT_BLOCK);//计算Eboot起始Sector toc.id[0].sgList[0].dwLength = toc.id[0].dwTtlSectors;//传递Sectors数 dwSectorsNeeded = toc.id[0].dwTtlSectors; dwRAM = VIRTUAL_TO_PHYSICAL(toc.id[0].dwLoadAddress);//计算Eboot在RAM中的物理地址 JumpAddr = toc.id[0].dwJumpAddress ? VIRTUAL_TO_PHYSICAL(toc.id[0].dwJumpAddress) : VIRTUAL_TO_PHYSICAL(toc.id[0].dwLoadAddress);//Eboot复制完成后的跳转地址 i = 0; //从NandFlash Block 2开始复制Eboot到SDRAM中 while (dwSectorsNeeded && i < MAX_SG_SECTORS) { dwSector = toc.id[0].sgList[i].dwSector; dwLength = toc.id[0].sgList[i].dwLength; // read each sg segment while (dwLength) { if ( !FMD_ReadSector(dwSector, (LPBYTE)dwRAM, NULL, 1) ) { // Uart_SendString("ERR_DISK_OP_FAIL2: "); // Uart_SendDWORD(dwSector, TRUE); } dwSector++; dwLength--; dwRAM += SECTOR_SIZE; } dwSectorsNeeded -= toc.id[0].sgList[i].dwLength; i++; } return ERR_SUCCESS; }
(5)启动Eboot
通过调用Launch(JumpAddr)函数跳转到Eboot
Launch是用汇编写的,Eboot中也会调用这个函数来跳转到内核
EXPORT Launch Launch nop nop nop nop mov pc, r0 ; Jump to PhysicalAddress nop MOV_PC_LR END
(6)Main
void Main(void) { DWORD err; MMU_EnableICache(); Uart_Init(); Uart_SendString(SIGN_ON); NF_Init(); ReadEbootFromNand(); Launch(JumpAddr); Uart_SendString("/nBoot ERROR:"); Uart_SendDWORD(err, TRUE); while (1); }
(7)超级终端中的显示结果,关于Eboot的移植将在后文中阐述