Need to modify the U-Boot environment from Linux

There are multiple reasons for wanting to modify U-Boot variables from Linux, one of them being to implement A/B update mechanisms. Typically, after you’ve flashed a device with a new version, you’ll set the upgrade_available U-Boot variable to 1, reboot, and let U-Boot try to boot the new version.
If the system boots as expected, reaching an advanced enough stage where all the services have been started successfully, you can restore this variable to 0. Otherwise, you’ll reboot the system, automatically or manually. As long as upgrade_available U-Boot variable remains set to 1, U-Boot will count the failures (bootcount), and it is exceeds a given threshold (bootlimit), will run a command (altbootcmd) to restore the previous version of the system.
Doing it from a C program – Security reasons
While it would be very easy to use the fw_printenv and fw_setenv commands from libubootenv to modify the U-Boot environment from a shell script in Linux, this is far from being an idea solution from a security perspective. Such commands would make it easier for an attacker having gained sufficient privileges to modify any variable in the U-Boot environment, and therefore hijack or at least disrupt the way the system boots.
A better idea is to still use libubootenv, but from your own C program, doing exactly what’s necessary, and not touching any other variable. While it’s not making it impossible to make unwanted changes to the U-Boot environment, at least it makes the attacker’s job harder.
Sample C program
Using libubootenv is pretty easy, and very well documented. Unfortunately, if you’re trying to get started with AI help, it will get it wrong in multiple ways. At least, that was the case with ChatGPT.
So, here’s a program that works. Feel free to reuse it and adapt it to your own needs.
/* Program to set the "upgrade_available" U-Boot variable to "0"
* when it was initially "1"
*
* This is done through the libubootenv C library
* See https://deepwiki.com/sbabic/libubootenv/3-api-reference for details
*
* Compile it with:
* gcc -o validate_update -lubootenv validate-update.c
*
* Copyright Michael Opdenacker <michael.opdenacker@rootcommit.com>
* License: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libuboot.h>
int main(void)
{
struct uboot_ctx *ctx;
const char *value;
int ret, err = 0;
libuboot_initialize(&ctx, NULL);
if (!ctx) {
fprintf(stderr, "Failed to initialize libubootenv\n");
return 1;
}
ret = libuboot_read_config(ctx, "/etc/fw_env.config");
if (ret < 0) {
fprintf(stderr, "Failed to read libubootenv configuration\n");
err = 1;
goto exit;
}
ret = libuboot_open(ctx);
if (ret) {
fprintf(stderr, "Failed to open U-Boot environment\n");
err = 1;
goto exit;
}
value = libuboot_get_env(ctx, "upgrade_available");
if (!value) {
/* No update in progress, exiting */
err = 0;
goto close;
}
if (strcmp(value, "1") == 0) {
printf("Setting upgrade_available to 0\n");
ret = libuboot_set_env(ctx, "upgrade_available", "0");
if (ret) {
fprintf(stderr, "Failed to set variable\n");
err = 1;
goto close;
return 1;
}
ret = libuboot_env_store(ctx);
if (ret) {
fprintf(stderr, "Failed to store environment\n");
err = 1;
goto close;
}
}
close:
libuboot_close(ctx);
exit:
libuboot_exit(ctx);
return err;
}
You can also download it directly from Root Commit’s code-samples repository.
References
- libubootenv C API reference
- Implementing A/B System Updates with U-Boot: a presentation I gave in 2022.
Also happy to support you through your system development tasks!
