nand flash很容易有壞塊出現,一旦出現壞塊數據就會丟失,如果是燒錄、寫入的時候檢測到壞塊,驅動可以幫忙跳過不寫入之類的,但這不是一個完整的壞塊管理,因為我們還是需要寫入成功。
1、page和block的區別
- flash寫的時候一般以page為單位,一個page的大小是2KByte。
- flash擦除的時候一般以block為單位,一個block為64個page,所以一個block就是0x20000/128KByte。
- flash壞的時候都是以塊為單位,也就是我們說的壞塊。
2、新建nmbm分區
需要單獨給nmbm一個分區,用來做壞塊管理,還有壞塊備份。
- 分區的大小代碼里面是根據flash總大小算出一個給nmbm使用的分區大小。
- 默認是flash總大小的1/16
- 如果想修改nmbm分區大小,可以修改CONFIG_NMBM_MAX_RATIO和CONFIG_NMBM_MAX_BLOCKS值來調整。
比如我們的flash是128MByte,就是1024個block,會給出(128/16)=8MByte,也就是64個block為nmbm。
所以nmbm的起始block就是1024*(15/16)= 960,從960 block到1024
代碼如下:
static bool nmbm_create_new(struct nmbm_instance *ni)
{
bool success;
/* Determine the boundary of management blocks */
ni->mgmt_start_ba = ni->block_count * (NMBM_MGMT_DIV - ni->lower.max_ratio) / NMBM_MGMT_DIV;
if (ni->lower.max_reserved_blocks && ni->block_count - ni->mgmt_start_ba > ni->lower.max_reserved_blocks)
ni->mgmt_start_ba = ni->block_count - ni->lower.max_reserved_blocks;
nlog_info(ni, "NMBM management region starts at block %u [0x%08llx]\n",
ni->mgmt_start_ba, ba2addr(ni, ni->mgmt_start_ba));
nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba, ni->block_count - 1);
...
}
打印如下:
Initializing NMBM ...
NMBM management region starts at block 960 [0x07800000]
3、nmbm如何如何管理和備份壞塊
nmbm會在flash的把flash的最后一段地址,比如64個block做為壞塊管理分區,然后這64個block又被分為幾個模塊。
基礎的三個模塊:
- 主信息block:main_table_ba為nmbm分區的第一個block 960
- 備份信息block:backup_table_ba為nmbm分區的第四個block 963
- 簽名block:signature_ba為nmbm分區的最后一個block 1023
所有的壞塊管理內容都會被記錄到主信息block里面,內容包括:
- 整個flash有哪個block是壞的
- 這個壞的block是否被寫入替換了,替換的物理地址在nmbm分區的哪一block
- nmbm分區還剩余多少個block可以用來替換壞塊
如果有壞塊,nmbm是如何替換的:
- 最高替換block:mapping_blocks_top_ba,替換的時候,從nmbm分區的尾部開始替換,也就是1022塊開始。比如Bad block 794壞了,此時會使用1022塊來替換794塊,由nmbm來做映射管理。
- 最低替換block:mapping_blocks_ba,從1022往前替換,最多只能替換到964這個地址。
4、uboot下支持nmbm功能
打開nmbm功能支持
# CONFIG_NMBM is not set
CONFIG_NMBM=y
# CONFIG_ENABLE_NAND_NMBM is not set
CONFIG_ENABLE_NAND_NMBM=y
CONFIG_NMBM_MAX_RATIO=1
CONFIG_NMBM_MAX_BLOCKS=32
打開nmbm命令支持
CONFIG_CMD_NMBM=y
修改mtd分區設備為nmbm
CONFIG_NMBM_MTD=y
CONFIG_MTDIDS_DEFAULT="nand0=nand0"
CONFIG_MTDPARTS_DEFAULT="mtdparts=nand0:512k(u-boot),128k(uboot-env),128k(Factory),32768k(firmware),32768k(firmware2)"
CONFIG_MTDIDS_DEFAULT="nmbm0=nmbm0"
CONFIG_MTDPARTS_DEFAULT="mtdparts=nmbm0:512k(u-boot),128k(uboot-env),128k(Factory),32768k(firmware),32768k(firmware2)"
Uboot env信息也通過nmbm寫入
CONFIG_ENV_IS_IN_NAND=y
# CONFIG_ENV_IS_IN_NAND is not set
CONFIG_ENV_IS_IN_NMBM=y
日志信息
# CONFIG_NMBM_LOG_LEVEL_DEBUG is not set
CONFIG_NMBM_LOG_LEVEL_INFO=y
# CONFIG_NMBM_LOG_LEVEL_WARN is not set
# CONFIG_NMBM_LOG_LEVEL_ERR is not set
# CONFIG_NMBM_LOG_LEVEL_EMERG is not set
# CONFIG_NMBM_LOG_LEVEL_NONE is not set
5、uboot下nmbm信息查看
概況信息查下:
nmbm nmbm0 info
nmbm0:
Total blocks: 1024
Data blocks: 960
Management start block: 960
Info table size: 0x2000
Main info table start block: 960
Backup info table start block: 963
Signature block: 1023
Mapping blocks top address: 1022
Mapping blocks limit address: 964
整個falsh狀態查看:
nmbm nmbm0 state
Physical blocks:
Legends:
- Good data block
+ Good management block
B Bad block
I Main info table
i Backup info table
M Remapped spare block
S Signature block
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
--------------------------B-------------------------------------
----------------------------------------------------------------
------------------------------------------B---------------------
I++i++++++++++++++++++++++++B++++++++++++++++++++++++++++++++MMS```
啟動日志打?。?/p>
U-Boot SPL 2018.09 (Aug 12 2022 - 01:07:52 +0000)
Trying to boot from NAND
Initializing NMBM ...
NMBM management region starts at block 960 [0x07800000]
Bad block 794 [0x06340000]
Bad block 938 [0x07540000]
Bad block 988 [0x07b80000]
Signature has been written to block 1023 [0x07fe0000]
Main info table has been written to block 960
Backup info table has been written to block 963
Logic block 958 mapped to physical blcok 1022
Logic block 959 mapped to physical blcok 1021
NMBM has been successfully created
U-Boot 2018.09 (Aug 12 2022 - 01:07:52 +0000)
CPU: MediaTek MT7621AT ver 1, eco 3
Clocks: CPU: 880MHz, DDR: 1200MHz, Bus: 220MHz, XTAL: 40MHz
Model: MediaTek MT7621 reference board (NAND)
DRAM: 128 MiB
NAND: 128 MiB
Initializing NMBM ...
Signature found at block 1023 [0x07fe0000]
First info table with writecount 2 found in block 960
Second info table with writecount 2 found in block 963
NMBM has been successfully attached
Loading Environment from NMBM... OK
In: uartlite0@1e000c00
Out: uartlite0@1e000c00
Err: uartlite0@1e000c00
Net:
Warning: eth@1e100000 (eth0) using random MAC address - 7e:22:d3:74:d6:0b
eth0: eth@1e100000
DEBUG_UBOOT is OFF
******************************
Software System Reset Occurred
******************************
Saving Environment to NMBM... Erasing on NMBM...
Writing on NMBM... OK
OK
6、內核下支持nmbm功能
驅動配置開啟
CONFIG_NMBM=y
# CONFIG_NMBM_LOG_LEVEL_DEBUG is not set
CONFIG_NMBM_LOG_LEVEL_INFO=y
# CONFIG_NMBM_LOG_LEVEL_WARN is not set
# CONFIG_NMBM_LOG_LEVEL_ERR is not set
# CONFIG_NMBM_LOG_LEVEL_EMERG is not set
# CONFIG_NMBM_LOG_LEVEL_NONE is not set
CONFIG_NMBM_MTD=y
dts分區修改:
nmbm {
compatible = "generic,nmbm";
#address-cells = <1>;
#size-cells = <1>;
lower-mtd-device = <&nandflash>;
forced-create;
max-reserved-blocks = <32>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x00000 0x80000>;
read-only;
};
partition@80000 {
label = "uboot-env";
reg = <0x80000 0x20000>;
};
partition@140000 {
label = "Factory";
reg = <0x140000 0x20000>;
};
partition@1a0000 {
label = "firmware";
reg = <0x1a0000 0x2000000>;
};
partition@21a0000 {
label = "firmware2";
reg = <0x21a0000 0x2000000>;
};
};
};
內核啟動的時候也會跑到nmbm檢測,如果在uboot已經創建了nmbm的簽名塊,那內核就不會創建nmbm分區,直接讀取主信息block內容就可以。
如果Uboot后升級,那nmbm就沒有創建,內核會直接創建nmbm分區。
[ 1.187486] mtk-nand 1e003000.nand: Error applying setting, reverse things back
[ 1.195312] nand: device found, Manufacturer ID: 0xc2, Chip ID: 0xf1
[ 1.201665] nand: Macronix MX30LF1G28AD
[ 1.205513] nand: 128 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 128
[ 1.213751] Scanning device for bad blocks
[ 2.526704] Bad eraseblock 794 at 0x000006340000
[ 2.767685] Bad eraseblock 938 at 0x000007540000
[ 2.853815] Bad eraseblock 988 at 0x000007b80000
[ 2.918621] nmbm nmbm: Signature found at block 1023 [0x07fe0000]
[ 2.928248] nmbm nmbm: First info table with writecount 2 found in block 960
[ 2.945372] nmbm nmbm: Second info table with writecount 2 found in block 963
mtd分區的檢測,如果最后一個分區被其他分區占用(如log),則mtd會主動分割出nmbm使用。
[ 2.952510] nmbm nmbm: NMBM has been successfully attached
[ 2.958079] 13 ofpart partitions found on MTD device nmbm
[ 2.963473] Creating 13 MTD partitions on "nmbm":
[ 2.968194] 0x000000000000-0x000000060000 : "u-boot"
[ 2.974489] 0x000000060000-0x000000080000 : "uboot-env"
[ 3.108509] 0x000000140000-0x000000160000 : "Factory"
[ 3.121231] 0x0000001a0000-0x0000021a0000 : "firmware"
[ 3.341698] 2 fit-fw partitions found on MTD device firmware
[ 3.347396] 0x0000001a0000-0x000000520000 : "kernel"
[ 3.353704] 0x000000520000-0x0000021a0000 : "rootfs"
[ 3.360171] mtd: device 11 (rootfs) set to be root filesystem
[ 3.365994] 0x0000021a0000-0x0000041a0000 : "firmware2"
[ 3.385769] 0x000006c00000-0x000008000000 : "log"
[ 3.390571] mtd: partition "log" extends beyond the end of device "nmbm" -- size truncated to 0xc00000
7、內核下添加nmbm命令行支持
默認代碼內核沒辦法類似uboot下那也輸入命令行查看nmbm信息,可以通過添加最簡單的proc文件的方式查看。
將uboot下的查看信息函數簡單改造,拷到內核下即可
static int nmbm_state_show(struct seq_file *seq, void *v)
{
struct nmbm_mtd *nm;
enum nmmb_block_type bt;
uint32_t i;
list_for_each_entry(nm, &nmbm_devs, node) {
seq_printf(seq, "Physical blocks:\n");
seq_printf(seq, "\n");
seq_printf(seq, "Legends:\n");
seq_printf(seq, " - Good data block\n");
seq_printf(seq, " + Good management block\n");
seq_printf(seq, " B Bad block\n");
seq_printf(seq, " I Main info table\n");
seq_printf(seq, " i Backup info table\n");
seq_printf(seq, " M Remapped spare block\n");
seq_printf(seq, " S Signature block\n");
seq_printf(seq, "\n");
for (i = 0; i < nm->ni->block_count; i++) {
if (i % 64 == 0)
seq_printf(seq, " ");
bt = nmbm_debug_get_phys_block_type(nm->ni, i);
if (bt < __NMBM_BLOCK_TYPE_MAX)
seq_printf(seq, "%c", nmbm_block_legends[bt]);
else
seq_printf(seq, "?");
if (i % 64 == 63)
seq_printf(seq, "\n");
}
seq_printf(seq, "\n");
seq_printf(seq, "Logical blocks:\n");
seq_printf(seq, "\n");
seq_printf(seq, "Legends:\n");
seq_printf(seq, " - Good block\n");
seq_printf(seq, " + Initially remapped block\n");
seq_printf(seq, " M Remapped block\n");
seq_printf(seq, " B Bad/Unmapped block\n");
seq_printf(seq, "\n");
for (i = 0; i < nm->ni->data_block_count; i++) {
if (i % 64 == 0)
seq_printf(seq, " ");
if (nm->ni->block_mapping[i] < 0)
seq_printf(seq, "B");
else if (nm->ni->block_mapping[i] == i)
seq_printf(seq, "-");
else if (nm->ni->block_mapping[i] < nm->ni->data_block_count)
seq_printf(seq, "+");
else if (nm->ni->block_mapping[i] > nm->ni->mapping_blocks_top_ba &&
nm->ni->block_mapping[i] < nm->ni->signature_ba)
seq_printf(seq, "M");
else
seq_printf(seq, "?");
if (i % 64 == 63)
seq_printf(seq, "\n");
}
seq_printf(seq, "\n");
}
return 0;
}
static int nmbm_state_open(struct inode *inode, struct file *file)
{
return single_open(file, nmbm_state_show, PDE_DATA(inode));
}
static const struct file_operations nmbm_state_fops = {
.owner = THIS_MODULE,
.open = nmbm_state_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
之后就可以通過命令行查看
root@openwrt:~# cat /proc/nmbm_state
Physical blocks:
Legends:
- Good data block
+ Good management block
B Bad block
I Main info table
i Backup info table
M Remapped spare block
S Signature block
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
--------------------------B-------------------------------------
----------------------------------------------------------------
------------------------------------------B---------------------
----------------------------B---I++i++++++++++++++++++++++++MMMS