Rollback Protection through Ratcheting

Rollback protection prevents a computing system from being downgraded (rolled back) from a later version to an earlier one.
NVIDIA® Jetson™ devices use an implementation of rollback protection called ratcheting. Ratcheting is based on a common rollback architecture that uses a software version number (SWVN) and a hardware version number (HWVN). The SWVN is a binary value that is stored in a software component or a header associated with it; its value represents the software component’s version number. The HWVN is a value stored in hardware, typically in a fuse whose bits can each be burned (changed by software from 0 to 1), but cannot be changed back to 0.
During the boot process, a Jetson device’s software performs several ratchet checks; that is, it compares several SWVNs to the corresponding HWVNs.
If SWVN == HWVN, the device is loading the current version of the software component, and the boot process loads the component.
If SWVN > HWVN, the device is loading a new version of the operating system. The boot process loads the component, then performs a ratchet update; that is, it burns bits in the HWVN fuse to set it equal to the SWVN.
If SWVN < HWVN, the device is loading an old version of the operating system, and the boot software aborts the boot process.
Typically ratchet-checked version numbers are incremented in single steps, that is, version 0 is always followed by version 1, which is always followed by version 2, and so on. For each successive version the SWVN is incremented by 1, and one additional bit is burned in the HWVN.
An SWVN is ordinarily hard-coded in the software component’s source code and so becomes part of its compiled binary. You can also set the SWVN in your project build system and save it in a binary header which is combined with the program binary before it is readied for use.
In Jetson devices that use the T194 processor (NVIDIA® Jetson Xavier™ NX series and NVIDIA® Jetson AGX Xavier™ series), a software component’s SWVN is stored in its code or in its Boot Component Header (BCH), an NVIDIA binary header that stores many types of information about NVIDIA boot components.

Rollback Protection for MB1-BCT, MB2, and Later Components

This section describes ratchet checking and ratchet updating for MB2 and later components.
The major difference between ratchet checking and updating for these components is that MB1 BCT is protected by an HWVN stored in hardware fuses, while MB2 and later components are protected by HWVNs that are stored as ordinary data in MB1 BCT. The section MB2 and Later Components explains how this works.

MB1-BCT

The HWVN for MB1-BCT is stored in four OEM-set fuses:
FUSE_RESERVED_ODM8_0
FUSE_RESERVED_ODM9_0
FUSE_RESERVED_ODM10_0
FUSE_RESERVED_ODM11_0
All of these fuses have four-byte values with 32 programmable bits. Thus the number of times MB1 BCT can be ratchet-updated is 4×32 = 128.
MB1 ratchet-checks and loads MB1-BCT. You can define the SWVN for MB1-BCT by making changes in a ratchet configuration file. The SWVN from the ratchet configuration file is stored in the MB1 BCT BCH.
CBoot can ratchet-update the fuses for MB1 BCT if MB1 has not done so. MB1 stores the SWVN of MB1 BCT in a scratch register. When CBoot starts, it gets the SWVN of MB1 BCT from the scratch register and uses it to perform the ratchet check, and if necessary, the ratchet update. The sample code in Ratchet Updating in CBoot for MB1-BCT demonstrates this.
Note
In Jetson Linux release 32.7, MB1 does not ratchet-update MB1 BCT.

MB2 and Later Components

MB2 and later components (e.g. CBoot, TOS, and kernel) do not have HWVNs stored in fuses because an NVIDIA® Jetson™ processor does not have enough fuses to support them. Also, the number and identity of these components is subject to change by customers, so it isn’t feasible to assign a fixed hardware fuse to each component.
Instead, a copy of the HWVN for each of these components is stored in MB1-BCT, and is used to ratchet-check the component’s actual SWVN. Each component’s actual SWVN is stored in the component’s BCH.
Because MB1 ratchet-checks MB1-BCT before loading any other components, the HWVNs stored in MB1-BCT can be trusted once MB1-BCT has passed the ratchet checking test.

Ratchet Configuration File

