状况:带有Arm CPU的主板,旁边有Nand闪存。上电时,U-boot引导加载程序将启动,并将闪存内容复制到RAM,然后将控制权转移到RAM中的该代码。通过Buildroot组成的具有某些应用程序代码的Linux系统开始运行。它的整个文件系统作为单个UBIFS文件存储在闪存中,并且开始使用它。
设置了某个字节后,引导程序将保持控制,并开始TFTP传输以下载和存储新的闪存映像。
触发:板返回有缺陷。 Linux内核启动清楚地表明了这个问题:
[ 1.931150] Creating 8 MTD partitions on "atmel_nand":
[ 1.936285] 0x000000000000-0x000000040000 : "at91bootstrap"
[ 1.945280] 0x000000040000-0x0000000c0000 : "bootloader"
[ 1.954065] 0x0000000c0000-0x000000100000 : "bootloader env"
[ 1.963262] 0x000000100000-0x000000140000 : "bootloader redundant env"
[ 1.973221] 0x000000140000-0x000000180000 : "spare"
[ 1.981552] 0x000000180000-0x000000200000 : "device tree"
[ 1.990466] 0x000000200000-0x000000800000 : "kernel"
[ 1.999210] 0x000000800000-0x000010000000 : "rootfs"
...
[ 4.016251] ubi0: attached mtd7 (name "rootfs", size 248 MiB)
[ 4.022181] ubi0: PEB size: 131072 bytes (128 KiB), LEB size: 126976 bytes
[ 4.029040] ubi0: min./max. I/O unit sizes: 2048/2048, sub-page size 2048
[ 4.035941] ubi0: VID header offset: 2048 (aligned 2048), data offset: 4096
[ 4.042960] ubi0: good PEBs: 1980, bad PEBs: 4, corrupted PEBs: 0
[ 4.049033] ubi0: user volume: 2, internal volumes: 1, max. volumes count: 128
[ 4.056359] ubi0: max/mean erase counter: 2/0, WL threshold: 4096, image sequence number: 861993884
[ 4.065476] ubi0: available PEBs: 0, total reserved PEBs: 1980, PEBs reserved for bad PEB handling: 36
[ 4.074898] ubi0: background thread "ubi_bgt0d" started, PID 77
...
[ 4.298009] UBIFS (ubi0:0): UBIFS: mounted UBI device 0, volume 0, name "rootfs", R/O mode
[ 4.306415] UBIFS (ubi0:0): LEB size: 126976 bytes (124 KiB), min./max. I/O unit sizes: 2048 bytes/2048 bytes
[ 4.316418] UBIFS (ubi0:0): FS size: 155926528 bytes (148 MiB, 1228 LEBs), journal size 9023488 bytes (8 MiB, 72 LEBs)
[ 4.327197] UBIFS (ubi0:0): reserved for root: 0 bytes (0 KiB)
[ 4.333095] UBIFS (ubi0:0): media format: w4/r0 (latest is w5/r0), UUID AE9F77DC-04AF-433F-92BC-D3375C83B518, small LPT model
[ 4.346924] VFS: Mounted root (ubifs filesystem) readonly on device 0:15.
[ 4.356186] devtmpfs: mounted
[ 4.367038] Freeing unused kernel memory: 1024K
[ 4.371812] Run /sbin/init as init process
[ 4.568143] UBIFS (ubi0:1): background thread "ubifs_bgt0_1" started, PID 83
[ 4.644809] UBIFS (ubi0:1): recovery needed
[ 4.685823] ubi0 warning: ubi_io_read: error -74 (ECC error) while reading 126976 bytes from PEB 235:4096, read only 126976 bytes, retry
[ 4.732212] ubi0 warning: ubi_io_read: error -74 (ECC error) while reading 126976 bytes from PEB 235:4096, read only 126976 bytes, retry
[ 4.778705] ubi0 warning: ubi_io_read: error -74 (ECC error) while reading 126976 bytes from PEB 235:4096, read only 126976 bytes, retry
[ 4.824159] ubi0 error: ubi_io_read: error -74 (ECC error) while reading 126976 bytes from PEB 235:4096, read 126976 bytes
...导致异常,但是内核继续运行,然后检测到另一个错误:
[ 5.071518] ubi0 warning: ubi_io_read: error -74 (ECC error) while reading 126976 bytes from PEB 709:4096, read only 126976 bytes, retry
[ 5.118110] ubi0 warning: ubi_io_read: error -74 (ECC error) while reading 126976 bytes from PEB 709:4096, read only 126976 bytes, retry
[ 5.164447] ubi0 warning: ubi_io_read: error -74 (ECC error) while reading 126976 bytes from PEB 709:4096, read only 126976 bytes, retry
[ 5.210987] ubi0 error: ubi_io_read: error -74 (ECC error) while reading 126976 bytes from PEB 709:4096, read 126976 bytes
...但是令人印象深刻的是,该系统仍然可以运行并且运行良好。 为什么内核不将这些闪存块标记为坏块?这些数据还是无法读取,并且至少下一个图像闪烁可能会跳过这些坏块...
调查:因此,内核在闪存的“ rootfs”分区中发现有缺陷的PEB#235(十进制)。每个PEB为128KB,因此错误位于字节30,801,920(十进制)之后。由于“ rootfs”分区仅从闪存的字节0x800000开始,因此实际损坏的页面必须超出字节39,190,528(十进制)或0x2560000。确实可以肯定,在U-boot中使用 nand read 实用程序时:
U-Boot> nand read 0x20000000 0x2560000 0x1000
NAND read: device 0 offset 0x2560000, size 0x1000
4096 bytes read: OK
U-Boot> nand read 0x20000000 0x2561000 0x1000
NAND read: device 0 offset 0x2561000, size 0x1000
4096 bytes read: OK
U-Boot> nand read 0x20000000 0x2562000 0x1000
NAND read: device 0 offset 0x2562000, size 0x1000
PMECC: Too many errors
NAND read from offset 2562000 failed -5
0 bytes read: ERROR
因此,损坏的页面位于该闪存块内的偏移量8K处。 从其他各种帖子中,我了解到nand flash具有以128K块组织的2K页,在每2048个有效负载字节上有64个额外的“带外”字节,使每个页面的总大小为2112字节。无论如何,整个128K块都将被废弃,因为这是擦除大小。没问题,有足够的存储空间,我只想确保下一次闪烁将跳过此不良块。 由于Linux内核和引导加载程序都没有费心去标记坏块,因此我将在U-boot中手动完成此操作:
U-Boot> nand markbad 2562000
block 0x02562000 successfully marked as bad
对第二个不良闪存页面的类似调查显示,另一个错误位于闪存地址0x60a1000:
U-Boot> nand read 0 60A1000 800
NAND read: device 0 offset 0x60a1000, size 0x800
PMECC: Too many errors
NAND read from offset 60a1000 failed -5
0 bytes read: ERROR
因此在这里, nand markbad 实用程序也用于在此块上手动添加永久标记:
U-Boot> nand markbad 60a1000
block 0x060a1000 successfully marked as bad
并验证是否考虑了所有问题:
U-Boot> nand bad
Device 0 bad blocks:
02560000
060a0000
就像应该这样-从每个128K块的开头开始,两个块都被标记。
问题:所以我了解到,将64个OOB字节划分为2个字节的标记,38个字节的纠错代码和24个字节的日志记录。在每个2048个有效负载字节附带的所有OOB字节中,只有最开始的64个字节(伴随2KB的第一页)附加了其2个字节的标记代码,以指示整个128KB块的状态。应该在闪存设备本身中修改这两个字节 ,以使该状态持续存在。因此,在我的U-boot会话中,我没有启动Linux系统,而是重新启动了CPU并保留在U-boot中:
U-Boot> reset
resetting ...
RomBOOT
ba_offset = 0xc ...
AT91Bootstrap 3.6.0-00029-g0cd4e6a (Wed Nov 12 12:14:04 CET 2014)
NAND: ONFI flash detected
NAND: Manufacturer ID: 0x2c Chip ID: 0x32
NAND: Disable On-Die ECC
PMECC: page_size: 0x800, oob_size: 0x40, pmecc_cap: 0x4, sector_size: 0x200
NAND: Initialize PMECC params, cap: 0x4, sector: 0x200
NAND: Image: Copy 0x80000 bytes from 0x40000 to 0x26f00000
NAND: Done to load image
U-Boot 2013.10-00403-g1f9a20a (Nov 12 2014 - 12:14:27)
CPU: SAMA5D31
Crystal frequency: 12 MHz
CPU clock : 528 MHz
Master clock : 132 MHz
DRAM: 128 MiB
NAND: 256 MiB
MMC: mci: 0
In: serial
Out: serial
Err: serial
Net: macb0
Hit any key to stop autoboot: 0
U-Boot> nand info
Device 0: nand0, sector size 128 KiB
Page size 2048 b
OOB size 64 b
Erase size 131072 b
U-Boot> nand bad
Device 0 bad blocks:
U-Boot>
坏块已被遗忘-标记代码未未持续应用? 当然,此U-boot版本似乎很旧。从那时起, nand markbad 实用程序是否得到了改进?
解决方法:我自己修改了坏块内第一页的OOB字节。我将第一页的所有2112字节读取到RAM中,然后修改了2字节的标记代码,并将2112字节从RAM中写回到闪存中。从技术上讲,我应该擦除整个128K闪存页面,然后写回所有128K内容。但是我的懒惰今天受到了足够的挑战。可以将Nand闪存从1任意切换为0-这是很困难的反向操作,需要擦除才能将整个128K页面恢复为全0xFF。我注意到所有“块良好”标记都编码为0xFFFF,所以我认为写“ 0x0000”就足够了。
U-Boot> nand read.raw 0x20200000 0x2560000 1
NAND read: 2112 bytes read: OK
nand.read 的格式有点古怪,而 nand.read 则期望 size 作为最后一个参数字节,它希望 size 用页数表示。第一页就是我们所需要的,因此参数“ 1”可以解决问题。现在可以使用U-boot的 md 实用程序检查内容,这些内容已经转移到RAM中:
U-Boot> md 0x20200000 0x210
20200000: 23494255 00000001 00000000 01000000 UBI#............
20200010: 00080000 00100000 9cfb6033 00000000 ........3`......
...
202007e0: 00000000 00000000 00000000 00000000 ................
202007f0: 00000000 00000000 00000000 00000000 ................
20200800: ffffffff ffffffff ffffffff ffffffff ................
20200810: ffffffff ffffffff ffffffff ffffffff ................
20200820: ffffffff b0c9aa24 0008fdb8 00000000 ....$...........
20200830: 00000000 00000000 00000000 00000000 ................
请注意 md 实用程序如何期望其 size 参数以另一种格式出现:该期望以单词为单位。只是为了让我们保持警惕。 地址为0x20200800的转储清楚地显示了 markbad 是如何达到目的的:坏块的2个标记字节仍然位于0xFFFF上。
然后修改这些字节,便会派上另一个U-boot实用程序:
U-Boot> mm 0x20200800
20200800: ffffffff ? 00000000
20200804: ffffffff ? q
这有点粗糙,我更改了 4 个第一个OOB字节,而不仅仅是 2 个第一个标记字节。 Finall,将修改后的内容写回到flash中:
U-Boot> nand write.raw 0x20200000 0x2560000 1
NAND write: 2112 bytes written: OK
有趣的是,不正确诊断程序没有注意到刚刚被标记的块,即使在进行了可以失败。
U-Boot> nand bad
Device 0 bad blocks:
U-Boot>
但这不是引起警报的原因。以同样的方式手动标记了第二个坏块,并在再次重置时:
U-Boot> reset
resetting ...
RomBOOT
ba_offset = 0xc ...
AT91Bootstrap 3.6.0-00029-g0cd4e6a (Wed Nov 12 12:14:04 CET 2014)
...
U-Boot 2013.10-00403-g1f9a20a (Nov 12 2014 - 12:14:27)
...
Hit any key to stop autoboot: 0
U-Boot> nand bad
Device 0 bad blocks:
02560000
060a0000
U-Boot>
瞧瞧,“坏块”标记一直存在!下一个闪存存储操作巧妙地跳过了坏块,从而在闪存的各个分区中保存了一致的内核和文件系统。一直以来都是这个意图,但是似乎需要坚韧的手工工作。有没有自动化的方法?
答案 0 :(得分:0)
自2014年以来,U-Boot发生了很大变化。可能与您的问题相关的补丁包括:
请用U-Boot Git HEAD重新测试。如果仍然缺少某些内容,请将其报告给U-Boot开发人员列表,甚至最好将其发送给您。