--- /dev/null
+.. SPDX-License-Identifier: GPL-2.0+
+
+Boot Count Limit
+================
+
+This allows to detect multiple failed attempts to boot Linux.
+
+After a power-on reset, "bootcount" variable will be initialized with 1, and
+each reboot will increment the value by 1.
+
+If, after a reboot, the new value of "bootcount" exceeds the value of
+"bootlimit", then instead of the standard boot action (executing the contents of
+"bootcmd") an alternate boot action will be performed, and the contents of
+"altbootcmd" will be executed.
+
+If the variable "bootlimit" is not defined in the environment, the Boot Count
+Limit feature is disabled. If it is enabled, but "altbootcmd" is not defined,
+then U-Boot will drop into interactive mode and remain there.
+
+It is the responsibility of some application code (typically a Linux
+application) to reset the variable "bootcount", thus allowing for more boot
+cycles.
+
+BOOTCOUNT_EXT
+-------------
+
+This adds support for maintaining boot count in a file on an EXT filesystem.
+The file to use is define by:
+
+SYS_BOOTCOUNT_EXT_INTERFACE
+SYS_BOOTCOUNT_EXT_DEVPART
+SYS_BOOTCOUNT_EXT_NAME
+
+The format of the file is:
+
+==== =================
+type entry
+==== =================
+u8 magic
+u8 version
+u8 bootcount
+u8 upgrade_available
+==== =================
+
+To prevent unattended usage of "altbootcmd" the "upgrade_available" variable is
+used.
+If "upgrade_available" is 0, "bootcount" is not saved, if "upgrade_available" is
+1 "bootcount" is save.
+So the Userspace Application must set the "upgrade_available" and "bootcount"
+variables to 0, if a boot was successfully.
+This also prevents writes on all reboots.
#include <fs.h>
#include <mapmem.h>
-#define BC_MAGIC 0xbc
+#define BC_MAGIC 0xbd
+#define BC_VERSION 1
+
+typedef struct {
+ u8 magic;
+ u8 version;
+ u8 bootcount;
+ u8 upgrade_available;
+} bootcount_ext_t;
+
+static u8 upgrade_available = 1;
void bootcount_store(ulong a)
{
- u8 *buf;
+ bootcount_ext_t *buf;
loff_t len;
int ret;
return;
}
- buf = map_sysmem(CONFIG_SYS_BOOTCOUNT_ADDR, 2);
- buf[0] = BC_MAGIC;
- buf[1] = (a & 0xff);
+ /* Only update bootcount during upgrade process */
+ if (!upgrade_available)
+ return;
+
+ buf = map_sysmem(CONFIG_SYS_BOOTCOUNT_ADDR, sizeof(bootcount_ext_t));
+ buf->magic = BC_MAGIC;
+ buf->version = BC_VERSION;
+ buf->bootcount = (a & 0xff);
+ buf->upgrade_available = upgrade_available;
unmap_sysmem(buf);
ret = fs_write(CONFIG_SYS_BOOTCOUNT_EXT_NAME,
- CONFIG_SYS_BOOTCOUNT_ADDR, 0, 2, &len);
+ CONFIG_SYS_BOOTCOUNT_ADDR, 0, sizeof(bootcount_ext_t),
+ &len);
if (ret != 0)
puts("Error storing bootcount\n");
}
ulong bootcount_load(void)
{
- u8 *buf;
+ bootcount_ext_t *buf;
loff_t len_read;
int ret;
}
ret = fs_read(CONFIG_SYS_BOOTCOUNT_EXT_NAME, CONFIG_SYS_BOOTCOUNT_ADDR,
- 0, 2, &len_read);
- if (ret != 0 || len_read != 2) {
+ 0, sizeof(bootcount_ext_t), &len_read);
+ if (ret != 0 || len_read != sizeof(bootcount_ext_t)) {
puts("Error loading bootcount\n");
return 0;
}
- buf = map_sysmem(CONFIG_SYS_BOOTCOUNT_ADDR, 2);
- if (buf[0] == BC_MAGIC)
- ret = buf[1];
+ buf = map_sysmem(CONFIG_SYS_BOOTCOUNT_ADDR, sizeof(bootcount_ext_t));
+ if (buf->magic == BC_MAGIC && buf->version == BC_VERSION) {
+ upgrade_available = buf->upgrade_available;
+ if (upgrade_available)
+ ret = buf->bootcount;
+ } else {
+ puts("Incorrect bootcount file\n");
+ }
unmap_sysmem(buf);