事先声明本文不用于任何商业行为,仅用于本人学习与记录。所有截图版权均属于《Embedded Multi-Media Card (e•MMC) Electrical Standard (5.1)》
欢迎点赞、收藏、转发分享给朋友,禁止未经书面授权的复制、搬运、二次剪辑。
如有引用请注明出处。
标准规范仍然以《Embedded Multi-Media Card (e•MMC) Electrical Standard (5.1)》为准
写在最前:往后个人见解部分均用实线框出来。
本文涉及PSA、Lock/Unlock、SLEEP、Application-specific commands 、RPMB。从使用频率来看,本文全是重点!
本文完整讲解 eMMC 重放保护内存块(RPMB)的协议规范,RPMB 是 eMMC 内部具备防重放、消息认证能力的安全分区,专门用于存储密钥、安全固件、设备配置等敏感数据,依靠 HMAC-MAC 校验 + 单调递增写计数器实现安全防护,防止攻击者复制、篡改、重放安全指令。
章节依次覆盖 RPMB 全套交互命令与事务流程:包含 RPMB 基础能力、RPMB 寄存器、RPMB 密钥注入、计数器读取、带认证数据写、带认证数据读、带认证设备配置写、带认证设备配置读。 所有 RPMB 事务遵循统一框架:主机先发送 CMD25 发起 RPMB 请求包(512 字节帧),包内携带请求类型、随机数、地址、块计数、写计数器、MAC;设备校验 MAC、写计数器合法性,校验通过才执行对应操作,写操作成功后自动递增内部写计数器;主机再使用 CMD18 读取结果响应包,从响应包中校验 MAC、判断操作结果码。
协议规定了各类请求的请求类型码:0x0003(认证写)、0x0004(认证读)、0x0005(读取结果)、0x0006(认证设备配置写)、0x0007(认证设备配置读),同时定义了统一错误码体系:0x02 认证失败、0x03 计数器不匹配、0x04 地址错误、0x05 写入失败、0x06 读取失败、0x01 通用失败、0x85 计数器过期。 其中设备配置读写用于访问专属的设备配置索引(仅 0x0001、0x0002 合法),是 RPMB 下独立的安全配置通道,和普通 RPMB 数据读写分离;所有 RPMB 读写都有严格的 CMD23 块计数约束,不满足块计数要求直接返回通用失败。
整体流程特点:RPMB 本身不直接通过 CMD18/CMD25 传输业务数据,而是先发起请求、再读取结果的两阶段交互;单调写计数器是防重放的核心,每一次合法写操作计数器加一,旧的请求包无法再次复用;所有关键帧都需要 MAC 校验,缺少合法 MAC 的操作会直接拒绝,保证敏感存储的完整性与真实性。
6.6.17 Production State Awareness 生产状态感知
eMMC 设备可以根据自身是否处于生产环境的信息,采用和在终端现场使用时不一样的运行方式。
举例来说,在焊接前写入存储设备的数据,在设备焊接过程中更容易发生损坏。eMMC 设备可以在焊接前加载数据时使用 "特殊" 内部操作,以此降低生产不良率;焊接完成后,则使用 "常规" 操作。
扩展 CSD 寄存器中的 PRODUCTION_STATE_AWARENESS 133 字段,作为主机向设备上报当前是焊接前状态还是焊接后状态的机制。
本标准定义两种模式来管理设备的生产状态:手动模式、自动模式。
启动或重启该流程的触发条件是正确设置 PRE_LOADING_DATA_SIZE 字段。 在设置该字段之前,主机必须确认设备已清空,所有此前写入设备的数据,都已经通过 CMD35、CMD36 和 CMD38 命令完成擦除。
如果主机在生产状态感知流程中执行了数据擦除、覆盖已有数据或者重新分区操作,则需要重新设置 PRE_LOADING_DATA_SIZE,以此重启生产状态感知流程。
6.6.17.1 Manual Mode 手动模式
使用手动模式时,主机通过修改 PRODUCTION_STATE_AWARENESS 字段,显式设置生产状态。当设备处于生产阶段、尚未焊接,并且主机准备向设备灌入内容时,主机应将状态设置为 PRE_SOLDERING_WRITES。 内容灌入完成后,主机应设置为 PRE_SOLDERING_POST_WRITES 状态。在该状态下,主机不能向设备写入数据,直到完成焊接。焊接完成后,主机应将 PRODUCTION_STATE_AWARENESS 设置为正常状态。
在该模式下,主机需要等到 PRODUCTION_STATE_AWARENESS 被设置为 PRE_SOLDERING_WRITES 之后,才可以向设备写入任何数据。 如果该功能已开启,而主机在设备并非 PRE_SOLDERING_WRITES 状态时写入数据,设备可能返回错误。
PRODUCTION_STATE_AWARENESS 的状态切换耗时可能超出切换命令的超时时间。PRODUCTION_STATE_AWARENESS 状态切换的最大超时值,由设备厂商在 PRODUCTION_STATE_AWARENESS_TIMEOUT 218 字段中定义。
6.6.17.2 Auto Mode 自动模式
使用自动模式时,主机设置将要在焊接前灌入设备的数据总量。主机将 PRODUCTION_STATE_AWARENESS 设置为 AUTO_PRE_SOLDERING,通知设备当前使用自动模式。一旦全部数据灌入完成,设备自动将 PRODUCTION_STATE_AWARENESS 切回正常状态。
在该模式下,主机仅在将 PRODUCTION_STATE_AWARENESS 设置为 AUTO_PRE_SOLDERING 之后,才可以向设备写入数据。如果该功能已开启,而主机在设备并非 AUTO_PRE_SOLDERING 状态时写入数据,设备可能返回错误。
当灌入的数据量达到 PRE_LOADING_DATA_SIZE 设定值后,设备自动将 PRODUCTION_STATE_AWARENESS 切换为正常状态。 如果主机在正常状态下(已经写入 PRE_LOADING_DATA_SIZE 指定数量的数据,且焊接还未执行)继续写入数据,设备可能返回错误。
如果在数据传输过程中发生掉电重启,已经写入设备的数据量不会被清零。因此在这种场景下,主机应当擦除全部预灌入数据,并重新把 PRE_LOADING_DATA_SIZE 设置为目标大小。