The ratchet configuration file defines the HWVNs for MB2 and later components. The pathname of the ratchet configuration file is:
For NVIDIA® Jetson Xavier™ NX series:
Linux_for_Tegra/bootloader/t186ref/BCT/tegra194-mb1-bct-ratchet-p3668.cfg
For NVIDIA® Jetson AGX Xavier™ series:
Linux_for_Tegra/bootloader/t186ref/BCT/tegra194-mb1-bct-ratchet-p2888-0000-p2822-0000.cfg
Following is the initial contents of the ratchet configuration file for Jetson AGX Xavier series. The file for Jetson Xavier NX series is similar.
# Format of the CFG
# ratchet.<fw_index>.<loader_name>.<fw_name> = <ratchet_value>
# fw_index = Index of the firmware which is to be loaded
# loader_name = Binary which loads firmware
# fw_name = Name of the firmware to be loaded
# ratchet_value = Ratchet value firmware
 
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;
The first setting, ratchet.1.mb1.mb1bct, defines the SWVN of MB1-BCT. The settings whose names begin with ratchet.<n>.mb2 (<n> a unique integer) define the SWVNs of various MB2 and later components.
The tool that flashes the Jetson device reads and parses the ratchet configuration file and incorporates its settings into the flashed image. The SWVNs for MB2 and later components are incorporated into the respective components, where they serve the same purpose as other SWVNs, and into MB1-BCT, where they serve as HWVNs.
For example, suppose the current version number of CBoot is 11. When the device is flashed, this value is incorporated into CBoot’s BCH as its SWVN, and into MB1 BCT as its HWVN. If you try to “update” CBoot with an older version (e.g. version 3), version checking will compare the SWVN of 3 in the CBoot BCH to the HWVN of 11 in MB1 BCT and halt the booting process. Because MB1 BCT itself has been ratchet-checked against a genuine HWVN, its contents, including the CBoot HWVN, is reliable.
The following list illustrates this process. The first command lists the target device’s Bootloader partitions, identifying them as mmcblk0p6 and mmcblk0p7. The second and third commands copy an older image of (version 3) each partition over the present one. The fourth command (reboot) demonstrates the result of trying to boot the device with these old versions of the partitions.
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
Note
ratchet.7.mb1.membct must be set to zero in the current release. This version number controls memory BCT, for which Jetson Linux currently does not support rollback protection.

Incrementing the Version Number of MB2 and Later Components

Because the HWVNs for MB2 and later components are stored in MB1 BCT rather than in burnable fuses, they cannot be ratchet-updated at run time. To do so the boot software would have to patch the binary for MB1-BCT, and since it is normally signed by a PKC key, that is not feasible. To update one of these components and increment its version number, increment its version number setting in the ratchet configuration file, and also increment the value of ratchet.1.mb1.mb1bct to reflect the fact that MB1 BCT contains a new HWVN for the updated component. Then reflash the device or perform an Over-the-Air update of the updated component and MB1-BCT.
At boot time, MB1 finds a new version when it ratchet-checks MB1-BCT, and accordingly increments that component’s HWVN. The HWVNs stored in MB1 BCT for MB2 and later components can then be used to ratchet-check MB2 and later components.
If an updated component is later “updated” to an old version, it will fail the ratchet-check.

OEM-Set Components

The T194 SoC used in NVIDIA® Jetson™ NX and NVIDIA® AGX Xavier™ series modules has an additional set of fuses that are under the OEM vendor’s full control. These fuses are ODM_RESERVED0 through ODM_RESERVED7. All of these fuses are 32 bits long, so they provide a total of 256 fusible bits. You can use these fuses to implement your own rollback protection.
Rollback Protection for User Applications shows a sample application which demonstrates how to implement rollback protection for a user application by using these fuses.

Ratchet Updating in CBoot for MB1-BCT

Following is a complete sample application that demonstrates ratchet updating in CBoot for MB1 BCT.
/*
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
*
* NVIDIA CORPORATION and its licensors retain all intellectual property
* and proprietary rights in and to this software, related documentation
* and any modifications thereto. Any use, reproduction, disclosure or
* distribution of this software and related documentation without an express
* license agreement from NVIDIA CORPORATION is strictly prohibited
*/
 
#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 and its licensors retain all intellectual property
* and proprietary rights in and to this software, related documentation
@@ -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

Rollback Protection for User Applications

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;
}