diff --git a/doc/README.bootcount b/doc/README.bootcount new file mode 100644 index 0000000000..b1c22905c6 --- /dev/null +++ b/doc/README.bootcount @@ -0,0 +1,51 @@ +.. 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. diff --git a/drivers/bootcount/bootcount_ext.c b/drivers/bootcount/bootcount_ext.c index 075e590896..9639e638e9 100644 --- a/drivers/bootcount/bootcount_ext.c +++ b/drivers/bootcount/bootcount_ext.c @@ -7,11 +7,21 @@ #include #include -#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; @@ -21,20 +31,27 @@ void bootcount_store(ulong a) 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; @@ -45,15 +62,20 @@ ulong bootcount_load(void) } 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);