| 项目 | 手动模式(Manual Mode) | 自动模式(Auto Mode) |
|---|---|---|
| 模式入口 | 主机写 PRODUCTION_STATE_AWARENESS,显式切换状态 | 主机设置 PRODUCTION_STATE_AWARENESS = AUTO_PRE_SOLDERING 开启自动模式 |
| 预加载数据总量 | 由主机自行管控,协议不依靠 PRE_LOADING_DATA_SIZE 触发状态跳转 | 需要预先配置 PRE_LOADING_DATA_SIZE,代表焊接前预灌数据总大小 |
| 状态流转 | PRE_SOLDERING_WRITES(允许写预灌数据)→ PRE_SOLDERING_POST_WRITES(禁止写,等待焊接)→ 正常状态 | 写入数据量达到 PRE_LOADING_DATA_SIZE 阈值,设备自动切回正常状态 |
| 写权限约束 | 仅在 PRE_SOLDERING_WRITES 状态允许写入;其他状态写入可能报错 | 仅在 AUTO_PRE_SOLDERING 状态允许写入;达到容量阈值后自动禁止继续写入,写入可能报错 |
| 状态切换控制方 | 主机完全主动控制每一步状态切换 | 设备根据已写入数据量自动切换,主机只负责开启 |
| 超时相关 | 状态切换存在超时,最大时长由 PRODUCTION_STATE_AWARENESS_TIMEOUT 定义 | 无单独自动模式超时字段,依赖该统一超时字段 |
| 掉电重启处理 | 掉电后状态丢失,需要主机重新走状态配置流程 | 已写入数据量不会清零;掉电恢复后主机必须全盘擦除,重设 PRE_LOADING_DATA_SIZE |
| 触发重启流程条件 | 擦除、覆盖数据、重新分区后,主机需要重新配置状态 | 擦除、覆盖数据、重新分区后,必须重新设置 PRE_LOADING_DATA_SIZE,重启自动流程 |
| 使用场景 | 生产流程需要精细分段管控(灌数据、静置等待焊接、焊后恢复) | 预灌数据总量固定,希望灌完自动锁写,简化主机控制逻辑 |
6.6.18 Field Firmware Update 现场固件升级(FFU)
现场固件升级(FFU)用于在终端使用场景下实现功能增强。通过该机制,主机可以向 eMMC 设备下载新版本固件;下载成功后,主机通知 eMMC 设备将已下载的新固件安装到设备中。
为启动 FFU 流程,主机首先读取扩展 CSD 寄存器中的 SUPPORTED_MODES 和 FW_CONFIG 字段,确认 eMMC 设备是否支持 FFU 能力。如果 eMMC 设备支持 FFU 功能,主机即可启动 FFU 流程。FFU 流程的第一步是在扩展 CSD 寄存器的 MODE_CONFIG 字段中切换至 FFU 模式。在 FFU 模式下,主机可以使用封闭式或开放式命令(CMD17/CMD18/CMD24/CMD25)下载新固件以及读取厂商私有数据。该模式下,主机需要按照 FFU_ARG 字段中的定义设置这些命令的参数。如果命令参数与定义不符,设备行为未定义,FFU 流程可能失败。主机应当将块长度设置为 DATA_SECTOR_SIZE。下载的固件包必须按 DATA_SECTOR_SIZE 对齐(固件包内部可能需要填充对齐)。
进入 FFU 模式后,主机可以通过一条或多条写命令向设备发送新固件包。
主机将扩展 CSD 寄存器的 MODE_CONFIG 字段切回正常状态,即可恢复读写命令的常规功能。退出 FFU 模式可能会终止固件下载操作。当重新切回 FFU 模式时,主机需要读取扩展 CSD 寄存器中的 NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED 字段,查看成功下载的扇区数量,以此获取 FFU 状态信息。如果成功下载的扇区数量为 0,主机需要从第一个扇区重新下载固件包;如果成功下载的扇区数量大于 0,主机从下一个扇区继续下载,恢复固件下载流程。
如果设备支持 MODE_OPERATION_CODES 字段(由扩展 CSD 寄存器 FFU_FEATURES 字段内的 SUPPORTED_MODE_OPERATION_CODES 位标识),主机可以将 MODE_OPERATION_CODES 设置为 FFU_ABORT,通知设备终止 FFU 下载流程。收到该指令后,设备会将 NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED 字段置 0,并准备接收新的固件包。 如果主机已经成功完成固件包下载,可以将 MODE_OPERATION_CODES 设置为 FFU_INSTALL。设备会将 NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED 字段置 0,安装新固件,并把 MODE_CONFIG 切回正常状态,恢复读写命令的常规操作。主机随后应将 MODE_OPERATION_CODES 设置为 FFU_INSTALL。 如果主机在 FFU_INSTALL 命令成功执行前,在流程中途执行 CMD0、硬件复位或者上电重启,设备可以终止下载流程。
如果设备不支持 MODE_OPERATION_CODES 字段,主机切换至正常状态,然后发起 CMD0、硬件复位或者上电重启,完成新固件安装。这种场景下,设备不需要使用 NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED 字段。
上述两种场景中,只要主机尚未完成固件包下载,就执行 CMD0、硬件复位或者上电重启,都可能导致固件下载流程终止。
处于 FFU 模式时,如果主机发送不在推荐流程内的其他命令,设备行为未定义。
6.6.19 Device lock/unlock operation 设备锁定 / 解锁操作
密码保护功能允许主机通过提供密码将设备锁定,该密码后续用于解锁设备。密码和密码长度分别存放在 128 位的 PWD 寄存器与 8 位的 PWD_LEN 寄存器中。这两个寄存器属于非易失寄存器,设备上电复位不会清除其中内容。
主机可以通过置位扩展 CSD 中的永久密码禁用位(EXT_CSD 第 171 字节中的 PERM_PSWD_DIS 位),永久关闭密码保护功能。如果设备已经设置了密码,主机尝试通过该方式永久禁用 CMD42 相关功能时,该操作会失败,存储设备会在设备状态寄存器中将 ERROR 位(第 19 位)置 1。如果不需要密码保护功能,建议在设备上关闭该功能,避免被无意或者恶意设置。
如果设备已经永久禁用密码保护,主机尝试在该设备上使用密码保护功能(CMD42)会失败,状态寄存器中的 LOCK_UNLOCK_FAILED 位(第 24 位)会被置 1。
处于锁定状态的设备,可以响应并执行 "基础命令类(0 类)" 以及 "锁定设备命令类" 中的全部命令。因此主机可以执行复位、初始化、选中设备、查询状态等操作,但是无法访问设备内的数据。如果之前已经设置过密码(PWD_LEN 的值不为 0),设备上电后会自动进入锁定状态。
v4.3 及更高版本的设备,锁定命令仅作用于用户数据区。因此主机向设备下发锁定命令之后,仍然可以访问启动分区、RPMB 以及通用分区区域。
和现有的 CSD、CID 寄存器写命令类似,锁定 / 解锁命令仅能在传输状态下使用。这代表该命令不带地址参数,并且使用该命令前必须先选中设备。
设备锁定 / 解锁命令(CMD42)的结构与总线事务类型和普通单块写命令一致。传输的数据块包含该命令所需全部信息(密码设置模式、密码本身、设备锁定 / 解锁等)。表 16 描述了命令数据块的结构。
设备锁定 / 解锁命令(CMD42)仅能在设备工作于单数据速率模式时执行。在双倍数据速率模式下,CMD42 属于非法命令。

