# <span id="kernel_socinfo"/> boardinfo 调试指南

## 概述

boardinfo 驱动为上层软件提供了获取板级信息的接口。本文档将详细介绍 boardinfo 驱动的功能，并提供 sysfs 和 userspace API 两种访问方式。


## 功能描述
boardinfo 驱动具备以下主要功能：

- 读取关键硬件信息，包括芯片型号、名称、版本号、启动方式、SOC ID 及 DDR 信息等。
- 配置下次启动时使用的 BAK slot，以便在系统启动时切换到指定的备份状态。

通过以上功能，boardinfo 驱动能够为系统提供实时和准确的板级信息，确保系统运行的稳定性和可靠性。

**接口名称统一命名与 userspace API 返回值跟踪表（X5为例）**

| 接口名称       | 接口功能                                                                                   | 接口权限 |  userspace API 结果类型   | 返回值                                  |
|------------    | ----------------------------------------------------------------------------------------- | --------|------------------------   |---------------------------------------- |
| soc_gen        | 查看芯片型号                                                                               | RO      |  字符串                    | X5                                      |
| soc_name       | 查看芯片名称，包括子型号                                                                    | RO      |  字符串                    | X5M<br>X5H<br>UKNOWN                    |
| hw_name        | 查看硬件板子名称                                                                           | RO      |  字符串                    | x5-fpga<br>x5-soc<br>x5-svb<br>x5-evb   |
| board_version  | 查看硬件板子版本号                                                                         | RO      |  字符串                     | v1<br>v2<br>v3                          |
| hw_info        | 查看硬件板子全名称<br> (由<hw_name\>-<board_version\>-<ddr_vendor\>-<ddr_size\>-<ddr_freq\>组成)| RO  | 字符串                      | x5-evb-v2-cxmt-2048MB-4266<br>...       |
| bootdevice_name| 查看启动方式                                                                              | RO      | 字符串                      | emmc<br>sd<br>nand                      |
| soc_uid        | 查看 SOC 的 ID 号                                                                         | RO      | 字符串                     | soc唯一id                               |
| ddr_vendor     | 查看 DDR 厂商                                                                             | RO      | 字符串                     | DDR厂商                                 |
| ddr_type       | 查看 DDR类型，如LPDDR4或LPDDR4X                                                            | RO       | 字符串                     | LPDDR4<br>LPDDR4X                       |
| ddr_freq       | 查看 DDR最高速率，单位Mbps                                                                 | RO      | 字符串                      | 3200 Mbps<br>3733 Mbps<br>4266 Mbps     |
| ddr_size       | 查看 DDR容量，单位MB                                                                       | RO      | 字符串                      | 1024MB<br>2048MB<br>4096MB<br>8192MB    |
| board_id       | 查看硬件板子 ID                                                                            | RO      | 字符串                      | 当前board_id，以十六进制形式打印          |
| sec_chip       | 查看是否是secure芯片                                                                       | RO      | 字符串                      | 未烧key的芯片: nosec_chip<br>烧地瓜key的芯片: sec_chip1<br>烧客户自己public key hash的芯片: sec_chip2 |
| sec_boot       | 查看是否开启AVB和dm-verity校验                                                             | RO       | 字符串 | enable<br>disable |
| bak_slot       | 读：当前启动的 BAK slot；<br>写：设置下次启动的 BAK slot                                     | R/W      | 字符串                     | 0<br>1                                  |

**如何烧客户自己的public key hash，请参考[X5 Customer root rsa key hash烧录及使用](../system_component_development/security_development/efuse/cus_key_hash_update.html)**

## 驱动代码

socinfo 驱动代码位于 `kernel/drivers/soc/hobot/socinfo/socinfo.c`，主要功能如下：

- 读取 DTS 配置文件中配置的板级信息，并保存到对应变量中。
```C
	if (read_from_property(pdev, "soc_gen", &soc_gen) ||
		read_from_property(pdev, "soc_name", &soc_name) ||
		read_from_property(pdev, "hw_name", &hw_name) ||
		read_from_property(pdev, "board_version", &board_version) ||
		read_from_property(pdev, "hw_info", &hw_info) ||
		read_from_property(pdev, "bootdevice_name", &bootdevice_name) ||
		read_from_property(pdev, "soc_uid", &soc_uid) ||
		read_from_property(pdev, "board_id", &board_id) ||
		read_from_property(pdev, "sec_chip", &sec_chip) ||
		read_from_property(pdev, "sec_boot", &sec_boot)) {
		return -1;
	}
```

