第一部分 可能的坑

一、4MB区域分区详解

1.1 4MB区域的分区方式

方法一:GPT分区表方式(推荐)
# Linux下创建分区
sudo parted /dev/mmcblk0 --script \
    mklabel gpt \
    mkpart primary ext4 1MB 5MB \
    name 1 emmc_region \
    set 1 legacy_boot off
    
# 验证分区
sudo parted /dev/mmcblk0 print
# 输出:
# Number  Start   End     Size    File system  Name         Flags
# 1      1049kB  5243kB  4194kB  ext4         emmc_region
方法二:固定偏移量方式(无分区表)
/* 在eMMC固定位置预留4MB,不使用分区表 */
#define EMMC_RESERVED_START_LBA  2048    /* 起始块号(1MB对齐) */
#define EMMC_RESERVED_SIZE_LBA   8192    /* 4MB = 8192块(512B/块) */
​
/* 注意:此方式需要确保该区域不被其他分区覆盖 */

1.2 4MB区域的实际用途规划

┌─────────────────────────────────────────────────────────────────┐
│                   4MB eMMC 区域布局规划                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌────────────────────────────────────────────────────────────┐ │
│  │ Offset 0x000000 - 0x0FFFFF (1MB) - 关键数据区              │ │
│  │  ├─ 0x000000-0x00FFFF: 设备配置 (64KB)                    │ │
│  │  │   ├─ MAC地址、序列号、校准数据                          │ │
│  │  │   └─ 冗余备份 (镜像)                                    │ │
│  │  ├─ 0x010000-0x07FFFF: 工厂参数 (448KB)                   │ │
│  │  │   └─ 生产测试数据、硬件配置                              │ │
│  │  └─ 0x080000-0x0FFFFF: 运行时配置 (512KB)                 │ │
│  │      └─ 用户设置、网络配置、日志                            │ │
│  ├────────────────────────────────────────────────────────────┤ │
│  │ Offset 0x100000 - 0x2FFFFF (2MB) - 固件/应用区             │ │
│  │  ├─ 0x100000-0x1FFFFF: 安全固件 (1MB)                     │ │
│  │  │   └─ 加密密钥、安全启动固件                              │ │
│  │  └─ 0x200000-0x2FFFFF: 应用备份 (1MB)                     │ │
│  │      └─ 关键应用备份、恢复镜像                              │ │
│  ├────────────────────────────────────────────────────────────┤ │
│  │ Offset 0x300000 - 0x3FFFFF (1MB) - 文件系统区              │ │
│  │  └─ 小文件系统 (FAT32/ext4)                               │ │
│  │      └─ 脚本文件、证书、临时数据                            │ │
│  └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

二、文件系统对比与选择

2.1 三种文件系统详细对比

特性 ext4 FAT32 SquashFS
U-Boot支持 需配置CONFIG_CMD_EXT4 原生支持 需要CONFIG_CMD_SQUASHFS
Linux支持 完整支持 完整支持 完整支持
读写能力 读写 读写 只读
日志功能 有(可禁用)
压缩 支持(LZ4/ZSTD)
最大文件 16TB 4GB 4GB
4MB区域适用 动态配置数据 跨平台数据交换 只读固件/证书
坏块处理 由MMC层处理 由MMC层处理 由MMC层处理
元数据开销 ~4KB ~512B ~8KB(压缩)

2.2 选择建议

/* 使用场景决策树 */
const char* select_filesystem(int usage_type) {
    switch(usage_type) {
        case USAGE_CONFIG_RW:      /* 动态读写配置 */
            return "ext4";          /* 支持日志,数据安全 */
            
        case USAGE_CROSS_PLATFORM: /* U-Boot和Windows交换数据 */
            return "FAT32";         /* 兼容性最好 */
            
        case USAGE_FIRMWARE_RO:    /* 只读固件 */
            return "SquashFS";      /* 压缩节省空间 */
            
        case USAGE_TEMP_DATA:      /* 临时数据 */
            return "FAT32";         /* 轻量级,无日志开销 */
            
        default:
            return "ext4";          /* 默认选择 */
    }
}

三、U-Boot与Linux读写接口差异树形分析

3.1 完整接口调用链对比树

┌─────────────────────────────────────────────────────────────────────┐
│                    U-Boot 调用链(命令行)                           │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  User Command: mmc read 0xA0000000 0x4000 0x2000                   │
│       │                                                             │
│       ▼                                                             │
│  common/cmd_mmc.c::do_mmc_read()                                   │
│       │                                                             │
│       ├─► 参数解析:地址、起始块、块数                               │
│       │                                                             │
│       ▼                                                             │
│  drivers/mmc/mmc.c::mmc_bread()                                    │
│       │                                                             │
│       ├─► 块对齐检查                                                │
│       ├─► 循环调用 mmc_send_cmd()                                   │
│       │                                                             │
│       ▼                                                             │
│  drivers/mmc/mmc.c::mmc_send_cmd()                                 │
│       │                                                             │
│       ├─► 构建CMD17(单块)或CMD18(多块)                          │
│       ├─► 设置数据传输方向(读)                                     │
│       │                                                             │
│       ▼                                                             │
│  drivers/mmc/×××_mmc.c::×××_mmc_send_cmd()  [控制器驱动]           │
│       │                                                             │
│       ├─► 配置DMA地址(0xA0000000)                                │
│       ├─► 等待中断/轮询                                             │
│       │                                                             │
│       ▼                                                             │
│  eMMC 硬件寄存器                                                    │
│       │                                                             │
│       └─► 数据传输到DDR内存 0xA0000000                             │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘
​
┌─────────────────────────────────────────────────────────────────────┐
│                    Linux 调用链(用户态)                            │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  User App: read(fd, buffer, size)                                  │
│       │                                                             │
│       ▼                                                             │
│  glibc: syscall(SYS_read, ...)                                     │
│       │                                                             │
│       ▼                                                             │
│  kernel/fs/read_write.c::ksys_read()                               │
│       │                                                             │
│       ├─► fd → struct file *                                        │
│       ├─► file->f_op->read_iter()                                   │
│       │                                                             │
│       ▼                                                             │
│  kernel/fs/ext4/file.c::ext4_file_read_iter()  [VFS → ext4]       │
│       │                                                             │
│       ├─► 页缓存检查 (page cache)                                   │
│       │   ├─► 命中 → 直接从内存复制                                 │
│       │   └─► 未命中 → 调用底层读取                                 │
│       │                                                             │
│       ▼                                                             │
│  kernel/fs/ext4/inode.c::ext4_mpage_readpages()                    │
│       │                                                             │
│       ├─► 映射文件逻辑块到物理块                                     │
│       ├─► 构建bio请求                                               │
│       │                                                             │
│       ▼                                                             │
│  kernel/block/blk-core.c::submit_bio()                             │
│       │                                                             │
│       ├─► 请求合并、调度                                            │
│       ├─► 电梯算法(CFQ/deadline)                                  │
│       │                                                             │
│       ▼                                                             │
│  drivers/mmc/core/block.c::mmc_blk_issue_rq()                      │
│       │                                                             │
│       ├─► 转换为MMC命令                                              │
│       ├─► 处理分区切换                                              │
│       │                                                             │
│       ▼                                                             │
│  drivers/mmc/core/core.c::mmc_start_request()                      │
│       │                                                             │
│       ├─► 添加DMA映射                                               │
│       ├─► 发送命令到主机控制器                                      │
│       │                                                             │
│       ▼                                                             │
│  drivers/mmc/host/×××-mmc.c  [主机控制器驱动]                       │
│       │                                                             │
│       └─► 硬件数据传输                                              │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

3.2 关键差异点详细对比

┌────────────────────────────────────────────────────────────────────┐
│                    数据流差异对比树形图                              │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│  U-Boot 数据流:                                                  │
│  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐        │
│  │ Command │───►│ 解析    │───►│ MMC驱动 │───►│ 硬件    │        │
│  │ 参数    │    │ 地址    │    │ 直接DMA │    │ 寄存器  │        │
│  └─────────┘    └─────────┘    └─────────┘    └─────────┘        │
│       │              │              │              │               │
│       │              │              │              ▼               │
│       │              │              │         ┌─────────┐         │
│       │              │              └────────►│ eMMC    │         │
│       │              │                        │ 控制器  │         │
│       │              │                        └─────────┘         │
│       │              │                              │              │
│       │              ▼                              ▼              │
│       │         ┌─────────┐                  ┌─────────┐          │
│       └────────►│ DDR内存 │◄─────────────────│ eMMC    │          │
│                 │ 0xA...  │      DMA         │ 存储    │          │
│                 └─────────┘                  └─────────┘          │
│                                                                    │
│  特点:无缓存、无文件系统、直接物理地址                              │
│                                                                    │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│  Linux 数据流:                                                   │
│  ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐        │
│  │ 用户App │───►│ glibc   │───►│ VFS     │───►│ Page    │        │
│  │ read()  │    │ syscall │    │ 层      │    │ Cache   │        │
│  └─────────┘    └─────────┘    └─────────┘    └─────────┘        │
│       ▲              │              │              │               │
│       │              │              │              ▼               │
│       │              │              │         ┌─────────┐         │
│       │              │              │         │ 缺页    │         │
│       │              │              │         │ 处理    │         │
│       │              │              │         └─────────┘         │
│       │              │              │              │               │
│       │              │              ▼              ▼               │
│       │              │         ┌─────────┐    ┌─────────┐         │
│       │              │         │ 文件系统│───►│ Block   │         │
│       │              │         │ (ext4)  │    │ 层      │         │
│       │              │         └─────────┘    └─────────┘         │
│       │              │              │              │               │
│       │              │              │              ▼               │
│       │              │              │         ┌─────────┐         │
│       │              │              │         │ IO调度器│         │
│       │              │              │         └─────────┘         │
│       │              │              │              │               │
│       │              │              ▼              ▼               │
│       │              │         ┌─────────┐    ┌─────────┐         │
│       └──────────────┴─────────┤ MMC驱动 │───►│ eMMC    │         │
│                   复制         │ 层      │    │ 硬件    │         │
│                 (用户态/内核态) └─────────┘    └─────────┘         │
│                                                                    │
│  特点:多层缓存、文件系统抽象、虚拟地址                              │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

四、数据Pipeline流程树形分析

