Edit online

设计说明

4 Dec 2024
Read time: 4 minute(s)

源码说明

  • bsp/artinchip/drv/spienc/drv_spienc.c,SPIENC Driver 层实现

  • bsp/artinchip/hal/spienc/hal_spienc.c,SPIENC HAL 层实现

  • bsp/artinchip/include/hal/hal_spienc.h,SPIENC HAL 层接口头文件

模块架构

SPIENC 驱动 Driver 层采用直接调用 HAL 层的接口,如果只使用 HAL 层也可以支持 baremetal 方式的应用场景。


sw_system9

1. SPIENC 驱动的软件架构图

文件系统或者应用程序通过 MTD 层访问 SPI NOR / SPI NAND 设备的数据,SPI NOR / SPI NAND 驱动通过 SPI 总线传输数据。

在 SPI NOR / SPI NAND 驱动通过 SPI 总线访问相关数据时,其实需要发送一系列的命令来进行数据的读写。 这个过程中,命令通信的数据,包括一些读写 SPI NOR / SPI NAND 的寄存器数据都不能加密, 仅存储数据部分需要进行加密处理。因此,虽然 SPI_ENC 是在硬件总线上对数据进行加密, 但是软件使用时需要在 SPI NOR / SPI NAND 驱动 根据需要,提供必要的加解密信息(数据地址和长度), 并且启动相应的数据传输加解密。

因此在软件层次上,SPI NOR / SPI NAND 驱动依赖 SPI 驱动以及 SPI_ENC 驱动,而 SPI 驱动与 SPI_ENC 驱动是并列关系。

设计要点

在设计实现 SPI_ENC 的驱动时,主要考虑了 SPI_ENC 的本身特点,以及与 SPI NOR / SPI NAND 驱动的结合。

注:
由于 SPI_ENC 内部使用 eFuse 所提供的密钥,因此不需要通过外部设置密钥函数来设置密钥。
数据读写时启用

SPI NAND / SPI NOR 驱动通过发送命令的方式与 SPI NAND / SPI NOR 器件进行交互,从而实现数据的读写。 在使能 SPI_ENC 之后,SPI NAND / SPI NOR 驱动需要进行区分:

  • 非存储数据的 SPI 传输,不启动 SPI_ENC,按照原有驱动的流程执行

  • 存储数据的 SPI 传输,启动 SPI_ENC 进行加密或者解密

因此 SPI NAND / SPI NOR 驱动需要做一些改动,在对存储数据进行读写时,启动 SPI_ENC。

以 SPI NAND 为例: 初始化 spinand 时,同时进行加密相关的初始化。
spinand_flash_init();
|-> drv_spienc_init()
读操作流程:
spinand_read_page();
|-> SPINAND_FLASH_OPS->read_dataload();
|-> cpos = 1 + 2 + (SPINAND_FLASH_DUMMYBYTE * 8); /* cpos = cmd.nbyte + addr.nbyte + dummy.nbyte */
|-> drv_spienc_set_cfg(0, page * SPINAND_FLASH_PAGE_SIZE, cpos, data_len); /* 配置本次加密数据信息 */
|-> drv_spienc_start(); /* 启动 SPI_ENC */
|-> SPINAND_FLASH_OPS->read_quadoutput(qspi, 0, buf, data_len); /* 调用标准 SPI API 进行数据传输 */
|-> drv_spienc_stop(); /* 停止 SPI_ENC */
|-> drv_spienc_check_empty() /* 检查是否为空 page */
写操作流程:
spinand_write_page();
|-> SPINAND_FLASH_OPS->program_dataload();
|-> cpos = 1 + 2 + (SPINAND_FLASH_DUMMYBYTE * 8); /* cpos = cmd.nbyte + addr.nbyte + dummy.nbyte */
|-> drv_spienc_set_cfg(0, page * SPINAND_FLASH_PAGE_SIZE, cpos, data_len); /* 配置本次加密数据信息 */
|-> drv_spienc_start(); /* 启动 SPI_ENC */
|-> SPINAND_FLASH_OPS->program_dataload(); /* 调用标准 SPI API 进行数据传输 */
|-> drv_spienc_stop(); /* 停止 SPI_ENC */
|-> SPINAND_FLASH_OPS->program_execute();
空数据块的检测

SPI NAND / SPI NOR 在执行了擦除之后,存储单元上的数据被认为是空的,值都是 0xFF 。 但是在使用过程中,读取程序并不一定知道所读取的区域是否是被擦除过,因此在使能了 SPI_ENC 之后, 通过 SPI 读取回来该区域的数据都是被 SPI_ENC 解密后的数据。原本是被擦除后的 0xFF , 读回来的却是其他数据。

带来的问题:有些程序,如文件系统,会判断读回来的数据是否都为 0xFF ,如果是,则认为是未使用的块,做特殊处理。 现在读回来的数据却被改变了,会导致原来的处理逻辑全部失效。

为了解决上述问题,SPI_ENC 提供了一个空块检测功能。如下图所示:

  • 首先按照正常的流程读取一块数据

  • 传输完成之后,检查 SPI_ENC 的状态,如果提示解密前的所有数据都是 0xFF ,则软件将读取的结果全部置为 0xFF


spienc_empty_detect

2. 空块检测

相关的软件操作,在 spinand_read_page() 中读取完数据时进行。

Driver 层接口设计

1. drv_spienc_set_cfg
函数原型 void drv_spienc_set_cfg(u32 spi_bus, u32 addr, u32 cpos, u32 clen)
功能说明 配置加密数据信息
参数定义
spi_bus - 选择需要配置的 QSPI 设备
addr - 本次传输数据的起始地址
cpos - 本次传输密文数据的开始位置
clen - 本次传输密文的数据长度
返回值
注意事项 -
2. drv_spienc_start
函数原型 void drv_spienc_start(void)
功能说明 启动 SPI_ENC
参数定义
返回值
注意事项 -
3. drv_spienc_stop
函数原型 void drv_spienc_stop(void)
功能说明 停止 SPI_ENC
参数定义
返回值
注意事项 -
4. drv_spienc_check_empty
函数原型 int drv_spienc_check_empty(void)
功能说明 检查本次读取数据是否全为空
参数定义
返回值 0,本次传输数据不全为空;1,本次传输数据全为空
注意事项 -

HAL 层接口设计

HAL 层的函数接口声明存放在 hal_spienc.h 中,主要接口有:
int hal_spienc_init(void);
void hal_spienc_set_cfg(u32 spi_bus, u32 addr, u32 cpos, u32 clen);
void hal_spienc_start(void);
void hal_spienc_stop(void);
int hal_spienc_check_empty(void);