[U-Boot] a few questions about saving bootcount in the environment

a few quick questions about this feature before i move on to the more widely-used stuff involving bootcount.
first, it seems that there's not a lot of saving bootcount in the environment ... as i see it, there's the taurus board, and there's the boards that include "siemens-am33x-common.h", of which i see six:
include/configs/rut.h:#include "siemens-am33x-common.h" include/configs/draco.h:#include "siemens-am33x-common.h" include/configs/etamin.h:#include "siemens-am33x-common.h" include/configs/rastaban.h:#include "siemens-am33x-common.h" include/configs/pxm2.h:#include "siemens-am33x-common.h" include/configs/thuban.h:#include "siemens-am33x-common.h"
so this tells me that there's not a whole lot of that feature being used, so i won't spend much time on it.
also, just to confirm, the "upgrade_available" variable is used *exclusively* for the case of bootcount in the environment and nowhere else, correct? so, again, if i'm not using the environment, i don't care about it.
finally, i read this in the README:
CONFIG_BOOTCOUNT_ENV If no softreset save registers are found on the hardware "bootcount" is stored in the environment. To prevent a saveenv on all reboots, the environment variable "upgrade_available" is used. If "upgrade_available" is 0, "bootcount" is always 0, if "upgrade_available" is 1 "bootcount" is incremented in the environment. So the Userspace Applikation must set the "upgrade_available" and "bootcount" variable to 0, if a boot was successfully.
now, i can see where one wants to reset "bootcount" to zero once you boot successfully, but why would you also set "upgrade_available" to zero? don't you want to keep using that feature when you boot in the future?
rday

Dear Robert,
In message alpine.LFD.2.20.1607231318540.16761@localhost.localdomain you wrote:
so this tells me that there's not a whole lot of that feature being used, so i won't spend much time on it.
Right, it is only a last resort when you cannot find any better place to storeit (in a hardware register that survives resets).
also, just to confirm, the "upgrade_available" variable is used *exclusively* for the case of bootcount in the environment and nowhere else, correct? so, again, if i'm not using the environment, i don't care about it.
Right. It is only needed to reduce the number of writes to the environment.
now, i can see where one wants to reset "bootcount" to zero once you boot successfully, but why would you also set "upgrade_available" to zero? don't you want to keep using that feature when you boot in the future?
--> Heiko ?
Best regards,
Wolfgang Denk

On Mon, Jul 25, 2016 at 06:57:21AM +0200, Wolfgang Denk wrote:
Dear Robert,
In message alpine.LFD.2.20.1607231318540.16761@localhost.localdomain you wrote:
so this tells me that there's not a whole lot of that feature being used, so i won't spend much time on it.
Right, it is only a last resort when you cannot find any better place to storeit (in a hardware register that survives resets).
That's not strictly true. One of the things I noticed recently is that Mender uses bootcount, in environment, as a least common denominator. And thrown in a file in a filesystem, in so far as you trust the underlying black box to be good about reads/writes and wear levelling, it's robust enough (for certain values of robust and enough). We're dipping into one of those areas where experts have varying opinions on what's good enough, hence all the qualifiers. But it is a useful option. And neatly circumvents the need for a "driver" to clear the count too.
[snip]
now, i can see where one wants to reset "bootcount" to zero once you boot successfully, but why would you also set "upgrade_available" to zero? don't you want to keep using that feature when you boot in the future?
--> Heiko ?
Heiko wrote a kernel driver, for the non-env side of things at least at one point, and the DT bindings didn't get approved. I picked it up years ago, tried, and had different DT binding approval problems and never cycled back to it while I was at TI playing with that kind of stuff. Which is why the bootcount in env side of things is so attractive, it's just fw_setenv/printenv once you're happy with your system boot.

