资讯动态

Nor flash 写保护

发布时间:2026/9/29 11:26:29 来源:尧图企业网站定制
1 spi 访问总线参考其他的spi 总线访问2spi cmd3 写保护接口分析状态寄存器-1 位定义位名称功能S7SRP状态寄存器保护配合 WPS 位S6SEC扇区/块保护选择064KB 块14KB 扇区S5TB顶部/底部保护选择0顶部1底部S4BP2块保护位 2S3BP1块保护位 1S2BP0块保护位 0S1WEL写使能锁存只读S0BUSY写进行中只读保护 1/2 容量8MB的配置对于 W25Q128JV16MB保护上半部分 8MB地址0x800000 - 0xFFFFFF的配置为SECTBBP2BP1BP0保护区域00101128~255 块8MBUpper 1/2uboot 分析#ifndef _RTK_NOR_PROTECT_H_ #define _RTK_NOR_PROTECT_H_ #include common.h #include spi_flash.h /* --- 指令集 (参考 Figure 8.1.2) --- */ #define CMD_WRITE_ENABLE 0x06 #define CMD_WRITE_DISABLE 0x04 #define CMD_READ_STATUS1 0x05 #define CMD_WRITE_STATUS1 0x01 #define CMD_READ_STATUS2 0x35 #define CMD_WRITE_STATUS2 0x31 #define CMD_READ_STATUS3 0x15 #define CMD_WRITE_STATUS3 0x11 /* 独立块锁指令 (参考 Figure 7.1.10) */ #define CMD_INDIVIDUAL_BLOCK_LOCK 0x36 #define CMD_INDIVIDUAL_BLOCK_UNLOCK 0x39 #define CMD_READ_BLOCK_LOCK 0x3D #define CMD_GLOBAL_BLOCK_LOCK 0x7E #define CMD_GLOBAL_BLOCK_UNLOCK 0x98 /* --- Status Register-1 位定义 (S7-S0) --- */ #define SR1_SRP (1 7) // Status Register Protect #define SR1_SEC (1 6) // Sector/Block Protect #define SR1_TB (1 5) // Top/Bottom Protect #define SR1_BP2 (1 4) #define SR1_BP1 (1 3) #define SR1_BP0 (1 2) #define SR1_WEL (1 1) // Write Enable Latch #define SR1_BUSY (1 0) // Write In Progress /* --- Status Register-2 位定义 (S15-S8) --- */ #define SR2_SUS (1 7) // Suspend Status #define SR2_CMP (1 6) // Complement Protect #define SR2_LB3 (1 5) #define SR2_LB2 (1 4) #define SR2_LB1 (1 3) #define SR2_QE (1 1) // Quad Enable #define SR2_SRL (1 0) // Status Register Lock #endif /* _RTK_NOR_PROTECT_H_ *//* 假设的底层 SPI 读写函数需根据瑞昱 SDK 替换 */ extern int rtk_spi_flash_read(u8 *buf, u32 addr, u32 len); extern int rtk_spi_flash_write(u8 *buf, u32 addr, u32 len); extern int rtk_spi_flash_cmd_write(u8 cmd, u32 addr, u8 *buf, u32 len); /* 等待 Flash 内部操作完成 (轮询 BUSY 位 S0) */ static int nor_wait_ready(void) { u8 status; int timeout 100000; // 超时计数 do { /* 发送 0x05 读取 SR1 */ if (rtk_spi_flash_cmd_write(CMD_READ_STATUS1, 0, status, 1) ! 0) return -1; if ((status SR1_BUSY) 0) return 0; udelay(10); } while (timeout-- 0); printf(NOR: Wait ready timeout!\n); return -1; } /* 写使能 */ static int nor_write_enable(void) { u8 cmd CMD_WRITE_ENABLE; if (rtk_spi_flash_cmd_write(cmd, 0, NULL, 0) ! 0) return -1; return 0; } /* 读取状态寄存器 */ int rtk_nor_read_sr(u8 reg_num, u8 *val) { u8 cmd; if (reg_num 1) cmd CMD_READ_STATUS1; else if (reg_num 2) cmd CMD_READ_STATUS2; else if (reg_num 3) cmd CMD_READ_STATUS3; else return -1; return rtk_spi_flash_cmd_write(cmd, 0, val, 1); } /* 写入状态寄存器 */ int rtk_nor_write_sr(u8 reg_num, u8 val) { u8 cmd; if (nor_wait_ready() ! 0) return -1; if (nor_write_enable() ! 0) return -1; if (reg_num 1) cmd CMD_WRITE_STATUS1; else if (reg_num 2) cmd CMD_WRITE_STATUS2; else if (reg_num 3) cmd CMD_WRITE_STATUS3; else return -1; /* 注意写状态寄存器通常需要发送数据 */ if (rtk_spi_flash_cmd_write(cmd, 0, val, 1) ! 0) return -1; return nor_wait_ready(); }/* 全局块锁 (发送 0x7E) - 锁定所有块 */ int rtk_nor_global_block_lock(void) { if (nor_write_enable() ! 0) return -1; if (rtk_spi_flash_cmd_write(CMD_GLOBAL_BLOCK_LOCK, 0, NULL, 0) ! 0) return -1; return nor_wait_ready(); } /* 全局块解锁 (发送 0x98) - 解锁所有块 */ int rtk_nor_global_block_unlock(void) { if (nor_write_enable() ! 0) return -1; if (rtk_spi_flash_cmd_write(CMD_GLOBAL_BLOCK_UNLOCK, 0, NULL, 0) ! 0) return -1; return nor_wait_ready(); } /* 独立块锁 - 锁定指定地址所在的块/扇区 */ int rtk_nor_block_lock(u32 addr) { if (nor_write_enable() ! 0) return -1; /* 发送 0x36 24位地址 */ if (rtk_spi_flash_cmd_write(CMD_INDIVIDUAL_BLOCK_LOCK, addr, NULL, 0) ! 0) return -1; return nor_wait_ready(); } /* 独立块解锁 - 解锁指定地址所在的块/扇区 */ int rtk_nor_block_unlock(u32 addr) { if (nor_write_enable() ! 0) return -1; /* 发送 0x39 24位地址 */ if (rtk_spi_flash_cmd_write(CMD_INDIVIDUAL_BLOCK_UNLOCK, addr, NULL, 0) ! 0) return -1; return nor_wait_ready(); } /* 读取指定地址的块锁状态 (返回 1: 锁定, 0: 未锁定) */ int rtk_nor_is_block_locked(u32 addr) { u8 lock_status 0; /* 发送 0x3D 24位地址读取 1 字节 */ if (rtk_spi_flash_cmd_write(CMD_READ_BLOCK_LOCK, addr, lock_status, 1) ! 0) return -1; /* 状态字节的 bit0 为 1 表示锁定为 0 表示未锁定 */ return (lock_status 0x01) ? 1 : 0; }命令行测试#include command.h static int do_rtk_nor_protect(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { if (argc 3) { printf(Usage: rtk_nor_protect lock|unlock|status addr [len]\n); return CMD_RET_USAGE; } char *cmd argv[1]; u32 addr simple_strtoul(argv[2], NULL, 16); u32 len (argc 3) ? simple_strtoul(argv[3], NULL, 16) : 0x1000; // 默认 4KB if (strcmp(cmd, lock) 0) { rtk_nor_protect_range(addr, len); } else if (strcmp(cmd, unlock) 0) { rtk_nor_unprotect_range(addr, len); } else if (strcmp(cmd, status) 0) { int locked rtk_nor_is_block_locked(addr); printf(Address 0x%08X is %s\n, addr, locked ? LOCKED : UNLOCKED); } else { return CMD_RET_USAGE; } return CMD_RET_SUCCESS; } U_BOOT_CMD( rtk_nor_protect, 4, 0, do_rtk_nor_protect, RTK NOR Flash Protect Commands, lock addr [len] - Lock block/sector\n unlock addr [len] - Unlock block/sector\n status addr - Check lock status );/* include/spi_flash.h 中添加 */ struct spi_flash { /* ... 现有成员 ... */ int (*flash_lock)(struct spi_flash *flash, u32 ofs, size_t len); int (*flash_unlock)(struct spi_flash *flash, u32 ofs, size_t len); int (*flash_is_locked)(struct spi_flash *flash, u32 ofs, size_t len); }; /** * spi_flash_protect - 通用写保护接口 * flash: SPI flash 设备 * start: 起始地址 * len: 保护长度 * prot: true保护false解除保护 * return: 0 成功负值失败 */ int spi_flash_protect(struct spi_flash *flash, u32 start, size_t len, bool prot) { if (prot) return flash-flash_lock(flash, start, len); else return flash-flash_unlock(flash, start, len); }/* common/cmd_sf.c 中添加 */ static int do_spi_protect(int argc, char * const argv[]) { int ret 0; loff_t start, len; bool prot false; if (argc ! 4) return -1; if (!str2off(argv[2], start)) { puts(start sector is not a valid number\n); return 1; } if (!str2off(argv[3], len)) { puts(len is not a valid number\n); return 1; } if (strcmp(argv[1], lock) 0) prot true; else if (strcmp(argv[1], unlock) 0) prot false; else return -1; ret spi_flash_protect(flash, start, len, prot); return ret 0 ? 0 : 1; } /* 在 do_spi_flash 命令表中注册 */ else if (strcmp(cmd, protect) 0) ret do_spi_protect(argc, argv);/* common/cmd_sf.c 中添加 */ static int do_spi_protect(int argc, char * const argv[]) { int ret 0; loff_t start, len; bool prot false; if (argc ! 4) return -1; if (!str2off(argv[2], start)) { puts(start sector is not a valid number\n); return 1; } if (!str2off(argv[3], len)) { puts(len is not a valid number\n); return 1; } if (strcmp(argv[1], lock) 0) prot true; else if (strcmp(argv[1], unlock) 0) prot false; else return -1; ret spi_flash_protect(flash, start, len, prot); return ret 0 ? 0 : 1; } /* 在 do_spi_flash 命令表中注册 */ else if (strcmp(cmd, protect) 0) ret do_spi_protect(argc, argv);测试 sf probe 0SF: Detected W25Q128JV with page size 256 Bytes, erase size 64 KiB, total 16 MiB sf protect lock 0x800000 0x800000# 保护上半部分 8MB sf erase 0x800000 0x10000offset 0x800000 is protected and cannot be erasedSF: 65536 bytes 0x800000 Erased: ERROR sf protect unlock 0x800000 0x800000 sf erase 0x800000 0x10000SF: 65536 bytes 0x800000 Erased: OK内核相关配置写保护相关宏定义配置/* drivers/mtd/spi-nor/spi-nor.c */ #define SR_BP0 BIT(2) /* Block protect 0 */ #define SR_BP1 BIT(3) /* Block protect 1 */ #define SR_BP2 BIT(4) /* Block protect 2 */ #define SR_TB BIT(5) /* Top/Bottom protect */ #define SR_SEC BIT(6) /* Sector/Block protect */ #define SR_SRP BIT(7) /* Status register protect */ /** * stm_lock - 基于 ST Micro 保护模型的锁定接口 * nor: spi_nor 结构体指针 * ofs: 起始偏移 * len: 保护长度 * * 计算 BP[2:0] 值保护指定区域 */ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) { struct mtd_info *mtd nor-mtd; u8 status_old, status_new; u8 mask SR_BP2 | SR_BP1 | SR_BP0; loff_t lock_len; int ret; /* 读取当前状态寄存器 */ status_old read_sr(nor); if (status_old 0) return status_old; /* 计算需要保护的长度 */ if (ofs len mtd-size) lock_len ofs len - (mtd-size / 2); else lock_len ofs len; /* 根据保护长度计算 BP 位值 */ if (lock_len 0) { dev_err(nor-dev, invalid Length to protect\n); return -EINVAL; } /* 保护 1/2 容量8MB */ if (lock_len mtd-size / 2) { /* 保护上半部分BP21, BP10, BP01, TB0 */ status_new (status_old ~mask) | SR_BP2 | SR_BP0; } else if (lock_len mtd-size / 64) { status_new (status_old ~mask) | SR_BP0; } else if (lock_len mtd-size / 32) { status_new (status_old ~mask) | SR_BP1; } else if (lock_len mtd-size / 16) { status_new (status_old ~mask) | SR_BP1 | SR_BP0; } else if (lock_len mtd-size / 8) { status_new (status_old ~mask) | SR_BP2; } else if (lock_len mtd-size / 4) { status_new (status_old ~mask) | SR_BP2 | SR_BP0; } else { status_new (status_old ~mask) | SR_BP2 | SR_BP1 | SR_BP0; } /* 只在需要增加保护时写入 */ if ((status_new mask) (status_old mask)) return 0; write_enable(nor); ret write_sr(nor, status_new); if (ret 0) return ret; return write_sr_and_check(nor, status_new, mask); } /** * stm_unlock - 基于 ST Micro 保护模型的解锁接口 */ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) { struct mtd_info *mtd nor-mtd; u8 status_old, status_new; u8 mask SR_BP2 | SR_BP1 | SR_BP0; loff_t lock_len; int ret; status_old read_sr(nor); if (status_old 0) return status_old; if (ofs len mtd-size) lock_len ofs len - (mtd-size / 2); else lock_len ofs len; if (lock_len 0) { dev_err(nor-dev, invalid Length to unlock\n); return -EINVAL; } /* 计算解锁后应保留的保护位 */ if (lock_len mtd-size / 64) { status_new (status_old ~mask); } else if (lock_len mtd-size / 32) { status_new (status_old ~mask) | SR_BP0; } else if (lock_len mtd-size / 16) { status_new (status_old ~mask) | SR_BP1; } else if (lock_len mtd-size / 8) { status_new (status_old ~mask) | SR_BP1 | SR_BP0; } else if (lock_len mtd-size / 4) { status_new (status_old ~mask) | SR_BP2; } else if (lock_len mtd-size / 2) { status_new (status_old ~mask) | SR_BP2 | SR_BP0; } else { status_new (status_old ~mask) | SR_BP2 | SR_BP1 | SR_BP0; } if ((status_new mask) (status_old mask)) return 0; write_enable(nor); ret write_sr(nor, status_new); if (ret 0) return ret; return write_sr_and_check(nor, status_new, mask); }/* drivers/mtd/spi-nor/spi-nor.c - 设备表 */ static const struct flash_info spi_nor_ids[] { /* ... 其他设备 ... */ { w25q128, INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, /* ... */ }; /* spi_nor_scan 中注册保护回调 */ if (JEDEC_MFR(info) SNOR_MFR_ST || JEDEC_MFR(info) SNOR_MFR_MICRON || JEDEC_MFR(info) SNOR_MFR_WINBOND) { nor-flash_lock stm_lock; nor-flash_unlock stm_unlock; nor-flash_is_locked stm_is_locked; }测试代码/* 用户空间测试程序通过 ioctl 操作 MTD 写保护 */ #include stdio.h #include fcntl.h #include sys/ioctl.h #include mtd/mtd-user.h #define MTD_DEVICE /dev/mtd0 #define PROTECT_START 0x800000 #define PROTECT_LEN 0x800000 int main(void) { int fd; struct erase_info_user erase; int ret; fd open(MTD_DEVICE, O_RDWR); if (fd 0) { perror(open MTD_DEVICE); return -1; } /* 1. 保护上半部分 8MB */ printf(Locking 0x%x - 0x%x...\n, PROTECT_START, PROTECT_START PROTECT_LEN); /* 通过 SPI-NOR 驱动接口设置保护 */ ret ioctl(fd, MEMLOCK, (struct erase_info_user){ .start PROTECT_START, .length PROTECT_LEN }); if (ret 0) { perror(MEMLOCK failed); close(fd); return -1; } printf(Lock OK\n); /* 2. 尝试擦除受保护区域应失败 */ erase.start PROTECT_START; erase.length 0x10000; ret ioctl(fd, MEMERASE, erase); if (ret 0) { printf(Erase protected region: FAILED (expected)\n); } else { printf(ERROR: Erase protected region succeeded!\n); } /* 3. 尝试擦除非保护区域应成功 */ erase.start 0x000000; erase.length 0x10000; ret ioctl(fd, MEMERASE, erase); if (ret 0) { printf(Erase unprotected region: OK\n); } else { perror(Erase unprotected region failed); } /* 4. 解除保护 */ printf(Unlocking 0x%x - 0x%x...\n, PROTECT_START, PROTECT_START PROTECT_LEN); ret ioctl(fd, MEMUNLOCK, (struct erase_info_user){ .start PROTECT_START, .length PROTECT_LEN }); if (ret 0) { perror(MEMUNLOCK failed); close(fd); return -1; } printf(Unlock OK\n); /* 5. 再次擦除此时应成功 */ erase.start PROTECT_START; erase.length 0x10000; ret ioctl(fd, MEMERASE, erase); if (ret 0) { printf(Erase after unlock: OK\n); } else { perror(Erase after unlock failed); } close(fd); return 0; }

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