设计说明
源码说明
-
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 方式的应用场景。
文件系统或者应用程序通过 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 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。
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
相关的软件操作,在 spinand_read_page() 中读取完数据时进行。
Driver 层接口设计
函数原型 | void drv_spienc_set_cfg(u32 spi_bus, u32 addr, u32 cpos, u32 clen) |
---|---|
功能说明 | 配置加密数据信息 |
参数定义 |
spi_bus - 选择需要配置的 QSPI 设备
addr - 本次传输数据的起始地址
cpos - 本次传输密文数据的开始位置
clen - 本次传输密文的数据长度
|
返回值 | 无 |
注意事项 | - |
函数原型 | void drv_spienc_start(void) |
---|---|
功能说明 | 启动 SPI_ENC |
参数定义 | 无 |
返回值 | 无 |
注意事项 | - |
函数原型 | void drv_spienc_stop(void) |
---|---|
功能说明 | 停止 SPI_ENC |
参数定义 | 无 |
返回值 | 无 |
注意事项 | - |
函数原型 | int drv_spienc_check_empty(void) |
---|---|
功能说明 | 检查本次读取数据是否全为空 |
参数定义 | 无 |
返回值 | 0,本次传输数据不全为空;1,本次传输数据全为空 |
注意事项 | - |
HAL 层接口设计
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);