4.1 完整数据流Pipeline

═══════════════════════════════════════════════════════════════════════
                        数据写入Pipeline(4MB区域)
═══════════════════════════════════════════════════════════════════════
​
Stage 1: 应用层准备
┌─────────────────────────────────────────────────────────────────────┐
│  [用户数据] → 缓冲区分配 → 数据准备完成                              │
│  耗时: O(1)                                                         │
│  关键点: 数据对齐、大小边界检查                                       │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
Stage 2: 系统调用层(仅Linux)
┌─────────────────────────────────────────────────────────────────────┐
│  write(fd, buf, size)                                              │
│    │                                                                │
│    ├─► 用户态/内核态切换 (约1-2μs)                                  │
│    ├─► copy_from_user() 复制数据到内核缓冲区 (size/带宽)            │
│    │    └─► 性能: ~5GB/s on DDR4                                    │
│    └─► 返回系统调用                                                 │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
Stage 3: 页缓存层(仅Linux)
┌─────────────────────────────────────────────────────────────────────┐
│  Page Cache 处理:                                                  │
│    ├─► 查找/分配页帧                                               │
│    ├─► 标记脏页 (dirty page)                                       │
│    └─► 后台pdflush线程定期刷盘                                     │
│  延迟: 异步写入,实际落盘延迟不确定                                 │
│  风险: 掉电丢失数据(需调用fsync)                                  │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
Stage 4: 文件系统层
┌─────────────────────────────────────────────────────────────────────┐
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │ ext4 处理流程:                                               │  │
│  │   ├─► 检查权限/磁盘配额                                       │  │
│  │   ├─► 日志记录 (jbd2) - 可选                                 │  │
│  │   │    └─► 先写日志,后写数据 (data=ordered模式)             │  │
│  │   ├─► 块分配 (ext4_get_block)                               │  │
│  │   │    ├─► 查询inode块映射                                   │  │
│  │   │    └─► 分配新块(如需要)                                 │  │
│  │   └─► 生成bio请求                                            │  │
│  └──────────────────────────────────────────────────────────────┘  │
│                                                                    │
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │ FAT32 处理流程:                                              │  │
│  │   ├─► 查找/更新FAT表                                         │  │
│  │   ├─► 更新目录项                                             │  │
│  │   └─► 直接生成bio(无日志)                                   │  │
│  └──────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
Stage 5: 块层
┌─────────────────────────────────────────────────────────────────────┐
│  Block Layer:                                                     │
│    ├─► 请求合并 (merge adjacent requests)                         │
│    │    └─► 减少IO次数,提升吞吐量                                 │
│    ├─► IO调度器:                                                  │
│    │    ├─► CFQ: 公平队列 (桌面/服务器)                           │
│    │    ├─► Deadline: 延迟优先 (实时系统)                         │
│    │    └─► NOOP: 无调度 (SSD/eMMC推荐)                          │
│    └─► 生成块设备请求 (struct request)                            │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
Stage 6: MMC核心层
┌─────────────────────────────────────────────────────────────────────┐
│  MMC Core (drivers/mmc/core/):                                    │
│    ├─► 分区映射: LBA → 物理地址                                   │
│    ├─► 坏块管理: 重映射 (由eMMC内部处理)                          │
│    ├─► 命令构建:                                                  │
│    │    ├─► CMD24: 单块写                                         │
│    │    ├─► CMD25: 多块写                                         │
│    │    └─► 自动CMD23: 预定义块数 (eMMC 5.0+)                     │
│    └─► DMA准备: 映射内存地址                                       │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
Stage 7: MMC主机控制器驱动
┌─────────────────────────────────────────────────────────────────────┐
│  Host Controller Driver:                                          │
│    ├─► 配置控制器寄存器                                           │
│    ├─► 设置DMA描述符                                              │
│    ├─► 发送命令到eMMC                                             │
│    ├─► 等待中断/轮询完成                                          │
│    └─► 处理错误/重试                                              │
│  耗时: 取决于总线速度 (HS400: 200MB/s, HS200: 200MB/s)           │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
Stage 8: eMMC硬件
┌─────────────────────────────────────────────────────────────────────┐
│  eMMC Device Internal:                                            │
│    ├─► MMC协议解析                                                │
│    ├─► 写入缓冲区 (DRAM-like cache)                               │
│    ├─► 磨损均衡算法 (动态/静态)                                   │
│    ├─► 坏块替换 (BBT - Bad Block Table)                           │
│    └─► 实际写入NAND Flash (最耗时)                                │
│        └─► 编程时间: ~1-2ms per page (256KB)                      │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
                            [数据持久化完成]

4.2 各阶段性能瓶颈分析

Pipeline 瓶颈分析树
│
├─ Stage 1-2 (应用层)
│  ├─ 瓶颈: 数据拷贝 (copy_from_user)
│  ├─ 优化: 使用mmap绕过拷贝
│  └─ 延迟: 0.5-2μs per 4KB
│
├─ Stage 3 (页缓存) [Linux only]
│  ├─ 瓶颈: 脏页回写延迟
│  ├─ 优化: sync/fsync强制落盘
│  └─ 延迟: 异步,可能5ms-5s
│
├─ Stage 4 (文件系统)
│  ├─ 瓶颈: 元数据更新、日志写入
│  ├─ 优化: 禁用日志 (ext4: data=writeback)
│  └─ 延迟: 0.1-1ms per operation
│
├─ Stage 5-6 (块层+MMC)
│  ├─ 瓶颈: 请求队列深度
│  ├─ 优化: 使用blktrace分析
│  └─ 延迟: 0.01-0.1ms
│
└─ Stage 7-8 (硬件)
   ├─ 瓶颈: NAND编程时间、总线速度
   ├─ 优化: 使用HS400模式、批量写入
   └─ 延迟: 1-2ms per page (256KB)

五、文件变成只读的Bug分析与调试

5.1 常见只读问题原因树形分析

文件变成只读 (Read-Only) 问题树
│
├─ 1. 文件系统层面
│  │
│  ├─ 1.1 文件系统错误 (FS Corruption)
│  │   ├─ 症状: dmesg显示 "ext4-fs error"
│  │   ├─ 原因: 突然掉电、硬件不稳定
│  │   ├─ 检查: fsck /dev/mmcblk0p1
│  │   └─ 修复: e2fsck -p /dev/mmcblk0p1
│  │
│  ├─ 1.2 文件系统挂载为只读 (ro)
│  │   ├─ 症状: mount | grep emmc 显示 (ro)
│  │   ├─ 原因: 
│  │   │   ├─ 内核检测到错误自动remount-ro
│  │   │   ├─ /etc/fstab 配置为 ro
│  │   │   └─ 手动mount时加了 -o ro
│  │   ├─ 检查: cat /proc/mounts | grep emmc
│  │   └─ 修复: mount -o remount,rw /mnt/emmc
│  │
│  └─ 1.3 文件系统日志回放失败
│      ├─ 症状: "Journal has aborted"
│      ├─ 原因: 日志区域损坏
│      └─ 修复: tune2fs -O ^has_journal /dev/mmcblk0p1
│
├─ 2. 文件权限层面
│  │
│  ├─ 2.1 文件属性不可变 (Immutable)
│  │   ├─ 症状: chattr +i 设置
│  │   ├─ 检查: lsattr file.bin
│  │   ├─ 输出: ----i--------e--- file.bin
│  │   └─ 修复: chattr -i file.bin
│  │
│  ├─ 2.2 文件权限不足
│  │   ├─ 症状: Permission denied
│  │   ├─ 检查: ls -l file.bin
│  │   └─ 修复: chmod 666 file.bin
│  │
│  └─ 2.3 目录权限不足
│      ├─ 症状: 目录不可写
│      └─ 修复: chmod 777 /mnt/emmc
│
├─ 3. 存储设备层面
│  │
│  ├─ 3.1 eMMC进入只读模式
│  │   ├─ 症状: mmcblk0: error -110, critical target failure
│  │   ├─ 原因:
│  │   │   ├─ 超过寿命 (eMMC写寿命耗尽)
│  │   │   ├─ 坏块过多触发保护
│  │   │   └─ 温度过高保护
│  │   ├─ 检查: mmc extcsd read /dev/mmcblk0
│  │   │   └─ 关注: EXT_CSD_PRE_EOL_INFO (预寿命信息)
│  │   └─ 修复: 硬件替换 (不可逆)
│  │
│  ├─ 3.2 分区表损坏
│  │   ├─ 症状: "Partition table invalid"
│  │   ├─ 检查: fdisk -l /dev/mmcblk0
│  │   └─ 修复: 重建分区表
│  │
│  └─ 3.3 eMMC硬件故障
│      ├─ 症状: 持续CRC错误、CMD超时
│      ├─ 检查: mmc health check
│      └─ 修复: 更换硬件
│
└─ 4. 内核层面
   │
   ├─ 4.1 驱动Bug导致remount-ro
   │   ├─ 症状: "mmc0: timeout waiting for hardware interrupt"
   │   ├─ 分析: 查看dmesg时间戳
   │   └─ 修复: 升级内核/驱动
   │
   └─ 4.2 电源管理问题
       ├─ 症状: 唤醒后变只读
       ├─ 原因: 休眠时eMMC掉电
       └─ 修复: 调整PM策略

5.2 调试手段与处理流程

