
# X5 eFuse 介绍

**以下关于开启secure boot要烧写secure bank13-20，non secure bank10的bit0的介绍，仅针对于由地瓜key对BL2加密签名的芯片，如果是由客户key签名BL2的芯片([X5 Customer root rsa key hash烧录及使用](../efuse/cus_key_hash_update.html))，可以忽略对应部分**

## 概述
eFuse（电子熔丝）是一种可编程的非易失性存储技术，广泛用于各种硬件平台上，如微控制器（ MCU）、处理器、嵌入式设备、 SoC（系统级芯片）等。 eFuse 通过电子方式进行编程，能够存储和保护敏感信息，且具有在编程后不可更改的特性。

eFuse 通常用于存储设备的安全密钥、配置参数、设备标识符、校准数据等重要信息。

## X5 eFuse 特性

X5 eFuse 分为 secure bank 和 non-secure bank，每个 region 有 32 个 bank，每个 bank 是 32bit，即每个 region 有 128Byte。其布局如下

![efuse_banks](./_static/_images/efuse_banks.png)


**支持特性如下**

- eFuse 比特位默认均为 0，可以从 0 写为 1 ，但不能从 1 写为 0 （eFuse 硬件特性）

- 用户可烧写的区域

| 区域 |       bank      |      用途     |      备注     |
|-------|-------------|------------|------------|
| secure  | 13 - 20 | 公钥hash 对应文件 `bl2_rot_prikey.pem` | **当使用客户的 key 签名 BL2，这些 bank 由客户自由定义 </br>考虑到安全问题，这些 bank 写过数据不为0后，不论是否 lock，都不允许再次写入了** |
| secure  | 25 - 28 | 用户的 user root aes key | |
| non-secure  | 10 | 配置开启 secure boot 和 关闭 JTAG | **当使用客户的 key 签名 BL2，bit0 由客户自由定义** |
| non-secure  | 11 - 13 | 用户自定义数据 | **在没有 lock 的情况下，这些 bank 中未更新为1的 bit，仍然可以继续更新** |

- 特殊 bank 说明
  - secure 和 non-secure region 的 bank0 的每个 bit 表示对应的 bank 是否被 lock， 1 表示 lock， 0 表示没有 lock.
  - non-secure bank10 用于设置 secure boot 和 JTAG 使能开关，其定义如下

| 比特位 | 功能描述           | 定义                                           | 默认值 |
| ------ | ------------------ | ---------------------------------------------- | ------ |
| bit0   | enable secure boot | 0 : 关闭 secure boot </br>1 : 开启 secure boot | 0      |
| bit1   | disable debug port | 0 : 开启 JTAG </br> 1 : 关闭 JTAG              | 0      |