- ERASE:置 1 代表强制擦除操作(其余所有位必须为 0),并且仅发送命令字节。
- LOCK/UNLOCK:置 1 = 锁定设备;置 0 = 解锁设备。 注:该位可以和 SET_PWD 同时置 1,但是不允许和 CLR_PWD 同时置 1。
- CLR_PWD:置 1 = 清除密码。
- SET_PWD:置 1 = 向 PWD 寄存器写入新密码。
- PWD_LEN:定义后续密码的长度(单位:字节)。合法密码长度范围为 1 至 16 字节。
- PWD:密码(根据命令类型,为新密码或者当前正在使用的密码)。
主机在发送设备锁定 / 解锁命令之前,需要先定义数据块大小,以此支持不同长度的密码。
下面各小节定义了多种锁定 / 解锁命令序列:
设置密码:
- 如果还未选中设备,先选中设备(CMD7)
- 通过 CMD16 定义块长度,该长度由 8 位设备锁定 / 解锁模式、8 位密码长度(单位:字节)以及新密码的字节数共同决定。如果是替换密码,块长度需要同时包含旧密码与新密码,两个密码随本条命令一起发送。
- 在数据线上发送对应数据块长度的设备锁定 / 解锁命令(CMD42),数据块附带 16 位 CRC。数据块需要标识模式(SET_PWD)、长度(PWD_LEN)以及密码本身。如果是替换密码,PWD_LEN 的值需要包含旧密码和新密码两者长度,PWD 字段内先放当前正在使用的旧密码,后面紧跟新密码。旧密码和新密码各自长度都不能超过 16 字节。如果新密码长度超出 16 字节,状态寄存器的 LOCK_UNLOCK_FAILED 错误位会被置 1,原密码保持不变。
- 如果尝试替换密码,但 PWD_LEN 仅填写了旧密码的长度,状态寄存器的 LOCK_UNLOCK_FAILED 错误位会被置 1,原密码保持不变。
- 如果发送的旧密码不正确(长度或者内容不匹配),状态寄存器的 LOCK_UNLOCK_FAILED 错误位会被置 1,原密码保持不变。如果旧密码校验通过,则新密码以及对应的长度会分别存入 PWD 和 PWD_LEN 字段。
注:密码长度寄存器(PWD_LEN)用于标识当前是否已经设置密码。该值为 0 代表没有设置密码;PWD_LEN 不为 0 时,设备上电之后会自动锁定。可以在当前上电会话内直接置位 LOCK/UNLOCK 位(同时设置密码),或者单独下发锁定命令,让设备立刻进入锁定状态。
重置密码:
- 如果还未选中设备,先选中设备(CMD7)
- 通过 CMD16 定义块长度,该长度由 8 位设备锁定 / 解锁模式、8 位密码长度(单位:字节)以及当前正在使用密码的字节数共同决定。
- 在数据线上发送对应数据块长度的设备锁定 / 解锁命令(CMD42),数据块附带 16 位 CRC。数据块需要标识模式 CLR_PWD、长度(PWD_LEN)以及密码本身(LOCK/UNLOCK 位无作用)。如果密码和 PWD_LEN 与发送的内容匹配,则 PWD 寄存器内容被清空,PWD_LEN 置为 0。如果密码校验失败,则状态寄存器的 LOCK_UNLOCK_FAILED 错误位会被置 1。
锁定设备:
- 如果还未选中设备,先选中设备(CMD7)。
- 通过 CMD16 定义块长度,长度由 8 位设备锁定 / 解锁模式、8 位密码长度(单位:字节)以及当前正在使用密码的字节数共同决定。
- 在数据线上发送对应数据块长度的设备锁定 / 解锁命令(CMD42),数据块附带 16 位 CRC。数据块需要标识模式 LOCK/UNLOCK、长度(PWD_LEN)以及密码本身(PWD)。
如果数据块中的密码与当前保存的密码一致,设备将被锁定,并且设备状态寄存器中的设备锁定位会被置 1。如果密码不一致,状态寄存器中的 LOCK_UNLOCK_FAILED 错误位会被置 1。
注:可以在同一个序列中完成设置密码并锁定设备。也就是说,主机可以执行设置密码所需的全部步骤,包括设置密码,同时通过在同一条 CMD42 中置位 LOCK 位来锁定设备。
如果之前已经设置过密码(PWD_LEN 不为 0),设备上电后会自动锁定。若尝试锁定一个没有密码的设备,操作将失败,状态寄存器中的 LOCK_UNLOCK_FAILED 错误位会被置 1。
解锁设备:
- 如果还未选中设备,先选中设备(CMD7)。
- 通过 CMD16 定义块长度,长度由 8 位设备锁定 / 解锁模式、8 位密码长度(单位:字节)以及当前正在使用密码的字节数共同决定。
- 在数据线上发送对应数据块长度的设备锁定 / 解锁命令(CMD42),数据块附带 16 位 CRC。数据块需要标识模式 LOCK/UNLOCK、长度(PWD_LEN)以及密码本身(PWD)。
如果数据块中的密码与当前保存的密码一致,设备将被解锁,并且设备状态寄存器中的设备锁定位会被清除。如果密码不一致,状态寄存器中的 LOCK_UNLOCK_FAILED 错误位会被置 1。
注:这种解锁仅在当前上电周期内有效。只要 PWD 寄存器中的密码没有被清除,设备下次上电后仍会自动重新锁定。唯一的永久解锁方式是清除密码。
对未锁定的设备执行解锁尝试,操作将失败,状态寄存器中的 LOCK_UNLOCK_FAILED 错误位会被置 1。
强制擦除:
- 如果用户忘记密码(PWD 内容),可以将设备内全部数据连同密码一起擦除。该操作称为强制擦除。
- 如果还未选中设备,先选中设备(CMD7)。
- 通过 CMD16 将块长度设置为 1 字节(8 位设备锁定 / 解锁命令)。在数据线上发送 1 字节长度的设备锁定 / 解锁命令(CMD42),数据块附带 16 位 CRC。该数据块需要标识模式 ERASE(ERASE 位必须是唯一置 1 的位)。
如果数据字段内除 ERASE 位以外还有其他置 1 的位,状态寄存器的 LOCK_UNLOCK_FAILED 错误位会被置 1,擦除请求会被拒绝。如果命令被接受,设备内所有内容都会被擦除,包括 PWD 和 PWD_LEN 寄存器内容,被锁定的设备随之解锁。此外,如果设备带有临时写保护,该保护会被解除(开启写使能),CSD 中的临时写保护位以及所有临时写保护组都会被清除。
如果下发强制擦除命令时,设备上存在上电写保护或者永久写保护的写保护组,该命令会执行失败(设备保持锁定),状态寄存器的 LOCK_UNLOCK_FAILED 错误位会被置 1。
对未锁定的设备执行强制擦除会失败,LOCK_UNLOCK_FAILED 错误位会被置 1。如果在永久写保护的介质上下发强制擦除命令,命令会失败(设备保持锁定),状态寄存器的 LOCK_UNLOCK_FAILED 错误位会被置 1。
强制擦除超时时间在 0 号参数中定义。v4.3 及更高版本的设备,当主机下发强制擦除时,只会擦除用户数据区(包含增强属性区)内存储的数据。强制擦除不会作用于启动分区、RPMB 以及通用分区区域。
Table68中 Device status第25位显示设备是否上锁,这个是很有用的,主机的response会显示设备的状态,这个能一眼看出设备上锁,那就无法操作设备了。
6.6.20 Application-specific commands 应用专用命令
eMMC 系统设计目标是为各类应用场景提供标准接口。在这样的使用场景下,预计会有客户 / 应用需要自定义功能。为了提供统一方式实现这类自定义功能,本标准定义了两类通用命令:
- 应用专用命令 ------APP_CMD(CMD55) 设备收到该命令后,会将紧随其后的命令识别为应用专用命令 ACMD。ACMD 的结构和普通 eMMC 标准命令一致,也可以使用和标准命令相同的命令编号。设备依靠该命令紧跟在 APP_CMD 之后这一特征,识别它是 ACMD。APP_CMD 的唯一作用是:如果紧跟在它后面的命令编号存在 ACMD 重载定义,就使用非标准的 ACMD 版本。举个例子:如果设备定义了 ACMD13,但没有定义 ACMD7,那么在 APP_CMD 之后收到 CMD13 时,该命令会被解析为非标准的 ACMD13;但 CMD7 仍然按标准 CMD7 解析。
为了满足不同厂商的具体需求,主机还可以使用 APP_CMD 及其后续的 "通用命令"。
- 发送 APP_CMD(CMD55)。主机通过 APP_CMD 及其后续命令(包括标准命令和厂商自定义命令),向设备发出信号,表示下一条命令是应用专用命令。
- 发送所需的 ACMD。设备收到 APP_CMD 后的响应中会包含 APP_CMD 状态位,用于指示下一条命令是否被识别为 ACMD。如果下一条命令未被识别为 ACMD,则会被当作普通标准命令处理,并在设备状态寄存器中报告错误。如果下一条命令既不是有效的 ACMD,也不是有效的标准命令,则会被视为非法命令。
如果某条后续命令既不是有效的 ACMD,也不是有效的标准命令,那么它将被当作标准命令处理,并返回非法命令错误。从 eMMC 设备的角度来看,ACMD 编号可由设备制造商在标准命令编号范围内自行定义。
- 通用命令 ------GEN_CMD(CMD56)。 通用命令的数据传输方向由单块读命令或写命令决定。GEN_CMD 与这两类命令的区别在于:GEN_CMD 不定义传输的数据格式,而是将数据格式的定义留给设备制造商。主机必须在发送 GEN_CMD 之前先选中设备(CMD7)。如果设备支持单数据速率模式,那么在单数据速率模式下,数据块大小由 BLOCK_LEN 寄存器定义;如果设备处于 DDR 模式,则数据块大小固定为 512 字节。
这个也很有用CMD55一般厂家如果没有特殊要求使用,我们固件本身也会用于一些debug信息的设置等。
6.6.21 Sleep (CMD5) 睡眠(CMD5)
睡眠命令用于在待机状态(Stby)和睡眠状态(Sleep/Awake)之间切换。在睡眠状态下,设备不会响应除 RESET 命令以外的任何命令。睡眠状态下的存储器设备会保持当前存储的所有数据,并在主机发出带有参数 0x0000FFFF 的 RESET 命令(CMD0)后返回待机状态。所有其他命令均由设备忽略。
睡眠状态与待机状态之间的切换时间定义在 EXT_CSD 寄存器的相关字段中,具体包括:CMD5 超时时间、CMD5 响应时间、从睡眠状态唤醒时间以及睡眠期间的电流消耗。这些参数分别定义在 EXT_CSD 寄存器的 15 号、16 号、17 号和 18 号字节,以及 19 号、20 号、21 号和 22 号字节。
睡眠状态通过向设备发送带有参数 0x0000FFFF 的 SLEEP/AWAKE 命令(CMD5)进入。当主机将设备切换到睡眠状态时,设备会通过拉低 DATA0 数据线来指示进入睡眠状态。进入睡眠状态后,主机无需再发送其他命令。
该命令用于将设备从待机状态切换到睡眠状态。存储器设备通过拉低 DATA0 数据线来指示已进入睡眠状态。进入睡眠状态后,主机无需再发送其他命令。
设备通过拉低 DATA0 数据线来指示已完成从睡眠状态的唤醒。唤醒完成后,主机可以继续进行其他操作。
进入睡眠状态后,设备的电源可能会被关闭,这是为了进一步降低功耗。因此,建议主机在进入睡眠状态后关闭设备电源。为了使设备能够成功恢复,主机必须在退出睡眠状态之前,将设备电源电压恢复至最低工作电压水平(Awake minimum)。
已锁定的设备可以进入睡眠状态:先通过 CMD7 取消选中设备,CMD7 属于 0 类命令,可以在锁定状态下执行,之后再下发睡眠命令。这样可以让设备在锁定的同时降低功耗。已锁定的设备后续可以通过唤醒命令退出睡眠状态,进入待机状态。
不允许反向操作:设备已经进入睡眠之后,再对设备执行锁定操作。
休眠,让设备进入低功耗模式,减小电流。
6.6.22 Replay Protected Memory Block 重放保护内存块(RPMB)
RPMB我在UFS3.1的12.4节中提到过,介绍了RPMB的功能,这里不过多介绍,各位小伙伴可以连链接过去看。这里主要介绍eMMC对RPMP区的实现。
RPMB 支持带签名的访问方式。该功能让系统可以经过身份校验、防重放的方式,向这块专用存储区域写入数据。实现方式是先将认证密钥(共享密钥)烧录到 eMMC 存储器中。
在烧录密钥的阶段,系统还无法完成身份认证,因此密钥烧录必须在安全环境中进行,例如 OEM 生产车间。后续访问 RPMB 区域的读写操作,都需要使用该认证密钥生成消息认证码(MAC)进行签名。
随机数生成以及计数寄存器用于额外防护报文重放攻击,防止攻击者抓取报文后重复发送。
6.6.22.1 The Data Frame for Replay Protected Memory Block Access 访问重放保护内存块的数据帧
用于访问 RPMB 区域的数据帧包含下列数据字段。

