最近核心闆進行了改版,以前兩個bootloader放在dataflash裡面,現在闆子上隻留了nand,是以bootstrap,u-boot,kernel,fs全存放在了nand上。同時,除了原有的256m,也新改出了一批64m nand的闆子,針對這兩種情況,需要對bsp進行修改。
同為256m的相對好改,隻涉及到samba的燒寫流程,偏移量,鏡像生成等,沒啥好說的。問題主要出在64m nand,由于原有的256使用的是yaffs2,而64m則是yaffs,是以有一定修改量,也出了些問題。我們主要靠u-boot燒寫核心及fs的鏡像,而問題就出現在fs的燒寫上。最詭異的問題是,幾塊闆子有的一直沒問題,剩下的有時候有問題有時候又沒問題,很難定性問題.
bootstrap的修改參考:
http://blog.csdn.net/kailan818/archive/2009/12/14/5004934.aspx
u-boot的鏡像燒寫修改參考:
http://blogold.chinaunix.net/u3/90065/showart_1780393.html
mkyaffsimage自然也不能用以前256m使用的工具,圖省事,我拿了tq2440開發闆自帶的為64m記憶體提供的工具,誰知道其實給我添了不少麻煩。
bsp修改完,用samba把bootstrap,u-boot燒寫到nand上,随後再用u-boot通過u盤燒寫kernel,fs。完畢重新開機後進入系統發現,隻要有寫操作,yaffs就會報page xxx in gc has no object....的錯誤。跟蹤發現這是yaffs損耗平衡出錯資訊。問題是将将寫一點東西,怎麼會需要做損耗均衡,并且出錯呢?經過一長串痛苦的跟蹤,發現最終是ecc對不上的問題。
問題看來出在鏡像燒寫上,那麼我們來review一下後來添加的代碼:
343 }else if ( s != NULL && !strcmp(s, ".yaffs")){
344 nand_write_options_t opts;
345 memset(&opts, 0, sizeof(opts));
346 opts.buffer = (u_char*) addr;
347 opts.length = size;
348 opts.offset = off;
349
350 opts.noecc = 1;
351 opts.writeoob = 1;
352 opts.blockalign = 1;
353 opts.quiet = quiet;
354 opts.skipfirstblk = 1;
355 ret = nand_write_opts(nand, &opts);
最終證明,第350行導緻出錯,那麼一步步跟下去,看看為什麼我這裡無法采用這個選項。 進入nand_write_opts: 338 339 if (opts->noecc) { 340 memcpy(&meminfo->oobinfo, &none_oobinfo, 341 sizeof(meminfo->oobinfo)); 342 oobinfochanged = 1; 343 } 這裡使用none_oobinfo: 266 static struct nand_oobinfo none_oobinfo = { 267 .useecc = MTD_NANDECC_OFF, 268 }; 可以看到,這裡的ecc标志是eccoff。 繼續往後走: 395
396 while (imglen && (mtdoffset < meminfo->size)) {
。。。。。。 454 455 memcpy(data_buf, buffer, readlen); 456 buffer += readlen; 457 458 if (opts->writeoob) { 459 461 memcpy(oob_buf, buffer, meminfo->oobsize); 462 buffer += meminfo->oobsize; 463 464 466 result = meminfo->write_oob(meminfo, 467 mtdoffset, 468 meminfo->oobsize, 469 &written, 470 (unsigned char *) 471 &oob_buf); 。。。。。。 480 481 482 result = meminfo->write(meminfo, 483 mtdoffset, 484 meminfo->oobblock, 485 &written, 486 (unsigned char *) &data_buf); 下面開始按頁寫資料,從記憶體位址内讀鏡像向nand裡寫。注意由于是64m,是以是512+16。第466行調用writeoob先寫16位元組的oob資料。在第482行調用write開始寫頁。注意這裡每頁寫完也根據情況計算ecc并且重填前面的oob區,分析我們關心的write,其實是nand_write: 1620 static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) 1621 { 1622 return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL)); 1623 } 再進入nand_write: 1679 1680 if (oobsel == NULL) 1681 oobsel = &mtd->oobinfo; 1682 1683 1684 if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { 1685 oobsel = this->autooob; 1686 autoplace = 1; 1687 } 1688 if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) 1689 autoplace = 1; 由于nand_write傳入的oobsel是null,是以這裡會選擇我們以前設定的結構體,用的也是我們指定的校驗方法,後面開始寫頁: 1706 1707 while (written < len) { 。。。。。。 1715 ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0)); 再進去看一看: 903 int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; 。。。。。。 915 switch (eccmode) { 916 917 case NAND_ECC_NONE: 918 // printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); 919 this->write_buf(mtd, this->data_poi, mtd->oobblock); 920 break; 。。。。。。 932 default: 933 eccbytes = this->eccbytes; 934 for (; eccsteps; eccsteps--) { 935 936 this->enable_hwecc(mtd, NAND_ECC_WRITE); 937 this->write_buf(mtd, &this->data_poi[datidx], this->eccsize); 938 this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code); 939 for (i = 0; i < eccbytes; i++, eccidx++) 940 oob_buf[oob_config[eccidx]] = ecc_code[i]; 。。。。。。 955 this->write_buf(mtd, oob_buf, mtd->oobsize); 這裡會根據我們選擇的ecc方式來做。首先我們看到,不管是無ecc,還是注釋掉noecc=1後走的default分支,都是先write_buf這個512位元組的data區,如果是noecc,那麼就打住了。由于鏡像中本身就有mkyaffsimage時生成的ecc,是以這裡隻是單純的拷貝。而下面的分支,則會在寫完nand以後,再進行一個ecc計算,随後根據計算結果重填前面已寫過的oob區的相關位。938行計算ecc,940重填,第955行重寫oob。這裡noecc也會重填,不過由于無修改,是以無所謂。 為什麼u-boot在mkyaffsimage已經算過ecc的情況下還要再計算一次才正确呢?其實這裡有一個ecc配合的問題。ecc有mtd及yaffs兩種,算法各不相同,是以一定要對應上。核心裡編譯選項選擇的是let yaffs do its own ecc,那麼鏡像制作也一定要按照這個格式。這裡由于偷懶,直接拿别人的工具,沒源碼也不知道實作,u-boot燒寫部分又照抄網上的文章沒仔細研究,核心也按照以前的需求來配的,是以3個都沒有對上,導緻了錯誤。除了u-boot的noecc=1去掉,也可以保留這裡不動,将核心裡let yaffs do its own ecc去掉, 讓mtd層來做, 一樣可以。總之鏡像的校驗和核心使用的ecc一定要對上。
其實最終的問題解決,隻是注釋掉一個noecc,或者改一個核心選項,但足足調了n久。是以網絡的友善使得問題的解決變的簡單,但有時候也會帶來麻煩。在可能的情況下,還是要對抄來的代碼進行研究,了解背後的原因,知其是以然,否則不但學不到東西,還有可能導緻莫名其妙或者難以解決的問題。