通过棘轮机制实现回滚保护
回滚保护可以防止计算系统从较新版本降级(回滚)到较旧版本。
NVIDIA® Jetson™ 设备使用一种名为棘轮机制的回滚保护实现方案。棘轮机制基于一种通用的回滚架构,该架构使用软件版本号 (SWVN) 和硬件版本号 (HWVN)。SWVN 是一个二进制值,存储在软件组件或与其关联的标头中;其值表示软件组件的版本号。HWVN 是一个存储在硬件中的值,通常存储在熔丝中,熔丝的每一位都可以被熔断(由软件从 0 更改为 1),但不能改回 0。
在启动过程中,Jetson 设备的软件会执行多次棘轮检查;也就是说,它会将多个 SWVN 与相应的 HWVN 进行比较。
• 如果 SWVN == HWVN,则设备正在加载当前版本的软件组件,启动过程将加载该组件。
• 如果 SWVN > HWVN,则设备正在加载新版本的操作系统。启动过程将加载该组件,然后执行棘轮更新;也就是说,它会熔断 HWVN 熔丝中的位,使其等于 SWVN。
• 如果 SWVN < HWVN,则设备正在加载旧版本的操作系统,启动软件将中止启动过程。
通常,棘轮检查的版本号以单步方式递增,即版本 0 始终后跟版本 1,版本 1 始终后跟版本 2,依此类推。对于每个后续版本,SWVN 递增 1,并且在 HWVN 中熔断一个额外的位。
SWVN 通常硬编码在软件组件的源代码中,因此成为其编译二进制文件的一部分。您也可以在项目构建系统中设置 SWVN,并将其保存在二进制标头中,该标头与程序二进制文件组合在一起,然后准备使用。
在使用 T194 处理器(NVIDIA® Jetson Xavier™ NX 系列和 NVIDIA® Jetson AGX Xavier™ 系列)的 Jetson 设备中,软件组件的 SWVN 存储在其代码或其启动组件标头 (BCH) 中,BCH 是一个 NVIDIA 二进制标头,存储有关 NVIDIA 启动组件的多种类型的信息。
MB1-BCT、MB2 和后续组件的回滚保护
本节介绍 MB2 和后续组件的棘轮检查和棘轮更新。
这些组件的棘轮检查和更新之间的主要区别在于,MB1 BCT 受硬件熔丝中存储的 HWVN 保护,而 MB2 和后续组件受存储为 MB1 BCT 中普通数据的 HWVN 保护。
MB2 和后续组件部分解释了其工作原理。
MB1-BCT
MB1-BCT 的 HWVN 存储在四个 OEM 设置的熔丝中
• FUSE_RESERVED_ODM8_0
• FUSE_RESERVED_ODM9_0
• FUSE_RESERVED_ODM10_0
• FUSE_RESERVED_ODM11_0
所有这些熔丝都具有四字节值,带有 32 个可编程位。因此,MB1 BCT 可以进行棘轮更新的次数为 4×32 = 128。
MB1 棘轮检查并加载 MB1-BCT。您可以通过在棘轮配置文件中进行更改来定义 MB1-BCT 的 SWVN。来自棘轮配置文件的 SWVN 存储在 MB1 BCT BCH 中。
如果 MB1 尚未执行棘轮更新,则 CBoot 可以更新 MB1 BCT 的熔丝。MB1 将 MB1 BCT 的 SWVN 存储在暂存寄存器中。当 CBoot 启动时,它会从暂存寄存器获取 MB1 BCT 的 SWVN,并使用它来执行棘轮检查,并在必要时执行棘轮更新。
CBoot 中针对 MB1-BCT 的棘轮更新中的示例代码演示了这一点。
注意 | 在 Jetson Linux 32.7 版本中,MB1 不会对 MB1 BCT 进行棘轮更新。 |
MB2 和后续组件
MB2 和后续组件(例如 CBoot、TOS 和内核)没有存储在熔丝中的 HWVN,因为 NVIDIA® Jetson™ 处理器没有足够的熔丝来支持它们。此外,这些组件的数量和标识可能会被客户更改,因此为每个组件分配固定的硬件熔丝是不可行的。
相反,每个组件的 HWVN 副本都存储在 MB1-BCT 中,并用于棘轮检查组件的实际 SWVN。每个组件的实际 SWVN 都存储在该组件的 BCH 中。
由于 MB1 在加载任何其他组件之前都会对 MB1-BCT 进行棘轮检查,因此一旦 MB1-BCT 通过棘轮检查测试,就可以信任 MB1-BCT 中存储的 HWVN。
棘轮配置文件
棘轮配置文件定义了 MB2 和后续组件的 HWVN。棘轮配置文件的路径名为
• 对于 NVIDIA® Jetson Xavier™ NX 系列
Linux_for_Tegra/bootloader/t186ref/BCT/tegra194-mb1-bct-ratchet-p3668.cfg
• 对于 NVIDIA® Jetson AGX Xavier™ 系列
Linux_for_Tegra/bootloader/t186ref/BCT/tegra194-mb1-bct-ratchet-p2888-0000-p2822-0000.cfg
以下是 Jetson AGX Xavier 系列棘轮配置文件的初始内容。Jetson Xavier NX 系列的文件与之类似。
# CFG 的格式
# ratchet.<fw_index>.<loader_name>.<fw_name> = <ratchet_value>
# fw_index = 要加载的固件的索引
# loader_name = 加载固件的二进制文件
# fw_name = 要加载的固件的名称
# ratchet_value = 棘轮值固件
ratchet.1.mb1.mb1bct = 0;
ratchet.2.mb1.spefw = 0;
ratchet.3.mb1.dramecc = 0;
ratchet.4.mb1.ist_ucode = 0;
ratchet.5.mb1.bpmp_ist = 0;
ratchet.6.mb1.mb2 = 0;
ratchet.7.mb1.membct = 0;
ratchet.11.mb2.cpubl = 0;
ratchet.12.mb2.tos = 0;
ratchet.13.mb2.eks = 0;
ratchet.14.mb2.bpmpfw = 0;
ratchet.15.mb2.bpmpfwdtb = 0;
ratchet.16.mb2.sce = 0;
ratchet.17.mb2.rce = 0;
ratchet.18.mb2.ape = 0;
ratchet.19.mb2.cpubldtb = 0;
ratchet.30.cpubl.bootimg = 0;
ratchet.31.cpubl.kerneldtb = 0;
第一个设置 ratchet.1.mb1.mb1bct 定义了 MB1-BCT 的 SWVN。名称以 ratchet.<n>.mb2 (<n> 是唯一的整数) 开头的设置定义了各种 MB2 和后续组件的 SWVN。
刷新 Jetson 设备的工具会读取并解析棘轮配置文件,并将其设置合并到刷写的镜像中。MB2 和后续组件的 SWVN 被合并到各自的组件中,在其中它们的作用与其他 SWVN 相同,并被合并到 MB1-BCT 中,在其中它们充当 HWVN。
例如,假设 CBoot 的当前版本号为 11。当设备被刷新时,此值将作为其 SWVN 合并到 CBoot 的 BCH 中,并作为其 HWVN 合并到 MB1 BCT 中。如果您尝试使用旧版本(例如版本 3)“更新” CBoot,则版本检查会将 CBoot BCH 中版本 3 的 SWVN 与 MB1 BCT 中版本 11 的 HWVN 进行比较,并停止启动过程。由于 MB1 BCT 本身已根据真正的 HWVN 进行了棘轮检查,因此其内容(包括 CBoot HWVN)是可靠的。
以下列表说明了此过程。第一个命令列出了目标设备的启动加载程序分区,将其标识为 mmcblk0p6 和 mmcblk0p7。第二个和第三个命令将每个分区的旧镜像(版本 3)复制到当前镜像之上。第四个命令 (reboot) 演示了尝试使用这些旧版本的分区启动设备的结果。
root@tegra-ubuntu:~# ls -l /dev/disk/by-partlabel/|grep cpu-bootloader
lrwxrwxrwx 1 root 15 Jan 28 2018 cpu-bootloader -> ../../mmcblk0p6
lrwxrwxrwx 1 root 15 Jan 28 2018 cpu-bootloader_b -> ../../mmcblk0p7
root@tegra-ubuntu:~# dd if=./cboot_t194_sigheader.bin.encrypt.signed of=/dev/mmcblk0p6
868+1 records in
868+1 records out
444816 bytes (445 kB, 434 KiB) copied, 0.0466383 s, 9.5 MB/s
root@tegra-ubuntu:~# dd if=./cboot_t194_sigheader.bin.encrypt.signed of=/dev/mmcblk0p7
868+1 records in
868+1 records out
444816 bytes (445 kB, 434 KiB) copied, 0.0560411 s, 7.9 MB/s
root@tegra-ubuntu:~# reboot
...<snip>...
[0000.445] I> parsing oem signed section of cpubl header done
[0000.450] E> Binary(35) version mismatch
[0000.453] E> Expected version: 11
[0000.457] E> Binary version: 3
[0000.459] I> load/auth: execution failed
[0000.463] E> Top caller module: LOADER, error module: LOADER, reason: 0x02, aux_info: 0x01
[0000.471] I> AB warm reset
注意 | ratchet.7.mb1.membct 在当前版本中必须设置为零。此版本号控制内存 BCT,Jetson Linux 目前不支持对其进行回滚保护。 |
递增 MB2 和后续组件的版本号
由于 MB2 和后续组件的 HWVN 存储在 MB1 BCT 中,而不是存储在可熔断的熔丝中,因此它们无法在运行时进行棘轮更新。要做到这一点,启动软件必须修补 MB1-BCT 的二进制文件,但由于它通常由 PKC 密钥签名,因此这是不可行的。要更新这些组件之一并递增其版本号,请递增棘轮配置文件中其版本号设置,并同时递增 ratchet.1.mb1.mb1bct 的值,以反映 MB1 BCT 包含已更新组件的新 HWVN 这一事实。然后重新刷新设备或执行已更新组件和 MB1-BCT 的空中更新。
在启动时,MB1 在对 MB1-BCT 进行棘轮检查时会找到新版本,并相应地递增该组件的 HWVN。然后,MB1 BCT 中存储的 MB2 和后续组件的 HWVN 可用于棘轮检查 MB2 和后续组件。
如果更新后的组件稍后被“更新”为旧版本,则它将无法通过棘轮检查。
OEM 设置的组件
NVIDIA® Jetson™ NX 和 NVIDIA® AGX Xavier™ 系列模块中使用的 T194 SoC 具有一组额外的熔丝,这些熔丝完全受 OEM 供应商控制。这些熔丝是 ODM_RESERVED0 到 ODM_RESERVED7。所有这些熔丝均为 32 位长,因此它们总共提供 256 个可熔断位。您可以使用这些熔丝来实现您自己的回滚保护。
用户应用程序的回滚保护显示了一个示例应用程序,演示了如何使用这些熔丝为用户应用程序实现回滚保护。
CBoot 中针对 MB1-BCT 的棘轮更新
以下是一个完整的示例应用程序,演示了 CBoot 中针对 MB1 BCT 的棘轮更新。
/*
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
*
* NVIDIA CORPORATION 及其许可方保留对本软件、相关文档的全部知识产权
* 和专有权利
* 及其所有修改。未经 NVIDIA CORPORATION 明确许可协议,严禁对本软件和相关文档进行任何使用、复制、披露或
* 分发
* 许可协议
*/
#define MODULE TEGRABL_ERR_RATCHET
#include <tegrabl_debug.h>
#include <tegrabl_error.h>
#include <tegrabl_fuse.h>
#include <tegrabl_io.h>
#include <tegrabl_addressmap.h>
#include <arscratch.h>
#define SCRATCH_MB1_BCT_OEM_RATCHET SCRATCH_SECURE_RSV98_SCRATCH_0
#define FUSE_MB1BCT_RATCHET_MAX 128
struct mb1bct_ratchets {
uint32_t odm8;
uint32_t odm9;
uint32_t odm10;
uint32_t odm11;
};
static uint32_t do_thermometer_decoding(uint32_t therm_encoded_val)
{
return (32 - __builtin_clz(therm_encoded_val));
}
static tegrabl_error_t get_mb1bct_hw_version(struct mb1bct_ratchets *hw_version)
{
tegrabl_error_t err = TEGRABL_NO_ERROR;
uint32_t odm8, odm9, odm10, odm11;
if (hw_version == NULL) {
err = TEGRABL_ERROR(TEGRABL_ERR_BAD_PARAMETER, 0);
goto out;
}
err = tegrabl_fuse_read(FUSE_RESERVED_ODM8, &odm8, sizeof(odm8));
if (err != TEGRABL_NO_ERROR) {
pr_error("Failed to read fuse: odm_reserved8: 0x%x\n", err);
goto out;
}
err = tegrabl_fuse_read(FUSE_RESERVED_ODM9, &odm9, sizeof(odm9));
if (err != TEGRABL_NO_ERROR) {
pr_error("Failed to read fuse: odm_reserved9: 0x%x\n", err);
goto out;
}
err = tegrabl_fuse_read(FUSE_RESERVED_ODM10, &odm10, sizeof(odm10));
if (err != TEGRABL_NO_ERROR) {
pr_error("Failed to read fuse: odm_reserved10: 0x%x\n", err);
goto out;
}
err = tegrabl_fuse_read(FUSE_RESERVED_ODM11, &odm11, sizeof(odm11));
if (err != TEGRABL_NO_ERROR) {
pr_error("Failed to read fuse: odm_reserved11: 0x%x\n", err);
goto out;
}
hw_version->odm8 = odm8;
hw_version->odm9 = odm9;
hw_version->odm10 = odm10;
hw_version->odm11 = odm11;
pr_info("MB1BCT odm8: %u, odm9: %u, odm10: %u, odm11: %u\n",
odm8, odm9, odm10, odm11);
out
return err;
}
static tegrabl_error_t do_bump_up_mb1bct_hw_version(
uint32_t index,
uint32_t value,
struct mb1bct_ratchets *ratchets)
{
tegrabl_error_t err = TEGRABL_NO_ERROR;
int target;
switch (index) {
case 0
if (value == ratchets->odm8)
return TEGRABL_NO_ERROR;
target = FUSE_RESERVED_ODM8;
break;
case 1
if (value == ratchets->odm9)
return TEGRABL_NO_ERROR;
target = FUSE_RESERVED_ODM9;
break;
case 2
if (value == ratchets->odm10)
return TEGRABL_NO_ERROR;
target = FUSE_RESERVED_ODM10;
break;
case 3
if (value == ratchets->odm11)
return TEGRABL_NO_ERROR;
target = FUSE_RESERVED_ODM11;
break;
default
pr_error("Invalid odm_reserved index: %u\n", index);
return TEGRABL_ERROR(TEGRABL_ERR_BAD_PARAMETER, 1);
}
err = tegrabl_fuse_write(target, &value, sizeof(value));
if (err != TEGRABL_NO_ERROR) {
pr_error("Burn odm_reserved%u failed: 0x%x\n",
index + 8, err);
return err;
}
pr_info("Updated odm_reserved%u to 0x%x\n", index + 8, value);
return err;
}
static tegrabl_error_t bump_up_mb1bct_hw_version(
uint32_t sw_ver,
struct mb1bct_ratchets *ratchets)
{
uint32_t value = 0;
uint32_t i, index;
tegrabl_error_t err = TEGRABL_NO_ERROR;
index = 0;
for (i = 0; i < sw_ver; i++) {
value |= (1 << (i % 32));
if ((i + 1) % 32 == 0) {
err = do_bump_up_mb1bct_hw_version(index, value, ratchets);
if (err != TEGRABL_NO_ERROR) {
pr_error("Bump up MB1BCT ratchet fuse failed: 0x%x\n", err);
return err;
}
index++;
value = 0;
}
}
if (value != 0)
err = do_bump_up_mb1bct_hw_version(index, value, ratchets);
return err;
}
static tegrabl_error_t update_mb1bct_ratchet_fuse(void)
{
tegrabl_error_t err = TEGRABL_NO_ERROR;
uint32_t mb1bct_sw_ver;
struct mb1bct_ratchets ratchets;
uint32_t mb1bct_hw_ver;
mb1bct_sw_ver = NV_READ32(NV_ADDRESS_MAP_SCRATCH_BASE + SCRATCH_MB1_BCT_OEM_RATCHET);
pr_info("MB1BCT SW ver: 0x%x\n", mb1bct_sw_ver);
/* MB1BCT uses fuses below to store the hardware version
* FUSE_RESERVED_ODM8_0[0:31]
* FUSE_RESERVED_ODM9_0[0:31]
* FUSE_RESERVED_ODM10_0[0:31]
* FUSE_RESERVED_ODM11_0[0:31]
*/
if (mb1bct_sw_ver > FUSE_MB1BCT_RATCHET_MAX) {
pr_error("MB1BCT(%u) sw version is too large, skip the "
"ratchet fuse updating.\n", mb1bct_sw_ver);
err = TEGRABL_ERROR(TEGRABL_ERR_INVALID_VERSION, 0);
goto out;
}
err = get_mb1bct_hw_version(&ratchets);
if (err != TEGRABL_NO_ERROR) {
pr_error("Failed to get MB1BCT HW version.\n");
goto out;
}
mb1bct_hw_ver = do_thermometer_decoding(ratchets.odm8) +
do_thermometer_decoding(ratchets.odm9) +
do_thermometer_decoding(ratchets.odm10) +
do_thermometer_decoding(ratchets.odm11);
pr_info("Got MB1BCT HW version: %u\n", mb1bct_hw_ver);
/* Compare the software version and the hardware version */
if (mb1bct_sw_ver < mb1bct_hw_ver) {
pr_error("Bug: MB1BCT SW ratchet version(%u) is older than "
"HW ratchet version(%u)\n", mb1bct_sw_ver, mb1bct_hw_ver);
err = TEGRABL_ERROR(TEGRABL_ERR_INVALID, 0);
goto out;
} else if (mb1bct_sw_ver == mb1bct_hw_ver) {
/* Fuse value is up-to-date, so skip fuse update */
err = TEGRABL_NO_ERROR;
goto out;
} else {
/* Start ratchet fuse updating */
err = bump_up_mb1bct_hw_version(mb1bct_sw_ver, &ratchets);
if (err != TEGRABL_NO_ERROR) {
pr_error("Update MB1BCT field ratchet fuse failed: 0x%x\n", err);
goto out;
}
pr_info("MB1BCT ratchet fuse updating finished.\n");
}
out
return err;
}
tegrabl_error_t update_ratchet_fuse(void)
{
/* Optional: You can assign a fuse bit to indicate if the ratchet fuse
* updating is needed or not. This is a reasonable requirement sometimes,
* e.g: when the board has A/B slots enabled, it is possible that we will
* have different MB1-BCT/MB2/... boot components which have different versions.
* So always doing a ratchet fuse updating may break the booting of one boot chain.
* Read and check the fuse here to decide whether the fuse updating
* below should be opt-out.
* Read the A/B chain status here then to decide whether a fuse updating is
* needed is also an option.
*
* Summarize the ideas in pseudo codes
* optin = read_optin_fuse()
* if (optin == 0)
* goto out;
*
* check_ab_status()
* if !ab_in_sync()
* goto out;
*
* ......
*/
return update_mb1bct_ratchet_fuse();
}
To make these sample codes work, additional changes are needed as shown below
diff --git a/bootloader/partner/t18x/cboot/platform/t194/platform.c b/bootloader/partner/t18x/cboot/platform/t194/platform.c
index 815f90e716a5..0eb6e3eaa95b 100644
--- a/bootloader/partner/t18x/cboot/platform/t194/platform.c
+++ b/bootloader/partner/t18x/cboot/platform/t194/platform.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2020, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2017-2021, NVIDIA CORPORATION. All rights reserved.
*
* NVIDIA CORPORATION 及其许可方保留对本软件、相关文档的全部知识产权
* 和专有权利
@@ -84,6 +84,7 @@
#include <arpmc_misc.h>
#include <tegrabl_reset_prepare.h>
#include <tegrabl_io.h>
+#include <ratchet_update.h>
static bool is_comb_uart_initialized = false;
@@ -650,6 +651,12 @@ void platform_init(void)
}
#endif
+ err = update_ratchet_fuse();
+ if (err != TEGRABL_NO_ERROR) {
+ pr_error("Error in ratchet updating.\n");
+ goto fail;
+ }
+
#if defined(CONFIG_ENABLE_NCT)
err = tegrabl_nct_init();
if (err != TEGRABL_NO_ERROR) {
diff --git a/bootloader/partner/t18x/cboot/platform/t194/rules.mk b/bootloader/partner/t18x/cboot/platform/t194/rules.mk
index 2ce07f963c71..f30c505691c1 100644
--- a/bootloader/partner/t18x/cboot/platform/t194/rules.mk
+++ b/bootloader/partner/t18x/cboot/platform/t194/rules.mk
@@ -87,6 +87,7 @@ endif
MODULE_SRCS += \
$(LOCAL_DIR)/platform.c \
$(LOCAL_DIR)/platform_config.c \
+ $(LOCAL_DIR)/ratchet_update.c \
$(LOCAL_DIR)/../../../../$(TARGET_FAMILY)/common/lib/config_storage/config_storage.c
MEMBASE := 0x96000000
diff --git a/bootloader/partner/t19x/common/drivers/fuse/rules.mk b/bootloader/partner/t19x/common/drivers/fuse/rules.mk
index 446d1f1f4aab..9d8d773b82bc 100644
--- a/bootloader/partner/t19x/common/drivers/fuse/rules.mk
+++ b/bootloader/partner/t19x/common/drivers/fuse/rules.mk
@@ -19,8 +19,8 @@ GLOBAL_INCLUDES += \
$(LOCAL_DIR)/../../../../$(TARGET_FAMILY)/common/include/drivers
MODULE_SRCS += \
- $(LOCAL_DIR)/tegrabl_fuse_read.c
-
+ $(LOCAL_DIR)/tegrabl_fuse_read.c \
+ $(LOCAL_DIR)/tegrabl_fuse_write.c
include make/module.mk
用户应用程序的回滚保护
Following is a complete sample application that demonstrates how to implement rollback protection for a user application.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
/* Can't exceed 128 because we use 4 reserved_odm
* fuses to store the hardware version.
*/
#define SW_VERSION 2
#define BUF_SIZE 1024
#define SUCCESS 0
#define FAILED 1
#define FUSE_PATH "/sys/devices/3820000.efuse/3820000.efuse:efuse-burn/reserved_odm%d"
#define CHECK_RET(ret) \
do { \
if ((ret) != SUCCESS) \
return (ret); \
} while (0)
typedef struct {
unsigned int odm0;
unsigned int odm1;
unsigned int odm2;
unsigned int odm3;
unsigned int hw_version;
} hw_ratchets;
int read_odm_reserved(int index, unsigned int *value)
{
char path[BUF_SIZE];
char buf[BUF_SIZE];
FILE *fp = NULL;
int count = 0;
int ret = SUCCESS;
long int result = 0;
char *endptr = NULL;
if (value == NULL) {
fprintf(stderr, "value is a NULL pointer.\n");
return EINVAL;
}
memset(path, 0, sizeof(path));
snprintf(path, BUF_SIZE, FUSE_PATH, index);
fp = fopen(path, "r");
if (fp == NULL) {
fprintf(stderr, "Can't open file: %s. Reason: %s\n",
path, strerror(errno));
return errno;
}
memset(buf, 0, sizeof(buf));
/* 0xXXXXXXXX is 10 bytes */
count = fread(buf, 1, 10, fp);
if (count != 10 || ferror(fp)) {
fprintf(stderr, "Read file: %s failed.\n", path);
ret = EIO;
goto out;
}
if (buf[0] == '0' && buf[1] == 'x') {
result = strtol(buf, &endptr, 16);
if (*endptr != '\0') {
fprintf(stderr, "Convert the value of reserved_odm failed: %s\n", buf);
ret = EINVAL;
goto out;
}
*value = (unsigned int)result;
} else {
fprintf(stderr, "Corrupted file: %s.\n", path);
ret = EIO;
goto out;
}
out
if (fp != NULL)
fclose(fp);
return ret;
}
/* Count 1 in the param "val" */
int decode_odm_reserved(unsigned int val)
{
return (32UL - __builtin_clz(val));
}
int do_bump_up_hw_version(int index, unsigned int value, hw_ratchets *ratchets)
{
char path[BUF_SIZE];
char cmd[BUF_SIZE];
int status = SUCCESS;
switch (index) {
case 0
if (value == ratchets->odm0)
return SUCCESS;
break;
case 1
if (value == ratchets->odm1)
return SUCCESS;
break;
case 2
if (value == ratchets->odm2)
return SUCCESS;
break;
case 3
if (value == ratchets->odm3)
return SUCCESS;
break;
default
fprintf(stderr, "Invalid odm_reserved index: %d\n", index);
return EINVAL;
}
memset(path, 0, sizeof(path));
memset(cmd, 0, sizeof(cmd));
snprintf(path, BUF_SIZE, FUSE_PATH, index);
snprintf(cmd, BUF_SIZE, "echo %x > %s", value, path);
fprintf(stdout, "Start running: %s\n", cmd);
fflush(0);
status = system(cmd);
if (status) {
fprintf(stderr, "Updating hw version failed: %d\n", status);
return status;
}
return SUCCESS;
}
int bump_up_hw_version(int sw_ver, hw_ratchets *ratchets)
{
int value = 0;
int i, index;
int ret = SUCCESS;
index = 0;
for (i = 0; i < sw_ver; i++) {
value |= (1 << (i % 32));
if ((i + 1) % 32 == 0) {
ret = do_bump_up_hw_version(index, value, ratchets);
CHECK_RET(ret);
index++;
value = 0;
}
}
if (value != 0)
ret = do_bump_up_hw_version(index, value, ratchets);
return ret;
}
int main(int argc, char *argv[])
{
unsigned int odm0, odm1, odm2, odm3;
unsigned int hw_version = 0;
int ret = SUCCESS;
hw_ratchets ratchets;
if (geteuid() != 0) {
fprintf(stderr, "This program needs root priviledge.\n");
return FAILED;
}
/* odm_reserved0 - odm_reserved3 are used for rollback protection */
odm0 = odm1 = odm2 = odm3 = 0;
CHECK_RET(read_odm_reserved(0, &odm0));
CHECK_RET(read_odm_reserved(1, &odm1));
CHECK_RET(read_odm_reserved(2, &odm2));
CHECK_RET(read_odm_reserved(3, &odm3));
fprintf(stdout, "Read reserved_odm0: %d, reserved_odm1: %d, "
"reserved_odm2: %d, reserved_odm3: %d\n",
odm0, odm1, odm2, odm3);
hw_version += decode_odm_reserved(odm0);
hw_version += decode_odm_reserved(odm1);
hw_version += decode_odm_reserved(odm2);
hw_version += decode_odm_reserved(odm3);
memset(&ratchets, 0, sizeof(hw_ratchets));
ratchets.odm0 = odm0;
ratchets.odm1 = odm1;
ratchets.odm2 = odm2;
ratchets.odm3 = odm3;
ratchets.hw_version = hw_version;
fprintf(stdout, "The current hardware version is: %d\n", hw_version);
fprintf(stdout, "The current software version is: %d\n", SW_VERSION);
if (SW_VERSION == hw_version) {
fprintf(stdout, "The software version is equal to the hardware version.\n");
fprintf(stdout, "Rollback protection checking passed.\n");
ret = SUCCESS;
goto out;
}
if (SW_VERSION < hw_version) {
fprintf(stderr, "The software version is less than the hardware version.\n");
fprintf(stderr, "Rollback protection checking failed.\n");
ret = FAILED;
goto out;
}
if (SW_VERSION > hw_version) {
fprintf(stdout, "The software version is greater than the hardware version.\n");
fprintf(stdout, "Rollback protection checking passed.\n");
fprintf(stdout, "The hardware version needs to be bumped up.\n");
fprintf(stdout, "Start updating the hardware version...\n");
ret = bump_up_hw_version(SW_VERSION, &ratchets);
goto out;
}
out
return ret;
}