[U-Boot] [RFC] Safe Linux Updater

Hi,
One month ago, I sent a first request for comments about an open source automatic updater for embedded systems using U-boot: the goal of this project was to split a flash memory or hard disk drive in multiple partition (2 or more) and install a new kernel and/or root filesystem on an empty or outdated partition.
After reboot, we count the boot attempts on this new version of the system and if it fails to boot, we switch back to the last working partition. This guarantees we will eventually boot on a correct partition and that there is no chance to have an unsupervised equipment hang at the u-boot prompt.
The u-boot environment would contain some variables to handle name, state and boot attempt count of each partition of the system. I would use CONFIG_ENV_OFFSET_REDUND to make writing on environment powerfail-safe. The environment contains 3 variables by partitions: - part_X_flag: handles state of partition X. It can be NONE for empty partition, OK for working system, LOCK for locking a partition to install a new system on it, UPDATED for new system version (we count boot attempts for this partition) and BAD for bad system which doesn't work. - part_X_count: handles boot attempts on partition X. - part_X_cmd: U-boot command used to boot on the partition X (e.g. partition address in flash). Environment contains also another variable: "boot_seq" which handles a list of bootable partition sorted by version.
As suggested in previous emails, I have written two scripts to implement that: - An u-boot script to interpret all environment variables and boot on correct partition and count boot attempts when system boots on a new version. - A Linux script to change environment variables when an update is installed.
The U-boot script reads "boot_seq" and boots on first partition of the list. If this partition is flagged OK, system boot normally. If the partition is flagged UPDATED, system try to boot on it and count boot attempts until attempts limit is reached. Then, the script will boot on next partition into boot list, always in counting boot attempts. This operation continue while partitions are available in the list. Partition flagged with NONE, LOCK or BAD are skipped.
The Linux script has all commands necessary to automatically find and lock the best partition to install an update, to mark the locked partition as UPDATED and commands to mark a new version as good with OK or bad with BAD. This script uses fw_printenv and fw_setenv from U-boot tools.
I would like your comments/suggestions on this system to improve it and to make sure this goes well into the general philosophy of U-Boot. Moreover, if it is possible, to upstream these scripts.
Best regards, Alexandre Dilly

Dear Alexandre
In message 130325823.452426.1371459938406.JavaMail.root@openwide.fr you wrote:
One month ago, I sent a first request for comments about an open source automatic updater for embedded systems using U-boot: the goal of this project was to split a flash memory or hard disk drive in multiple partition (2 or more) and install a new kern el and/or root filesystem on an empty or outdated partition.
Could you please restrict yourline length to some 70 characters or so? Thanks!!
The u-boot environment would contain some variables to handle name, state and boot attempt count of each partition of the system. I would use CONFIG_ENV_OFFSET_REDUND to make writing on environment powerfail-safe. The environment contains 3 variables by partitions:
- part_X_flag: handles state of partition X. It can be NONE for empty partition, OK for working system, LOCK for locking a partition to install a new system on it, UPDATED for new system version (we count boot attempts for this partition) and BAD for ba
d system which doesn't work.
- part_X_count: handles boot attempts on partition X.
- part_X_cmd: U-boot command used to boot on the partition X (e.g. partition address in flash).
Environment contains also another variable: "boot_seq" which handles a list of bootable partition sorted by version.
This sounds like a bad idea, for two reasons:
1) U-Boot already supports the boot count feature (but of course this hardware-specific as you need to find persistent storage for the counter). However, this does not consider which exact boot command has been executed (in your case: what the used boot partition was), it just counts the number of resets after the last power-on. Actually this is all you should need.
Please note that this is a feature standardized for example in the Open Source Development Labs Carrier Grade Linux Requirements Definition, which says something like: "CGL shall provide support for detecting a repeating reboot cycle due to recurring failures and will go to an offline state if this occurs."
2) Defining the boot counter as part of the envrionment requires automatic writes to the environment for each reset / reboot of the board. This is considered a bad idea, as it causes excessive flash wear. Normally you want to avoid all erase / write operations to the boot loader and it's private data structures in the process of a normal reboot / reset.
Best regards,
Wolfgang Denk

