探究eFuse:硬件保障与系统安全的核心
图1: 编程熔断的 eFuse
eFUSE的全名是"Electrically Programmable Read-Only Memory Fuse",它是一种电可编程只读存储器。 eFUSE是一种用于存储固定信息的存储器,它的主要特点是一旦编程后就无法再进行擦除或修改,类似于传统的只读存储器(ROM)的功能。
作为系统工程师,虽然不会对eFuse 这块做深入的开发,更多的还是依赖芯片厂商自己的实现,然后取出自己的数据,但是通过对 eFuse 大致的了解,能够对整机的一些概念紧密联系起来,让知识图谱更全面。
目前手机行业内,出厂都会做熔丝处理,熔丝之后,不能进行随意刷机,非熔丝设备一般属于开发设备,用来内部自定义刷机。 熔丝过程中,有一个步骤就是写入设备ID信息, 熔丝设备通常具有唯一的设备ID,可以用防止软件篡改和仿冒,一些商业化行为的唯一标记等。 这些标识通常是在制造过程中预先烧录到eFUSE中的,用于识别每个设备的唯一性,Bootloader可以读取eFUSE中的这些标识,并在设备启动时使用它们来进行身份验证。
eFuse不是软件上的分区
eFUSE(熔断保护)通常是硬件特性,而不是预置的软件特性,eFUSE是一种一次性可编程的硬件存储器,用于存储固定的数据或配置信息,例如设备序列号、密钥、硬件配置等。 eFUSE在手机或其他设备的主板上实现,并由硬件厂商预置一些不可更改的数据。这些数据通常在制造过程中被烧录到eFUSE中,并且无法在后续的使用中被修改。因此,eFUSE中存储的信息是设备固有的、不可更改的。 在 Android 系统中,eFUSE通常被用于存储一些重要的设备信息和安全相关的数据,例如设备唯一标识、安全密钥、加密相关信息等。这些数据的安全性和不可篡改性是系统的重要保障,因此通常会将它们存储在eFUSE中,以防止被非法篡改或泄漏。 由于eFUSE的特性,它一般不受 Android 系统软件的影响,也不是可编程的软件特性。因此,Google 原生 Android 系统上的eFUSE通常是由设备硬件厂商预置和配置的,与 Android 系统软件无关。
BootLoader 实现读取
eFUSE 在Bootloader中扮演着重要的角色,用于存储关键的设备信息和安全配置,帮助确保设备的启动过程和运行环境的安全性。它是Android设备等嵌入式系统中重要的安全保障措施之一。
Android bootloader 是一个引导加载程序,它位于 Android 设备的存储芯片中,负责启动设备并加载操作系统。它是设备上的第一个软件程序,负责执行一系列初始化任务和配置操作,以确保设备能够正确启动。 目前 bootloader 不属于 AOSP 内标准的模块,一般是芯片厂商在维护,目录结构也不统一,对于 eFuse 的读取模块也在 lk 内, 高通的 bsp 模块一般是独立编译,MTK和展讯所在的目录也不太一样,常见的有这些目录结构:
- bsp/bootloader/lk
- kernel/lk/
eFuse 存在 BootLoader 中的功能有:
-
安全启动验证 Bootloader是在设备启动时运行的第一个软件程序,负责初始化硬件和加载操作系统,在安全启动流程中,Bootloader通常会检查 eFUSE 中的安全配置信息,例如是否启用了 Secure Boot(安全启动)功能。 如果eFUSE中的安全标志被设置为启用,Bootloader会执行相应的安全验证流程,以确保设备启动的固件和操作系统是经过验证的,从而防止未经授权的固件加载。
-
设备唯一标识 Bootloader可能会使用eFUSE中存储的设备唯一标识(Device ID)作为设备的身份认证信息。 这些标识通常是在制造过程中预先烧录到eFUSE中的,用于识别每个设备的唯一性。 Bootloader可以读取eFUSE中的这些标识,并在设备启动时使用它们来进行身份验证。
-
安全密钥存储 某些Bootloader可能会使用eFUSE来存储安全密钥,例如用于加密和解密数据的密钥。这些密钥通常是在制造过程中预先烧录到eFUSE中的,以确保密钥的安全性和不可篡改性。
-
防止硬件修改 Bootloader可能会使用eFUSE来检查设备硬件的配置和状态,以确保硬件没有被修改或篡改。通过读取eFUSE中存储的硬件配置信息,Bootloader可以识别任何不合法的硬件修改,从而确保设备的完整性和安全性。
eFuse 和 NVRAM 的区别
eFUSE(Electrically Programmable Read-Only Memory Fuse)和 NVRAM(Non-Volatile Random Access Memory)
-
eFUSE(电可编程只读存储器):
- 特性:eFUSE是一种只读存储器,一旦编程后就无法再进行擦除或修改。它是一次性可编程的,一旦被烧录,存储的数据将变为永久性的。
- 用途:eFUSE通常用于存储一些重要的设备信息和安全相关的数据,例如设备唯一标识、安全密钥、硬件配置等。这些数据的安全性和不可篡改性是系统的重要保障。
- 编程:eFUSE通常在制造过程中被烧录,由硬件厂商预置一些不可更改的数据。它一般不受软件的影响,也不是可编程的软件特性。
-
NVRAM(非易失性随机存取存储器):
- 特性:NVRAM是一种随机访问存储器,它可以读取和写入数据,并且数据在断电后仍然保持不变,具有非易失性特性。
- 用途:NVRAM通常用于存储一些配置信息、用户设置、操作系统状态等需要持久化保存的数据。
- 编程:NVRAM中的数据通常由操作系统或应用程序进行读取和写入,可以在运行时进行修改和更新。
在手机行业中, eFuse 涉及到的开发一般是工具开发工程师在处理,兼容不同芯片,将设备ID等基础功能兼容好,NVRAM的开发更多是涉及到软件工程师,根据实际的需求来读写数据。
比如常见的电子保卡,WIFI、MAC等一些格式化需要保留的信息等。
MTK的刷机设备上通过 Download Only 以及 Android设备自带的恢复出厂设置就不会将 NVRAM 存储的数据清除, 但是 Format ALL+Download(格式化刷机) 就会将NVRAM数据清除。
eFuse 一般就是一次写入,比如设备ID, NVRAM是根据实际的需求,由软件工程师来进行读写。
熔丝工具
熔丝工具(Fuse Programming Tool)是用于编程eFuse的特定工具,其原理主要涉及以下方面:
- 接口和通信
熔丝工具需要与目标设备建立通信,并通过特定的接口与设备进行数据传输。通常,熔丝工具使用USB、JTAG、SWD(Serial Wire Debug)等接口与目标设备进行连接。 - 认证和权限
为了确保对eFuse的编程是安全可靠的,熔丝工具通常需要进行认证和权限验证。这可以防止未经授权的访问者对设备的eFuse进行编程。 - 操作命令
熔丝工具通过发送特定的操作命令到目标设备来控制eFuse的编程。这些命令可能包括读取eFuse的状态、擦除eFuse、编程eFuse等。 - 保护机制
为了防止误操作或恶意篡改,熔丝工具可能会实现一些保护机制,如写保护功能、编程次数限制等。 - 错误处理
熔丝工具需要能够处理可能出现的错误情况,如通信错误、设备响应超时、编程失败等,以保证编程的准确性和稳定性。
熔丝工具原理可能因厂商、芯片型号和设备特性而异, 因此,在使用熔丝工具时,需要严格按照厂商提供的文档和指导进行操作,避免因错误的操作而导致设备损坏或数据丢失, 此外,由于eFuse是一次性编程的,一旦编程完成,其中的信息将无法修改或擦除,因此操作时必须谨慎。
所以对于工具工程师而言,这里面核心的工作任务就是兼容不同芯片,保证熔丝的稳定。
一般涉及到 eFuse 写入的稳定性问题,一般都是芯片厂内部的逻辑,对于手机厂商而言,如果遇到设备ID失败,异常的问题,就会被归类为影响整机的严重问题。
安全问题
任天堂的 Switch 掌机通过 eFuse 来防止设备降级, 在用户升级系统时,机器会熔断一定数量 eFuse ,而用户试图降级系统时会校验版本和熔丝的数量,如果不匹配就无法刷入系统。 软破解的任天堂的Switch,就是通过外接锡纸的形式然后进入到一个大气层系统,然后在进入到Switch原始的系统, 对于设备的破解而言,一般都是推荐用户在离线的模式下进行,一个是防止软件上的数据更新导致的异常行为被发现,一个是设备升级版本之后,会对 eFuse进行熔断到当前的系统版本,如果熔断到最新版本后,可能就没法在刷入三方系统。
eFuse 是可以通过电子显微镜看见熔断的情况,见图一。
在大型安全SOC芯片中, eFuse 是一种重要的非易失性存储单元,由熔丝结构构成,通过熔丝可以在芯片上编程并存储信息.常常用于存储安全相关的内容,比如密钥、密码等信息, 因此对于芯片的安全系统非常重要。但是由于工艺尺寸的突飞猛进,efuse作为一种较为脆弱的电路单元,常常在使用过程中由于静电或者其他原因造成efuse损坏,造成整个芯片和系统无法工作,只能整机报废,从而导致大量的浪费。
eFuse 读取
对于设备ID而言,一般是通过在 BootLoader 内部读取 eFuse存储位中的信息得到设备ID,可以写入到属性,然后Android 系统进入后,就可以通过读取属性得到唯一的设备ID。 大部分的情况而言,设备ID的异常问题,是出现在 BootLoader 的读取里面,需要手机厂商自行去解决。
例如,在某些系统中,eFuse 可能被配置为存储一个 32 位的整数值。读取该 eFuse 后,得到的数据可能是一个 32 位的二进制数,或以十六进制表示的 8 位数(4 字节)。
比如通过芯片厂商提供的接口,能够获取到32位的整数值,然后需要转换记录到本地, 比如fdt32_to_cpu
函数的实现:
#include <stdint.h>
uint32_t fdt32_to_cpu(const uint32_t *val)
{
// 假设系统为小端字节序(Little-Endian)
uint32_t result;
uint8_t *p = (uint8_t *)val;
result = (uint32_t)p[0];
result |= ((uint32_t)p[1] << 8);
result |= ((uint32_t)p[2] << 16);
result |= ((uint32_t)p[3] << 24);
return result;
}
fdt32_to_cpu 函数接受一个指向 FDT 中存储 32 位整数的指针 val,然后按照小端字节序将这个 32 位整数值转换为 CPU 的本地字节序,并将转换后的值返回。
本文只是从软件开发的角度,简单梳理一下eFuse 涉及到的模块以及基本概念,本文也在持续的更新中,如果你需要得到最新的更新,请访问: 探究eFuse:硬件保障与系统安全的核心