源码说明
- 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_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()
2
读操作流程:
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 */
2
3
4
5
6
7
8
写操作流程:
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();
2
3
4
5
6
7
8
空数据块的检测
SPI NAND / SPI NOR 在执行了擦除之后,存储单元上的数据被认为是空的,值都是 0xFF 。 但是在使用过程中,读取程序并不一定知道所读取的区域是否是被擦除过,因此在使能了 SPI_ENC 之后, 通过 SPI 读取回来该区域的数据都是被 SPI_ENC 解密后的数据。原本是被擦除后的 0xFF , 读回来的却是其他数据。
带来的问题:
有些程序,如文件系统,会判断读回来的数据是否都为 0xFF ,如果是,则认为是未使用的块,做特殊处理。
现在读回来的数据却被改变了,会导致原来的处理逻辑全部失效。
为了解决上述问题,SPI_ENC 提供了一个空块检测功能。如下图所示:
- 首先按照正常的流程读取一块数据
- 传输完成之后,检查 SPI_ENC 的状态,如果提示解密前的所有数据都是 0xFF ,则软件将读取的结果全部置为
0xFF
相关的软件操作,在 spinand_read_page()
中读取完数据时进行。
Driver 层接口设计
drv_spienc_set_cfg
函数原型 | void drv_spienc_set_cfg(u32 spi_bus, u32 addr, u32 cpos, u32 clen) |
---|---|
功能说明 | 配置加密数据信息 |
参数定义 | spi_bus - 选择需要配置的 QSPI 设备 addr - 本次传输数据的起始地址 cpos - 本次传输密文数据的开始位置 clen - 本次传输密文的数据长度 |
返回值 | 无 |
注意事项 |
drv_spienc_start
函数原型 | void drv_spienc_start(void) |
---|---|
功能说明 | 启动 SPI_ENC |
参数定义 | 无 |
返回值 | 无 |
注意事项 |
drv_spienc_stop
函数原型 | void drv_spienc_stop(void) |
---|---|
功能说明 | 停止 SPI_ENC |
参数定义 | 无 |
返回值 | 无 |
注意事项 |
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);
2
3
4
5