Dear Wolfgang,
I havn't seen the scripts of Alexandre but it sounds something like what we have already implemented.
Wolfgang Denk wrote:
Please note that this is a feature standardized for example in the Open Source Development Labs Carrier Grade Linux Requirements Definition, which says something like: "CGL shall provide support for detecting a repeating reboot cycle due to recurring failures and will go to an offline state if this occurs."
As I read Alexandre, the aim is to revert to a previous functional image, not to go to an offline state.
Normally you want to avoid all erase / write operations to the boot loader and it's private data structures in the process of a normal reboot / reset.
But a failing boot is not a normal boot. This should only occur when an update fails. After a maximum number of failing boots, the old functional image is used and there is no need to update the counter any more.
Best Regards, Mats

Hi Mats,
On 17/06/2013 15:25, Mats Kärrman wrote:
Dear Wolfgang,
I havn't seen the scripts of Alexandre but it sounds something like what we have already implemented.
Wolfgang Denk wrote:
Please note that this is a feature standardized for example in the Open Source Development Labs Carrier Grade Linux Requirements Definition, which says something like: "CGL shall provide support for detecting a repeating reboot cycle due to recurring failures and will go to an offline state if this occurs."
As I read Alexandre, the aim is to revert to a previous functional image, not to go to an offline state.
This is already done in u-boot checking the value of the boot counter (in not persistency storage) and calling a script that switch back to the previous copy, if any.
I find that the proposal does not scale well. Having partitions on a disk / SDCARd is a case, but we have several different way to boot. Think about kernel / rootfs into UBI or UBIFS, or saved as raw data in other kind of storages (NOR, SPI,..). Because we are talking about the feature "updating", this should be abstracted from the specific case to be generalized in U-Boot.
Normally you want to avoid all erase / write operations to the boot loader and it's private data structures in the process of a normal reboot / reset.
But a failing boot is not a normal boot. This should only occur when an update fails.
There are runtime conditions that can cause the boot to fail, due for example to power-supply. Or a degrading of the resources (flash gets wrong), and so on.
Even if a failure due to a wrong update is a common case to have a failing boot, this is not the only use case.
Best regards, Stefano Babic