5.2.1 问题诊断命令树
# ==================== 诊断命令树 ====================
​
# 1. 检查挂载状态
mount | grep emmc
cat /proc/mounts | grep emmc
​
# 2. 检查dmesg错误
dmesg | grep -i "mmc\|ext4\|fat\|error\|read-only"
dmesg | grep -i "remount"
​
# 3. 检查文件系统完整性
# ext4:
e2fsck -n /dev/mmcblk0p1          # 只检查,不修复
e2fsck -p /dev/mmcblk0p1          # 自动修复
dumpe2fs -h /dev/mmcblk0p1 | grep -i "error"
​
# FAT32:
fsck.vfat -v /dev/mmcblk0p1
​
# 4. 检查eMMC健康状态
# 安装mmc-utils
git clone https://git.kernel.org/pub/scm/utils/mmc/mmc-utils.git
cd mmc-utils && make
​
# 读取EXT_CSD寄存器
./mmc extcsd read /dev/mmcblk0
​
# 关键字段解读:
# EXT_CSD_PRE_EOL_INFO: 预寿命信息
#   0x00: 正常
#   0x01: 消耗80%寿命
#   0x02: 消耗90%寿命 (警告)
#   0x03: 寿命耗尽 (只读)
# 
# EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A/B: 使用时间估算
​
# 5. 性能测试
# 写入测试
dd if=/dev/zero of=/mnt/emmc/test bs=1M count=10 conv=fsync
# 读取测试
dd if=/mnt/emmc/test of=/dev/null bs=1M
​
# 6. 跟踪系统调用
strace -e open,write,read,fsync dd if=/dev/zero of=/mnt/emmc/test bs=1M count=1
​
# 7. 监控块IO
blktrace -d /dev/mmcblk0 -o - | blkparse -i -
​
# 8. 查看eMMC统计
cat /sys/block/mmcblk0/stat
# 字段: 读完成次数, 读合并次数, 读扇区数, 读耗时(ms), 
#       写完成次数, 写合并次数, 写扇区数, 写耗时(ms)
5.2.2 调试脚本示例
#!/bin/bash
# debug_emmc_ro.sh - eMMC只读问题调试脚本
​
echo "========== eMMC Read-Only Debug Tool =========="
echo ""
​
# 1. 检查设备存在性
echo "[1] Checking eMMC device..."
if [ ! -e /dev/mmcblk0 ]; then
    echo "ERROR: /dev/mmcblk0 not found!"
    exit 1
fi
echo "OK: /dev/mmcblk0 exists"
echo ""
​
# 2. 检查分区
echo "[2] Checking partition..."
lsblk /dev/mmcblk0
echo ""
​
# 3. 检查挂载状态
echo "[3] Mount status:"
mount | grep mmcblk0 || echo "Not mounted"
echo ""
​
# 4. 检查写保护
echo "[4] Checking write-protect status:"
WP=$(cat /sys/block/mmcblk0/force_ro)
if [ "$WP" == "1" ]; then
    echo "WARNING: Device is force read-only!"
else
    echo "OK: Device is not force read-only"
fi
echo ""
​
# 5. 检查dmesg错误
echo "[5] Recent MMC errors in dmesg:"
dmesg | grep -i "mmc.*error" | tail -10
echo ""
​
# 6. 测试写入
echo "[6] Testing write capability..."
TEST_FILE="/tmp/emmc_test_$$"
dd if=/dev/zero of=$TEST_FILE bs=1M count=1 2>/dev/null
if [ -f "$TEST_FILE" ]; then
    echo "OK: Write to /tmp successful"
    rm -f $TEST_FILE
else
    echo "ERROR: Cannot write to /tmp"
fi
​
# 7. 如果挂载了,测试分区写入
MOUNT_POINT=$(mount | grep mmcblk0p1 | awk '{print $3}')
if [ -n "$MOUNT_POINT" ]; then
    echo "Testing write to $MOUNT_POINT..."
    TEST_FILE="$MOUNT_POINT/test_$$"
    echo "test" > $TEST_FILE 2>&1
    if [ $? -eq 0 ]; then
        echo "OK: Write successful"
        rm -f $TEST_FILE
    else
        echo "ERROR: Write failed! Attempting remount..."
        mount -o remount,rw $MOUNT_POINT 2>&1
        if [ $? -eq 0 ]; then
            echo "OK: Remounted as read-write"
        else
            echo "ERROR: Cannot remount as RW"
        fi
    fi
fi
echo ""
​
# 8. eMMC健康检查(如果有mmc工具)
if command -v mmc &> /dev/null; then
    echo "[7] eMMC health info:"
    mmc extcsd read /dev/mmcblk0 | grep -E "PRE_EOL|LIFE_TIME"
else
    echo "[7] mmc-utils not installed (optional)"
fi
​
echo ""
echo "========== Debug Complete =========="

5.3 只读问题处理流程图

┌─────────────────────────────────────────────────────────────────────┐
│                    只读问题处理决策树                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  发现文件只读                                                        │
│       │                                                              │
│       ▼                                                              │
│  ┌─────────────────────────────────────────────────────┐           │
│  │ 步骤1: 快速诊断                                     │           │
│  │  mount | grep -E "ro|rw"                           │           │
│  │  dmesg | tail -50                                   │           │
│  └─────────────────────────────────────────────────────┘           │
│       │                                                              │
│       ▼                                                              │
│  dmesg有错误?                                                      │
│       │                                                              │
│   ┌───┴───┐                                                        │
│  是      否                                                         │
│   │       │                                                         │
│   ▼       ▼                                                         │
│  ┌───┐   ┌─────────────────────────────────────────┐              │
│  │A  │   │ 步骤2: 检查挂载选项                      │              │
│  └───┘   │ mount | grep mmcblk0                     │              │
│          └─────────────────────────────────────────┘              │
│               │                                                      │
│               ▼                                                      │
│          挂载为ro?                                                 │
│               │                                                      │
│           ┌───┴───┐                                                │
│          是      否                                                 │
│           │       │                                                 │
│           ▼       ▼                                                 │
│          ┌───┐   ┌─────────────────────────────────┐              │
│          │B  │   │ 步骤3: 检查文件权限              │              │
│          └───┘   │ ls -l /mnt/emmc                  │              │
│                  └─────────────────────────────────┘              │
│                       │                                              │
│                       ▼                                              │
│                  权限不足?                                          │
│                       │                                              │
│                   ┌───┴───┐                                        │
│                  是      否                                         │
│                   │       │                                         │
│                   ▼       ▼                                         │
│                  ┌───┐   ┌─────────────────────────┐              │
│                  │C  │   │ 步骤4: 检查文件属性      │              │
│                  └───┘   │ lsattr /mnt/emmc/file    │              │
│                          └─────────────────────────┘              │
│                               │                                      │
│                               ▼                                      │
│                          不可变属性?                                │
│                               │                                      │
│                           ┌───┴───┐                                │
│                          是      否                                 │
│                           │       │                                 │
│                           ▼       ▼                                 │
│                          ┌───┐   ┌─────────────────┐              │
│                          │D  │   │ 步骤5: eMMC硬件  │              │
│                          └───┘   │ 健康检查        │              │
│                                  └─────────────────┘              │
│                                       │                              │
│                                       ▼                              │
│                                  寿命耗尽?                          │
│                                       │                              │
│                                   ┌───┴───┐                        │
│                                  是      否                         │
│                                   │       │                         │
│                                   ▼       ▼                         │
│                                  ┌───┐   ┌─────────────────┐      │
│                                  │E  │   │ 步骤6: 文件系统  │      │
│                                  └───┘   │ 完整性检查      │      │
│                                          └─────────────────┘      │
│                                               │                      │
│                                               ▼                      │
│                                          损坏?                      │
│                                               │                      │
│                                           ┌───┴───┐                │
│                                          是      否                 │
│                                           │       │                 │
│                                           ▼       ▼                 │
│                                          ┌───┐   ┌─────────────┐  │
│                                          │F  │   │ 步骤7: 驱动  │  │
│                                          └───┘   │ Bug分析     │  │
│                                                  └─────────────┘  │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘
​
处理方案:
┌─────────────────────────────────────────────────────────────────────┐
│ A: 文件系统错误修复                                                 │
│   ├─ e2fsck -f -y /dev/mmcblk0p1                                   │
│   └─ 如果失败,考虑备份后重新格式化                                  │
│                                                                      │
│ B: 重新挂载为读写                                                   │
│   ├─ mount -o remount,rw /mnt/emmc                                  │
│   └─ 如果失败,检查/etc/fstab是否配置为ro                           │
│                                                                      │
│ C: 修改权限                                                         │
│   ├─ chmod 666 /mnt/emmc/file                                       │
│   └─ chown user:group /mnt/emmc/file                                │
│                                                                      │
│ D: 移除不可变属性                                                   │
│   └─ chattr -i /mnt/emmc/file                                       │
│                                                                      │
│ E: 硬件替换(不可恢复)                                             │
│   ├─ 备份重要数据                                                   │
│   ├─ 更换eMMC芯片                                                   │
│   └─ 重新烧录固件                                                   │
│                                                                      │
│ F: 文件系统修复                                                     │
│   ├─ fsck.ext4 -f -y /dev/mmcblk0p1                                 │
│   └─ 如果频繁损坏,检查电源稳定性                                    │
└─────────────────────────────────────────────────────────────────────┘

5.4 预防性措施

/* 预防只读问题的代码实践 */
​
/**
 * @fn      safe_write_with_fsync
 * @brief   安全的写入操作,确保数据持久化
 */
int safe_write_with_fsync(const char *filename, const uint8_t *data, size_t size)
{
    int fd;
    ssize_t written;
    int ret = -1;
    
    fd = open(filename, O_WRONLY | O_CREAT | O_SYNC, 0644);
    if (fd < 0) {
        printf("Failed to open %s: %s\n", filename, strerror(errno));
        return -1;
    }
    
    /* 写入数据 */
    written = write(fd, data, size);
    if (written != (ssize_t)size) {
        printf("Write failed\n");
        goto out;
    }
    
    /* 强制同步到存储(关键!)*/
    if (fsync(fd) != 0) {
        printf("fsync failed: %s\n", strerror(errno));
        goto out;
    }
    
    ret = 0;
    
out:
    close(fd);
    return ret;
}
​
/**
 * @fn      monitor_emmc_health
 * @brief   监控eMMC健康状态的后台线程
 */
void *monitor_emmc_health(void *arg)
{
    while (1) {
        /* 读取eMMC寿命信息 */
        FILE *fp = fopen("/sys/block/mmcblk0/device/life_time", "r");
        if (fp) {
            char life_time[64];
            if (fgets(life_time, sizeof(life_time), fp)) {
                int used_a = (life_time[0] & 0xFF);
                int used_b = (life_time[1] & 0xFF);
                
                /* 寿命超过90%时告警 */
                if (used_a >= 0x0A || used_b >= 0x0A) {
                    printf("WARNING: eMMC lifetime exceeded 90%%!\n");
                    /* 触发告警、发送日志等 */
                }
            }
            fclose(fp);
        }
        
        /* 每24小时检查一次 */
        sleep(24 * 3600);
    }
    return NULL;
}
​
/**
 * @fn      periodic_fsck
 * @brief   定期检查文件系统(在系统启动时)
 */