Dear Tom,
In message 20160725135754.GN14698@bill-the-cat you wrote:
Right, it is only a last resort when you cannot find any better place to storeit (in a hardware register that survives resets).
That's not strictly true. One of the things I noticed recently is that Mender uses bootcount, in environment, as a least common denominator. And thrown in a file in a filesystem, in so far as you trust the underlying black box to be good about reads/writes and wear levelling, it's robust enough (for certain values of robust and enough). We're dipping into one of those areas where experts have varying opinions on what's good enough, hence all the qualifiers. But it is a useful option. And neatly circumvents the need for a "driver" to clear the count too.
Agreed. Let me rephrase the warning, then: it is always a good idea to minimize the number of writes to the environment, especially when done automagically. One should at least avoid to re-write it on every boot, especially when storage is some (flash based) storage device with a limited number of erase/write cycles. And even on other stoage each write access includes the risk of errors.
Best regards,
Wolfgang Denk

On Mon, 25 Jul 2016, Wolfgang Denk wrote:
Dear Tom,
In message 20160725135754.GN14698@bill-the-cat you wrote:
Right, it is only a last resort when you cannot find any better place to storeit (in a hardware register that survives resets).
That's not strictly true. One of the things I noticed recently is that Mender uses bootcount, in environment, as a least common denominator. And thrown in a file in a filesystem, in so far as you trust the underlying black box to be good about reads/writes and wear levelling, it's robust enough (for certain values of robust and enough). We're dipping into one of those areas where experts have varying opinions on what's good enough, hence all the qualifiers. But it is a useful option. And neatly circumvents the need for a "driver" to clear the count too.
Agreed. Let me rephrase the warning, then: it is always a good idea to minimize the number of writes to the environment, especially when done automagically. One should at least avoid to re-write it on every boot, especially when storage is some (flash based) storage device with a limited number of erase/write cycles. And even on other stoage each write access includes the risk of errors.
i'm sure i'm asking the obvious, but if every boot to user space is successful, bootcount will have a value of zero each time, yes? so if user space code checks it and it's zero, then, you're done, no need to write.
rday

Dear Robert,
In message alpine.LFD.2.20.1607251355090.3253@localhost.localdomain you wrote:
i'm sure i'm asking the obvious, but if every boot to user space is successful, bootcount will have a value of zero each time, yes? so if user space code checks it and it's zero, then, you're done, no need to write.
As Heiko already explained, the update of the boot counter (= read from env, increment, rewrite environment to persistent storage) is done in U-Boot, and without "upgrade_available" it would happen on each and every boot of the system.
The boot counter is implemented in U-Boot - only the resetting is done in user space (and requires another write).
Best regards,
Wolfgang Denk

On Mon, 25 Jul 2016, Wolfgang Denk wrote:
Dear Robert,
In message alpine.LFD.2.20.1607251355090.3253@localhost.localdomain you wrote:
i'm sure i'm asking the obvious, but if every boot to user space is successful, bootcount will have a value of zero each time, yes? so if user space code checks it and it's zero, then, you're done, no need to write.
As Heiko already explained, the update of the boot counter (= read from env, increment, rewrite environment to persistent storage) is done in U-Boot, and without "upgrade_available" it would happen on each and every boot of the system.
The boot counter is implemented in U-Boot - only the resetting is done in user space (and requires another write).
whoops, i missed that bit, just trying to catch up now.
rday