**eFuse 的烧录（读写），可通过以下三个阶段**
- [BL2 阶段 ](#bl2-update-efuse)
- [uboot 阶段 ](#uboot-update-efuse)
- [kernel 阶段 ](#kernel-update-efuse)


<span id="bl2-update-efuse"/> </span>

# BL2 烧录 eFuse

## eFuse 配置文件
eFuse 的配置信息位于 BL2 CFG 文件中，文件路径为 `device/horizon/x5/board_cfg/soc/bl2_cfg/bl2_cfg.json`。

其内容如下所示：

```JSON
{
    "bl2_cfg": {
        ...
      },
      "efuse_cfg": {
        "bypass": 1,
        "secure_boot": "false",
        "debug_disable": "false",
        "burn_user_rot_key": "false",
        ...
      },
	   "nonsecure_bank": {
            "bank11":["0x0"],
            "bank12":["0x0"],
            "bank13":["0x0"]
        },
}
```
参数说明

- `bypass`: bl2 是否烧录 eFuse
  - 1 : 表示 bypass
  - 0 : 表示 bl2 将烧录 eFuse
- `secure_boot`: 是否开启 secure boot，对应 eFuse 的 non-secure bank10 bit0
  - false : 表示不开启 secure boot
  - true : 表示开启 secure boot，开启会将 Uboot 公钥的 hash 烧写到 eFuse secure region bank13~20
- `debug_disable`: 是否禁止 debug port，对应 eFuse 的 non-secure bank10 bit1
  - false : 表示开启 debug port
  - true : 表示禁止 debug port
- `burn_user_rot_key`: 是否要烧写 user root aes key。对应的 key 文件位于 `device/horizon/x5/board_cfg/soc/bl2_cfg/user_root.key`，同时注意为小端格式
- `nonsecure_bank`: 要烧写的 non-secure bank11/12/13 的值

**注意:**
- 在烧写 eFuse 之后，相应的 bank 都会被 lock
- BL2 CFG 会被打包到镜像中，如果要烧录 user root aes key，直接将 key 保存到外部 storage，如 eMMC 中并不安全，推荐使用以下方法：
  - 在 Linux 下调用接口的形式更新 key
  - 通过 UART/USB 形式下载 miniboot 更新 key，路径位于 `out/product/uart_usb`，并妥善保存该文件夹。

## eFuse 电源控制与烧录状态

### eFuse 烧录状态
BL2 可以通过一个 GPIO 指示烧录成功或失败的状态。如烧写成功拉高该 GPIO，烧写失败保持为低，在硬件上可通过外接 LED 用于显示 eFuse 烧录状态。

以 `device/horizon/x5/board_cfg/soc/bl2_cfg/bl2_cfg.json` 为例，通过 `efuse_cfg` 配置 :

```JSON
{
    "bl2_cfg": {
        ...
      },
      "efuse_cfg": {
        "bypass": 1,
        "secure_boot": "false",
        "debug_disable": "false",
        "burn_user_rot_key": "false",
        "status_gpio" : {
            "gpio_sub": "none",
            "gpio_group": 0,
            "gpio_num": 0
        },
        "delay_before_efuse": 0,
        "delay_after_efuse": 0
      },
        ...
}
```
- `status_gpio` 字段表示用于指示 eFuse 烧写状态的 GPIO 信息，其可配置参数如下
  - `gpio_sub`: 表示 GPIO 所在的 subsystem，包括 `aon`，`hsio`，`lsio`，`dsp`
  - `gpio_group`: 表示 GPIO 的 group
  - `gpio_num`: 表示 GPIO 号
  - `polarity`：可选参数，表示 GPIO 的默认值， default_low 默认为低， default_high 默认为高，该字段不写时，选择为 default_low

- `delay_before_efuse`: 烧写前操作 `power_gpio` 后到真正烧写 eFuse 的延迟时间，单位 ms，目的是为了保证在烧写之前  eFuse 电源是稳定的
  - 默认配置为 0 时，延迟时间为 1s
  - 这个时间只有在真正烧写 eFuse 时才会起作用。例如在产线烧录固件后第一次启动， BL2 将要烧录 eFuse，此时延时时间起作用，之后再次启动由于不会重复烧写 eFuse，因此这个延时时间也就失效了

- `delay_after_efuse`: 表示烧写完成之后的动作，其定义如下

| delay_after_efuse | 烧写完成之后的行为                            |
|-------------------|-----------------------------------------------|
| 0                 | 烧写成功代码继续执行，烧写失败停止            |
| \-1               | 烧写成功与否，代码都停止                      |
| 正值              | 烧写失败代码停止，成功则 delay 一段时间，单位 ms |
| 负值（不包括 -1 ）  | 烧写成功失败都 delay 一段时间，单位 ms

### eFuse 电源控制
在硬件设计时 eFuse 默认电源关闭，只有在烧写 eFuse 的阶段才上电， BL2 在烧录 eFuse 之前会操作一个 GPIO（如拉高电平）来通知外部给 eFuse 上电，烧录完成后再次操作一次（如拉低）来通知烧录完毕， eFuse 应下电。

**注意: X5 默认使用的是 `aon gpio0_7` 控制 eFuse 电源，如果使用其他 GPIO，则需要添加特殊字段 `power_gpio` 描述该信息**

如下所示，使用方法和 `status_gpio` 字段类似。
```JSON
{
    "bl2_cfg": {
        ...
      },
      "efuse_cfg": {
        ...
        "power_gpio" : {
            "gpio_sub": "none",
            "gpio_group": 0,
            "gpio_num": 0
        },
        ...
      },
        ...
}
```

### 使用示例
示例 : 以 `aon gpio0_7` 为 power GPIO，`hsio1_1` 为 state GPIO ，两个 GPIO 默认值都是低，则最终的状态如下

| power GPIO       | status  | 描述                             |
|--------------|---------|---------------------------------------|
| 先拉高再拉低  |  拉高   | 烧写成功                               |
| 先拉高再拉低  |  拉低   | 烧写失败                               |
| 一直未拉高    |  x | 未进入烧写 eFuse 流程，如上电失败 |

<span id="uboot-update-efuse"/> </span>

# uboot 读写 eFuse

在 uboot 命令支持读写 eFuse，支持三个 eFuse 命令 dump， read 和 write。

## 读取 eFuse

uboot 读取 eFuse 信息命令 `efuse dump`

该命令用于读取 socid， public key hash 和 non-secure 区域的值

![efuse_dump](./_static/_images/efuse_dump.jpg)

### 读取 eFuse 指定 bank 值
uboot 读取 eFuse 指定 bank 值命令格式  `efuse read [type] [bank_index]`
参数说明
- `type`: secure 表示是读取 secure region， nonsecure 表示读取 non-secure region
- `bank_index`: 表示要读取的 bank 的 index， 16 进制形式

**注意: 可读区域**
- secure region 的 bank0 ， bank13~bank20 可以读
- non secure region 的所有 bank 均可读

执行命令后，将返回相应 bank 的值以及它的 lock 状态
```
Hobot>efuse read nonsecure 0xc
value:0xabcdef12
lock:true

Hobot>efuse read secure 0x13
value:0xed3137
lock:true
```

## 写入 eFuse
该命令用于写入 eFuse 相应 bank，命令格式如下
```
efuse write [type] [bank_index] [bank_value] [lock_status]
```
- `type`: secure 表示写 secure region， nonsecure 表示写 non-secure region
- `bank_index`: 表示要写的 bank 的 index， 16 进制形式
- `bank_value`: 表示要写的 bank 的 value， 16 进制形式
- `lock_status`: 烧写之后是否要 lock， 取值范围 lock/unlock

**注意: 可读区域**
- secure region 的 bank13~bank20 ， bank25~bank28 可写
- non-secure region 的 bank10/11/12/13 可写。

**示例: 将 0x1eff5f0d 写入 secure region bank20**
```
Hobot>efuse write secure 0x14 0x1eff5f0d lock
write SECURE bank:0x14 success
```
## 注意事项
- 执行 efuse write 命令之后，需要在重启之后才能读到正确的数据
- eFuse 电源默认是关闭状态，通过 `aon gpio0_7` 来控制，在 efuse write 命令中，其控制时序如下 :
  1. 先将该 `aon gpio0_7` 拉高，给 eFuse 上电
  2. 等待 1S，待 eFuse 电源稳定之后，再写 eFuse
  3. 写 eFuse 完成，控制 GPIO 下电


**示例: 将 user root aes key 写入 secure bank 25 - 28**

烧录 user root aes key，对应的 key 文件位于 `device/horizon/x5/board_cfg/soc/bl2_cfg/user_root.key`，同时注意为小端格式

```
$ hexdump user_root.key -C
00000000  78 56 34 12 89 67 45 23  9a 78 56 34 ab 89 67 45  |xV4..gE#.xV4..gE|
00000010
```

user root aes key 的内容如上，则对应烧录
```
secure bank[25]=0x12345678

secure bank[26]=0x23456789

secure bank[27]=0x3456789a

secure bank[28]=0x456789ab
```

<span id="kernel-update-efuse"/> </span>

# Linux 读写 eFuse

在 Linux 中提供了接口用于读写 eFuse，动态库是 `libefuse.so`，头文件是 `drobot_efuse.h`

## API 参考
- [drobot_efuse_read](#drobot_efuse_read) : 读 eFuse 接口
- [drobot_efuse_write](#drobot_efuse_write) : 写 eFuse 接口


## 数据结构
<span id="efuse_type"/> </span>

### efuse_type

enum efuse_type 表示 efuse 的类型

| 名称      |  含义             |
| :-------- | :--------------- |
| EFUSE_SECURE  | secure region         |
| EFUSE_NONSECURE | non-secure region |


<span id="efuse_info"/> </span>

### efuse_info

struct efuse_info 是描述 efuse 某个 bank 信息的结构体

| 名称      | 类型            | 最小值 | 最大值 | 默认值 | 含义             |
| :-------- | :-------------- | :----- | :----- | :----- | :--------------- |
| type  | [efuse_type](#efuse_type)      | -      | -      | -      | efuse 的类型         |
| bank | uint32_t | -      | -      | -      | bank 的 index 值 |
| value | uint32_t | -      | -      | -      | bank 的 value 值 |
| lock | bool | -      | -      | -      | 该 bank 是否 lock |


## 接口说明

<span id="drobot_efuse_read"/> </span>

### drobot_efuse_read
#### 【函数声明】
```C
int drobot_efuse_read(struct efuse_info *efuse);
```
#### 【功能描述】
用于读 eFuse 某个 bank 的值
#### 【参数描述】
- [IN] struct efuse_info *efuse : 要读取的 efuse bank 属性结构体指针， efuse bank 属性结构体为 [efuse_info](#efuse_info)
#### 【返回值】
- 成功，返回 0
- 失败：异常为负值

<span id="drobot_efuse_write"/> </span>


### drobot_efuse_write
#### 【函数声明】
```C
int drobot_efuse_write(struct efuse_info *efuse);
```
#### 【功能描述】
用于写 eFuse 某个 bank 的值
#### 【参数描述】
- [IN] struct efuse_info *efuse : 要写入的 efuse bank 属性结构体指针， efuse bank 属性结构体为 [efuse_info](#efuse_info)
#### 【返回值】
- 成功，返回 0
- 失败：异常为负值

**示例: 将 user root aes key 写入 secure bank 25 - 28**

烧录 user root aes key，对应的 key 文件位于 `device/horizon/x5/board_cfg/soc/bl2_cfg/user_root.key`，同时注意为小端格式

```
$ hexdump user_root.key -C
00000000  78 56 34 12 89 67 45 23  9a 78 56 34 ab 89 67 45  |xV4..gE#.xV4..gE|
00000010
```

user root aes key 的内容如上，则对应烧录
```
secure bank[25]=0x12345678

secure bank[26]=0x23456789

secure bank[27]=0x3456789a

secure bank[28]=0x456789ab
```

# 注意事项

## 开启 secure boot 的操作顺序

**以下仅针对于烧了地瓜KEY的芯片，如果是烧的是客户自己的KEY，请跳过**

non secure bank10 的 bit0 用于开启 secure boot，开启后启动阶段将验证各个阶段镜像。如果没有烧写公钥 hash （即 secure region bank13~20 ），将导致验证失败。所以开启 secure boot 之前要先烧写公钥 hash。
- 公钥的 hash 数据保存在 BSP 路径 `out/deploy/uboot/pubkey-hash.txt`，可以在 uboot 阶段烧入 secure region bank13~20 。

## 强制开启 debug port
non secure bank10 的 bit1 用于关闭 debug port，若烧写 eFuse 将其关闭之后，仍然需要开启 debug，可通过 BL2 CFG 的 socid 白名单机制进行开启。

**注意: 最多支持100个 socid 白名单**

- 通过以下命令获取 socid
```
hrut_socuid
```

- 将 socid 填充到 BL2 CFG 文件中
```
{
    "bl2_cfg": {
      "feature": {
        ...
      },
      "efuse_cfg": {
        .....
      },
      "socid":["0x12345678123456781234567812345678","0x0", "0x0", "0x0", "0x0","0x0", "0x0", "0x0", "0x0", "0x0",
      "0x0","0x0", "0x0", "0x0", "0x0","0x0", "0x0", "0x0", "0x0", "0x0",
      "0x0","0x0", "0x0", "0x0", "0x0","0x0", "0x0", "0x0", "0x0", "0x0",
      "0x0","0x0", "0x0", "0x0", "0x0","0x0", "0x0", "0x0", "0x0", "0x0",
      "0x0","0x0", "0x0", "0x0", "0x0","0x0", "0x0", "0x0", "0x0", "0x0",
      "0x0","0x0", "0x0", "0x0", "0x0","0x0", "0x0", "0x0", "0x0", "0x0",
      "0x0","0x0", "0x0", "0x0", "0x0","0x0", "0x0", "0x0", "0x0", "0x0",
      "0x0","0x0", "0x0", "0x0", "0x0","0x0", "0x0", "0x0", "0x0", "0x0",
      "0x0","0x0", "0x0", "0x0", "0x0","0x0", "0x0", "0x0", "0x0", "0x0",
      "0x0","0x0", "0x0", "0x0", "0x0","0x0", "0x0", "0x0", "0x0", "0x0"]
    }
}
```
编译之后，再次烧写镜像，将会开启 debug port