void periodic_fsck(const char *device)
{
    struct stat st;
    char cmd[256];
    
    /* 检查上次检查时间 */
    if (stat("/var/lib/fsck_timestamp", &st) == 0) {
        time_t now = time(NULL);
        if (now - st.st_mtime < 30 * 24 * 3600) {
            return; /* 30天内检查过 */
        }
    }
    
    /* 执行文件系统检查 */
    snprintf(cmd, sizeof(cmd), "e2fsck -p %s", device);
    int ret = system(cmd);
    
    if (ret == 0) {
        /* 更新检查时间 */
        system("touch /var/lib/fsck_timestamp");
    } else {
        printf("fsck returned %d, manual intervention needed\n", ret);
    }
}

六、完整解决方案总结

6.1 推荐方案组合

使用场景 分区方式 文件系统 访问方式
U-Boot配置参数 固定偏移量 无文件系统 mmc raw读写
Linux系统配置 GPT分区 ext4 文件系统API
跨平台数据 GPT分区 FAT32 文件系统API
只读固件 GPT分区 SquashFS 文件系统API

6.2 关键注意事项

  1. 分区对齐: 起始扇区必须对齐到1MB (2048扇区),避免性能损失

  2. 文件系统选择: 根据实际需求选择,不要盲目使用ext4

  3. 同步操作: 关键数据必须使用fsync()O_SYNC

  4. 健康监控: 定期检查eMMC寿命信息,提前预警

  5. 掉电保护: 考虑使用UPS或超级电容确保关键写入完成

第二部分 uboot 处理阶段

U-Boot与Linux读写接口对比分析

U-Boot的读写接口确实与Linux完全不同。这是嵌入式开发中需要特别注意的地方。


1. 接口差异对比表

维度 U-Boot Linux
设备访问方式 直接命令操作 + 内存地址 VFS文件系统接口
数据传递 通过内存地址(如0xA0000000 通过用户态缓冲区指针
块操作 mmc read/write 按块操作 read()/write() 系统调用
文件系统 ext4load/ext4write 命令 POSIX API(open/read/write)
设备标识 mmc 0:6(设备号:分区号) /dev/mmcblk0p6
抽象层次 直接操作MMC控制器 经过块层→文件系统→VFS
API风格 命令行 + C函数接口 标准POSIX系统调用

2. U-Boot MMC接口详解

2.1 底层API函数(U-Boot源码)

U-Boot的MMC驱动位于drivers/mmc/mmc.c,核心函数如下:

/**
 * @file    uboot_mmc_interface.h
 * @brief   U-Boot MMC底层接口定义(根据源码分析)
 * @note    这些是U-Boot内部的函数,命令层会调用它们
 */
​
#ifndef UBOOT_MMC_INTERFACE_H
#define UBOOT_MMC_INTERFACE_H
​
#include <mmc.h>
​
/**
 * @struct  mmc
 * @brief   MMC设备结构体(U-Boot定义)
 * @note    位于 include/mmc.h
 */
struct mmc {
    void *priv;                 /**< 控制器私有数据 */
    uint64_t capacity;          /**< 设备容量(字节) */
    uint32_t block_size;        /**< 块大小(通常512字节) */
    uint32_t bus_width;         /**< 总线宽度(1/4/8位) */
    uint32_t clock;             /**< 时钟频率(Hz) */
    /* ... 更多字段 ... */
};
​
/**
 * @fn      mmc_send_cmd
 * @brief   发送MMC命令到设备
 * @param   mmc     MMC设备指针
 * @param   cmd     命令结构体
 * @param   data    数据指针(可选)
 * @return  0 成功,负数为错误
 * @note    这是最底层的命令发送函数
 */
int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data);
​
/**
 * @fn      mmc_bread
 * @brief   从MMC设备读取块数据
 * @param   dev     MMC设备指针(或udevice/block_desc,取决于配置)
 * @param   start   起始块号(LBA)
 * @param   blkcnt  块数量
 * @param   dst     目标内存地址
 * @return  成功读取的块数量,0为失败
 * @note    
 * 【设计模式】适配器模式 - 统一BLK层和传统接口
 * 【性能分析】O(n) 时间复杂度,n为块数量,受总线速度限制
 */
ulong mmc_bread(struct mmc *mmc, lbaint_t start, lbaint_t blkcnt, void *dst);
​
/**
 * @fn      mmc_bwrite
 * @brief   向MMC设备写入块数据
 * @param   mmc     MMC设备指针
 * @param   start   起始块号(LBA)
 * @param   blkcnt  块数量
 * @param   src     源内存地址
 * @return  成功写入的块数量,0为失败
 * @note    需要配置 CONFIG_MMC_WRITE 才能启用
 */
ulong mmc_bwrite(struct mmc *mmc, lbaint_t start, lbaint_t blkcnt, const void *src);
​
/**
 * @fn      mmc_berase
 * @brief   擦除MMC设备块
 * @param   mmc     MMC设备指针
 * @param   start   起始块号(LBA)
 * @param   blkcnt  块数量
 * @return  成功擦除的块数量,0为失败
 * @note    擦除粒度受 Erase Group Size 限制
 */
ulong mmc_berase(struct mmc *mmc, lbaint_t start, lbaint_t blkcnt);
​
/**
 * @fn      mmc_switch
 * @brief   切换MMC分区或配置
 * @param   mmc     MMC设备指针
 * @param   set     设置类型
 * @param   index   索引
 * @param   value   值
 * @return  0 成功,负数为错误
 */
int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value);
​
#endif /* UBOOT_MMC_INTERFACE_H */
2.2 文件系统访问函数

U-Boot的文件系统接口位于fs/目录,提供以下功能:

/**
 * @file    uboot_fs_interface.h
 * @brief   U-Boot文件系统抽象层
 * @note    这些函数被 ext4load/fatload 等命令使用
 */
​
/**
 * @fn      fs_set_blk_dev
 * @brief   设置当前操作的块设备
 * @param   ifname      接口名称("mmc", "usb", "scsi"等)
 * @param   dev_part    设备号:分区号(如 "0:6")
 * @return  0 成功,负数为错误
 */
int fs_set_blk_dev(const char *ifname, const char *dev_part);
​
/**
 * @fn      fs_read
 * @brief   从文件系统读取文件
 * @param   filename    文件路径
 * @param   addr        加载到内存的地址
 * @param   offset      文件内偏移
 * @param   len         读取长度
 * @return  实际读取的字节数,负数为错误
 * @note    
 * 【关键差异】U-Boot需要指定内存地址,Linux不需要
 * 【性能分析】每次调用都会重新挂载文件系统,无缓存
 */
int fs_read(const char *filename, ulong addr, loff_t offset, ulong len);
​
/**
 * @fn      fs_write
 * @brief   向文件系统写入文件
 * @param   filename    文件路径
 * @param   addr        数据所在内存地址
 * @param   offset      文件内偏移
 * @param   len         写入长度
 * @return  实际写入的字节数,负数为错误
 * @note    需要配置 CONFIG_CMD_EXT4_WRITE 或 CONFIG_CMD_FAT_WRITE
 */
int fs_write(const char *filename, ulong addr, loff_t offset, ulong len);
​
/**
 * @fn      fs_ls
 * @brief   列出目录内容
 * @param   dirname     目录路径
 * @return  0 成功,负数为错误
 */
int fs_ls(const char *dirname);

3. U-Boot命令层接口

U-Boot用户实际使用的是命令行命令,其实现位于common/cmd_mmc.c

命令 语法 功能 对应底层函数
mmc list mmc list 列出所有MMC设备 遍历mmc设备列表
mmc dev mmc dev [dev] [part] 切换当前设备/分区 mmc_select_hwpart()
mmc info mmc info 显示设备信息 读取CSD/EXT_CSD
mmc read mmc read addr blk# cnt 原始块读取 mmc_bread()
mmc write mmc write addr blk# cnt 原始块写入 mmc_bwrite()
mmc erase mmc erase blk# cnt 原始块擦除 mmc_berase()
ext4load ext4load <dev>:<part> <addr> <file> 读取ext4文件 fs_read()
ext4write ext4write <dev>:<part> <addr> <file> <bytes> 写入ext4文件 fs_write()
fatload fatload <dev>:<part> <addr> <file> 读取FAT文件 fs_read()
fatwrite fatwrite <dev>:<part> <addr> <file> <bytes> 写入FAT文件 fs_write()

4. U-Boot专用代码实现

基于上述接口差异,下面是专门为U-Boot编写的4MB区域管理代码:

/**
 * @file    uboot_emmc_manager.c
 * @brief   U-Boot环境下的eMMC 4MB区域管理
 * @author  Embedded Team
 * @date    2026-03-30
 * 
 * @details 本文件实现U-Boot控制台下的eMMC管理功能。
 *          与Linux版本的关键差异:
 *          1. 使用内存地址传递数据(如0xA0000000)
 *          2. 直接调用mmc_bread/mmc_bwrite等底层函数
 *          3. 通过命令或API操作,无VFS层
 * 
 * 【设计模式】
 * - 命令模式:将每个操作封装为独立函数
 * - 策略模式:支持RAW模式和文件系统模式
 */
​
#include <common.h>
#include <command.h>
#include <mmc.h>
#include <fs.h>
#include <malloc.h>
#include <linux/ctype.h>
​
/* ==================== 配置定义 ==================== */
#define EMMC_DEVICE_NUM        0       /**< eMMC设备号 */
#define EMMC_PARTITION_NUM     1       /**< 分区号(GPT分区1) */
#define EMMC_REGION_START_BLK  2048    /**< 4MB区域起始块(1MB对齐) */
#define EMMC_REGION_BLK_COUNT  (4 * 1024 * 1024 / 512)  /**< 4MB = 8192块 */
#define EMMC_TEMP_ADDR         0xA0000000  /**< 临时内存地址(需bdinfo确认安全) */
#define EMMC_REGION_FILE       "/emmc_region.bin"  /**< 区域文件路径 */
​
/* ==================== 内部函数 ==================== */
​
/**
 * @fn      uboot_mmc_get_device
 * @brief   获取MMC设备指针
 * @return  mmc结构体指针,失败返回NULL
 * @internal
 * 
 * 【设计模式】工厂模式 - 根据设备号创建设备引用
 */