Hello Tom,
Am 25.07.2016 um 15:57 schrieb Tom Rini:
On Mon, Jul 25, 2016 at 06:57:21AM +0200, Wolfgang Denk wrote:
Dear Robert,
In message alpine.LFD.2.20.1607231318540.16761@localhost.localdomain you wrote:
so this tells me that there's not a whole lot of that feature being used, so i won't spend much time on it.
Right, it is only a last resort when you cannot find any better place to storeit (in a hardware register that survives resets).
That's not strictly true. One of the things I noticed recently is that Mender uses bootcount, in environment, as a least common denominator. And thrown in a file in a filesystem, in so far as you trust the underlying black box to be good about reads/writes and wear levelling, it's robust enough (for certain values of robust and enough). We're dipping into one of those areas where experts have varying opinions on what's good enough, hence all the qualifiers. But it is a useful option. And neatly circumvents the need for a "driver" to clear the count too.
[snip]
now, i can see where one wants to reset "bootcount" to zero once you boot successfully, but why would you also set "upgrade_available" to zero? don't you want to keep using that feature when you boot in the future?
--> Heiko ?
Heiko wrote a kernel driver, for the non-env side of things at least at one point, and the DT bindings didn't get approved. I picked it up years ago, tried, and had different DT binding approval problems and never cycled back to it while I was at TI playing with that kind of stuff. Which is why the bootcount in env side of things is so attractive, it's just fw_setenv/printenv once you're happy with your system boot.
Yep ... why do we need "upgrade_available"?
in ./common/autoboot.c on *every* boot bootcount_store() gets called, so on every boot (which can happen very often) the Environment would be written ... not good, for long life of your product (if you have the Env in raw nand flash for example, as for the siemens boards)
If the bootcounter feature is only needed if you really did an update, to most writtes are unnecessary ... so the Userspace App can set also "upgrade_available" to enable the bootcount feature in case we save the bootcounter in the Environment ... and save a lot of writtes to the Env
But if you say "I don;t care how often I write the Environment" you simply can set "upgrade_available" to 1 and have the "original" behaviour of the bootcount feature (incrementing bootcount on each reboot ...)
bye, Heiko

On Mon, 25 Jul 2016, Heiko Schocher wrote:
... snip ...
Yep ... why do we need "upgrade_available"?
in ./common/autoboot.c on *every* boot bootcount_store() gets called, so on every boot (which can happen very often) the Environment would be written ... not good, for long life of your product (if you have the Env in raw nand flash for example, as for the siemens boards)
If the bootcounter feature is only needed if you really did an update, to most writtes are unnecessary ... so the Userspace App can set also "upgrade_available" to enable the bootcount feature in case we save the bootcounter in the Environment ... and save a lot of writtes to the Env
But if you say "I don;t care how often I write the Environment" you simply can set "upgrade_available" to 1 and have the "original" behaviour of the bootcount feature (incrementing bootcount on each reboot ...)
ok, i am now *totally* confused, so let's start at the beginning. it's my understanding that selecting CONFIG_BOOTCOUNT_LIMIT means you want to use the boot counter, it does *not* select what method you want to use to store the boot counter, correct? but here's the confusing part.
if i look in common/autoboot.c, i see:
const char *bootdelay_process(void) { char *s; int bootdelay; #ifdef CONFIG_BOOTCOUNT_LIMIT unsigned long bootcount = 0; unsigned long bootlimit = 0; #endif /* CONFIG_BOOTCOUNT_LIMIT */
#ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); bootcount++; bootcount_store(bootcount); setenv_ulong("bootcount", bootcount); <----- ????? bootlimit = getenv_ulong("bootlimit", 10, 0); #endif /* CONFIG_BOOTCOUNT_LIMIT */
... snip ...
and that's what confuses the heck out of me. i thought that, depending on the technique i wanted to use to store the boot counter (RAM, I2C, whatever), it would use *exclusively* that technique, and while i can see the use of the routines bootcount_load() and bootcount_store(), which are redefined based on the technique you select:
$ grep -rw bootcount_store * bootcount_at91.c:void bootcount_store(ulong a) bootcount_blackfin.c:void bootcount_store(ulong cnt) bootcount.c:__weak void bootcount_store(ulong a) bootcount_davinci.c:void bootcount_store(ulong a) bootcount_env.c:void bootcount_store(ulong a) bootcount_i2c.c:void bootcount_store(ulong a) bootcount_i2c.c: bootcount_store(0); bootcount_ram.c:void bootcount_store(ulong a) $
it appears that, no matter what, the environment *is* updated every single time because of this line in the bootdelay_process() routine:
setenv_ulong("bootcount", bootcount);
why? it seems, from the above, that no matter what boot counter mechanism you use, the environment will be updated because of that line.
i can certainly see in bootcount_env.c where using "upgrade_available" can deselect writing to the environment based on invoking bootcount_store(), but that in no way stops that explicit call to setenv_ulong() in that routine above.
so how am i misunderstanding this? it seems that no matter what boot counter storage mechanism i select, i'll be writing to the environment every time *anyway*.
rday

Dear Robert,
In message alpine.LFD.2.20.1607260734020.10018@localhost.localdomain you wrote:
ok, i am now *totally* confused, so let's start at the beginning. it's my understanding that selecting CONFIG_BOOTCOUNT_LIMIT means you
right.
want to use the boot counter, it does *not* select what method you want to use to store the boot counter, correct? but here's the confusing part.
Which exact part is confusing?
#ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); bootcount++; bootcount_store(bootcount); setenv_ulong("bootcount", bootcount); <----- ????? bootlimit = getenv_ulong("bootlimit", 10, 0); #endif /* CONFIG_BOOTCOUNT_LIMIT */
This is only the equivalent of a "setenv", so you have a variable in the environment which you can use in scripts or compare against "bootlimit".
There is NO automatic "saveenv" here...
and that's what confuses the heck out of me. i thought that, depending on the technique i wanted to use to store the boot counter (RAM, I2C,
The key word here is "store", i. e. store in such a way that the value survives a warm boot / reset. The copy in RAM which is created above does NOT survive.
whatever), it would use *exclusively* that technique, and while i can see the use of the routines bootcount_load() and bootcount_store(), which are redefined based on the technique you select:
These methods access the persistent copy of the variable. The setenv_ulong() just creates a transient work copy.
it appears that, no matter what, the environment *is* updated every single time because of this line in the bootdelay_process() routine:
setenv_ulong("bootcount", bootcount);
Yes, it is. But it is NOT saved to persistent storage.
Best regards,
Wolfgang Denk

