U靴“ nand markbad”无效

时间:2019-09-24 14:56:05

标签: u-boot

状况:带有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>

瞧瞧,“坏块”标记一直存在!下一个闪存存储操作巧妙地跳过了坏块,从而在闪存的各个分区中保存了一致的内核和文件系统。一直以来都是这个意图,但是似乎需要坚韧的手工工作。有没有自动化的方法?

1 个答案:

答案 0 :(得分:0)

自2014年以来,U-Boot发生了很大变化。可能与您的问题相关的补丁包括:

  • dc0b69fa9f97(“ mtd:nand:mxs_nand:允许启用BBT支持”)
  • c4adf9db5d38(“ spl:nand:sunxi:取消对所谓的“综合征”模式的支持”)
  • 8d1809a96699(“ spl:nand:简单:用芯片专用的read_buf()替换readb()”)

请用U-Boot Git HEAD重新测试。如果仍然缺少某些内容,请将其报告给U-Boot开发人员列表,甚至最好将其发送给您。