static struct mmc *uboot_mmc_get_device(void)
{
    struct mmc *mmc = find_mmc_device(EMMC_DEVICE_NUM);
    
    if (!mmc) {
        printf("[ERROR] MMC device %d not found\n", EMMC_DEVICE_NUM);
        return NULL;
    }
    
    /* 初始化MMC设备(如果未初始化) */
    if (mmc_init(mmc)) {
        printf("[ERROR] MMC device %d init failed\n", EMMC_DEVICE_NUM);
        return NULL;
    }
    
    /* 切换到指定分区 */
    if (mmc_switch_part(mmc, EMMC_PARTITION_NUM)) {
        printf("[ERROR] Failed to switch to partition %d\n", EMMC_PARTITION_NUM);
        return NULL;
    }
    
    return mmc;
}
​
/**
 * @fn      uboot_mmc_raw_write
 * @brief   原始块写入(绕过文件系统)
 * @param   mmc         MMC设备指针
 * @param   start_blk   起始块号
 * @param   blk_cnt     块数量
 * @param   src_addr    源内存地址
 * @return  写入的块数量,0为失败
 * @internal
 * 
 * 【设计模式】策略模式 - RAW写入策略
 * 【性能分析】直接操作块设备,无文件系统开销
 */
static ulong uboot_mmc_raw_write(struct mmc *mmc, lbaint_t start_blk, 
                                  lbaint_t blk_cnt, ulong src_addr)
{
    printf("[INFO] Writing %lu blocks from 0x%08lX to block %lu\n",
           blk_cnt, src_addr, start_blk);
    
    /* 调用底层块写入函数 */
    ulong written = mmc_bwrite(mmc, start_blk, blk_cnt, (void *)src_addr);
    
    if (written != blk_cnt) {
        printf("[ERROR] Write failed: wrote %lu, expected %lu\n", 
               written, blk_cnt);
        return 0;
    }
    
    return written;
}
​
/**
 * @fn      uboot_mmc_raw_read
 * @brief   原始块读取(绕过文件系统)
 * @param   mmc         MMC设备指针
 * @param   start_blk   起始块号
 * @param   blk_cnt     块数量
 * @param   dst_addr    目标内存地址
 * @return  读取的块数量,0为失败
 * @internal
 */
static ulong uboot_mmc_raw_read(struct mmc *mmc, lbaint_t start_blk,
                                 lbaint_t blk_cnt, ulong dst_addr)
{
    printf("[INFO] Reading %lu blocks to 0x%08lX from block %lu\n",
           blk_cnt, dst_addr, start_blk);
    
    ulong read = mmc_bread(mmc, start_blk, blk_cnt, (void *)dst_addr);
    
    if (read != blk_cnt) {
        printf("[ERROR] Read failed: read %lu, expected %lu\n", 
               read, blk_cnt);
        return 0;
    }
    
    return read;
}
​
/**
 * @fn      uboot_mmc_raw_erase
 * @brief   原始块擦除
 * @param   mmc         MMC设备指针
 * @param   start_blk   起始块号
 * @param   blk_cnt     块数量
 * @return  擦除的块数量,0为失败
 * @internal
 * 
 * 【设计模式】命令模式 - 擦除命令封装
 */
static ulong uboot_mmc_raw_erase(struct mmc *mmc, lbaint_t start_blk,
                                  lbaint_t blk_cnt)
{
    printf("[INFO] Erasing %lu blocks from block %lu\n", blk_cnt, start_blk);
    
    ulong erased = mmc_berase(mmc, start_blk, blk_cnt);
    
    if (erased != blk_cnt) {
        printf("[ERROR] Erase failed: erased %lu, expected %lu\n", 
               erased, blk_cnt);
        return 0;
    }
    
    return erased;
}
​
/**
 * @fn      uboot_fs_file_read
 * @brief   通过文件系统读取文件
 * @param   filename    文件路径
 * @param   dst_addr    目标内存地址
 * @param   max_len     最大读取长度
 * @return  实际读取字节数,0为失败
 * @internal
 * 
 * 【关键差异】需要先设置块设备,然后指定内存地址
 */
static int uboot_fs_file_read(const char *filename, ulong dst_addr, ulong max_len)
{
    char dev_part[16];
    int ret;
    
    /* 构造设备:分区字符串,如 "0:1" */
    snprintf(dev_part, sizeof(dev_part), "%d:%d", 
             EMMC_DEVICE_NUM, EMMC_PARTITION_NUM);
    
    /* 设置块设备 */
    ret = fs_set_blk_dev("mmc", dev_part);
    if (ret) {
        printf("[ERROR] Failed to set block device %s\n", dev_part);
        return 0;
    }
    
    /* 读取文件到内存 */
    ret = fs_read(filename, dst_addr, 0, max_len);
    if (ret < 0) {
        printf("[ERROR] Failed to read file %s\n", filename);
        return 0;
    }
    
    printf("[INFO] Read %d bytes from %s to 0x%08lX\n", ret, filename, dst_addr);
    return ret;
}
​
/**
 * @fn      uboot_fs_file_write
 * @brief   通过文件系统写入文件
 * @param   filename    文件路径
 * @param   src_addr    源内存地址
 * @param   len         写入长度
 * @return  实际写入字节数,0为失败
 * @internal
 * 
 * @note    U-Boot默认禁用ext4write,需要配置:
 *         CONFIG_CMD_EXT4_WRITE=y
 */
static int uboot_fs_file_write(const char *filename, ulong src_addr, ulong len)
{
    char dev_part[16];
    int ret;
    
    snprintf(dev_part, sizeof(dev_part), "%d:%d", 
             EMMC_DEVICE_NUM, EMMC_PARTITION_NUM);
    
    ret = fs_set_blk_dev("mmc", dev_part);
    if (ret) {
        printf("[ERROR] Failed to set block device %s\n", dev_part);
        return 0;
    }
    
    ret = fs_write(filename, src_addr, 0, len);
    if (ret < 0) {
        printf("[ERROR] Failed to write file %s\n", filename);
        return 0;
    }
    
    printf("[INFO] Wrote %d bytes from 0x%08lX to %s\n", ret, src_addr, filename);
    return ret;
}
​
/* ==================== 公共API ==================== */
​
/**
 * @fn      uboot_emmc_raw_region_write
 * @brief   通过原始块设备写入4MB区域
 * @param   data        数据缓冲区(内存地址)
 * @param   size        数据大小(字节)
 * @param   offset      区域偏移(字节)
 * @return  0成功,负数为错误
 * 
 * 【使用场景】需要绕过文件系统直接操作块设备
 * 【性能】最高,无文件系统开销
 */
int uboot_emmc_raw_region_write(ulong data_addr, ulong size, ulong offset)
{
    struct mmc *mmc;
    lbaint_t start_blk;
    ulong blk_cnt;
    ulong written;
    
    /* 参数验证 */
    if (data_addr == 0 || size == 0 || offset + size > 4 * 1024 * 1024) {
        printf("[ERROR] Invalid parameters\n");
        return -1;
    }
    
    /* 获取MMC设备 */
    mmc = uboot_mmc_get_device();
    if (!mmc) return -2;
    
    /* 计算起始块和块数(512字节块) */
    start_blk = EMMC_REGION_START_BLK + (offset / 512);
    blk_cnt = (size + 511) / 512;  /* 向上取整到块对齐 */
    
    /* 执行写入 */
    written = uboot_mmc_raw_write(mmc, start_blk, blk_cnt, data_addr);
    
    return (written == blk_cnt) ? 0 : -3;
}
​
/**
 * @fn      uboot_emmc_raw_region_read
 * @brief   通过原始块设备读取4MB区域
 * @param   buffer_addr 目标内存地址
 * @param   size        读取大小(字节)
 * @param   offset      区域偏移(字节)
 * @return  0成功,负数为错误
 */
int uboot_emmc_raw_region_read(ulong buffer_addr, ulong size, ulong offset)
{
    struct mmc *mmc;
    lbaint_t start_blk;
    ulong blk_cnt;
    ulong read;
    
    if (buffer_addr == 0 || size == 0 || offset + size > 4 * 1024 * 1024) {
        return -1;
    }
    
    mmc = uboot_mmc_get_device();
    if (!mmc) return -2;
    
    start_blk = EMMC_REGION_START_BLK + (offset / 512);
    blk_cnt = (size + 511) / 512;
    
    read = uboot_mmc_raw_read(mmc, start_blk, blk_cnt, buffer_addr);
    
    return (read == blk_cnt) ? 0 : -3;
}
​
/**
 * @fn      uboot_emmc_raw_region_erase
 * @brief   擦除4MB区域(原始块方式)
 * @return  0成功,负数为错误
 */
int uboot_emmc_raw_region_erase(void)
{
    struct mmc *mmc;
    ulong erased;
    
    mmc = uboot_mmc_get_device();
    if (!mmc) return -1;
    
    erased = uboot_mmc_raw_erase(mmc, EMMC_REGION_START_BLK, EMMC_REGION_BLK_COUNT);
    
    return (erased == EMMC_REGION_BLK_COUNT) ? 0 : -2;
}
​
/**
 * @fn      uboot_emmc_fs_region_write
 * @brief   通过文件系统写入4MB区域
 * @param   data_addr   数据内存地址
 * @param   size        数据大小
 * @return  0成功,负数为错误
 * 
 * @note    使用前需确保分区已格式化为ext4或FAT
 *         并且已配置CONFIG_CMD_EXT4_WRITE
 */
int uboot_emmc_fs_region_write(ulong data_addr, ulong size)
{
    int written;
    
    if (data_addr == 0 || size == 0 || size > 4 * 1024 * 1024) {
        return -1;
    }
    
    written = uboot_fs_file_write(EMMC_REGION_FILE, data_addr, size);
    
    return (written == (int)size) ? 0 : -2;
}
​
/**
 * @fn      uboot_emmc_fs_region_read
 * @brief   通过文件系统读取4MB区域
 * @param   buffer_addr 目标内存地址
 * @param   max_len     最大读取长度
 * @return  实际读取字节数,负数为错误
 */
int uboot_emmc_fs_region_read(ulong buffer_addr, ulong max_len)
{
    int read;
    
    if (buffer_addr == 0 || max_len == 0) {
        return -1;
    }
    
    read = uboot_fs_file_read(EMMC_REGION_FILE, buffer_addr, max_len);
    
    return read;
}
​
/**
 * @fn      uboot_emmc_region_info
 * @brief   显示4MB区域信息
 */