RPMB 数据帧的字节序为高位字节优先(MSB first,大端)。例如:写计数器最高位字节 Write Counter MSB 11 存放计数器值的最高字节。
名称:请求 / 响应类型
- 长度:2 字节
- 方向:请求(发往存储器)、响应(来自存储器)
- 描述:定义发往存储器、或从存储器返回的请求与响应类型。表 18 列出了已定义的请求和响应。响应类型和上一条重放保护内存块读写请求一一对应。

请求消息类型 0x0001 认证密钥烧录请求 0x0002 读取写计数器值 - 请求 0x0003 带认证的数据写请求 0x0004 带认证的数据读请求 0x0005* 结果读取请求 0x0006 带认证的设备配置写请求 0x0007 带认证的设备配置读请求
响应消息类型 0x0100 认证密钥烧录响应 0x0200 读取写计数器值 - 响应 0x0300 带认证的数据写响应 0x0400 带认证的数据读响应 0x0500 保留 0x0600 带认证的设备配置写响应 0x0700 带认证的设备配置读响应
注:结果读取请求没有对应的响应类型,因为该请求读取的结果,永远和上一次烧录操作相关。
名称:认证密钥 / 消息认证码(MAC)
- 长度:32 字节(256 位)
- 方向:请求(密钥或 MAC)/ 响应(MAC)
- 描述:根据请求 / 响应类型,此字段代表认证密钥或者消息认证码(MAC)。MAC 放在最后一个(或唯一一个)数据块中传输。
名称:操作结果
- 长度:2 字节
- 方向:响应
- 描述:包含写计数器状态(有效、过期),以及本次访问 RPMB 是否成功的信息。表 19 为 RPMB 操作结果的数据结构。表 20 列出了已定义结果以及失败对应的原因。