Dear Mats,
In message ED3E0BCACD909541BA94A34C4A164D4C4FC9145C@post.tritech.se you wrote:
I havn't seen the scripts of Alexandre but it sounds something like what we have already implemented.
I haven'ty seen the scripts either, but this is something that has been implemented many times before, with many variations depending on specific project needs. But as I mentioned before, most users do not push such scripts into mainline...
Please note that this is a feature standardized for example in the Open Source Development Labs Carrier Grade Linux Requirements Definition, which says something like: "CGL shall provide support for detecting a repeating reboot cycle due to recurring failures and will go to an offline state if this occurs."
As I read Alexandre, the aim is to revert to a previous functional image,
Yes, of course. And the boot counter is the mechanism that will decide when to do that.
not to go to an offline state.
Indeed. U-Boots boot counter will select an alternative boot command in this case - you can use this to hard hang or power off the board and so implement strictly CGL conformant behaviour, or you can do somthing else like fall back to the previous version.
Normally you want to avoid all erase / write operations to the boot loader and it's private data structures in the process of a normal reboot / reset.
But a failing boot is not a normal boot. This should only occur when an update fails. After a maximum number of failing boots, the old functional
Alexandre wrote about a boot counter; he did not mention that he would update this only on failed boot attempts.
Also, in my experience one should be especially careful when something fails, and in such a situation I would all the more restrain from messing with the environment if it can be avoided (and here it's trivial to avoid).
image is used and there is no need to update the counter any more.
Did you have a look at the current implementation of the boot counter for the systems where it is supported? Yes, we even do have a system where the boot counter is stored in an environment variable (due to hardware restrictions and the expectation that the system will normally not need to be rebooted at all), but normally is can be easily avoided to meddle with the environment for this functionality.
Best regards,
Wolfgang Denk

Dear Wolfgang, Mats and Stefano,
Thanks for your answers,
In message ED3E0BCACD909541BA94A34C4A164D4C4FC9145C@post.tritech.se you wrote:
I haven't seen the scripts of Alexandre but it sounds something like what we have already implemented.
I haven't seen the scripts either, but this is something that has been implemented many times before, with many variations depending on specific project needs. But as I mentioned before, most users do not push such scripts into mainline...
Yes, this kind of system has been implemented many times before but, we can't found any examples in u-boot source, so the idea was to give a generic example to gain time in updater development...
Please note that this is a feature standardized for example in the Open Source Development Labs Carrier Grade Linux Requirements Definition, which says something like: "CGL shall provide support for detecting a repeating reboot cycle due to recurring failures and will go to an offline state if this occurs."
As I read Alexandre, the aim is to revert to a previous functional image,
Yes, of course. And the boot counter is the mechanism that will decide when to do that.
not to go to an offline state.
Indeed. U-Boots boot counter will select an alternative boot command in this case - you can use this to hard hang or power off the board and so implement strictly CGL conformant behaviour, or you can do something else like fall back to the previous version.
U-boots boot counter is not persistent which implies we can't handle some error cases like power fail. That's why I suggested to use environment to store boot counter with redundant option to be power safe.
Normally you want to avoid all erase / write operations to the boot loader and it's private data structures in the process of a normal reboot / reset.
But a failing boot is not a normal boot. This should only occur when an update fails. After a maximum number of failing boots, the old functional
Alexandre wrote about a boot counter; he did not mention that he would update this only on failed boot attempts.
Also, in my experience one should be especially careful when something fails, and in such a situation I would all the more restrain from messing with the environment if it can be avoided (and here it's trivial to avoid).
image is used and there is no need to update the counter any more.
Did you have a look at the current implementation of the boot counter for the systems where it is supported? Yes, we even do have a system where the boot counter is stored in an environment variable (due to hardware restrictions and the expectation that the system will normally not need to be rebooted at all), but normally is can be easily avoided to meddle with the environment for this functionality.
In fact, what Mats explain was my idea: the system updates boot counter only when it detects a partition marked as "updated". When Linux has booted, it does some specific verifications and then mark the partition as "good" or as "bad". So, boot counter is not updated any more until a new update is installed on system. So, like said Mats, count update only occurs when an update fails and then it doesn't cause excessive memory wear, because we have a limit like 3 for boot attempts.
I find that the proposal does not scale well. Having partitions on a disk / SDCARd is a case, but we have several different way to boot. Think about kernel / rootfs into UBI or UBIFS, or saved as raw data in other kind of storages (NOR, SPI,..). Because we are talking about the feature "updating", this should be abstracted from the specific case to be generalized in U-Boot.
About partition, since the script uses an environment variable to store boot command for each partition, it can be a disk partition or a UBI partition or a raw memory area containing kernel or rootfs which will be loaded with boot command.
To illustrate all ideas, I join to this mail the both scripts. These scripts are definitively not final versions!
U-boot script: diff --git a/uboot_script b/uboot_script new file mode 100644 index 0000000..b189643 --- /dev/null +++ b/uboot_script @@ -0,0 +1,64 @@ +setenv tmp_update +if test "z$force_rescue" != "z"; then + echo "Boot on rescue partition..." + run part_r_cmd + echo "System cannot boot!" + exit 1 +fi +if test "z$max_attempts" = "z"; then + echo "Set maximum boot attempts to default (=3)" + setenv max_attempts 3 +fi +for i in $boot_seq; +do + setenv tmp setenv tmp $part_${i}_flag + run tmp + if test "z$tmp" != "z"; then + if test $tmp = "OK" -o $tmp = "UPDATE"; then + if test $tmp = "UPDATE"; then + setenv tmp_update 1 + echo "Partition $i is an update" + fi + if test "z$tmp_update" != "z"; then + setenv tmp setenv tmp $part_${i}_count + run tmp + if test "z$tmp" != "z"; then + echo "Try to boot on partition $i..." + else + echo "Reset counter and try to boot on partition $i..." + setenv "part_${i}_count" 0 + setenv tmp 0 + fi + while test $tmp -lt $max_attempts; + do + setexpr tmp $tmp + 1 + echo " -> Attempt $tmp on partition $i" + setenv "part_${i}_count" $tmp + setenv tmp + setenv tmp_update + saveenv + run part_${i}_cmd + echo " -> Boot failed!" + done + if test $tmp -eq $max_attempts; then + setenv "part_${i}_count" 254 + setenv tmp + setenv tmp_update + saveenv + fi + echo "Partition $i failed to boot!" + else + echo "Boot on partition $i..." + run part_${i}_cmd + echo " -> Boot failed on partition $i!" + fi + fi + else + echo "No flag for partition $i" + fi +done +echo "Boot on partitions failed! Boot on rescue partition..." +run part_r_cmd +echo "System cannot boot!" +exit 1 +
Linux script: diff --git a/smart_boot.sh b/smart_boot.sh new file mode 100755 index 0000000..a0561ef --- /dev/null +++ b/smart_boot.sh @@ -0,0 +1,523 @@ +#!/bin/sh + +# Script Configuration +SETENV_CMD="fw_setenv" # Path to fw_setenv binary +PRINTENV_CMD="fw_printenv" # Path to fw_printenv binary +TMP_FILE="/tmp/smart_boot.tmp" # Temporary file used for env writting +# Entry name in env +PART_FLAG_NAME="part_${i}_flag" # Partition 'i' flag +PART_CMD_NAME="part_${i}_cmd" # Boot command for partition 'i' +PART_COUNT_NAME="part_${i}_count" # Boot ttempts number for partition 'i' +PART_TEXT_NAME="part_${i}_text" # Partition 'i' description text +PART_NB_NAME="part_nb" # Total partition number +BOOT_SEQ_NAME="boot_seq" # Boot sequence (ordrer = "first second ... last") +FORCE_RESCUE_NAME="force_rescue" # Boot on rescue flag +MAX_ATTEMPT_NAME="max_attempts" # Number of attempts before switching to previous partition +BOOT_PART_NAME="boot_part" # Name of param appended to linux boot command line. This param contain booted partition index + +# Print usage +print_usage () { + echo "Usage: ./smart_boot TASK PARMAS" 1>&2 + echo "List of tasks (=TASK) available:" 1>&2 + echo " print: Print boot configuration." 1>&2 + echo " allocate: Allocate an empty/bad partition or " 1>&2 + echo " oldest partition to install an update." 1>&2 + echo " update: Mark the allocated parititon with UPDATED flag. " 1>&2 + echo " Needs to call 'allocate' before." 1>&2 + echo " validate: Validate a partition with UPDATED flag." 1>&2 + echo " invalidate: Invalidate a partition with UPDATED flag." 1>&2 + echo " partition: Return theorical booted partition." 1>&2 + echo " real-partition: Return real booted partition (from linux cmdline)." 1>&2 + echo " clean: Corrects boot configuration." 1>&2 + echo " force-rescue: Force system to boot on rescue partition." 1>&2 + echo " unforce-rescue: Unforce system to boot on rescue partition." 1>&2 + echo " status: Return a status number of boot case." 1>&2 + echo "Manual informations:" 1>&2 + echo " get-flag PART: Get flag for partition PART." 1>&2 + echo " get-text PART: Get text description for partition PART." 1>&2 + echo " get-count PART: Get boot attempt count for partition PART." 1>&2 + echo " get-boot-seq: Get boot sequence." 1>&2 + echo "Manual modification:" 1>&2 + echo " set-flag PART FLAG: Set flag with FLAG for partition PART." 1>&2 + echo " set-text PART TXT: Set text description with TXT for partition PART." 1>&2 + echo " set-count PART CNT: Set boot attempt count to CNT for partition PART." 1>&2 + echo " set-boot-seq SEQ: Set boot sequence with SEQ." 1>&2 + return +} + +# Verify number of arguments and print usage if lower than 1 +if [ $# -lt 1 ]; then + print_usage + exit 1 +fi + +# Get arguments +task=$1 + +# Get number of partition +part_nb=$($PRINTENV_CMD -n $PART_NB_NAME 2>/dev/null) +if [ $part_nb -lt 2 ]; then + echo "Bad number of partition: you need at least 2 partitions!" 1>&2 + exit 1 +fi + +# Count entries in boot_seq +boot_seq_count=0 +boot_seq="$($PRINTENV_CMD -n $BOOT_SEQ_NAME 2>/dev/null)" +if [ $? -eq 0 ]; then + boot_seq_count=$(echo $boot_seq | wc -w) +fi + +# Get maximum boot attempts +nb_attempt="$($PRINTENV_CMD -n $MAX_ATTEMPT_NAME 2>/dev/null)" +if [ $? -ne 0 ]; then + nb_attempt=3 + echo "No maximum boot attempts in environment: set with default value (3)!" 1>&2 +fi + +# Switch case for each tasks (=TASK) +case $task in + # Print boot configuration + print ) + # Print flags for each partition + for i in $(seq 1 $part_nb) + do + flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null) + count=$($PRINTENV_CMD -n $(eval echo $PART_COUNT_NAME) 2>/dev/null) + echo "Partition $i: $flag (boot attempts: $count)" + txt=$($PRINTENV_CMD -n $(eval echo $PART_TEXT_NAME) 2>/dev/null) + if [ $? -eq 0 ]; then + echo " ($txt)" + fi + done + # Print boot sequence + echo "Boot sequence: $($PRINTENV_CMD -n $(eval echo $BOOT_SEQ_NAME) 2>/dev/null)" + ;; + # Allocate an empty/bad partition or oldest partition to install an update. + allocate ) + i=0 + # If all partitions are used, pick the oldest + if [ $boot_seq_count -eq $part_nb ]; then + # Verify flag of first partition + i=${boot_seq%% *} + flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null) + if [ "$flag" = "OK" ]; then + # Get last partition in boot seq + i=$(echo $boot_seq | awk '{print $'$part_nb'}') + fi + # Else search for an allocated partition or a partition unavailable in boot sequence + else + part=0 + for i in $(seq 1 $part_nb) + do + #If partition isn't in boot sequenc + if [ "${boot_seq/$i/}" = "$boot_seq" ]; then + # Get partition flag + flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null) + if [ $flag = "LOCK" ]; then + echo "Partition $i is already allocated!" 1>&2 + echo $i + exit 0 + else + part=$i + break + fi + fi + done + i=$part + fi + + # Change flag of partition with 'LOCK' and remove partition from boot sequence + if [ $i -gt 0 ]; then + echo "$(eval echo $PART_FLAG_NAME) LOCK" > $TMP_FILE + echo "$BOOT_SEQ_NAME ${boot_seq/$i/}" >> $TMP_FILE + $SETENV_CMD -s $TMP_FILE + if [ $? -ne 0 ]; then + echo "Failed to write in Environment!" + exit 1 + fi + echo "Partition $i has been allocated." 1>&2 + echo $i + else + echo "Can't allocate a parititon!" 1>&2 + exit 1 + fi + ;; + # Mark the allocated parititon with UPDATED flag. Needs to call 'allocate' before. + update ) + # Search allocated partition + part=0 + for i in $(seq 1 $part_nb) + do + #If partition isn't in boot sequenc + if [ "${boot_seq/$i/}" = "$boot_seq" ]; then + # Get partition flag + flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null) + if [ "z$flag" != "z" -a $flag = "LOCK" ]; then + part=$i + break + fi + fi + done + if [ $part -gt 0 ]; then + i=$part + # Set partition flag to UPDATE and add partition to boot sequence + echo "$(eval echo $PART_FLAG_NAME) UPDATE" > $TMP_FILE + echo "$BOOT_SEQ_NAME $i $boot_seq" >> $TMP_FILE + if [ $# -gt 1 ]; then + echo "$(eval echo $PART_TEXT_NAME) $2" >> $TMP_FILE + else + echo "$(eval echo $PART_TEXT_NAME)" >> $TMP_FILE + fi + for i in $(seq 1 $part_nb) + do + echo "$(eval echo $PART_COUNT_NAME) 0" >> $TMP_FILE + done + $SETENV_CMD -s $TMP_FILE + if [ $? -ne 0 ]; then + echo "Failed to write in Environment!" + exit 1 + fi + echo "Partition $part has been marked as an update." 1>&2 + echo $part + else + echo "Please allocate a partition before calling 'update'!" 1>&2 + exit 1 + fi + ;; + # Validate a partition with UPDATED flag. + validate ) + # Get flag of first partition of boot sequence + if [ $boot_seq_count -lt 1 ]; then + echo "No partitions in boot sequence!" 1>&2 + exit 1 + fi + i=${boot_seq%% *} + flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null) + if [ $? -eq 0 -a $flag = "UPDATE" ]; then + # Verify number of attempts + count=$($PRINTENV_CMD -n $(eval echo $PART_COUNT_NAME) 2>/dev/null) + if [ $? -ne 0 -o $count -gt $nb_attempt ]; then + echo "Partition $i has failed to boot: you can't mark it as good! Please use invalidate or clean." 1>&2 + exit 1 + fi + # Set flag to OK + $SETENV_CMD "$(eval echo $PART_FLAG_NAME)" "OK" + if [ $? -ne 0 ]; then + echo "Failed to write in Environment!" + exit 1 + fi + echo "Partition $i has been marked as ok. Now system will boot on this partition." 1>&2 + echo $i + else + echo "The first partition is not an update!" 1>&2 + exit 1 + fi + ;; + # Invalidate a partition with UPDATED flag. + invalidate ) + # Get flag of first partition of boot sequence + if [ $boot_seq_count -lt 1 ]; then + echo "No partitions in boot sequence!" 1>&2 + exit 1 + fi + i=${boot_seq%% *} + flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null) + if [ $? -eq 0 -a $flag = "UPDATE" ]; then + # Set flag to BAD and remove partition from boot sequence + echo "$(eval echo $PART_FLAG_NAME) BAD" > $TMP_FILE + echo "$BOOT_SEQ_NAME ${boot_seq#* }" >> $TMP_FILE + $SETENV_CMD -s $TMP_FILE + if [ $? -ne 0 ]; then + echo "Failed to write in Environment!" + exit 1 + fi + echo "Partition $i has been marked as bad." 1>&2 + echo $i + else + echo "The first partition is not an update!" 1>&2 + exit 1 + fi + ;; + # Return theorical booted partition. + partition ) + update=0 + force=$($PRINTENV_CMD -n $(eval echo $FORCE_RESCUE_NAME) 2>/dev/null) + if [ $? -eq 0 ]; then + echo "Booted on rescue partition (forced)." 1>&2 + echo 0 + exit 0 + fi + for i in $boot_seq + do + # Get flag and boot attempt count for partition + count=$($PRINTENV_CMD -n $(eval echo $PART_COUNT_NAME) 2>/dev/null) + if [ $count -le $nb_attempt ]; then + echo "Booted on partition $i" 1>&2 + echo $i + exit 0 + fi + done + echo "Booted on rescue partition" 1>&2 + echo 0 + ;; + # Return real booted partition. + real-partition ) + boot_part="$(sed -n 's|.*'$BOOT_PART_NAME'=([^ ]+).*|\1|p' < /proc/cmdline)" + if [ "z$boot_part" != "z" ]; then + if [ $boot_part = "r" ]; then + echo "Booted on rescue partition" 1>&2 + echo 0 + else + echo "Booted on partition $boot_part" 1>&2 + echo $boot_part + fi + else + echo "No $BOOT_PART_NAME in kernel command line!" 1>&2 + exit 1 + fi + ;; + # Fix on boot configuration. + clean ) + update=0 + echo -n "" > $TMP_FILE + for i in $(seq 1 $part_nb) + do + #If partition is in boot sequenc + if [ "${boot_seq/$i/}" != "$boot_seq" ]; then + # Verify flag + flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null) + if [ $? -ne 0 ]; then + # Remove partition from boot sequence and set flag to NONE + echo "$(eval echo $PART_FLAG_NAME) NONE" >> $TMP_FILE + echo "$(eval echo $PART_TEXT_NAME)" >> $TMP_FILE + boot_seq=${boot_seq/$i/} + else + # Verify if it is an update or a good partition + if [ "$flag" != "OK" -a "$flag" != "UPDATE" ]; then + # Set partition as NONE and remove it from boot sequence + echo "$(eval echo $PART_FLAG_NAME) NONE" >> $TMP_FILE + echo "$(eval echo $PART_TEXT_NAME)" >> $TMP_FILE + boot_seq=${boot_seq/$i/} + else + # Verify if it is an update parition + if [ $flag = "UPDATE" ]; then + update=1 + fi + # If we are in an update case, verify boot attempts + if [ $update -eq 1 ]; then + count=$($PRINTENV_CMD -n $(eval echo $PART_COUNT_NAME) 2>/dev/null) + if [ $? -ne 0 -o $count -gt $nb_attempt ]; then + # Set partition as BAD and remove it from boot sequence + echo "$(eval echo $PART_FLAG_NAME) BAD" >> $TMP_FILE + boot_seq=${boot_seq/$i/} + else + #Set partition as OK + if [ $count -gt 0 ]; then + echo "$(eval echo $PART_FLAG_NAME) OK" >> $TMP_FILE + elif [ "$flag" = "UPDATE" ]; then + echo "$(eval echo $PART_FLAG_NAME) NONE" >> $TMP_FILE + echo "$(eval echo $PART_TEXT_NAME)" >> $TMP_FILE + boot_seq=${boot_seq/$i/} + fi + fi + fi + fi + # Reset boot attempt count + echo "$(eval echo $PART_COUNT_NAME) 0" >> $TMP_FILE + fi + else + # Set flag as NONE and boot attempt to 0 + echo "$(eval echo $PART_FLAG_NAME) NONE" >> $TMP_FILE + echo "$(eval echo $PART_TEXT_NAME)" >> $TMP_FILE + echo "$(eval echo $PART_COUNT_NAME) 0" >> $TMP_FILE + fi + done + echo "$BOOT_SEQ_NAME $boot_seq" >> $TMP_FILE + $SETENV_CMD -s $TMP_FILE + if [ $? -ne 0 ]; then + echo "Failed to write in Environment!" 1>&2 + exit 1 + fi + ;; + # Force to boot on rescue partition. + force-rescue ) + $SETENV_CMD $FORCE_RESCUE_NAME "1" + if [ $? -ne 0 ]; then + echo "Failed to write in Environment!" 1>&2 + exit 1 + fi + ;; + # Unforce boot on rescue partition. + unforce-rescue ) + $SETENV_CMD $FORCE_RESCUE_NAME "" + if [ $? -ne 0 ]; then + echo "Failed to write in Environment!" 1>&2 + exit 1 + fi + ;; + # Return a number for boot status + status ) + echo "Code meanings:" 1>&2 + echo " -> 0: Boot on normal partition." 1>&2 + echo " -> 1: Boot on updated partition (needs to be validated)." 1>&2 + echo " -> 2: Boot on normal partition after boot failure on updated partition." 1>&2 + echo " -> 3: Boot on rescue partition after multiple boot failure." 1>&2 + echo " -> 4: Boot on rescue partition." 1>&2 + echo " -> 5: Forced boot on rescue partition." 1>&2 + if $PRINTENV_CMD "$FORCE_RESCUE_NAME" 2>/dev/null; then + echo "Forced boot on rescue partition." 1>&2 + echo "5" + else + if [ $boot_seq_count -eq 0 ]; then + echo "Boot on rescue partition." 1>&2 + echo "4" + else + update=0 + for i in $boot_seq + do + flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null) + if [ $? -eq 0 -a $flag = "UPDATE" ]; then + update=1 + fi + if [ $update -eq 1 ]; then + count=$($PRINTENV_CMD -n $(eval echo $PART_COUNT_NAME) 2>/dev/null) + if [ $? -eq 0 -a $count -le $nb_attempt ]; then + if [ $flag = "OK" ]; then + echo "Boot on normal partition after boot failure on updated partition." 1>&2 + echo "2" + exit 0 + else + echo "Boot on updated partition (needs to be validated)." 1>&2 + echo "1" + exit 0 + fi + fi + else + echo "Boot on normal partition." 1>&2 + echo "0" + exit 0 + fi + done + echo "Boot on rescue partition after multiple boot failure." 1>&2 + echo "3" + fi + fi + ;; + # Get partition flag + get-flag ) + if [ $# -ge 2 -a $2 -le $part_nb -a $2 -ne 0 ]; then + i=$2 + flag=$($PRINTENV_CMD -n $(eval echo $PART_FLAG_NAME) 2>/dev/null) + if [ $? -eq 0 ]; then + echo "$flag" + else + echo "Flag partition not defined!" 1>&2 + exit 1 + fi + else + echo "Please specify a correct partition!" 1>&2 + exit 1 + fi + ;; + # Get partition text description + get-text ) + if [ $# -ge 2 -a $2 -le $part_nb -a $2 -ne 0 ]; then + i=$2 + text=$($PRINTENV_CMD -n $(eval echo $PART_TEXT_NAME) 2>/dev/null) + if [ $? -eq 0 ]; then + echo "$text" + else + echo "Flag partition not defined!" 1>&2 + exit 1 + fi + else + echo "Please specify a correct partition!" 1>&2 + exit 1 + fi + ;; + # Get boot attempt count of a partition + get-count ) + if [ $# -ge 2 -a $2 -le $part_nb -a $2 -ne 0 ]; then + i=$2 + count=$($PRINTENV_CMD -n $(eval echo $PART_COUNT_NAME) 2>/dev/null) + if [ $? -eq 0 ]; then + echo "$count" + else + echo "Flag partition not defined!" 1>&2 + exit 1 + fi + else + echo "Please specify a correct partition!" 1>&2 + exit 1 + fi + ;; + # Get boot sequence + get-boot-seq ) + echo "$($PRINTENV_CMD -n $BOOT_SEQ_NAME 2>/dev/null)" + ;; + # Set a partition flag + set-flag ) + if [ $# -ge 3 -a $2 -le $part_nb -a $2 -ne 0 ]; then + i=$2 + $SETENV_CMD "$(eval echo $PART_FLAG_NAME)" "$3" + echo "Flag of partition $i updated!" 1>&2 + else + echo "Please specify a partition and a flag to set!" 1>&2 + exit 1 + fi + ;; + # Set a partition text description + set-text ) + if [ $# -ge 3 -a $2 -le $part_nb -a $2 -ne 0 ]; then + i=$2 + $SETENV_CMD "$(eval echo $PART_TEXT_NAME)" "$3" + echo "Text description of partition $i updated!" 1>&2 + else + echo "Please specify a partition and a text to set!" 1>&2 + exit 1 + fi + ;; + # Set boot attempt count of a partition + set-count ) + if [ $# -ge 3 -a $2 -le $part_nb -a $2 -ne 0 ]; then + i=$2 + $SETENV_CMD "$(eval echo $PART_COUNT_NAME)" "$3" + echo "Boot attempt count of partition $i updated!" 1>&2 + else + echo "Please specify a partition and a count to set!" 1>&2 + exit 1 + fi + ;; + # Set boot sequence + set-boot-seq ) + if [ $# -ge 2 ]; then + # Verify sequence + for i in $2 + do + if [ $i -le 0 -o $i -gt $part_nb ]; then + echo "Bad boot sequence!" 1>&2 + exit 1 + fi + done + $SETENV_CMD "$BOOT_SEQ_NAME" "$2" + echo "Boot sequence changed." 1>&2 + else + echo "Please specify a boot sequence!" 1>&2 + exit 1 + fi + ;; + # Print usage message. + help ) + print_usage + ;; + # Unknown task + * ) + print_usage + exit 1 + ;; +esac + +exit 0 +
Best regards,
Alexandre Dilly
participants (4)
-
Alexandre Dilly
-
Mats Kärrman
-
Stefano Babic
-
Wolfgang Denk