void uboot_emmc_region_info(void)
{
    struct mmc *mmc;
    
    printf("\n=== eMMC 4MB Region Info ===\n");
    printf("Device: mmc %d:%d\n", EMMC_DEVICE_NUM, EMMC_PARTITION_NUM);
    printf("Region start block: %lu\n", EMMC_REGION_START_BLK);
    printf("Region block count: %lu (%lu bytes)\n", 
           EMMC_REGION_BLK_COUNT, EMMC_REGION_BLK_COUNT * 512);
    printf("Region size: 4 MB\n");
    
    mmc = uboot_mmc_get_device();
    if (mmc) {
        printf("Block size: %u bytes\n", mmc->block_size);
        printf("Bus width: %u-bit\n", mmc->bus_width);
        printf("Clock: %u Hz\n", mmc->clock);
    }
    
    printf("============================\n\n");
}

5. U-Boot命令封装(可选)

可以将上述函数封装为U-Boot命令,方便调试:

/**
 * @file    cmd_emmc_region.c
 * @brief   U-Boot命令:emmc_region
 * @note    编译后可在U-Boot控制台直接使用
 */
​
#include <command.h>
​
/**
 * @fn      do_emmc_region
 * @brief   emmc_region 命令处理函数
 * 
 * 用法:
 *   emmc_region info          - 显示区域信息
 *   emmc_region write <addr> <size> - 从内存写入区域
 *   emmc_region read <addr> <size>  - 读取区域到内存
 *   emmc_region erase         - 擦除区域
 */
static int do_emmc_region(struct cmd_tbl *cmdtp, int flag, int argc,
                          char *const argv[])
{
    ulong addr, size;
    int ret;
    
    if (argc < 2) {
        return CMD_RET_USAGE;
    }
    
    if (strcmp(argv[1], "info") == 0) {
        uboot_emmc_region_info();
        return CMD_RET_SUCCESS;
    }
    
    if (strcmp(argv[1], "write") == 0) {
        if (argc < 4) return CMD_RET_USAGE;
        addr = simple_strtoul(argv[2], NULL, 16);
        size = simple_strtoul(argv[3], NULL, 10);
        ret = uboot_emmc_raw_region_write(addr, size, 0);
        return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
    }
    
    if (strcmp(argv[1], "read") == 0) {
        if (argc < 4) return CMD_RET_USAGE;
        addr = simple_strtoul(argv[2], NULL, 16);
        size = simple_strtoul(argv[3], NULL, 10);
        ret = uboot_emmc_raw_region_read(addr, size, 0);
        return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
    }
    
    if (strcmp(argv[1], "erase") == 0) {
        ret = uboot_emmc_raw_region_erase();
        return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
    }
    
    return CMD_RET_USAGE;
}
​
U_BOOT_CMD(
    emmc_region, 4, 1, do_emmc_region,
    "eMMC 4MB region management",
    "info          - Show region info\n"
    "emmc_region write <addr> <size> - Write from memory to region\n"
    "emmc_region read <addr> <size>  - Read region to memory\n"
    "emmc_region erase               - Erase region"
);

6. 使用示例

6.1 U-Boot控制台操作
# 进入U-Boot控制台(启动时按空格键)
​
# 1. 查看MMC设备
=> mmc list
mmc@fe2e0000: 0 (eMMC)
mmc@fe2c0000: 1 (SD)
​
# 2. 切换eMMC设备
=> mmc dev 0
switch to partitions #0, OK
mmc0(part 0) is current device
​
# 3. 查看分区
=> mmc part
Partition Map for MMC device 0 -- Partition Type: EFI
Part Start LBA End LBA Name
1 0x00004000 0x00005fff "uboot"
2 0x00006000 0x00007fff "misc"
3 0x00008000 0x00017fff "emmc_region"   # 4MB区域分区
​
# 4. 使用自定义命令操作4MB区域
=> emmc_region info
​
# 5. 准备测试数据(在内存0xA0000000填充0x55)
=> mw.b 0xA0000000 0x55 0x1000
​
# 6. 写入4MB区域
=> emmc_region write 0xA0000000 0x1000
​
# 7. 读取验证
=> emmc_region read 0xA1000000 0x1000
=> md.b 0xA1000000 0x20    # 查看内存数据
6.2 与Linux对比示例
操作 U-Boot命令 Linux命令
查看分区 mmc part cat /proc/partitions
读取文件 ext4load mmc 0:1 0xA0000000 /file.bin cat /mnt/emmc/file.bin
写入文件 ext4write mmc 0:1 0xA0000000 /file.bin 0x1000 dd if=/dev/zero of=/mnt/emmc/file.bin bs=1k count=4
原始读块 mmc read 0xA0000000 0x4000 0x2000 dd if=/dev/mmcblk0 of=/tmp/data bs=512 skip=16384 count=8192
原始写块 mmc write 0xA0000000 0x4000 0x2000 dd if=/tmp/data of=/dev/mmcblk0 bs=512 seek=16384 count=8192

7. 关键差异总结