名称:写计数器
- 长度:4 字节
- 方向:请求和响应
- 描述:用于统计主机成功执行带认证数据写请求、带认证设备配置写请求的总次数计数器。
名称:数据地址
- 长度:2 字节
- 方向:请求和响应
- 描述:待写入或读取重放保护内存块的数据地址。该地址是被访问半扇区(256 字节)的序号,首地址为 0x0000。CMD18 和 CMD25 中的地址参数会被忽略。
名称:随机数(Nonce)
- 长度:16 字节
- 方向:请求和响应
- 描述:由主机生成、用于请求的随机数,eMMC 重放保护内存块模块会将该随机数原样复制到响应中。
名称:数据
- 长度:256 字节
- 方向:请求和响应
- 描述:经过签名访问操作写入或读出的数据。
名称:块计数
- 长度:2 字节
- 方向:请求
- 描述:请求读取或烧录的块数量(块即半扇区,256 字节)。该值和 CMD23 参数中的计数值保持一致。
6.6.22.2 Memory Map of the Replay Protected Memory Block 重放保护内存块的内存映射
重放保护内存块包含以下寄存器和存储分区:
名称:认证密钥
- 大小:32 字节
- 类型:一次性写入
- 描述:一次性可编程认证密钥寄存器。该寄存器不可覆盖、擦除或者读出。eMMC 重放保护内存块模块使用该密钥,在计算 MAC 时对访问操作进行身份校验。
名称:写计数器
- 大小:4 字节
- 类型:只读
- 描述:用于统计主机成功执行带认证数据写请求、带认证设备配置写请求的总次数计数器。eMMC 出厂后初始值为 0x00000000。每一次成功烧录访问,eMMC 重放保护内存块模块会自动将该计数器加 1。该值不会随复位操作改变。当计数器达到最大值 0xFFFFFFFF 之后,将不再继续累加(防止溢出),并且操作结果字段的第 7 位会永久置 1。
名称:数据区
- 大小:最小 128KB(RPMB_SIZE_MULT × 128KB)
- 类型:可读可写
- 描述:只有通过校验成功的带认证读写访问,才可以读写这片数据。主机可以覆盖写入这片数据,但这片数据不能被擦除。
6.6.22.3 Message Authentication Code Calculation 消息认证码计算
消息认证码(MAC)采用 HMAC SHA-256 算法计算,算法定义参考 HMAC-SHA 规范。HMAC SHA-256 计算以密钥和报文作为输入,输出的 MAC 长度为 256 位(32 字节),嵌入在数据帧中,作为请求或响应报文的一部分。
用于 MAC 计算的密钥,始终是存储在 eMMC 内部的 256 位认证密钥。 MAC 计算所用的输入报文,由数据帧内各个字段拼接而成,不包含填充字节、MAC 本身、起始位、CRC16 以及结束位。也就是按顺序,对数据帧的 283:0 字节进行 MAC 计算。
如果一次请求或响应由多个数据帧组成,那么 MAC 的输入报文,按发送顺序依次拼接每个数据帧的 283:0 字节。MAC 仅附加在最后一个数据帧中。
示例:假设写计数器的值为 0x12345678。主机想要向地址 0x0010 写入两个半扇区,第一个半扇区是 256 字节的 0xAA,第二个半扇区是 256 字节的 0xBB。 此时主机需要发送下面两个请求帧:第二个请求帧内的字段为 HMAC。详见表 21 示例。