- 实现 sysfs 接口，提供上层软件获取板级信息的接口。<br>
```C
static struct class_attribute soc_gen_attribute =
	__ATTR(soc_gen, 0444, soc_gen_show, NULL);

static struct class_attribute soc_name_attribute =
	__ATTR(soc_name, 0444, soc_name_show, NULL);

static struct class_attribute hw_name_attribute =
	__ATTR(hw_name, 0444, hw_name_show, NULL);

static struct class_attribute board_version_attribute =
	__ATTR(board_version, 0444, board_version_show, NULL);

static struct class_attribute hw_info_attribute =
	__ATTR(hw_info, 0444, hw_info_show, NULL);

static struct class_attribute bootdevice_name_attribute =
	__ATTR(bootdevice_name, 0444, bootdevice_name_show, NULL);

static struct class_attribute soc_uid_attribute =
	__ATTR(soc_uid, 0444, soc_uid_show, NULL);

static struct class_attribute ddr_vender_attribute =
	__ATTR(ddr_vendor, 0444, ddr_vender_show, NULL);

static struct class_attribute ddr_type_attribute =
	__ATTR(ddr_type, 0444, ddr_type_show, NULL);

static struct class_attribute ddr_freq_attribute =
	__ATTR(ddr_freq, 0444, ddr_freq_show, NULL);

static struct class_attribute ddr_size_attribute =
	__ATTR(ddr_size, 0444, ddr_size_show, NULL);

static struct class_attribute board_id_attribute =
	__ATTR(board_id, 0444, board_id_show, NULL);

static struct class_attribute sec_chip_attribute =
	__ATTR(sec_chip, 0444, sec_chip_show, NULL);

	static struct class_attribute sec_boot_attribute =
	__ATTR(sec_boot, 0444, sec_boot_show, NULL);

static struct class_attribute bak_slot_attribute =
	__ATTR(bak_slot, 0644, bak_slot_show, bak_slot_store);
```

"soc_gen" 和 "soc_name" 需要在 Kernel DTS 文件中配置，路径是`kernel/arch/arm64/boot/dts/hobot/x5.dtsi`，节点名为 "socinfo"。<br>

```bash
当前在 kernel dts 中 "hw_name" 和 "board_version" 的占位符长度为16字节，"hw_info" 的占位符长度为32字节，如果对应的字段超出了占位符的长度，将会导致 socinfo 此驱动初始化失败。
```

![kernel_dts.png](./_static/_images/44-Boardinfo_Guide/kernel_dts.png)