On Tue, 26 Jul 2016, Wolfgang Denk wrote:
Dear Robert,
In message alpine.LFD.2.20.1607260734020.10018@localhost.localdomain you wrote:
ok, i am now *totally* confused, so let's start at the beginning. it's my understanding that selecting CONFIG_BOOTCOUNT_LIMIT means you
right.
want to use the boot counter, it does *not* select what method you want to use to store the boot counter, correct? but here's the confusing part.
Which exact part is confusing?
#ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); bootcount++; bootcount_store(bootcount); setenv_ulong("bootcount", bootcount); <----- ????? bootlimit = getenv_ulong("bootlimit", 10, 0); #endif /* CONFIG_BOOTCOUNT_LIMIT */
This is only the equivalent of a "setenv", so you have a variable in the environment which you can use in scripts or compare against "bootlimit".
There is NO automatic "saveenv" here...
ah, you're right, you're right, sorry. little by little ...
rday

Hello Robert,
Am 26.07.2016 um 14:21 schrieb Robert P. J. Day:
On Mon, 25 Jul 2016, Heiko Schocher wrote:
... snip ...
Yep ... why do we need "upgrade_available"?
in ./common/autoboot.c on *every* boot bootcount_store() gets called, so on every boot (which can happen very often) the Environment would be written ... not good, for long life of your product (if you have the Env in raw nand flash for example, as for the siemens boards)
If the bootcounter feature is only needed if you really did an update, to most writtes are unnecessary ... so the Userspace App can set also "upgrade_available" to enable the bootcount feature in case we save the bootcounter in the Environment ... and save a lot of writtes to the Env
But if you say "I don;t care how often I write the Environment" you simply can set "upgrade_available" to 1 and have the "original" behaviour of the bootcount feature (incrementing bootcount on each reboot ...)
ok, i am now *totally* confused, so let's start at the beginning. it's my understanding that selecting CONFIG_BOOTCOUNT_LIMIT means you want to use the boot counter, it does *not* select what method you want to use to store the boot counter, correct? but here's the
Yes, thats correct.
confusing part.
if i look in common/autoboot.c, i see:
const char *bootdelay_process(void) { char *s; int bootdelay; #ifdef CONFIG_BOOTCOUNT_LIMIT unsigned long bootcount = 0; unsigned long bootlimit = 0; #endif /* CONFIG_BOOTCOUNT_LIMIT */ #ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); bootcount++; bootcount_store(bootcount); setenv_ulong("bootcount", bootcount); <----- ????? bootlimit = getenv_ulong("bootlimit", 10, 0); #endif /* CONFIG_BOOTCOUNT_LIMIT */ ... snip ...
and that's what confuses the heck out of me. i thought that, depending on the technique i wanted to use to store the boot counter (RAM, I2C, whatever), it would use *exclusively* that technique, and while i can see the use of the routines bootcount_load() and bootcount_store(), which are redefined based on the technique you select:
$ grep -rw bootcount_store * bootcount_at91.c:void bootcount_store(ulong a) bootcount_blackfin.c:void bootcount_store(ulong cnt) bootcount.c:__weak void bootcount_store(ulong a) bootcount_davinci.c:void bootcount_store(ulong a) bootcount_env.c:void bootcount_store(ulong a) bootcount_i2c.c:void bootcount_store(ulong a) bootcount_i2c.c: bootcount_store(0); bootcount_ram.c:void bootcount_store(ulong a) $
it appears that, no matter what, the environment *is* updated every single time because of this line in the bootdelay_process() routine:
setenv_ulong("bootcount", bootcount);
why? it seems, from the above, that no matter what boot counter
Yes, the bootcount variable gets set here ... but the environment gets not written...
mechanism you use, the environment will be updated because of that line.
The Environment gets written when saveenv() gets called ... or?
i can certainly see in bootcount_env.c where using "upgrade_available" can deselect writing to the environment based on invoking bootcount_store(), but that in no way stops that explicit call to setenv_ulong() in that routine above.
so how am i misunderstanding this? it seems that no matter what boot counter storage mechanism i select, i'll be writing to the environment every time *anyway*.
The Environmentvariable gets updated on boot, but not written into the storage device.
bye, Heiko

On Tue, 26 Jul 2016, Heiko Schocher wrote:
... snip ...
it appears that, no matter what, the environment *is* updated every single time because of this line in the bootdelay_process() routine:
setenv_ulong("bootcount", bootcount);
why? it seems, from the above, that no matter what boot counter
Yes, the bootcount variable gets set here ... but the environment gets not written...
mechanism you use, the environment will be updated because of that line.
The Environment gets written when saveenv() gets called ... or?
... snip ...
yes, i already apologized to wolfgang for being so clueless, i didn't see the difference between "setenv" and "saveenv". anyway, i'm still writing a wiki page on this that i can use for future embedded linux classes, so ... onward.
rday

Hello Robert,
Am 27.07.2016 um 14:03 schrieb Robert P. J. Day:
On Tue, 26 Jul 2016, Heiko Schocher wrote:
... snip ...
it appears that, no matter what, the environment *is* updated every single time because of this line in the bootdelay_process() routine:
setenv_ulong("bootcount", bootcount);
why? it seems, from the above, that no matter what boot counter
Yes, the bootcount variable gets set here ... but the environment gets not written...
mechanism you use, the environment will be updated because of that line.
The Environment gets written when saveenv() gets called ... or?
... snip ...
yes, i already apologized to wolfgang for being so clueless, i didn't see the difference between "setenv" and "saveenv". anyway, i'm still writing a wiki page on this that i can use for future embedded linux classes, so ... onward.
no problem, I saw your response to wolfgang to late ...
bye, Heiko
participants (5)
-
Heiko Schocher
-
Heiko Schocher
-
Robert P. J. Day
-
Tom Rini
-
Wolfgang Denk