特性 U-Boot Linux
内存地址 必须指定(如0xA0000000 由内核管理,用户空间用指针
数据传递 直接DMA到指定物理地址 经过用户态/内核态拷贝
文件系统缓存 有页缓存
并发访问 单线程命令执行 支持多进程并发
错误处理 命令返回码 errno + 异常
分区识别 需要手动mmc part 内核自动识别

8. 配置建议

在U-Boot源码中需要启用的配置选项:

# Kconfig配置
CONFIG_CMD_MMC=y           # 启用mmc命令
CONFIG_MMC_WRITE=y         # 启用MMC写入功能
CONFIG_CMD_EXT4=y          # 启用ext4文件系统支持
CONFIG_CMD_EXT4_WRITE=y    # 启用ext4写入(默认禁用!)
CONFIG_CMD_FAT=y           # 启用FAT支持
CONFIG_CMD_FAT_WRITE=y     # 启用FAT写入
CONFIG_MMC_HS400_SUPPORT=y # 启用HS400高速模式(可选)

重点提醒

  • CONFIG_CMD_EXT4_WRITE 默认是禁用的,需要在menuconfig中手动开启

  • 使用原始块操作(mmc read/write)无需文件系统支持,但需要自己管理块映射

  • U-Boot无文件系统缓存,每次文件操作都会重新挂载

第三部分 kernel 处理阶段

Linux/U-Boot eMMC 4MB区域管理方案

1. 方案概述

Linux和U-Boot已提供完整的文件系统支持,可直接使用现有方案管理eMMC的4MB区域。

核心结论:

  • eMMC自带MMC控制器,内置坏块管理和磨损均衡

  • 直接使用块设备 + 文件系统接口,无需操作原始NAND

  • 现有文件系统已满足4MB区域管理需求


2. 文件系统选型

文件系统 适用场景 优点 缺点
FAT32 U-Boot与Linux共享数据 U-Boot原生支持,跨平台兼容性好 不支持权限,单文件<4GB
ext4 Linux专用数据分区 日志功能,性能好,支持大文件 U-Boot支持有限
SquashFS 只读数据(如固件) 压缩存储,节省空间 只读,不可写

推荐组合:

  • U-Boot可访问的分区 → FAT32

  • Linux专用分区 → ext4


3. 分区创建方法

方法一:使用fdisk分区(Linux环境)
# 1. 查看eMMC设备
lsblk                    # 查找 /dev/mmcblkX
sudo fdisk /dev/mmcblk0
​
# 2. 创建分区(fdisk交互命令)
Command (m for help): n   # 新建分区
Select (default p): p     # 主分区
Partition number: 1       # 分区号
First sector: 2048        # 起始扇区(1MB对齐)
Last sector: +4M          # 大小4MB
​
Command (m for help): w   # 保存并退出
​
# 3. 格式化分区
sudo mkfs.ext4 /dev/mmcblk0p1      # ext4格式
# 或
sudo mkfs.vfat -F 32 /dev/mmcblk0p1 # FAT32格式
​
# 4. 挂载使用
sudo mount /dev/mmcblk0p1 /mnt/emmc
方法二:使用parted分区
sudo parted /dev/mmcblk0
(parted) mklabel gpt
(parted) mkpart primary ext4 1MB 5MB    # 4MB空间
(parted) quit
方法三:U-Boot中分区
# 在U-Boot中创建GPT分区
=> gpt write mmc 0 ${partitions}
=> mmc part

4. 代码实现架构

4.1 目录树形结构
emmc_manager/
├── include/
│   ├── emmc_manager.h        # 模块公共接口
│   ├── partition_manager.h   # 分区管理接口
│   └── fs_interface.h        # 文件系统抽象层
├── src/
│   ├── emmc_manager.c        # 主控模块
│   ├── partition_manager.c   # 分区操作实现
│   ├── fs_interface.c        # 文件系统封装
│   ├── mmc_io.c              # MMC块设备IO
│   └── config.c              # 配置管理
├── tests/
│   ├── test_emmc.c           # 单元测试
│   └── benchmark.c           # 性能测试
└── docs/
    └── doxygen_config        # Doxygen配置

4.2 关键函数树形分析
emmc_manager/
│
├── emmc_init()                     # 模块初始化
│   ├── mmc_device_open()          # 打开MMC设备
│   ├── detect_partition()         # 检测分区信息
│   └── fs_mount()                 # 挂载文件系统
│
├── emmc_write_region()             # 写入4MB区域
│   ├── validate_region()          # 区域范围验证
│   ├── fs_open()                  # 打开文件
│   ├── fs_write()                 # 写入数据
│   └── fs_sync()                  # 同步到存储
│
├── emmc_read_region()              # 读取4MB区域
│   ├── fs_open()                  # 打开文件
│   ├── fs_read()                  # 读取数据
│   └── fs_close()                 # 关闭文件
│
├── emmc_erase_region()             # 擦除区域
│   ├── fs_unlink()                # 删除文件
│   └── fs_trim()                  # TRIM优化
│
└── emmc_get_status()               # 状态查询
    ├── mmc_get_csd()              # 获取CSD寄存器
    └── fs_statvfs()               # 获取文件系统状态

5. 完整代码实现

5.1 头文件 - emmc_manager.h
/**
 * @file    emmc_manager.h
 * @brief   eMMC 4MB区域管理模块头文件
 * @author  Embedded Team
 * @version 1.0.0
 * @date    2026-03-30
 * 
 * @details 提供eMMC存储设备4MB专用区域的统一管理接口。
 *          支持分区创建、文件读写、区域擦除等操作。
 *          【设计模式】门面模式 - 为复杂的eMMC操作提供简化接口
 */
​
#ifndef EMMC_MANAGER_H
#define EMMC_MANAGER_H
​
#include <stdint.h>
#include <stddef.h>
​
#ifdef __cplusplus
extern "C" {
#endif
​
/**
 * @defgroup emmc_config 配置宏定义
 * @{
 */
#define EMMC_DEVICE_PATH       "/dev/mmcblk0"      /**< eMMC设备路径 */
#define EMMC_PARTITION_NUM     1                   /**< 分区编号 */
#define EMMC_MOUNT_POINT       "/mnt/emmc"         /**< 挂载点路径 */
#define EMMC_REGION_SIZE       (4 * 1024 * 1024)   /**< 区域大小 4MB */
#define EMMC_REGION_FILE       "/mnt/emmc/region.bin" /**< 区域文件路径 */
/** @} */
​
/**
 * @enum    emmc_error_t
 * @brief   错误码定义
 */
typedef enum {
    EMMC_OK = 0,                /**< 操作成功 */
    EMMC_ERR_INIT = -1,         /**< 初始化失败 */
    EMMC_ERR_DEVICE = -2,       /**< 设备打开失败 */
    EMMC_ERR_MOUNT = -3,        /**< 挂载失败 */
    EMMC_ERR_FILE_OPEN = -4,    /**< 文件打开失败 */
    EMMC_ERR_READ = -5,         /**< 读取失败 */
    EMMC_ERR_WRITE = -6,        /**< 写入失败 */
    EMMC_ERR_INVALID_PARAM = -7,/**< 参数无效 */
    EMMC_ERR_REGION_OVERFLOW = -8 /**< 区域越界 */
} emmc_error_t;
​
/**
 * @struct  emmc_region_info_t
 * @brief   区域信息结构体
 */
typedef struct {
    uint64_t offset;            /**< 区域起始偏移(字节) */
    uint64_t size;              /**< 区域大小(字节) */
    char     mount_point[64];   /**< 挂载点路径 */
    char     device_path[64];   /**< 设备路径 */
    uint32_t block_size;        /**< 块大小(字节) */
} emmc_region_info_t;
​
/**
 * @fn      emmc_init
 * @brief   初始化eMMC管理模块
 * @return  EMMC_OK 成功,其他失败
 * 
 * 【设计模式】单例模式 - 确保模块只初始化一次
 * 【性能分析】O(1) 时间复杂度,主要耗时在设备探测和挂载操作
 */
emmc_error_t emmc_init(void);
​
/**
 * @fn      emmc_deinit
 * @brief   反初始化eMMC管理模块
 * @return  EMMC_OK 成功,其他失败
 */
emmc_error_t emmc_deinit(void);
​
/**
 * @fn      emmc_write_region
 * @brief   向4MB区域写入数据
 * @param   data    待写入数据缓冲区指针
 * @param   size    待写入数据大小(字节)
 * @param   offset  区域内部偏移(字节)
 * @return  EMMC_OK 成功,其他失败
 * 
 * 【设计模式】策略模式 - 根据offset和size选择最优写入策略
 * 【性能分析】O(n) 时间复杂度,n为写入大小,受底层块设备写入速度限制
 */
emmc_error_t emmc_write_region(const uint8_t *data, size_t size, uint64_t offset);
​
/**
 * @fn      emmc_read_region
 * @brief   从4MB区域读取数据
 * @param   buffer  数据缓冲区指针
 * @param   size    待读取数据大小(字节)
 * @param   offset  区域内部偏移(字节)
 * @return  EMMC_OK 成功,其他失败
 * 
 * 【设计模式】策略模式 - 根据offset和size选择最优读取策略
 * 【性能分析】O(n) 时间复杂度,n为读取大小,受底层块设备读取速度限制
 */
emmc_error_t emmc_read_region(uint8_t *buffer, size_t size, uint64_t offset);
​
/**
 * @fn      emmc_erase_region
 * @brief   擦除4MB区域(删除区域文件)
 * @return  EMMC_OK 成功,其他失败
 * 
 * 【设计模式】命令模式 - 将擦除操作封装为命令对象
 * 【性能分析】O(1) 时间复杂度,实际擦除由文件系统完成
 */
emmc_error_t emmc_erase_region(void);
​
/**
 * @fn      emmc_get_region_info
 * @brief   获取4MB区域信息
 * @param   info    区域信息结构体指针
 * @return  EMMC_OK 成功,其他失败
 */
emmc_error_t emmc_get_region_info(emmc_region_info_t *info);
​
/**
 * @fn      emmc_verify_region
 * @brief   验证4MB区域完整性
 * @param   expected_checksum   期望校验和
 * @return  EMMC_OK 校验通过,其他失败
 */
emmc_error_t emmc_verify_region(uint32_t expected_checksum);
​
#ifdef __cplusplus
}
#endif
​
#endif /* EMMC_MANAGER_H */
5.2 源文件 - emmc_manager.c
/**
 * @file    emmc_manager.c
 * @brief   eMMC 4MB区域管理模块实现
 * @author  Embedded Team
 * @version 1.0.0
 * @date    2026-03-30
 * 
 * @details 实现eMMC存储设备4MB区域的完整管理功能。
 *          支持FAT32和ext4文件系统,提供文件读写、区域擦除等操作。
 * 
 * 【设计模式汇总】
 * 1. 门面模式 - emmc_manager.h提供简化接口
 * 2. 单例模式 - 模块初始化状态管理
 * 3. 策略模式 - 根据操作类型选择不同IO策略
 * 4. 命令模式 - erase操作封装
 * 5. 适配器模式 - 适配不同文件系统接口
 */
​
#include "emmc_manager.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
​
/** @brief 模块初始化标志 - 单例模式状态管理 */
static int g_initialized = 0;
​
/** @brief 区域信息缓存 */
static emmc_region_info_t g_region_info = {
    .offset = 0,
    .size = EMMC_REGION_SIZE,
    .mount_point = EMMC_MOUNT_POINT,
    .device_path = EMMC_DEVICE_PATH,
    .block_size = 512
};
​
/**
 * @fn      mmc_device_open
 * @brief   打开MMC块设备
 * @return  设备文件描述符,负数为错误
 * @internal
 */
static int mmc_device_open(void)
{
    int fd = open(EMMC_DEVICE_PATH, O_RDWR);
    if (fd < 0) {
        fprintf(stderr, "[ERROR] Failed to open %s: %s\n", 
                EMMC_DEVICE_PATH, strerror(errno));
        return -errno;
    }
    return fd;
}
​
/**
 * @fn      mmc_get_block_size
 * @brief   获取MMC设备块大小
 * @param   fd  设备文件描述符
 * @return  块大小(字节),0表示失败
 * @internal
 */
static uint32_t mmc_get_block_size(int fd)
{
    uint32_t block_size = 512;
    /* 使用ioctl获取块设备扇区大小 */
    if (ioctl(fd, BLKSSZGET, &block_size) < 0) {
        /* 默认512字节扇区 */
        block_size = 512;
    }
    return block_size;
}
​
/**
 * @fn      fs_mount
 * @brief   挂载文件系统
 * @return  EMMC_OK 成功,其他失败
 * @internal
 * 
 * 【设计模式】适配器模式 - 适配不同文件系统类型
 */
static emmc_error_t fs_mount(void)
{
    struct stat st;
    char partition_path[64];
    
    /* 创建挂载点目录 */
    if (stat(g_region_info.mount_point, &st) != 0) {
        if (mkdir(g_region_info.mount_point, 0755) != 0) {
            fprintf(stderr, "[ERROR] Failed to create mount point: %s\n", 
                    strerror(errno));
            return EMMC_ERR_MOUNT;
        }
    }
    
    /* 构建分区设备路径 /dev/mmcblk0p1 */
    snprintf(partition_path, sizeof(partition_path), "%sp%d", 
             EMMC_DEVICE_PATH, EMMC_PARTITION_NUM);
    
    /* 尝试挂载ext4 */
    if (mount(partition_path, g_region_info.mount_point, "ext4", 
              MS_NOATIME, NULL) == 0) {
        return EMMC_OK;
    }
    
    /* 尝试挂载vfat/FAT32 */
    if (mount(partition_path, g_region_info.mount_point, "vfat", 
              MS_NOATIME, NULL) == 0) {
        return EMMC_OK;
    }
    
    fprintf(stderr, "[ERROR] Failed to mount %s: %s\n", 
            partition_path, strerror(errno));
    return EMMC_ERR_MOUNT;
}
​
/**
 * @fn      fs_umount
 * @brief   卸载文件系统
 * @return  EMMC_OK 成功,其他失败
 * @internal
 */
static emmc_error_t fs_umount(void)
{
    if (umount(g_region_info.mount_point) != 0) {
        fprintf(stderr, "[ERROR] Failed to umount: %s\n", strerror(errno));
        return EMMC_ERR_MOUNT;
    }
    return EMMC_OK;
}
​
/**
 * @fn      fs_file_write
 * @brief   向文件写入数据
 * @param   data    数据缓冲区
 * @param   size    写入大小
 * @param   offset  文件内偏移
 * @return  EMMC_OK 成功,其他失败
 * @internal
 * 
 * 【性能分析】pwrite是原子操作,适合多线程场景
 *           写入速度取决于eMMC硬件性能,通常20-50MB/s
 */
static emmc_error_t fs_file_write(const uint8_t *data, size_t size, uint64_t offset)
{
    int fd;
    ssize_t written;
    char file_path[128];
    
    snprintf(file_path, sizeof(file_path), "%s/region.bin", 
             g_region_info.mount_point);
    
    /* 打开文件:读写模式,不存在则创建 */
    fd = open(file_path, O_RDWR | O_CREAT, 0644);
    if (fd < 0) {
        fprintf(stderr, "[ERROR] Failed to open file: %s\n", strerror(errno));
        return EMMC_ERR_FILE_OPEN;
    }
    
    /* 定位到偏移位置并写入 */
    written = pwrite(fd, data, size, offset);
    if (written != (ssize_t)size) {
        fprintf(stderr, "[ERROR] Write failed: wrote %zd, expected %zu\n", 
                written, size);
        close(fd);
        return EMMC_ERR_WRITE;
    }
    
    /* 确保数据同步到存储 */
    fsync(fd);
    close(fd);
    
    return EMMC_OK;
}
​
/**
 * @fn      fs_file_read
 * @brief   从文件读取数据
 * @param   buffer  数据缓冲区
 * @param   size    读取大小
 * @param   offset  文件内偏移
 * @return  EMMC_OK 成功,其他失败
 * @internal
 * 
 * 【性能分析】预读机制会提高顺序读性能
 *           随机读性能取决于eMMC控制器
 */
static emmc_error_t fs_file_read(uint8_t *buffer, size_t size, uint64_t offset)
{
    int fd;
    ssize_t read_bytes;
    char file_path[128];
    
    snprintf(file_path, sizeof(file_path), "%s/region.bin", 
             g_region_info.mount_point);
    
    fd = open(file_path, O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "[ERROR] Failed to open file: %s\n", strerror(errno));
        return EMMC_ERR_FILE_OPEN;
    }
    
    read_bytes = pread(fd, buffer, size, offset);
    if (read_bytes != (ssize_t)size) {
        fprintf(stderr, "[ERROR] Read failed: read %zd, expected %zu\n", 
                read_bytes, size);
        close(fd);
        return EMMC_ERR_READ;
    }
    
    close(fd);
    return EMMC_OK;
}
​
/**
 * @fn      fs_file_unlink
 * @brief   删除区域文件
 * @return  EMMC_OK 成功,其他失败
 * @internal
 * 
 * 【设计模式】命令模式 - 擦除操作封装
 */
