介绍Linux 内核中基于Sunxi 硬件平台的SID 模块驱动的详细设计,为软件编码和维护提供基 础。
内核版本Linux-5.4, Linux-4.9 的平台。
SID 驱动、Efuse 驱动、Sysinfo 驱动的维护、应用开发人员等。
SID 提供的功能可以分为四大部分:ChipID、SoC Version、Efuse 功能、一些状态位。
对于全志的SoC 来说,ChipID 用于该SoC 的唯一标识,如A83 的ChipID 标识其在所有A83 中的唯一(目前仅保证同一型号SoC 中的ChipID 唯一)。ChipID 由4 个word(16 个byte)组成,共128bit,通常放在Efuse(见2.1.3 节)的起始4 个word。具体ChipID 的bit 含义,请参考生产制造部为每颗SoC 定义的《ChipID 烧码规则》。
严格讲SoC Version 包含两部分信息: 1.Bonding ID,表示不同封装。
Version,表示改版编号。
说明:这两个信息所在的寄存器不一定都在SID 模块内部,且各平台位置不一,但软件上为了统一管理,都归属为SID 模块。
BSP 会返回这两个信息的组合值,由应用去判断和做出相应的处理。
对软件来说,Efuse 中提供了一个可编程的永久存储空间,特点是每一位只能写一次(从0到1)。 Efuse 接口方式,Efuse 容量大于512bit 采用SRAM 方式。带有SRAM 的硬件结构示意图如下:
Secure Enable标明当前系统的Security 属性是否打开,即是否运行了SecureBoot 和SecureOS。 芯片SecureEnable 状态位保存在SID 模块的0xa0 寄存器。
SID 是一个比较独立的模块,在Linux 内核中没有依赖其他子系统,在Sunxi 平台默认是ko 方式,存放在drivers/soc/sunxi 目录中。 SID 为其他模块提供API 的调用方式。关系如下图:
1)TV、Thermal、GMAC 的校准参数保存在SID 中; 2)Nand、SMP、VE 需要读取SoC Version; 3)CE 和HDMI 会用到SID 中的一些Key; 4)Sysinfo 比较特殊,为了方便用户空间获取、调试SID 信息,专门设计的一个字符型设 备驱动。
SID 模块在Device tree 中通常会用到两个模块的配置信息:sunxi-sid 以sun50iw10p1为例,需要在sun50iw10p1.dtsi 中添加节点:
sid@3006000 {
compatible = "allwinner,sun50iw10p1-sid", "allwinner,sunxi-sid";
reg = <0x0 0x03006000 0 0x1000>;
#address-cells = <1>;
#size-cells = <1>;
/* some guys has nothing to do with nvmem */
secure_status {
reg = <0x0 0>;
offset = <0xa0>;
size = <0x4>;
};
chipid {
reg = <0x0 0>;
offset = <0x200>;
size = <0x10>;
};
rotpk {
reg = <0x0 0>;
offset = <0x270>;
size = <0x20>;
};
};
在sid 下增加子节点secure_status, chipid, rotpk。就可以用key_info 来访问。
console:/ # echo chipid > /sys/class/sunxi_info/key_info ; cat /sys/class/sunxi_info/
key_info
console:/ # 00000400
SID 驱动的源代码目录下:
linux-4.9,linux-5.4
./drivers/soc/sunxi/
└── sunxi-sid.c // 实现了SID对外的所有API接口
对外提供的接口头文件:./include/linux/sunxi-sid.h
此配置项一般默认开,不需要重新配置 在longan 环境中在根目录执行./build.sh menconfig进入配置主界面,配置路径如下:
System Type
└─>ARM system type
└─>Allwinner Ltd. SUNXI family
版
配置界面图示:
SID 驱动本身没有注册为单独的模块,需要通过注册sysinfo 字符驱动(实现代码见drivers/char/sunxi-sysinfo/)来提供sysfs 节点。 在longan 环境中在根目录执行./build.sh menconfig进入配置主界面,配置路径如下
Device Drivers
└─>Character devices
└─>sunxi system info driver
配置界面图示:
SID 驱动内部的功能划分如下图所示:
总体上,SID 驱动内部可以分为两大部分: 1.SID Register RW,封装了对寄存器按位读取的接口,以及获取指定compatible 的模块基地址等。 2.SID Api,以API 的方式提供一些功能接口:获取Key、获取SoC Version、获取SecureEnable、获取ChipID 等。
3.2.1.1 key 的名称定义
在获取Key 的时候,调用者需要知道Key 的名称,以此作为索引的依据。Key 名称详见sunxisid.h:
1 #define EFUSE_CHIPID_NAME "chipid"
2 #define EFUSE_BROM_CONF_NAME "brom_conf"
3 #define EFUSE_BROM_TRY_NAME "brom_try"
4 #define EFUSE_THM_SENSOR_NAME "thermal_sensor"
5 #define EFUSE_FT_ZONE_NAME "ft_zone"
6 #define EFUSE_TV_OUT_NAME "tvout"
7 #define EFUSE_OEM_NAME "oem"
9 #define EFUSE_WR_PROTECT_NAME "write_protect"
10 #define EFUSE_RD_PROTECT_NAME "read_protect"
11 #define EFUSE_IN_NAME "in"
12 #define EFUSE_ID_NAME "id"
13 #define EFUSE_ROTPK_NAME "rotpk"
14 #define EFUSE_SSK_NAME "ssk"
15 #define EFUSE_RSSK_NAME "rssk"
16 #define EFUSE_HDCP_HASH_NAME "hdcp_hash"
17 #define EFUSE_HDCP_PKF_NAME "hdcp_pkf"
18 #define EFUSE_HDCP_DUK_NAME "hdcp_duk"
19 #define EFUSE_EK_HASH_NAME "ek_hash"
20 #define EFUSE_SN_NAME "sn"
21 #define EFUSE_NV1_NAME "nv1"
22 #define EFUSE_NV2_NAME "nv2"
23 #define EFUSE_BACKUP_KEY_NAME "backup_key"
24 #define EFUSE_RSAKEY_HASH_NAME "rsakey_hash"
25 #define EFUSE_RENEW_NAME "renewability"
26 #define EFUSE_OPT_ID_NAME "operator_id"
27 #define EFUSE_LIFE_CYCLE_NAME "life_cycle"
28 #define EFUSE_JTAG_SECU_NAME "jtag_security"
29 #define EFUSE_JTAG_ATTR_NAME "jtag_attr"
30 #define EFUSE_CHIP_CONF_NAME "chip_config"
31 #define EFUSE_RESERVED_NAME "reserved"
32 #define EFUSE_RESERVED2_NAME "reserved2"
33 /* For KeyLadder */
34 #define EFUSE_KL_SCK0_NAME "keyladder_sck0"
35 #define EFUSE_KL_KEY0_NAME "keyladder_master_key0"
36 #define EFUSE_KL_SCK1_NAME "keyladder_sck1"
37 #define EFUSE_KL_KEY1_NAME "keyladder_master_key1"
sunxi-sid.h 不是所有key 都能访问,一般可以访问的已经在dts 定义。
3.2.2.1 soc_ver_map
用于管理多个SoC 的Version 信息,方便用查表的方式实现SoC Version API。其中有两个分量:id,即BondingID;rev[],用于保存BondingID 和Version 的各种组合值。定义在sunxi-sid.c 中:
#define SUNXI_VER_MAX_NUM 8
struct soc_ver_map {
u32 id;
u32 rev[SUNXI_VER_MAX_NUM];
};
对于一个SoC 定义一个soc_ver_map 结构数组,使用id 和不同Version 在rev[] 中查找对应的组合值。
3.2.2.2 soc_ver_reg
SoC Version、BondingID、SecureEnable 的存储位置因SoC 而异,所以定义了一个结构来记录这类信息的位置,包括属于那个模块(基地址)、偏移、掩码、位移等。定义见sunxisid.c:
#define SUNXI_SOC_ID_INDEX 1
#define SUNXI_SECURITY_ENABLE_INDEX 2
struct soc_ver_reg {
s8 compatile[48];
u32 offset;
u32 mask;
u32 shift;
};
每个SoC 会定义一个soc_ver_reg 数组,目前各元素的定义如下: 0 - SoC Version 信息在寄存器中的位置。 1 - BondingID 信息在寄存器中的位置。 2 - SecureEnable 信息在寄存器中的位置。
定义几个static 全局变量,用于保存解析后的ChipID、SoC_Ver 等信息:
static unsigned int sunxi_soc_chipid[4]; static unsigned int sunxi_serial[4]; static int sunxi_soc_secure; static unsigned int sunxi_soc_bin; static unsigned int sunxi_soc_ver;
本节中,这里把SoC Ver、ChipID、SecureEnable 信息统称为“SoC 信息”,因为他们的读取过程非常相似。都是遵循以下流程:
在读取Efuse 中Key 的时候,需要判断是否存在、以及访问权限,过程有点复杂,用以下流程图进行简单说明。
• 作用:获取SoC 平台的名称,实际上是一个BSP 研发代号,如sun8iw11。 • 参数: • buf: 用于保存平台名称的缓冲区 • size:buf 的大小 • 返回: • 返回buf 中平台名称的实际拷贝长度(如果size 小于名称长度,返回size)。
• 作用:获取SoC 的ChipID(从Efuse 中读到的原始内容,包括数据内容和顺序)。 • 参数: • chipid:用于保存ChipID 的缓冲区 • 返回: • 会返回0,无实际意义
• 作用:获取SoC 的序列号(由ChipID 加工而来,格式定义见《chipid 接口的实现方案》。 • 参数: • serial:用于保存序列号的缓冲区 • 返回: • 会返回0,无实际意义
• 作用:获取SoC 的ChipID 的第一个字节,要求转换为字符串格式。 • 参数: • serial:用于打印ChipID 第一个字节的缓冲区 • 返回: • 只会返回8(4 个字节的十六进制打印长度),无实际意义
• 作用:获取FZ ZONE 的最后一个字节,要求转换为字符串格式。 • 参数: • serial:用于打印ChipID 第一个字节的缓冲区 • 返回: • 只会返回8(4 个字节的十六进制打印长度),无实际意义
• 作用:获取rotpk 的状态,是否烧码 • 参数: • status:用于记录是否烧码的缓冲区;0,未烧;1,已烧 • 返回: • %d 的长度,无实际意义
• 作用:获取整个系统的Secure 状态,即安全系统是否启用。 • 参数: • 无 • 返回: • 0,未启用安全系统;1,启用
• 作用:用于芯片分bin,部分SoC 平台才支持。 • 参数: • 无 • 返回: • 0: fail • 1: normal • 2: faster • 3: fastest
• 作用:获取SoC 的版本信息。 • 参数: • 无 • 返回: • 返回一个十六进制的编号,需要调用者去判断版本号然后做出相应的处理。详情参看dts, sid 节点。
n) • 作用:读取Efuse 中的一个key 信息。 • 参数: • key_name - Key 的名称,定义详见sunxi-sid.h • buf - 用于保存Key 值的缓冲区 • size - buf 的大小 • 返回: • 0: success • other: fail 版
void __iomem **base, s8 *compatible, u32 sec) • 作用:从DTS 中获取指定模块的寄存器基地址。 • 参数: • pnode - 用于保存获取到的模块node 信息 • base - 用于保存获取到的寄存器基地址 • compatible - 模块名称,用于匹配DTS 中的模块 • 返回: • 0: success • other: fail
void __iomem *base, u32 sec) • 作用:释放一个模块的基地址。 • 参数: • pnode - 保存模块node 信息 • base - 该模块的寄存器基地址 • 返回: • 无
u32 mask, u32 sec) • 作用:从一个模块的寄存器中,读取指定位置的bit 信息。 • 参数: • name - 模块名称,用于匹配DTS 中的模块 • offset - 寄存器相当于基地址的偏移 • shift - 该bit 在寄存器中的位移 • mask - 该bit 的掩码值 • 返回:
• 0,fail • other,获取到的实际bit 信息 版
/sys/class/sunxi_info/sys_info 此节点文件可以打印出一些SoC 信息,包括版本信息、ChipID 等:
# cat /sys/class/sunxi_info/sys_info sunxi_platform : sun50iw10p1 sunxi_secure : secure sunxi_chipid : 00000000000000000000000000000000 sunxi_chiptype : 00000400 sunxi_batchno : 0x1
/sys/class/sunxi_info/key_info 此节点用于获取指定名称的Key 信息。方法是先写入一个Key 名称,然后就可以读取到Key 的内容。执行效果如下:
# echo chipid > /sys/class/sunxi_info/key_info ; cat /sys/class/sunxi_info/key_info 0xf1c1b200: 0x00000400 0xf1c1b204: 0x00000000 0xf1c1b208: 0x00000000 0xf1c1b20c: 0x00000000
当启用安全系统后,Non-Secure 空间将无法访问大部分的Efuse 信息,这个时候需要通过SMC 指令来读取这些Key 信息。此时不能再使用普通的寄存器读接口readl(),而是调用的SMC 接口:
目前,sunxi_smc_readl() 的实现在源代码sunxi-smc.c,该文件保存在drivers/char/sunxisysinfo。
int sunxi_smc_readl(phys_addr_t addr)
目前,sunxi_smc_readl() 的实现在源代码sunxi-smc.c,该文件保存在drivers/char/sunxisysinfo。
全部0条评论
快来发表一下你的评论吧 !