6.6.22.4 Accesses to the Replay Protected Memory Block 重放保护内存块的访问
将从设备置于传输状态后,主机发送 CMD6(SWITCH 命令),用来设置 EXT_CSD 寄存器 179 号字节中的 PARTITION_ACCESS 位。完成该操作之后,主机就可以使用多块读、多块写命令(CMD23、CMD18、CMD25)访问重放保护内存块分区。定义好的访问方式在后续小节中列出。
如果出现未定义参数组合、非法命令序列,或者在带认证写操作期间发生高优先级中断,都会导致访问失败。
- 数据烧录场景:数据不会被写入
- 数据读取场景:读出的数据为 0x00(多块场景下,所有帧都返回 0x00)
结果寄存器会标记通用失败(0x01)(多块场景下所有帧都标记该失败)。如果一次访问触发多个错误,结果寄存器只上报最先检测到的错误类型。错误检测顺序在下面各类访问流程中定义。完成重放保护内存块分区的数据访问之后,需要清除 PARTITION_ACCESS 位。
6.6.22.4.1 Programming of the Authentication Key 认证密钥的烧录
认证密钥通过多块写命令 CMD25 进行烧录。在发送 CMD25 之前,先通过 CMD23 将块计数设置为 1,并且参数的第 31 位置 1,代表本次烧录使用可靠写入类型。如果块计数没有设置为 1,或者参数第 31 位没有置 1,后续的多块写命令一定会失败,并标记通用失败。
密钥信息承载在数据包内,数据包长度为 512 字节,包含请求类型信息和认证密钥。请求类型值 0x0001 代表认证密钥烧录请求。

eMMC 在 CRC 状态之后,通过 DAT0 线的忙信号,指示密钥正在烧录。也可以通过 CMD13 轮询状态。R1 响应返回的是通用状态,不包含密钥烧录成功的状态信息。
密钥烧录是否成功,需要读取重放保护内存块的结果寄存器来判断。读取结果的流程同样由多块写命令 CMD25 发起。在 CMD25 之前,由 CMD23 将块计数设置为 1。如果块计数没有设置为 1,后续多块写命令必须失败,并标记通用失败。
请求类型信息放在数据包中,数据包大小 512 字节,包含请求类型信息。请求类型值 0x0005 代表发起结果寄存器读取请求。

在 CRC 状态之后,eMMC 会通过 DAT0 线发出忙信号,表示正在处理结果读取请求。收到该命令后,主机应通过 CMD13 读取设备状态,并使用 CMD23 将块计数设为 1。若块计数不为 1,则后续的多块读命令必须失败,并指示通用失败。
结果信息通过返回的数据帧传送,数据帧长度为 512 字节,其中包含结果信息和密钥编程状态。响应类型值 0x0100 对应密钥编程结果请求的响应。