static emmc_error_t fs_file_unlink(void)
{
    char file_path[128];
    
    snprintf(file_path, sizeof(file_path), "%s/region.bin", 
             g_region_info.mount_point);
    
    if (unlink(file_path) != 0 && errno != ENOENT) {
        fprintf(stderr, "[ERROR] Failed to unlink: %s\n", strerror(errno));
        return EMMC_ERR_WRITE;
    }
    
    return EMMC_OK;
}
​
/**
 * @fn      validate_offset_and_size
 * @brief   验证读写偏移和大小是否在4MB区域内
 * @param   size    操作大小
 * @param   offset  区域偏移
 * @return  EMMC_OK 有效,EMMC_ERR_REGION_OVERFLOW 越界
 * @internal
 */
static emmc_error_t validate_offset_and_size(size_t size, uint64_t offset)
{
    if (size == 0) {
        return EMMC_ERR_INVALID_PARAM;
    }
    
    if (offset + size > EMMC_REGION_SIZE) {
        fprintf(stderr, "[ERROR] Region overflow: offset=%llu, size=%zu, region=%d\n",
                (unsigned long long)offset, size, EMMC_REGION_SIZE);
        return EMMC_ERR_REGION_OVERFLOW;
    }
    
    return EMMC_OK;
}
​
/* ==================== 公共API实现 ==================== */
​
/**
 * @brief   初始化eMMC管理模块
 * @note    单例模式实现,重复调用返回成功
 */
emmc_error_t emmc_init(void)
{
    /* 单例模式检查:已初始化则直接返回 */
    if (g_initialized) {
        return EMMC_OK;
    }
    
    int fd = mmc_device_open();
    if (fd < 0) {
        return EMMC_ERR_DEVICE;
    }
    
    /* 获取设备块大小 */
    g_region_info.block_size = mmc_get_block_size(fd);
    close(fd);
    
    /* 挂载文件系统 */
    emmc_error_t ret = fs_mount();
    if (ret != EMMC_OK) {
        return ret;
    }
    
    g_initialized = 1;
    printf("[INFO] eMMC module initialized, block size=%u bytes\n", 
           g_region_info.block_size);
    
    return EMMC_OK;
}
​
/**
 * @brief   反初始化eMMC管理模块
 */
emmc_error_t emmc_deinit(void)
{
    if (!g_initialized) {
        return EMMC_OK;
    }
    
    emmc_error_t ret = fs_umount();
    if (ret != EMMC_OK) {
        return ret;
    }
    
    g_initialized = 0;
    printf("[INFO] eMMC module deinitialized\n");
    
    return EMMC_OK;
}
​
/**
 * @brief   向4MB区域写入数据
 * @note    策略模式:根据offset和size自动选择最优写入方式
 */
emmc_error_t emmc_write_region(const uint8_t *data, size_t size, uint64_t offset)
{
    /* 参数验证 */
    if (data == NULL) {
        return EMMC_ERR_INVALID_PARAM;
    }
    
    emmc_error_t ret = validate_offset_and_size(size, offset);
    if (ret != EMMC_OK) {
        return ret;
    }
    
    if (!g_initialized) {
        return EMMC_ERR_INIT;
    }
    
    /* 策略选择:大数据块使用直接写入,小数据考虑缓存策略 */
    ret = fs_file_write(data, size, offset);
    
    return ret;
}
​
/**
 * @brief   从4MB区域读取数据
 * @note    策略模式:根据offset和size选择最优读取方式
 */
emmc_error_t emmc_read_region(uint8_t *buffer, size_t size, uint64_t offset)
{
    /* 参数验证 */
    if (buffer == NULL) {
        return EMMC_ERR_INVALID_PARAM;
    }
    
    emmc_error_t ret = validate_offset_and_size(size, offset);
    if (ret != EMMC_OK) {
        return ret;
    }
    
    if (!g_initialized) {
        return EMMC_ERR_INIT;
    }
    
    ret = fs_file_read(buffer, size, offset);
    
    return ret;
}
​
/**
 * @brief   擦除4MB区域
 * @note    命令模式:将擦除操作封装为独立的命令
 */
emmc_error_t emmc_erase_region(void)
{
    if (!g_initialized) {
        return EMMC_ERR_INIT;
    }
    
    /* 命令模式执行:删除文件实现逻辑擦除 */
    emmc_error_t ret = fs_file_unlink();
    
    if (ret == EMMC_OK) {
        printf("[INFO] Region erased successfully\n");
    }
    
    return ret;
}
​
/**
 * @brief   获取4MB区域信息
 */
emmc_error_t emmc_get_region_info(emmc_region_info_t *info)
{
    if (info == NULL) {
        return EMMC_ERR_INVALID_PARAM;
    }
    
    memcpy(info, &g_region_info, sizeof(emmc_region_info_t));
    return EMMC_OK;
}
​
/**
 * @brief   验证4MB区域完整性
 * @note    计算区域文件CRC32并与期望值比较
 */
emmc_error_t emmc_verify_region(uint32_t expected_checksum)
{
    uint8_t *buffer = NULL;
    uint32_t calculated_checksum = 0;
    emmc_error_t ret;
    
    if (!g_initialized) {
        return EMMC_ERR_INIT;
    }
    
    /* 分配缓冲区 */
    buffer = (uint8_t *)malloc(EMMC_REGION_SIZE);
    if (buffer == NULL) {
        return EMMC_ERR_READ;
    }
    
    /* 读取整个区域 */
    ret = emmc_read_region(buffer, EMMC_REGION_SIZE, 0);
    if (ret != EMMC_OK) {
        free(buffer);
        return ret;
    }
    
    /* 简单累加校验和(实际应用应使用CRC32) */
    for (uint64_t i = 0; i < EMMC_REGION_SIZE; i++) {
        calculated_checksum += buffer[i];
    }
    
    free(buffer);
    
    if (calculated_checksum == expected_checksum) {
        printf("[INFO] Region verification PASSED\n");
        return EMMC_OK;
    } else {
        printf("[ERROR] Region verification FAILED: calc=0x%08X, expected=0x%08X\n",
               calculated_checksum, expected_checksum);
        return EMMC_ERR_READ;
    }
}

6. 使用示例

#include "emmc_manager.h"
#include <stdio.h>
#include <string.h>
​
int main(void)
{
    emmc_error_t ret;
    uint8_t write_data[1024];
    uint8_t read_data[1024];
    
    /* 1. 初始化模块 */
    ret = emmc_init();
    if (ret != EMMC_OK) {
        printf("Init failed: %d\n", ret);
        return -1;
    }
    
    /* 2. 准备测试数据 */
    memset(write_data, 0xAA, sizeof(write_data));
    
    /* 3. 写入数据到区域偏移0位置 */
    ret = emmc_write_region(write_data, sizeof(write_data), 0);
    if (ret == EMMC_OK) {
        printf("Write success\n");
    }
    
    /* 4. 读取数据验证 */
    memset(read_data, 0, sizeof(read_data));
    ret = emmc_read_region(read_data, sizeof(read_data), 0);
    if (ret == EMMC_OK) {
        if (memcmp(write_data, read_data, sizeof(write_data)) == 0) {
            printf("Read verification PASSED\n");
        }
    }
    
    /* 5. 擦除区域 */
    ret = emmc_erase_region();
    
    /* 6. 反初始化 */
    emmc_deinit();
    
    return 0;
}

7. 性能分析

操作 时间复杂度 实际耗时(4MB) 影响因素
顺序写入 O(n) ~0.2-0.5秒 eMMC写入速度20-50MB/s
顺序读取 O(n) ~0.1-0.3秒 eMMC读取速度50-100MB/s
随机读写 O(n) 略高于顺序 eMMC控制器缓存命中率
擦除操作 O(1) <0.1秒 文件系统删除操作
挂载操作 O(1) ~0.05秒 文件系统类型和日志

优化建议:

  1. 使用noatime挂载选项减少写入

  2. 分区起始地址对齐到1MB边界(2048扇区)

  3. 格式化时设置stridestripe-width优化ext4

  4. 启用写缓存但配合fsync确保关键数据持久化


8. 总结

问题 结论
是否需要自己编写文件系统? 不需要,Linux/U-Boot已支持ext4/FAT32
哪些文件系统可用? ext4、FAT32、SquashFS
如何分区4MB区域? fdisk/parted创建,起始扇区对齐到2048
代码设计模式 门面模式、单例模式、策略模式、命令模式、适配器模式
性能预期 读50-100MB/s,写20-50MB/s
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