"board_id"、"hw_name"、"board_version" 等信息需要在 Uboot DTS 文件中配置，路径是`uboot/arch/arm/dts/x5.dtsi`，节点名为 "board_type"。<br>
 DTS 中的 board_type_array 字段对应 "board_id"，board_id 相关修改请参考[修改 Board ID 的宏定义](../board_bring_up.html#id13)。<br>
 hardward_array 字段对应 "hw_name"，board_version 字段对应 "board_version"。

![uboot_dts1.png](./_static/_images/44-Boardinfo_Guide/uboot_dts1.png)

## 功能使用

### sysfs 接口

sysfs 接口路径位于 `/sys/class/socinfo/`，目前支持以下信息的获取：

例如要查看板级接口信息，可在命令行执行以下命令：

```
#查看芯片型号
root@buildroot:~# cat /sys/class/socinfo/soc_gen
x5

#查看芯片名称
root@buildroot:~# cat /sys/class/socinfo/soc_name
X5M

#查看硬件板子名称
root@buildroot:~# cat /sys/class/socinfo/hw_name
X5_EVB_LP4

#查看硬件板子版本号
root@buildroot:~# cat /sys/class/socinfo/board_version
1_B

#查看硬件板子全名称
root@buildroot:~# cat /sys/class/socinfo/hw_info
X5_EVB_LP4_1_B_cxmt_4096MB_4266

#查看启动方式
root@buildroot:~# cat /sys/class/socinfo/bootdevice_name
emmc

#查看SOC的ID号
root@buildroot:~# cat /sys/class/socinfo/soc_uid
0x308064960e31499301f0822400000000

#查看DDR厂商
root@buildroot:~# cat /sys/class/socinfo/ddr_vendor
cxmt

#查看DDR类型
root@buildroot:~# cat /sys/class/socinfo/ddr_type
LPDDR4

#查看DDR最高速率
root@buildroot:~# cat /sys/class/socinfo/ddr_freq
4266 Mbps

#查看DDR容量
root@buildroot:~# cat /sys/class/socinfo/ddr_size
4096MB

#查看硬件板子 ID
root@buildroot:~# cat /sys/class/socinfo/board_id
0x0202

#查看芯片secure类型
root@buildroot:~# cat /sys/class/socinfo/sec_chip
sec_chip1

#查看是否开启secure boot
root@buildroot:~# cat /sys/class/socinfo/sec_boot
enable

#查看当前启动的 BAK slot
root@buildroot:~# cat /sys/class/socinfo/bak_slot
0
#设置下次启动的 BAK slot
root@buildroot:~# echo 1 > /sys/class/socinfo/bak_slot
root@buildroot:~# reboot
root@buildroot:~# cat /sys/class/socinfo/bak_slot
1

```

**注意**：如果芯片名称 `soc_name` 是 `UKNOWN`，则属于是早期出厂芯片，未在eFUSE中烧写对应bit，需要根据芯片丝印区分芯片类型。

hw_info 由 `<hw_name>-<board_version>-<ddr_vendor>-<ddr_size>-<ddr_freq>` 组成：

- hw_name: X5_EVB_LP4
- board_version: 1_B
- ddr_vendor: cxmt
- ddr_size: 4096MB
- ddr_freq: 4266

### userspace API 接口

源码路径：`hbre/hbutils/boardinfo`

头文件: `boardinfo.h`

链接库: `libboardinfo.so.1`

**接口定义**

**hb_get_boardinfo**

【函数声明】

int32_t hb_get_boardinfo(char \*key, void \*dst, uint32_t len);

【参数描述】

- [OUT] key: 要获取的板级信息
- [OUT] dst: 接收结果的缓存空间
- [IN] len: 缓存空间最大长度

【返回值】

- HB_BINFO_SUCCESS：成功
- HB_BINFO_INVALID_PARAM： 非法参数，如空指针
- HB_BINFO_SHORT_BUF： 缓存空间过小
- HB_BINFO_PLAT_NOT_SUPPORT: 本平台不支持的板级信息

【功能描述】

获取板级信息 key，结果将存入 dst，并返回错误代码。


**动态库接口返回的错误类型**

动态库错误值定义如下：

```C
enum hb_binfo_retval {
	HB_BINFO_SUCCESS,
	HB_BINFO_INVALID_PARAM,
	HB_BINFO_SHORT_BUF,
	HB_BINFO_PLAT_NOT_SUPPORT,
};
```

**示例代码**

调用 userspace API 接口时，需先链接动态库，示例代码如下：

```C
#include <stdio.h>
#include <stdint.h>
#include "boardinfo.h"

int main(void) {
	int32_t ret;
	char buf[1024];

	ret = hb_get_boardinfo("soc_gen", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("soc_gen:%s\n", buf);
	}

	ret = hb_get_boardinfo("soc_name", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("soc_name:%s\n", buf);
	}

	ret = hb_get_boardinfo("hw_name", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("hw_name:%s\n", buf);
	}

	ret = hb_get_boardinfo("board_version", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("board_version:%s\n", buf);
	}

	ret = hb_get_boardinfo("hw_info", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("hw_info:%s\n", buf);
	}

	ret = hb_get_boardinfo("bootdevice_name", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("bootdevice_name:%s\n", buf);
	}

	ret = hb_get_boardinfo("soc_uid", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("soc_uid:%s\n", buf);
	}

	ret = hb_get_boardinfo("ddr_type", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("ddr_type:%s\n", buf);
	}

	ret = hb_get_boardinfo("ddr_size", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("ddr_size:%s\n", buf);
	}

	ret = hb_get_boardinfo("ddr_vendor", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("ddr_vendor:%s\n", buf);
	}

	ret = hb_get_boardinfo("ddr_freq", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("ddr_freq:%s\n", buf);
	}

	ret = hb_get_boardinfo("board_id", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("board_id:%s\n", buf);
	}

	ret = hb_get_boardinfo("sec_chip", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("sec_chip:%s\n", buf);
	}

	ret = hb_get_boardinfo("sec_boot", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("sec_boot:%s\n", buf);
	}

	ret = hb_get_boardinfo("board_id", buf, 2);
	if (ret == 0) {
		printf("hb_get_boardinfo test HB_BINFO_SHORT_BUF failed\n");
	} else {
		printf("hb_get_boardinfo test HB_BINFO_SHORT_BUF pass\n");
	}

	ret = hb_get_boardinfo("bak_slot", buf, 1024);
	if (ret) {
		printf("hb_get_boardinfo failed, ret:%d\n", ret);
	} else {
		printf("bak_slot:%s\n", buf);
	}

	ret = hb_get_boardinfo(NULL, buf, 1024);
	if (ret == 0) {
		printf("hb_get_boardinfo test HB_BINFO_INVALID_PARAM failed\n");
	} else {
		printf("hb_get_boardinfo test HB_BINFO_INVALID_PARAM pass\n");
	}

	ret = hb_get_boardinfo("xx", buf, 1024);
	if (ret == 0) {
		printf("hb_get_boardinfo test HB_BINFO_INVALID_PARAM failed\n");
	} else {
		printf("hb_get_boardinfo test HB_BINFO_INVALID_PARAM pass\n");
	}

	return 0;
}
```

## 常见问题

- 使用 userspace API 接口时，如果传入的 key 值不存在，则返回 HB_BINFO_INVALID_PARAM 错误，此时 dst 指针不会被修改，需要检查返回值是否为 0。