在认证密钥完成编程之前,不允许访问重放保护内存块,也不能通过先写入认证密钥再读取结果寄存器的方式进行检测。若认证密钥尚未编程,结果字段将返回 0x0001(表示认证密钥未编程)。
若密钥编程时发生 CRC 错误,则返回结果 0x0005;若认证密钥编程期间发生其他错误,则返回结果 0x0001(通用失败)。
6.6.22.4.2 Reading of the Counter Value 计数器值读取
计数器读取流程由多块写命令 CMD25 发起。在发送 CMD25 之前,需要通过 CMD23 将块计数设置为 1。如果块计数没有设置为 1,则后续的多块写命令一定会失败,并标记通用失败。
请求类型信息承载在数据包内,数据包长度为 512 字节,包含请求类型信息和随机数。请求类型值 0x0002 代表发起计数器值读取请求。

eMMC 在 CRC 状态之后,通过 DAT0 线的忙信号,表示请求正在处理。 计数器值本身通过多块读命令 CMD18 读出。在发送 CMD18 之前,需要通过 CMD23 将块计数设置为 1。如果块计数没有设置为 1,则多块读命令一定会失败,并标记通用失败。
计数器值放在读出的数据包中传输,数据包长度 512 字节,包含响应类型信息、请求中收到的随机数副本、写计数器值、MAC 以及操作结果。

读取计数器值失败时,返回结果为 0x06(读取失败)。如果发生其他错误,结果为 0x01(通用失败)。如果计数器已经到期,返回结果的第 7 位会置 1(对应结果值分别为 0x80、0x86、0x81)。
6.6.22.4.3 Authenticated Data Write 带认证的数据写
带认证的数据写操作通过多块写命令 CMD25 发起。在发送 CMD25 之前,先通过 CMD23 将块计数设置为待编程数据的块数。若使用可靠写入类型,还应将参数的第 31 位置 1。
支持的数据大小由 EXT_CSD 寄存器中的 RPMB_REL_WR_EN(EXT 166)决定:
- 当 RPMB_REL_WR_EN=0 时,RPMB 数据访问不支持可靠写入;CMD23 参数中的块计数必须设为 1,且后续多块写命令必须按每个数据块单独发起。
- 当 RPMB_REL_WR_EN=1 时,主机可在一次多块写操作中传输多个数据块。
RPMB 相关的两种块大小均受支持:
- 256 字节半扇区:由 RPMB_REL_WR=0 配置;
- 512 字节扇区:由 RPMB_REL_WR=1 配置。
若块计数未设置为 1,且 EXT_CSD 寄存器指示不支持可靠写入,则后续的多块写命令必须失败,并标记通用失败。
数据帧以数据包形式发送,每个数据包长度为 512 字节,包含请求类型信息、块计数、起始地址、随机数、写计数器值、数据以及 MAC。若为多块写操作,除最后一个数据帧外,其余帧的 MAC 均设为 0。若未设置地址,则 MAC 从地址 0 开始计算。
主机应按扇区序号递增的顺序编程数据,请求类型值 0x0003 表示带认证的数据写请求。

CRC 状态之后,eMMC 通过 DAT0 线的忙信号表示正在处理请求并编程数据块。若为多块写操作,主机可通过 CMD13 轮询状态。R1 响应返回通用状态,不指示数据写操作的成功或失败。
eMMC 收到数据后,会首先校验该块对应的写计数器值。若计数器已过期,则返回结果 0x80(计数器过期),并将写计数器值返回给主机。
若地址无效,也会返回结果 0x80(地址越界)。若在多块写操作中任一地址无效,则所有地址均视为无效。若地址有效但写计数器值不匹配,则返回结果 0x05(认证失败)。若写计数器有效但 MAC 不匹配,则返回结果 0x80(MAC 错误)。
如果两个 MAC 值不一致,eMMC 将结果置为 0x02(认证失败),不会向设备写入任何数据。
如果请求中的 MAC 和设备计算出的 MAC 相等,eMMC 会对比请求里的写计数器与设备内部存储的写计数器。如果两个计数器不一致,eMMC 将结果置为 0x03(计数器错误),不会向设备写入任何数据。MAC 校验和写计数器校验都成功后,本次写请求视为认证通过。请求中的数据会写入请求指定的地址,同时写计数器自增 1。
如果写入失败,返回结果为 0x05(写入失败)。写入流程中发生其他错误时,返回结果为 0x01(通用失败)。主机需要读取重放保护内存块的结果寄存器,来确认数据烧录是否成功。
结果读取流程由多块写命令 CMD25 发起。发送 CMD25 之前,通过 CMD23 将块计数设置为 1。如果块计数没有设置为 1,后续多块写命令一定会失败,并标记通用失败。
请求类型信息承载在数据包内,数据包长度为 512 字节,包含请求类型信息。请求类型值 0x0005 代表发起结果寄存器读取请求。

eMMC 在 CRC 状态之后,通过 DAT0 线的忙信号,表示请求正在处理。 结果本身通过多块读命令 CMD18 读出。发送读命令之前,通过 CMD23 将块计数设置为 1。如果块计数没有设置为 1,多块读命令一定会失败,并标记通用失败。 结果信息放在读出的数据包中传输,数据包长度 512 字节,包含响应类型信息、更新后的计数器值、数据地址、MAC 以及数据烧录操作的结果。

6.6.22.4.4 Authenticated Data Read 带认证的数据读
带认证的数据读操作由多块写命令 CMD25 发起。发送 CMD25 之前,通过 CMD23 将块计数设置为 1。如果块计数没有设置为 1,后续的多块写命令一定会失败,并标记通用失败。
请求类型信息承载在数据包内,数据包长度为 512 字节,包含请求类型信息、随机数和地址。请求类型值 0x0004 代表带认证的数据读请求。

CRC 状态之后,eMMC 通过 DAT0 线的忙信号表示正在处理读请求。若请求中不存在错误(如地址越界等),则数据读取通过多块读命令 CMD18 完成。发送 CMD18 之前,通过 CMD23 将块计数设置为待读取的块数。
注意:
- RPMB 分区中的块数通过 EXT_CSD 寄存器中的 RPMB_SIZE_MULT 读取;该寄存器指示 RPMB 分区的总块数,以 128KB 为单位。
- 主机实现中的块计数器应为 256 字节。若此前未设置该计数器,则主机应读取 RPMB 分区的总块数,并按每个块的 MAC 计算,生成完整访问所需的块数。
若从目标位置读取数据失败,eMMC 返回结果 0x06(读取失败);若读取过程中发生其他错误,则返回结果 0x01(通用失败)。
数据帧以数据包形式返回,每个数据包长度为 512 字节,包含响应类型信息、请求中收到的随机数副本、数据、写计数器值、MAC 以及结果。除最后一个数据帧外,其余帧的 MAC 均设为 0。若未设置地址,则 MAC 从地址 0 开始计算。块计数应设置为单个首地址的全访问大小,且块计数应等于半扇区总数,而非首地址序号。

如果从设备内指定地址位置获取数据失败,返回结果为 0x06(读取失败)。如果读取流程期间发生其他错误,则返回结果为 0x01(通用失败)。
6.6.22.4.5 Authenticated Device Configuration Write 带认证的设备配置写
设备配置区域通过 RPMB 流程、使用多块写命令 CMD25 完成烧录。在发送 CMD25 之前,通过 CMD23 设置块计数,并且参数第 31 位置 1,代表本次烧录访问为可靠写入类型。块计数为待烧录的半扇区(256 字节)数量,块计数必须为 0x01,因此仅使用 256 字节这一种大小(单 512 字节帧)。
如果块计数没有设置为 1,或者参数第 31 位没有置 1,后续多块写命令将会失败,并在 RPMB 操作结果中标记通用失败。
数据承载在数据包内,数据包长度为 512 字节,包含请求类型信息、块计数、计数器值、数据起始地址、数据本身以及 MAC。该地址是待更新的设备配置寄存器的索引,也就是只有 0x0001 和 0x0002 是合法索引。请求类型值 0x0006 代表数据烧录请求。

CRC 状态之后,eMMC 通过 DAT0 线的忙信号表示正在烧录。也可以使用 CMD13 轮询状态。R1 响应返回通用访问状态(例如状态跳转),不包含数据烧录是否成功的状态。
当 eMMC 收到这条消息时,首先检查写计数器是否已经过期。如果写计数器过期,eMMC 将结果置为 0x85(写入失败,写计数器已过期),不会向设备写入任何数据。
如果主机访问保留区域(地址 0 以及地址 3\~255),设备不会返回错误,不会向设备写入任何数据。如果写计数器未过期,则根据字节 283:0(请求类型、结果 = 0x00、块计数、写计数器、地址、随机数 = 0x00、数据)计算 MAC,并和请求中的 MAC 做对比。如果两个 MAC 不一致,eMMC 将结果置为 0x02(认证失败),不会向设备写入任何数据。
如果请求中的 MAC 和计算得到的 MAC 相等,eMMC 会对比请求里的写计数器和设备内部存储的写计数器。如果两个计数器不一致,eMMC 将结果置为 0x03(计数器错误),不会向设备写入任何数据。MAC 校验和写计数器校验都成功后,本次写请求视为认证通过。请求中的数据会写入地址指定的设备配置区域,同时写计数器自增 1。
如果写入失败,返回结果为 0x05(写入失败)。写入流程期间发生其他错误,返回结果为 0x01(通用失败)。主机需要读取重放保护内存块的结果寄存器,确认数据烧录是否成功。
结果读取流程由多块写命令 CMD25 发起。发送 CMD25 之前,通过 CMD23 将块计数设置为 1。如果块计数没有设置为 1,后续多块写命令将会失败,并在 RPMB 操作结果中标记通用失败。
请求类型信息承载在数据包内,数据包长度为 512 字节,包含请求类型信息。请求类型值 0x0005 代表发起结果寄存器读取请求。 (参见表 28,结果寄存器读取请求数据包)
带认证的设备配置写响应的响应类型值为 0x0600。eMMC 在 CRC 状态之后,通过 DAT0 线发出忙信号,表示请求正在处理。结果本身通过多块读命令 CMD18 读出。在发送读命令之前,通过 CMD23 将块计数设置为 1。如果块计数没有设置为 1,则多块读命令将会失败,并在 RPMB 操作结果中标记通用失败。结果信息放在读出的数据包中传输,数据包长度 512 字节,包含响应类型信息、更新后的计数器值、设备配置区域索引、MAC 以及数据烧录操作的结果。

6.6.22.4.6 Authenticated Device Configuration Read 带认证的设备配置读
带认证的设备配置读流程由多块写命令 CMD25 发起。在发送 CMD25 之前,通过 CMD23 将块计数设置为 1。如果块计数没有设置为 1,则后续多块写命令将会失败,并在 RPMB 操作结果中标记通用失败。
请求类型信息承载在数据包内,数据包长度为 512 字节,包含请求类型信息和随机数。请求类型值 0x0007 代表发起带认证的设备配置读请求。

eMMC 在 CRC 状态之后,通过 DAT0 线发出忙信号,表示请求正在处理。 数据本身通过多块读命令 CMD18 读出。在发送读命令之前,通过 CMD23 设置块计数。
块计数必须设置为 0x01。如果块计数没有设置为 0x01,则多块读命令将会失败,并在 RPMB 操作结果中标记通用失败。
数据信息放在读出的数据包中传输,数据包长度 512 字节,包含响应类型信息、块计数、请求中收到的随机数副本、数据地址、数据本身、MAC 以及结果。

如果从设备内部指定地址的设备配置区域读取数据失败,返回结果为 0x06(读取失败)。如果读取流程期间发生其他错误,则返回结果为 0x01(通用失败)。