[PATCH 3/4] bg_utils: Remove FAT16 restriction for environment

59 views
Skip to first unread message

Reichel Andreas

unread,
Jul 11, 2017, 9:41:33 AM7/11/17
to efibootg...@googlegroups.com, Reichel Andreas
* Allow FAT12, FAT16 and FAT32 for environment data.
* Minor code style fixes

Signed-off-by: Andreas Reichel <andreas.r...@siemens.com>
---
tools/bg_utils.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/tools/bg_utils.c b/tools/bg_utils.c
index 36ee279..257d8be 100644
--- a/tools/bg_utils.c
+++ b/tools/bg_utils.c
@@ -151,7 +151,7 @@ static FILE *open_config_file(CONFIG_PART *cfgpart, char *mode)
strncat(configfilepath, "/", 1);
strncat(configfilepath, FAT_ENV_FILENAME, strlen(FAT_ENV_FILENAME));
VERBOSE(stdout, "Probing config file at %s.\n", configfilepath);
- FILE* config = fopen(configfilepath, mode);
+ FILE *config = fopen(configfilepath, mode);
free(configfilepath);
return config;
}
@@ -224,7 +224,9 @@ bool probe_config_partitions(CONFIG_PART *cfgpart)
PedPartition *part = pd->part_list;
while (part) {
if (!part->fs_type || !part->fs_type->name ||
- strcmp(part->fs_type->name, "fat16") != 0) {
+ (strcmp(part->fs_type->name, "fat12") != 0 &&
+ strcmp(part->fs_type->name, "fat16") != 0 &&
+ strcmp(part->fs_type->name, "fat32") != 0)) {
part = ped_disk_next_partition(pd, part);
continue;
}
@@ -450,11 +452,14 @@ bool bgenv_write(BGENV *env)
case BGENVTYPE_FAT:
part = (CONFIG_PART *)env->desc;
if (!part) {
- VERBOSE(stderr, "Invalid config partition to store environment.\n");
+ VERBOSE(
+ stderr,
+ "Invalid config partition to store environment.\n");
return false;
}
if (!write_env(part, env->data)) {
- VERBOSE(stderr, "Could not write to %s\n", part->devpath);
+ VERBOSE(stderr, "Could not write to %s\n",
+ part->devpath);
return false;
}
return true;
--
2.11.0

Claudius Heine

unread,
Jul 11, 2017, 2:44:03 PM7/11/17
to Reichel Andreas, efibootg...@googlegroups.com
Are there any fat* left?
You could just use strncmp(part->fs_type->name, "fat", sizeof(char)*3);

> part = ped_disk_next_partition(pd,
> part);
> continue;
> }
> @@ -450,11 +452,14 @@ bool bgenv_write(BGENV *env)
> case BGENVTYPE_FAT:
> part = (CONFIG_PART *)env->desc;
> if (!part) {
> - VERBOSE(stderr, "Invalid config partition to
> store environment.\n");
> + VERBOSE(
> + stderr,
> + "Invalid config partition to store
> environment.\n");
> return false;
> }
> if (!write_env(part, env->data)) {
> - VERBOSE(stderr, "Could not write to %s\n",
> part->devpath);
> + VERBOSE(stderr, "Could not write to %s\n",
> + part->devpath);
> return false;
> }
> return true;
> --
> 2.11.0
>
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-54 Fax: (+49)-8142-66989-80 Email: c...@denx.de

PGP key: 6FF2 E59F 00C6 BC28 31D8 64C1 1173 CB19 9808 B153
Keyserver: hkp://pool.sks-keyservers.net
signature.asc

Andreas Reichel

unread,
Jul 12, 2017, 6:12:50 AM7/12/17
to Claudius Heine, efibootg...@googlegroups.com
This is an enumeration over all allowed file system types. Just
comparing the first three letters is not a robust and future oriented
way. Assume somebody will introduce FATXY or alike... then this can easily
break.
>> part = ped_disk_next_partition(pd,
>> part);
>> continue;
>> }
>> @@ -450,11 +452,14 @@ bool bgenv_write(BGENV *env)
>> case BGENVTYPE_FAT:
>> part = (CONFIG_PART *)env->desc;
>> if (!part) {
>> - VERBOSE(stderr, "Invalid config partition to
>> store environment.\n");
>> + VERBOSE(
>> + stderr,
>> + "Invalid config partition to store
>> environment.\n");
>> return false;
>> }
>> if (!write_env(part, env->data)) {
>> - VERBOSE(stderr, "Could not write to %s\n",
>> part->devpath);
>> + VERBOSE(stderr, "Could not write to %s\n",
>> + part->devpath);
>> return false;
>> }
>> return true;
>> --
>> 2.11.0
>>

--
Andreas Reichel, Dipl.-Phys. (Univ.), Software Consultant
+49-174-3180074

TNG Technology Consulting GmbH, Betastr. 13a, 85774 Unterfoehring
Geschaeftsfuehrer: Henrik Klagges, Christoph Stock, Dr. Robert Dahlke
Sitz: Unterfoehring * Amtsgericht Muenchen * HRB 135082

Claudius Heine

unread,
Jul 12, 2017, 7:05:31 AM7/12/17
to [ext] Andreas Reichel, Claudius Heine, efibootg...@googlegroups.com
Wouldn't you need to write code to set fs_type->name to the new and
shiny "FATXY" first?

Then maybe use fnmatch("fat[13][26]", part->fs_type->name, 0) and hope
that nobody creates a fat36 ;)

Ok leave it like that if you really want. I just don't like seeing this
kind of replicated code knowing the the strings are iterated over 3
times in a row while comparing them to very similar variations. But that
could just be premature optimization.

Reichel Andreas

unread,
Jul 12, 2017, 7:06:26 AM7/12/17
to efibootg...@googlegroups.com, Reichel Andreas
Add -v option also to bg_printenv to get library messages.

Signed-off-by: Andreas Reichel <andreas.r...@siemens.com>
---
tools/bg_setenv.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index 1a92748..06ce30a 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -32,7 +32,9 @@ static struct argp_option options_setenv[] = {
{"bootonce", 'b', 0, 0, "Simulate boot with update installed"},
{0}};

-static struct argp_option options_printenv[] = {{0}};
+static struct argp_option options_printenv[] = {
+ {"verbose", 'v', 0, 0, "Be verbose"},
+ {0}};

struct arguments {
bool output_to_file;
--
2.11.0

Reichel Andreas

unread,
Jul 12, 2017, 7:06:26 AM7/12/17
to efibootg...@googlegroups.com, Reichel Andreas
This series addresses the following:
* Verbosity with `bg_printenv`: Use the same -v flag as with `bg_setenv`.

* Fix detection for customized device nodes:
Currently, ebgpart.c detects devices by enumerating in
/sys/block. For each found directory, it assumes, the same name
exists in /dev as device node, which may fail if a user or udev
costumized the node names. The fix compares major and minor revision
to detect any existing node in /dev/ that matches.

* Remove FAT16 restriction of environment tools:
The restriction was formerly introduced as code for manual file
system handling was used. A restriction to FAT16 is not needed anymore
and UEFI supports all FAT formats anyhow.

* Update documentation for better user support.

Signed-off-by: Andreas Reichel <andreas.r...@siemens.com>

Reichel Andreas (4):
bg_printenv: Add -v option for verbosity
bg_utils: Fix detection for customized loop devs
bg_utils: Remove FAT16 restriction for environment
docs: Update documentation.

README.md | 283 ++++---------------------------------------
docs/API.md | 71 +++++++++++
docs/COMPILE.md | 48 ++++++++
docs/TODO.md | 29 +++++
docs/TOOLS.md | 86 +++++++++++++
docs/UPDATE.md | 99 +++++++++++++++
docs/USAGE.md | 165 +++++++++++++++++++++++++
swupdate-adapter/swupdate.md | 92 +++++++++++---
tools/bg_setenv.c | 4 +-
tools/bg_utils.c | 13 +-
tools/ebgpart.c | 105 +++++++++++++---
tools/ebgpart.h | 5 +-
12 files changed, 698 insertions(+), 302 deletions(-)
create mode 100644 docs/API.md
create mode 100644 docs/COMPILE.md
create mode 100644 docs/TODO.md
create mode 100644 docs/TOOLS.md
create mode 100644 docs/UPDATE.md
create mode 100644 docs/USAGE.md

--
2.11.0

Reichel Andreas

unread,
Jul 12, 2017, 7:06:27 AM7/12/17
to efibootg...@googlegroups.com, Reichel Andreas
Update and rework documentation for better user support.

Signed-off-by: Andreas Reichel <andreas.r...@siemens.com>
---
README.md | 283 ++++---------------------------------------
docs/API.md | 71 +++++++++++
docs/COMPILE.md | 48 ++++++++
docs/TODO.md | 29 +++++
docs/TOOLS.md | 86 +++++++++++++
docs/UPDATE.md | 99 +++++++++++++++
docs/USAGE.md | 165 +++++++++++++++++++++++++
swupdate-adapter/swupdate.md | 92 +++++++++++---
8 files changed, 592 insertions(+), 281 deletions(-)
create mode 100644 docs/API.md
create mode 100644 docs/COMPILE.md
create mode 100644 docs/TODO.md
create mode 100644 docs/TOOLS.md
create mode 100644 docs/UPDATE.md
create mode 100644 docs/USAGE.md

diff --git a/README.md b/README.md
index 95292e8..d29151f 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,17 @@ Provides the following functionality:
* Arm a hardware watchdog prior to loading an OS
* Provides a simple update mechanism with fail-save algorithm

+## Development ##
+
+Mailing list:
+[efibootg...@googlegroups.com](efibootg...@googlegroups.com)
+
+Archive:
+[https://www.mail-archive.com/efibootg...@googlegroups.com/](https://www.mail-archive.com/efibootg...@googlegroups.com)
+
+For sending patches, please refer to the mailing list and `CONTRIBUTING.md` in
+the source tree.
+
## Watchdog support ##

The following watchdog drivers are implemented:
@@ -13,273 +24,23 @@ The following watchdog drivers are implemented:
* Intel TCO
* Intel i6300esb

+Currently, it is not possible to disable the watchdog initialization. If no
+working watchdog is found, the boot process fails.
+
## Configuration ##

`efibootguard` reads its configuration from an environment storage. Currently,
the following environment backends are implemented:
* Dual FAT Partition storage

-## Update Mechanism ##
-
-Each environment configuration has a revision. The number of possible
-environments must be configured at compile-time. The larger the revision value,
-the newer the environment is. `efibootguard` always loads the latest environment
-data, meaning the one with the greatest revision value.
-
-The structure of the environment data is as follows:
-
-```
-struct _BG_ENVDATA {
- uint16_t kernelfile[ENV_STRING_LENGTH];
- uint16_t kernelparams[ENV_STRING_LENGTH];
- uint8_t testing;
- uint8_t boot_once;
- uint16_t watchdog_timeout_sec;
- uint32_t revision;
- uint32_t crc32;
-};
-```
-
-The fields have the following meaning:
-* `kernelfile`: Path to the kernel image, utf-16 encoding
-* `kernelparams`: Arguments to the kernel, utf-16 encoding
-* `testing`: A flag that specifies if the configuration is in test mode
-* `boot_once`: Set by `efibootguard` if it first boots a test configuration
-* `watchdog_timeout_sec`: Number of seconds, the watchdog times out after
-* `revision`: The revision number explained above
-* `crc32`: A crc32 checksum
-
-
-Assume, the system has been booted with a configuration that has a `revision` of
-`4`. The user can set a new configuration with a revision of `5`, with `testing`
-flag set. On the next reboot, `efibootguard` loads the configuration with the
-highest `revision` number. If it detects, that `testing` is set, it will enable
-the `boot_once` flag and boot the system with this configuration.
-
-### Example scenario 1 - Successful update ###
-
-Once booted, the user disables both `testing` and `boot_once` to confirm the
-udpate.
-
-### Example scenario 2 - System crash during first boot after update ###
-
-If the system crashes during boot, the watchdog will reset the system.
-Afterwards, `efibootguard` sees, that this configuration had already been tested
-before, because `boot_once` is already set. Thus, it will deactivate both
-`boot_once` and `testing` and load the 2nd latest configuration instead. It will
-signal to the user, that the update failed by setting the revision of the failed
-configuration to `0`.
-
-### Visual explanation of the update process ###
-
- +--------------++--------------+
- | || |
- | Rev: latest || Rev: oldest |
- | (working) || |
- | || |
- +--------------++--------------+
- | |
- +---+ | update
- | |
- v v
- +---------------+ +--------------+
- | | | |
- +----> | Rev: latest-1 | | Rev: latest |
- | | (working) | | testing: 1 |
- | | | | boot_once: 0 |
- | +---------------+ +--------------+
- | |
- | | reboot
- | v
- | +--------------+
- | | |
- | | Rev: latest |
- | | testing: 1 |
- | | boot_once: 1 |
- | +--------------+
- | | no
- | success? ------------------+
- | watchdog reboot | watchdog reboot |
- | yes, confirm | |
- | v v
- | +--------------+ +-------------------+
- | | | | |
- | | Rev: latest | | Rev: 0 |
- | | testing: 0 | | testing: 1 |
- | | boot_once: 0 | | boot_once: 1 |
- | +--------------+ +-------------------+
- | boots |
- +----- latest' = latest-1 +--------------------------------+
-
-
-## Environment Tools ##
-
-In the `tools` directory, there is a utility named `bg_setenv`/`bg_printenv`.
-With this, the user can display the configuration data or update as needed.
-
-**NOTE**: The environment tools only work, if the correct number of config
-partitions is detected. This also means that the stored configuration data has a
-valid checksum. If this is not the case, environments must be repaired first. To
-do so, follow the initial setup step explained below in the `Installation`
-section.
-
-To access configuration data on FAT partitions, the partition must either
-already be mounted, with access rights for the user using the tool, or the tool
-can mount the partition by itself, provided that it has the `CAP_SYS_ADMIN`
-capability. This is the case if the user is granted `root` privileges or the
-corresponding capability is set in the filesystem.
-
-*NOTE*: `CHAR16` environment variables are limited to 255 characters as stated
-in `include/envdata.h`.
-
-### Creating a new configuration ###
-
-In most cases, the user wants to create a new environment configuration, which
-can be done by
-
-```
-./bg_setenv -u --kernel="XXXX" --args="YYYY" --watchdog=25 --testing=1
-```
-
-The `-u` parameter tells `bg_setenv` to automatically overwrite the oldest
-configuration set and sets the revision value to the newest.
-
-If the user wants to specify the revision number and the configuration partition
-to overwrite manually, he can do so by
-
-```
-./bg_setenv -p 4 -r 13 [...]
-```
-
-which specifies to set the data set in partition number 4 to revision 13. Please
-keep in mind, that counting of configuration partitions starts with 0.
-
-Some debug output can be activated if compiling with
-```
-make DEBUG=1
-```
-
-#### Kernel Location ####
-
-To load the kernel from a different FAT partition than `efibootguard`, there are
-two possible mechanisms. One directly uses the label of the FAT partition,
-created with `dosfslabel`:
-
-```
-./bg_setenv -u --kernel="L:FATLABEL:kernelfile"
-```
-
-where `FATLABEL` is the label of the FAT partition. On some older UEFI
-implementations, the label is not supported properly and a user defined label
-can be created instead, which is a file named `EFILABEL` in the root directory
-of the corresponding FAT partition. This file contains an UTF-16le encoded
-partition name and can be used as follows:
-
-```
-./bg_setenv -u --kernel="C:USERLABEL:kernelfile"
-```
-
-### Interface API ###
-
-The library `libebgenv.a` provides an API to access the environment from a user
-program.
-
-The header file with the prototypes and a short description is `ebgenv.h`.
-
-Documentation and further examples can be found in
-`swupdate-adapter/swupdate.md`.
-
-The following example program opens the current environment and modifies the
-kernel file name:
-
-```c
-#include <stdbool.h>
-#include "ebgenv.h"
-
-int main(void)
-{
- ebg_env_open_current();
- ebg_env_set("kernelfile", "vmlinux-new");
- ebg_env_close();
- return 0;
-}
-```
-
-The following example program creates a new environment with the latest revision
-and sets it to the testing state:
-
-```c
-#include <stdbool.h>
-#include "ebgenv.h"
-
-int main(void)
-{
- ebg_env_create_new();
- ebg_env_set("kernelfile", "vmlinux-new");
- ebg_env_set("kernelparams", "root=/dev/bootdevice");
- ebg_env_set("watchdog_timeout_sec", "30");
- ebg_env_close();
- return 0;
-}
-```
-
-*Note*: If no watchdog timeout value is specified, a default of 30 seconds is
-set.
-
-## Required libraries and headers for compilation ##
-
-### Arch Linux ###
-
-```
-pacman -S gnu-efi-libs pciutils
-```
-
-### Debian 8 ###
-
-```
-apt-get install gnu-efi libparted-dev libpci-dev
-```
-
-## Installation ##
-
-### Environment setup ###
-
-Create the needed number of FAT16 partitions as defined by
-`CONFIG_PARTITION_COUNT` in `include/envdata.h`. Create a new `BGENV.DAT`
-configuration file with the `bg_setenv` tool and the `-f` option and copy the
-files to the FAT16 partitions.
-
-*NOTE*: Currently, FAT partitions must neither be `FAT12`, `FAT32` nor `FAT32e`.
-During detection of config partitions, all non-FAT16 partitions are ignored and
-do not count to the valid number of config partitions.
-
-### Bootloader installation ###
-
-Copy `efibootguard.efi` to `EFI/boot/` and rename it to bootx64.efi.
-
-If the system does not select this file for booting automatically, boot into
-`UEFI shell` and use the `bcfg` command.
-
-Issue the following command to list the currently configured boot sequence:
-```
-bcfg boot dump
-```
-
-The following command deletes item number `n`:
-```
-bcfg boot rm `n`
-```
-
-The following command create an entry for `bootx64.efi`:
-```
-bcfg boot add 0 fs0:\efi\boot\bootx64.efi "efi boot guard"
-```
-where the binary is on drive `fs0:`.
+See `Installation And Usage` for further information.

-Exit efi shell with the `reset` command.
+## Further Documentation ##

-## Future work ##
+* [Update Mechanism](docs/UPDATE.md)
+* [Environment Tools](docs/TOOLS.md)
+* [API Library](docs/API.md)
+* [Compilation Instructions](docs/COMPILE.md)
+* [Installation And Usage](docs/USAGE.md)
+* [Future Work](docs/TODO.md)

-* The number of valid config partitions expected by the bootloader and the tools
- is currently fixed to the number defined by `CONFIG_PARTITION_COUNT` in
- `include/envdata.h`. This value should be made configurable by a config flag.
diff --git a/docs/API.md b/docs/API.md
new file mode 100644
index 0000000..741cdad
--- /dev/null
+++ b/docs/API.md
@@ -0,0 +1,71 @@
+# API Library #
+
+## General information ##
+
+The library `libebgenv.a` provides an API to access the environment from a user
+space program.
+
+The header file with the interface definitions is
+[/swupdate-adapter/ebgenv.h](../swupdate-adapter/ebgenv.h).
+
+The interface provides functions to:
+* enable/disable for output to stdout and stderr
+* create a new environment
+* edit the latest environment
+* reset the error state
+* check the last update state
+
+An example and detailed information on how to interpret the returned state values
+is given in the [swupdate-adapter documentation](../swupdate-adapter/swupdate.md).
+
+To link a program to the library, you can install `efibootguard` with
+```
+make install
+```
+
+This will install `libebgenv.a` into your system for your linker to find it (usually
+to `/usr/lib/libebgenv.a`).
+
+If you want to cross-compile this library, you have to cross-compile the
+`efibootguard tools` since both tools and this API library both depend on a
+common static library. Refer to [compilation instructions](COMPILE.md).
+
+## Example programs ##
+
+The following example program opens the current environment and modifies the
+kernel file name:
+
+```c
+#include <stdbool.h>
+#include "ebgenv.h"
+
+int main(void)
+{
+ ebg_env_open_current();
+ ebg_env_set("kernelfile", "vmlinux-new");
+ ebg_env_close();
+ return 0;
+}
+```
+
+The following example program creates a new environment with the latest revision
+and sets it to the testing state:
+
+```c
+#include <stdbool.h>
+#include "ebgenv.h"
+
+int main(void)
+{
+ ebg_env_create_new();
+ ebg_env_set("kernelfile", "vmlinux-new");
+ ebg_env_set("kernelparams", "root=/dev/bootdevice");
+ ebg_env_set("watchdog_timeout_sec", "30");
+ ebg_env_close();
+ return 0;
+}
+```
+
+*Note*: If no watchdog timeout value is specified, a default of 30 seconds is
+set.
+
diff --git a/docs/COMPILE.md b/docs/COMPILE.md
new file mode 100644
index 0000000..51c2008
--- /dev/null
+++ b/docs/COMPILE.md
@@ -0,0 +1,48 @@
+# Compilation Instructions #
+
+## Required libraries and headers for compilation ##
+
+### Arch Linux ###
+
+```
+pacman -S gnu-efi-libs pciutils
+```
+
+### Debian 8 ###
+
+```
+apt-get install gnu-efi libpci-dev
+```
+
+## Compilation ##
+
+This project uses autotools. Here you find useful documentations for
+[autoconf](https://www.gnu.org/software/autoconf/manual/autoconf.html), and
+[automake](https://www.gnu.org/software/automake/manual/automake.html).
+
+To compile `efibootguard`, checkout the sources and run:
+
+```
+autoreconf -fi
+./configure
+make
+```
+
+To cross-compile, the environment variables must be set accordingly, i.e.
+`CXX=<compiler-to-use>`. The following example shows how to specify needed
+paths for an out-of-tree build, where cross-compilation environment variables
+have already been set before (i.e. by an embedded SDK from `yocto` or alike):
+
+```
+mkdir build
+cd build
+autoreconf -fi ..
+../configure --host=i586 --build=x86_64-unknown-linux-gnu \
+ --with-gnuefi-sys-dir=<sys-root-dir> \
+ --with-gnuefi-include-dir=<sys-root-dir>/usr/include/efi \
+ --with-gnuefi-lds-dir=<sys-root-dir>/usr/lib \
+ --with-gnuefi-lib-dir=<sys-root-dir>/usr/lib
+make
+```
+
+where `<sys-root-dir>` points to the wanted sysroot for cross-compilation.
diff --git a/docs/TODO.md b/docs/TODO.md
new file mode 100644
index 0000000..fbf5ee5
--- /dev/null
+++ b/docs/TODO.md
@@ -0,0 +1,29 @@
+# The following items will be implemented #
+
+* Application specific variables
+ * applications may need to store their own variables into the
+ bootloader environment. Currently this is not possible. A generic
+ method must be defined and implemented to account for generic
+ key-value pairs.
+
+* State refactoring
+ * Currently, there are three variables 'revision', 'testing',
+ 'boot_once', where the latter two are mapped onto a variable called
+ 'ustate'. The 'ustate' variable in turn equals an enum type variable
+ within swupdate, so that for the swupdate adapter, a complex mapping
+ must be implemented. To resolve this issue, the two variables
+ 'boot_once' and 'testing' will be unified to the 'ustate' variable,
+ which will have the same enum type as used in 'swupdate'.
+
+* API refactoring
+ * Currently, there are two APIs, a lower API 'bg_utils.c', and an
+ adapter-API 'ebgenv.c'. After refactoring the state variable, the API
+ will be simplified as well. It is possible, that only one API is
+ needed then.
+ * Function / Datatype / Variable names remind of Parted and should be
+ renamed if code developes independent of libparted.
+ * The number of valid config partitions expected by the bootloader and
+ the tools is currently fixed to the number defined by
+ `CONFIG_PARTITION_COUNT` in `include/envdata.h`. This value should be
+ made configurable by a config flag.
+
diff --git a/docs/TOOLS.md b/docs/TOOLS.md
new file mode 100644
index 0000000..a0d35ee
--- /dev/null
+++ b/docs/TOOLS.md
@@ -0,0 +1,86 @@
+# Environment Tools #
+
+Two tools exist for handling `efibootguard`'s environment:
+* `bg_setenv`
+* `bg_printenv`.
+
+The latter is a symbolic link to the first. The program recognizes its function
+from its filename. With these, the user can change environment content or
+display it.
+
+**NOTE**: The environment tools only work, if the correct number and type of
+config partitions is detected. This also means that the stored configuration
+data must have a valid checksum. If this is not the case, environments must be
+repaired first. To do so, follow the initial setup step explained below in the
+`Installation` section.
+
+## Creating an initial set of configurations ##
+
+If no valid environment is present, one can use the `-f` option to create a
+`BGENV.DAT` environment file in the current directory and restore a config
+partition by copying this file to it.
+
+To access configuration data on FAT partitions, the partition must either
+already be mounted, with access rights for the user using the tool, or the tool
+can mount the partition by itself. The latter is only possible if the tool has
+the `CAP_SYS_ADMIN` capability. This is the case if the user is `root` or the
+corresponding capability is set in the filesystem.
+
+
+## Updating a configuration ##
+
+In most cases, the user wants to update to a new environment configuration,
+which can be done with:
+
+```
+./bg_setenv -u --kernel="XXXX" --args="YYYY" --watchdog=25 --testing=1
+```
+
+The `-u` parameter tells `bg_setenv` to automatically overwrite the
+configuration with the lowest revision and sets its revision value to the
+highest. Hence, the oldest revision becomes the latest.
+
+*NOTE*: `CHAR16` environment variables are limited to 255 characters as stated
+in `include/envdata.h`.
+
+To overwrite a given configuration, specified by a fixed zero-based config
+partition number, i.e. `4`, execute:
+
+```
+./bg_setenv -p 4 [...]
+```
+
+To also specify a specific revision to set, i.e. `13`, execute:
+
+```
+./bg_setenv -p 4 -r 13 [...]
+```
+
+This specifies the revision number of the configuration in config partition
+number 4 to be set to 13.
+
+Please note that the config partition index is zero-based as displayed with:
+
+```
+bg_printenv
+```
+
+To confirm a tested environment, just issue:
+
+```
+bg_setenv -c
+```
+
+To simulate a failed update, with its environment data stored in config partition 1,
+issue:
+
+```
+bg_setenv -p 1 -b -t 1 -r 0
+```
+
+To simulate a reboot of a recently updated configuration stored in config partition 1,
+issue:
+
+```
+bg_setenv -p 1 -b
+```
diff --git a/docs/UPDATE.md b/docs/UPDATE.md
new file mode 100644
index 0000000..169fb9d
--- /dev/null
+++ b/docs/UPDATE.md
@@ -0,0 +1,99 @@
+# Update Mechanism #
+
+`efibootguard` works with a predefined number of configuration environments.
+The number is defined at compile-time. Each environment has a revision.
+`efibootguard` always loads the latest environment data, which is indicated by
+the highest revision value.
+
+The structure of the environment data is as follows:
+
+```c
+struct _BG_ENVDATA {
+ uint16_t kernelfile[ENV_STRING_LENGTH];
+ uint16_t kernelparams[ENV_STRING_LENGTH];
+ uint8_t testing;
+ uint8_t boot_once;
+ uint16_t watchdog_timeout_sec;
+ uint32_t revision;
+ uint32_t crc32;
+};
+```
+
+The fields have the following meaning:
+* `kernelfile`: Path to the kernel image, utf-16 encoded
+* `kernelparams`: Arguments to the kernel, utf-16 encoded
+* `testing`: A flag that specifies if the configuration is in test mode
+* `boot_once`: Set by `efibootguard` if it first boots a test configuration
+* `watchdog_timeout_sec`: Number of seconds, the watchdog times out after
+* `revision`: The revision number explained above
+* `crc32`: A crc32 checksum
+
+The following example cases demonstrate the meaning of the update-specific
+struct members.
+
+## Example cases ##
+
+Assume the following for the next examples: The system has been booted with a
+configuration that has a `revision` of `4`. The user can set a new
+configuration with a `revision` of `5`, with `testing` flag set. On the next
+reboot, `efibootguard` loads the configuration with the highest `revision`
+number. If it detects, that `testing` is set, it will enable the `boot_once`
+flag and boot the system with this configuration.
+
+### Example scenario 1 - Successful update ###
+
+Once booted, the user disables both `testing` and `boot_once` to confirm the
+update using the [tools](TOOLS.md).
+
+### Example scenario 2 - System crash during first boot after update ###
+
+If the system freezes during boot, the watchdog will reset the system. On the
+next boot `efibootguard` detects that this configuration had already been
+tested before, because `boot_once` is already set. Thus, it will load the 2nd
+latest configuration instead. The failed update is indicated by the revision
+of the failed configuration set to `0` with both `boot_once` and `testing`
+enabled. A revision of 0 is the lowest possible number and avoids that the
+corresponding configuration is booted again in future.
+
+## Visual explanation of the update process ##
+
+```
+ +--------------++--------------+
+ | || |
+ | Rev: latest || Rev: oldest |
+ +--------> | (working) || |
+ | | || |
+ | +--------------++--------------+
+ | |
+ | | update
+ | v
+ | +---------------++--------------+
+ | | || |
+ | | Rev: latest-1 || Rev: latest |
+ | | (working) || testing: 1 |
+ | | || boot_once: 0 |
+ | +---------------++--------------+
+ | |
+ | | reboot
+ | v
+ | +---------------++--------------+
+ | | || |
+ | | Rev: latest-1 || Rev: latest |
+ | | (working) || testing: 1 |
+ | | || boot_once: 1 |
+ | +---------------++--------------+
+ | | no
+ | success? ---------------------+
+ | | watchdog reboot |
+ | yes, confirm | |
+ | v v
+ | +----------++--------------+ +----------++--------------+
+ | | || | | || |
+ | | Rev: || Rev: latest | | Rev: || Rev: 0 |
+ | | latest-1 || testing: 0 | | latest-1 || testing: 1 |
+ | | || boot_once: 0 | | || boot_once: 1 |
+ | +----------++--------------+ +----------++--------------+
+ | boots |
+ +----- latest' = latest-1 +---------------------------+
+
+```
diff --git a/docs/USAGE.md b/docs/USAGE.md
new file mode 100644
index 0000000..690da67
--- /dev/null
+++ b/docs/USAGE.md
@@ -0,0 +1,165 @@
+# Installation AND Usage #
+
+In order to use `efibootguard` one needs to
+* have a valid disk partitioning scheme
+* have the bootloader binary installed in the proper place
+* have valid configuration files
+* configure the UEFI boot sequence (may be optional)
+
+## Creating a valid partitioning scheme ##
+
+UEFI by default supports FAT file systems, which are used to store
+configuration data for `efibootguard`. The following partition type GUIDS are
+supported for GPT partition entries:
+
+GUID | description
+-----|--------------------------------------------------------
+EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 | Microsoft default data partition
+C12A7328-F81F-11D2-BA4B-00A0C93EC93B | EFI System partition
+
+For robustness of the fail-safe mechanism, each configuration file revision is
+stored into a separate FAT partition. The following example shows how to create a
+new GPT using `parted`:
+
+**IMPORTANT**: Replace `/dev/sdX` with the correct block device.
+
+* Start `parted` for block device `/dev/sdX` and create an EFI system partition
+
+```
+# parted /dev/sdX
+(parted) mklabel GPT
+(parted) mkpart
+Partition name? []?
+File system type? [ext2]? fat32
+Start? 0%
+End? 20%
+(parted) toggle 1
+Flag to Invert? ESP
+```
+
+* Create two config partitions
+
+```
+(parted) mkpart
+Partition name? []?
+File system type? [ext2]? fat16
+Start? 20%
+End? 40%
+(parted) mkpart
+Partition name? []?
+File system type? [ext2]? fat16
+Start? 40%
+End? 60%
+```
+
+* Create two root partitions and leave `parted`
+
+```
+(parted) mkpart
+Partition name? []?
+File system type? [ext2]? ext4
+Start? 60%
+End? 80%
+(parted) mkpart
+Partition name? []?
+File system type? [ext2]? ext4
+Start? 80%
+End? 100%
+(parted) q
+```
+
+* Create all file systems
+
+```
+# mkfs.fat /dev/sdX1
+# mkfs.fat -F 16 /dev/sdX2
+# mkfs.fat -F 16 /dev/sdX3
+# mkfs.ext4 /dev/sdX4
+# mkfs.ext4 /dev/sdX5
+```
+
+*NOTE*: `FAT16`, as specified by `-F 16` is usefull for smaller partitions
+(i.e. 500 MB). `FAT12` and `FAT32` is also supported.
+
+## Install the boot loader binary file ##
+
+This example is for an `x64` architecture.
+
+```
+# mount /dev/sdX1 /mnt
+# mkdir -p /mnt/EFI/boot
+# cp efibootguardx64.efi /mnt/EFI/boot/bootx64.efi
+# umount /mnt
+```
+
+## Create a default configuration ##
+
+This step first creates a custom label contained in `EFILABEL`, which is later
+used to specify the kernel location.
+
+```
+# mount /dev/sdX2 /mnt && cd /mnt
+# echo -n "KERNEL1" | iconv -f ascii -t UTF-16LE > EFILABEL
+# bg_setenv -f -r 1 --kernelfile "C:KERNEL1:vmlinuz-linux" --kernelparams="root=/dev/sdX4 noinitrd"
+# umount /mnt
+# mount /dev/sdX3 /mnt && cd /mnt
+# echo -n "KERNEL2" | iconv -f ascii -t UTF-16LE > EFILABEL
+# bg_setenv -f -r 2 --kernelfile "C:KERNEL2:vmlinuz-linux" --kernelparams="root=/dev/sdX5 noinitrd"
+# umount /mnt
+```
+
+## Configuring UEFI boot sequence (Optional) ##
+
+If the system does not select the correct `bootx64.efi` for booting
+automatically, boot into `UEFI shell` and use the `bcfg` command.
+
+*NOTE*: If you do not have a `UEFI shell` you may consult google on how to obtain it.
+Alternatively, you may use the `efibootmgr` user space utility.
+
+Issue the following command to list the currently configured boot sequence:
+
+```
+bcfg boot dump
+```
+
+The following command deletes item number `n`:
+
+```
+bcfg boot rm `n`
+```
+
+The following command create an entry for `bootx64.efi`:
+
+```
+bcfg boot add 0 fs0:\efi\boot\bootx64.efi "efi boot guard"
+```
+
+where the binary is on drive `fs0:`.
+
+Exit `UEFI shell` with the `reset` command.
+
+## Kernel Location ##
+
+If you just specify a file name as `--kernelfile`, `efibootguard` loads the
+kernel from the same FAT partition as the boot loader binary itself.
+
+To load the kernel from a different FAT partition than `efibootguard`, there are
+two possible mechanisms. One directly uses the label of the FAT partition,
+created with `dosfslabel`:
+
+```
+./bg_setenv -u --kernel="L:FATLABEL:kernelfile"
+```
+
+where `FATLABEL` is the label of the FAT partition. On some older UEFI
+implementations, the label is not supported properly and a user defined label
+can be created instead, which is a file named `EFILABEL` in the root directory
+of the corresponding FAT partition. This file contains an UTF-16le encoded
+partition name and can be used as follows:
+
+```
+./bg_setenv -u --kernel="C:USERLABEL:kernelfile"
+```
+
+*NOTE*: Do not mix-up the file system label and the GPT entry label.
+
diff --git a/swupdate-adapter/swupdate.md b/swupdate-adapter/swupdate.md
index 334aa5f..0d955f1 100644
--- a/swupdate-adapter/swupdate.md
+++ b/swupdate-adapter/swupdate.md
@@ -1,5 +1,68 @@
# Update process with swupdate #

+
+## Update state mapping ##
+
+Swupdate-Suricatta works with an internal state variable, which is called `ustate`
+per default.
+
+The values of common interest are:
+
+ustate | meaning
+--------|-----------------
+0 | nothing to do
+1 | update installed (reboot pending)
+2 | update testing (after reboot)
+3 | update failed
+4 | state not available
+
+`efibootguard` works with three internal variables regarding the update mechanism:
+
+* `revision`
+* `testing`
+* `boot_once`
+
+The values of these variables are mapped onto ustate according to the following matrix:
+
+*Note*: A failed revision exists, if its `revision` is `0` and at the same time,
+both `boot_once` and `testing` are set to `1`. If such a revision exists in any of
+the stored environment partitions, this is marked as [FAILED] in the matrix below.
+
+`efibootguard` | `suricatta`
+---------------|------------------
+ (current env.)<br />testing = 0<br />boot_once = 0<br /><br />NOT [FAILED]| ustate = 0
+ (current env.)<br />testing = 1<br />boot_once = 0<br /><br />NOT [FAILED]| ustate = 1
+ (current env.)<br />testing = 1<br />boot_once = 1<br /><br />NOT [FAILED]| ustate = 2
+ [FAILED] | ustate = 3
+ Environment<br />Error | ustate = 4
+
+
+## Update state mapping with API functions ##
+
+1. Call `ebg_env_open_current`, which will initialize the configuration environment.
+
+2. Use the following logic
+
+```
+ebg_env_isupdatesuccessful() is false?
+ ustate = 3
+else
+ ebg_env_isokay() is true?
+ ustate = 0
+
+ ebg_env_isinstalled() is true?
+ ustate = 1
+
+ ebg_env_istesting() is true?
+ ustate = 2
+
+```
+
+3. call `ebg_env_close()`
+
+
+## Detailed example ##
+
Test environment: 2 config partitions, FAT16, GPT

**Initial suricatta state: OK**
@@ -25,7 +88,7 @@ test flag: disabled
boot once flag: not set
```

-## Installation of Update ##
+### Installation of Update ###

Used sw-description:

@@ -61,11 +124,10 @@ Command with block dev access to update efibootguard environment:

```
swupdate -v -i test.swu
-
```


-### Resulting environment ###
+#### Resulting environment ####

```
Config Partition #0 Values:
@@ -93,7 +155,7 @@ Test conditions:

Function to retrieve state: `ebg_env_isinstalled()`

-## Rebooting ##
+### Rebooting ###

efibootguard will detect the `testing` flag and set `boot_once`. This can be simulated by

@@ -103,7 +165,7 @@ bg_setenv -p X -b

where `X` is the 0-based index of the config partition to be updated. This sets the `boot_once` flag.

-### Resulting Environment ###
+#### Resulting Environment ####

```
Config Partition #0 Values:
@@ -131,13 +193,13 @@ Test conditions:

Function to retrieve state: `ebg_env_istesting()`

-## Confirming working update ##
+### Confirming working update ###

```
bg_setenv -c
```

-### Resulting environment ###
+#### Resulting environment ####

```
Config Partition #0 Values:
@@ -165,11 +227,12 @@ Test conditions:

Function to retrieve state: `ebg_env_isokay()`

-## Not confirming and rebooting ##
+### Not confirming and rebooting ###

After rebooting with state == INSTALLED and not confirming,
the resulting environment is:

+#### Resulting environment ####
```
Config Partition #0 Values:
revision: 15
@@ -202,7 +265,7 @@ bg_setenv -u -t 0
replaces the oldest environment with these and then updates the
newly set values (-t 0).*

-### Resulting environment ###
+#### Resulting environment ####

```
Config Partition #0 Values:
@@ -222,14 +285,3 @@ test flag: disabled
boot once flag: not set
```

-# State mapping #
-
-Condition A is ebg_env_isupdatesuccessful == true
-
-Suricatta state | condition | function
--------------------------------------------------------------------------------------
-FAILED | !A | !ebg_env_isupdatesuccessful()
-OK | A && testing == 0 | ebg_env_isokay()
-INSTALLED | A && testing == 1 && boot_once == 0 | ebg_env_isinstalled()
-TESTING | A && testing == 1 && boot_once == 1 | ebg_env_istesting()
-
--
2.11.0

Reichel Andreas

unread,
Jul 12, 2017, 7:06:27 AM7/12/17
to efibootg...@googlegroups.com, Reichel Andreas
If the user manually creates a loopback device node
in /dev, for example /dev/NAME and uses it to setup a
loopback device, the resulting base name of /sys/block/loop*
does not match that of /dev/NAME. On some systems,
/dev/loop* gets created automatically by using losetup, on
others not.
The solution is to read the major and minor revision
out of /sys/block/NAME/dev and look for the same
in /dev. Thus, the correct block device node in /dev
can be found.

Signed-off-by: Andreas Reichel <andreas.r...@siemens.com>
---
tools/ebgpart.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++--------
tools/ebgpart.h | 5 ++-
2 files changed, 94 insertions(+), 16 deletions(-)

diff --git a/tools/ebgpart.c b/tools/ebgpart.c
index 07edb05..be4ab09 100644
--- a/tools/ebgpart.c
+++ b/tools/ebgpart.c
@@ -353,27 +353,102 @@ bool check_partition_table(PedDevice *dev)
return true;
}

-void ped_device_probe_all()
+int scan_devdir(unsigned int fmajor, unsigned int fminor, char *fullname,
+ unsigned int maxlen)
{
- struct dirent *devfile;
- char fullname[256];
+ int result = 0;

- DIR *devdir = opendir(DEVDIRNAME);
+ DIR *devdir = opendir(DEVDIR);
if (!devdir) {
- VERBOSE(stderr, "Could not open %s\n", DEVDIRNAME);
- return;
+ VERBOSE(stderr, "Failed to open %s\n", DEVDIR);
+ return -1;
}
-
- /* get all files from devdir */
+ struct dirent *devfile;
do {
devfile = readdir(devdir);
- if (!devfile)
+ if (!devfile) {
break;
- if (strcmp(devfile->d_name, ".") == 0 ||
- strcmp(devfile->d_name, "..") == 0)
- continue;
+ }
+ snprintf(fullname, maxlen, "%s/%s", DEVDIR, devfile->d_name);
+ struct stat fstat;
+ if (stat(fullname, &fstat) == -1) {
+ VERBOSE(stderr, "stat failed on %s\n", fullname);
+ break;
+ }
+ if (major(fstat.st_rdev) == fmajor &&
+ minor(fstat.st_rdev) == fminor) {
+ VERBOSE(stdout, "Node found: %s\n", fullname);
+ result = 0;
+ break;
+ }
+ result = -1;
+ } while (devfile);
+ closedir(devdir);
+
+ return result;
+}
+
+int get_major_minor(char *filename, unsigned int *major, unsigned int *minor)
+{
+ if (!filename || !major || !minor) {
+ return -1;
+ }
+ FILE *fh = fopen(filename, "r");
+ if (fh == 0) {
+ VERBOSE(stderr, "Error opening %s for read", filename);
+ return -1;
+ }
+ int res = fscanf(fh, "%u:%u", major, minor);
+ fclose(fh);
+ if (res < 2) {
+ VERBOSE(stderr,
+ "Error reading major/minor of device entry. (%s)\n",
+ strerror(errno));
+ return -1;
+ };
+ return 0;
+}
+
+void ped_device_probe_all()
+{
+ struct dirent *sysblockfile;
+ char fullname[DEV_FILENAME_LEN];
+
+ DIR *sysblockdir = opendir(SYSBLOCKDIR);
+ if (!sysblockdir) {
+ VERBOSE(stderr, "Could not open %s\n", SYSBLOCKDIR);
+ return;
+ }

- snprintf(fullname, 255, "/dev/%s", devfile->d_name);
+ /* get all files from sysblockdir */
+ do {
+ sysblockfile = readdir(sysblockdir);
+ if (!sysblockfile) break;
+ if (strcmp(sysblockfile->d_name, ".") == 0 ||
+ strcmp(sysblockfile->d_name, "..") == 0)
+ continue;
+ snprintf(fullname, DEV_FILENAME_LEN, "/sys/block/%s/dev",
+ sysblockfile->d_name);
+ /* Get major and minor revision from /sys/block/sdX/dev */
+ unsigned int fmajor, fminor;
+ if (get_major_minor(fullname, &fmajor, &fminor) < 0) {
+ continue;
+ }
+ VERBOSE(stdout,
+ "Trying device with: Major = %d, Minor = %d, (%s)\n",
+ fmajor, fminor, fullname);
+ /* Check if this file is really in the dev directory */
+ snprintf(fullname, DEV_FILENAME_LEN, "%s/%s", DEVDIR,
+ sysblockfile->d_name);
+ struct stat fstat;
+ if (stat(fullname, &fstat) == -1) {
+ /* Node with same name not found in /dev, thus search
+ * for node with identical Major and Minor revision */
+ if (scan_devdir(fmajor, fminor, fullname,
+ DEV_FILENAME_LEN) != 0) {
+ continue;
+ }
+ }
/* This is a block device, so add it to the list*/
PedDevice *dev = calloc(sizeof(PedDevice), 1);
asprintf(&dev->model, "N/A");
@@ -385,9 +460,9 @@ void ped_device_probe_all()
free(dev->path);
free(dev);
}
- } while (devfile);
+ } while (sysblockfile);

- closedir(devdir);
+ closedir(sysblockdir);
}

void ped_partition_destroy(PedPartition *p)
diff --git a/tools/ebgpart.h b/tools/ebgpart.h
index 76fee9d..fbaa77c 100644
--- a/tools/ebgpart.h
+++ b/tools/ebgpart.h
@@ -25,6 +25,8 @@
#define _GNU_SOURCE
#endif

+#define DEV_FILENAME_LEN 256
+
#ifndef VERBOSE
#define VERBOSE(o, ...) \
if (verbosity) fprintf(o, __VA_ARGS__)
@@ -41,7 +43,8 @@
#include <string.h>
#include <stdlib.h>

-#define DEVDIRNAME "/sys/block"
+#define SYSBLOCKDIR "/sys/block"
+#define DEVDIR "/dev"

#define LB_SIZE 512

--
2.11.0

Reichel Andreas

unread,
Jul 12, 2017, 7:06:27 AM7/12/17
to efibootg...@googlegroups.com, Reichel Andreas

Reichel Andreas

unread,
Jul 12, 2017, 8:42:48 AM7/12/17
to efibootg...@googlegroups.com, Reichel Andreas
Add -v option also to bg_printenv to get library messages.

Signed-off-by: Andreas Reichel <andreas.r...@siemens.com>
---

Reichel Andreas

unread,
Jul 12, 2017, 8:42:48 AM7/12/17
to efibootg...@googlegroups.com, Reichel Andreas
12 files changed, 699 insertions(+), 301 deletions(-)

Reichel Andreas

unread,
Jul 12, 2017, 8:42:49 AM7/12/17
to efibootg...@googlegroups.com, Reichel Andreas
Update and rework documentation for better user support.

Signed-off-by: Andreas Reichel <andreas.r...@siemens.com>
---
README.md | 283 ++++---------------------------------------
docs/API.md | 71 +++++++++++
docs/COMPILE.md | 48 ++++++++
docs/TODO.md | 29 +++++
docs/TOOLS.md | 86 +++++++++++++
docs/UPDATE.md | 99 +++++++++++++++
docs/USAGE.md | 165 +++++++++++++++++++++++++
swupdate-adapter/swupdate.md | 92 +++++++++++---
8 files changed, 592 insertions(+), 281 deletions(-)
create mode 100644 docs/API.md
create mode 100644 docs/COMPILE.md
create mode 100644 docs/TODO.md
create mode 100644 docs/TOOLS.md
create mode 100644 docs/UPDATE.md
create mode 100644 docs/USAGE.md

+ return 0;
+}
+```
+
+The following example program creates a new environment with the latest revision
+and sets it to the testing state:
+
+```c
+#include <stdbool.h>
+#include "ebgenv.h"
+
+int main(void)
+{
+ ebg_env_create_new();
+ ebg_env_set("kernelfile", "vmlinux-new");
+ ebg_env_set("kernelparams", "root=/dev/bootdevice");
+ ebg_env_set("watchdog_timeout_sec", "30");
+ ebg_env_close();
+ return 0;
+}

Reichel Andreas

unread,
Jul 12, 2017, 8:42:49 AM7/12/17
to efibootg...@googlegroups.com, Reichel Andreas
* Allow FAT12, FAT16 and FAT32 for environment data.
* Minor code style fixes

Signed-off-by: Andreas Reichel <andreas.r...@siemens.com>
---

Reichel Andreas

unread,
Jul 12, 2017, 8:42:49 AM7/12/17
to efibootg...@googlegroups.com, Reichel Andreas
If the user manually creates a loopback device node
in /dev, for example /dev/NAME and uses it to setup a
loopback device, the resulting base name of /sys/block/loop*
does not match that of /dev/NAME. On some systems,
/dev/loop* gets created automatically by using losetup, on
others not.
The solution is to read the major and minor revision
out of /sys/block/NAME/dev and look for the same
in /dev. Thus, the correct block device node in /dev
can be found.

Signed-off-by: Andreas Reichel <andreas.r...@siemens.com>
---
tools/ebgpart.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++--------
tools/ebgpart.h | 5 ++-
2 files changed, 95 insertions(+), 15 deletions(-)

diff --git a/tools/ebgpart.c b/tools/ebgpart.c
index 07edb05..940652f 100644
--- a/tools/ebgpart.c
+++ b/tools/ebgpart.c
@@ -353,27 +353,104 @@ bool check_partition_table(PedDevice *dev)
return true;
}

-void ped_device_probe_all()
+int scan_devdir(unsigned int fmajor, unsigned int fminor, char *fullname,
+ unsigned int maxlen)
{
- struct dirent *devfile;
- char fullname[256];
+ int result = -1;

- DIR *devdir = opendir(DEVDIRNAME);
+ DIR *devdir = opendir(DEVDIR);
if (!devdir) {
- VERBOSE(stderr, "Could not open %s\n", DEVDIRNAME);
+ VERBOSE(stderr, "Failed to open %s\n", DEVDIR);
+ return result;
+ }
+ struct dirent *devfile;
+ do {
+ devfile = readdir(devdir);
+ if (!devfile) {
+ break;
+ }
+ snprintf(fullname, maxlen, "%s/%s", DEVDIR, devfile->d_name);
+ struct stat fstat;
+ if (stat(fullname, &fstat) == -1) {
+ VERBOSE(stderr, "stat failed on %s\n", fullname);
+ break;
+ }
+ if (major(fstat.st_rdev) == fmajor &&
+ minor(fstat.st_rdev) == fminor) {
+ VERBOSE(stdout, "Node found: %s\n", fullname);
+ result = 0;
+ break;
+ }
+ } while (devfile);
+ closedir(devdir);
+
+ return result;
+}
+
+int get_major_minor(char *filename, unsigned int *major, unsigned int *minor)
+{
+ if (!filename || !major || !minor) {
+ return -1;
+ }
+ FILE *fh = fopen(filename, "r");
+ if (fh == 0) {
+ VERBOSE(stderr, "Error opening %s for read", filename);
+ return -1;
+ }
+ int res = fscanf(fh, "%u:%u", major, minor);
+ fclose(fh);
+ if (res < 2) {
+ VERBOSE(stderr,
+ "Error reading major/minor of device entry. (%s)\n",
+ strerror(errno));
+ return -1;
+ };
+ return 0;
+}
+
+void ped_device_probe_all()
+{
+ struct dirent *sysblockfile;
+ char fullname[DEV_FILENAME_LEN];
+
+ DIR *sysblockdir = opendir(SYSBLOCKDIR);
+ if (!sysblockdir) {
+ VERBOSE(stderr, "Could not open %s\n", SYSBLOCKDIR);
return;
}

- /* get all files from devdir */
+ /* get all files from sysblockdir */
do {
- devfile = readdir(devdir);
- if (!devfile)
+ sysblockfile = readdir(sysblockdir);
+ if (!sysblockfile) {
break;
- if (strcmp(devfile->d_name, ".") == 0 ||
- strcmp(devfile->d_name, "..") == 0)
+ }
+ if (strcmp(sysblockfile->d_name, ".") == 0 ||
+ strcmp(sysblockfile->d_name, "..") == 0) {
continue;
-
- snprintf(fullname, 255, "/dev/%s", devfile->d_name);
+ }
@@ -385,9 +462,9 @@ void ped_device_probe_all()

Jan Kiszka

unread,
Jul 20, 2017, 12:40:05 PM7/20/17
to [ext] Reichel Andreas, efibootg...@googlegroups.com
Thanks, applied this already so that you can drop it from the next
round. But please, please add the from lines.

Jan

--
Siemens AG, Corporate Technology, CT RDA ITP SES-DE
Corporate Competence Center Embedded Linux

Jan Kiszka

unread,
Jul 20, 2017, 12:43:03 PM7/20/17
to [ext] Reichel Andreas, efibootg...@googlegroups.com
On 2017-07-12 14:38, [ext] Reichel Andreas wrote:
static - the compiler should complain about lacking prototype, or didn't
we enable that check?
static

> +{
> + if (!filename || !major || !minor) {

Really possible or just over-defensive programming? Specifically when
the caller is local, I'm reluctant to sprinkle such checks.

Looking at the caller, it's impossible. So drop these pointless checks.
sizeof(fullname) would be better, here and below.

Jan Kiszka

unread,
Jul 20, 2017, 12:45:37 PM7/20/17
to [ext] Reichel Andreas, efibootg...@googlegroups.com
On 2017-07-12 14:38, [ext] Reichel Andreas wrote:
> * Allow FAT12, FAT16 and FAT32 for environment data.
> * Minor code style fixes

Two patches, please.
Changes look good otherwise.

Jan Kiszka

unread,
Jul 20, 2017, 2:05:32 PM7/20/17
to [ext] Reichel Andreas, efibootg...@googlegroups.com
On 2017-07-12 14:38, [ext] Reichel Andreas wrote:
Hmm, does that make sense? The sane use case is rather to modify the
other env, not the current one. What is a valid use case to touch the
current env at all?
This implementation aspect is a bit too detailed when someone just want
to understand the usage.

> +from its filename. With these, the user can change environment content or
> +display it.
> +
> +**NOTE**: The environment tools only work, if the correct number and type of
> +config partitions is detected. This also means that the stored configuration
> +data must have a valid checksum. If this is not the case, environments must be
> +repaired first. To do so, follow the initial setup step explained below in the
> +`Installation` section.
> +
> +## Creating an initial set of configurations ##
> +
> +If no valid environment is present, one can use the `-f` option to create a
> +`BGENV.DAT` environment file in the current directory and restore a config
> +partition by copying this file to it.
> +
> +To access configuration data on FAT partitions, the partition must either
> +already be mounted, with access rights for the user using the tool, or the tool
> +can mount the partition by itself. The latter is only possible if the tool has
> +the `CAP_SYS_ADMIN` capability. This is the case if the user is `root` or the
> +corresponding capability is set in the filesystem.
> +

Isn't this description kind of replicating the walk-through in the usage
section? Better have things in one place that risking to repeat yourself
or, even worse, have both parts diverge over time (which happened
already, see below).

> +
> +## Updating a configuration ##
> +
> +In most cases, the user wants to update to a new environment configuration,
> +which can be done with:
> +
> +```
> +./bg_setenv -u --kernel="XXXX" --args="YYYY" --watchdog=25 --testing=1
> +```
> +
> +The `-u` parameter tells `bg_setenv` to automatically overwrite the
> +configuration with the lowest revision and sets its revision value to the
> +highest. Hence, the oldest revision becomes the latest.
> +
> +*NOTE*: `CHAR16` environment variables are limited to 255 characters as stated
> +in `include/envdata.h`.

What is the specific meaning of CHAR16 here? For the user, I mean. Isn't
that an implementation detail? I do want to know that by var can't get
longer than 255 chars, though.

> +
> +To overwrite a given configuration, specified by a fixed zero-based config
> +partition number, i.e. `4`, execute:
> +
> +```
> +./bg_setenv -p 4 [...]

4 includes also non-FAT partitions?

> +```
> +
> +To also specify a specific revision to set, i.e. `13`, execute:
> +
> +```
> +./bg_setenv -p 4 -r 13 [...]
> +```
> +
> +This specifies the revision number of the configuration in config partition
> +number 4 to be set to 13.
> +
> +Please note that the config partition index is zero-based as displayed with:
> +
> +```
> +bg_printenv
> +```
> +
> +To confirm a tested environment, just issue:

"To mark an environment as working after booting into it and testing
essential features" - or so. I first thought I had to issue -c to
confirm an update prior to booting into it.

> +
> +```
> +bg_setenv -c
> +```
> +
> +To simulate a failed update, with its environment data stored in config partition 1,
> +issue:
> +
> +```
> +bg_setenv -p 1 -b -t 1 -r 0

It makes sense to use the long form of the arguments in the docs - makes
things more readable.
if needed - UEFI compliant BIOSes without preexisting configurations
have to fall back to the standard search path. Do you mention this
somewhere? That is

/EFI/BOOT/BOOT<arch>.EFI

> +
> +## Creating a valid partitioning scheme ##
> +
> +UEFI by default supports FAT file systems, which are used to store
> +configuration data for `efibootguard`. The following partition type GUIDS are
> +supported for GPT partition entries:
> +
> +GUID | description
> +-----|--------------------------------------------------------
> +EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 | Microsoft default data partition
> +C12A7328-F81F-11D2-BA4B-00A0C93EC93B | EFI System partition

Please align columns for text-only reading.
bg_setenv --help (current next - am I missing an update?) talks about
--kernel and --args. Moreover, which file will "-f" write out? That
parameter suggests a bit to take a name, but it seems that this name is
hard-coded. In that case, better take a path where the file with the
required name is placed. Also avoids that explicit cd then.

> +# umount /mnt
> +# mount /dev/sdX3 /mnt && cd /mnt
> +# echo -n "KERNEL2" | iconv -f ascii -t UTF-16LE > EFILABEL
> +# bg_setenv -f -r 2 --kernelfile "C:KERNEL2:vmlinuz-linux" --kernelparams="root=/dev/sdX5 noinitrd"
> +# umount /mnt

It's probably better to pull the setup of the second environment after
booting into the first one. Having both created initially makes no sense
in practice.

> +```
> +
> +## Configuring UEFI boot sequence (Optional) ##
> +
> +If the system does not select the correct `bootx64.efi` for booting
> +automatically, boot into `UEFI shell` and use the `bcfg` command.
> +
> +*NOTE*: If you do not have a `UEFI shell` you may consult google on how to obtain it.
> +Alternatively, you may use the `efibootmgr` user space utility.

I would recommend the second step first - UEFI shell is horrible for
people not familiar with DOS anymore.

> +
> +Issue the following command to list the currently configured boot sequence:
> +
> +```
> +bcfg boot dump
> +```
> +
> +The following command deletes item number `n`:
> +
> +```
> +bcfg boot rm `n`
> +```
> +
> +The following command create an entry for `bootx64.efi`:
> +
> +```
> +bcfg boot add 0 fs0:\efi\boot\bootx64.efi "efi boot guard"

In fact, that should never be needed for any non-broken BIOS. At most,
you would have to remove any existing settings and let the BIOS fall
back to that. I would not highlight the exceptional case here.

> +```
> +
> +where the binary is on drive `fs0:`.
> +
> +Exit `UEFI shell` with the `reset` command.
> +
> +## Kernel Location ##
> +
> +If you just specify a file name as `--kernelfile`, `efibootguard` loads the
> +kernel from the same FAT partition as the boot loader binary itself.
> +
> +To load the kernel from a different FAT partition than `efibootguard`, there are
> +two possible mechanisms. One directly uses the label of the FAT partition,
> +created with `dosfslabel`:
> +
> +```
> +./bg_setenv -u --kernel="L:FATLABEL:kernelfile"

Ah, this is now the new syntax? Or the old one? :)
Please align columns properly for non-markdown readers.

> +
> +`efibootguard` works with three internal variables regarding the update mechanism:
> +
> +* `revision`
> +* `testing`
> +* `boot_once`

Why do we need these internal vars? Why does the user need to know about
them? Is it planned to move to symbolic states instead (ustate with names)?

> +
> +The values of these variables are mapped onto ustate according to the following matrix:
> +
> +*Note*: A failed revision exists, if its `revision` is `0` and at the same time,
> +both `boot_once` and `testing` are set to `1`. If such a revision exists in any of
> +the stored environment partitions, this is marked as [FAILED] in the matrix below.
> +
> +`efibootguard` | `suricatta`
> +---------------|------------------
> + (current env.)<br />testing = 0<br />boot_once = 0<br /><br />NOT [FAILED]| ustate = 0
> + (current env.)<br />testing = 1<br />boot_once = 0<br /><br />NOT [FAILED]| ustate = 1
> + (current env.)<br />testing = 1<br />boot_once = 1<br /><br />NOT [FAILED]| ustate = 2
> + [FAILED] | ustate = 3
> + Environment<br />Error | ustate = 4

Here some alignment as well.

Andreas Reichel

unread,
Jul 21, 2017, 8:04:38 AM7/21/17
to Jan Kiszka, efibootg...@googlegroups.com
On Thu, Jul 20, 2017 at 08:05:31PM +0200, Jan Kiszka wrote:
> On 2017-07-12 14:38, [ext] Reichel Andreas wrote:
> > Update and rework documentation for better user support.
> >
>
> > +# umount /mnt
> > +# mount /dev/sdX3 /mnt && cd /mnt
> > +# echo -n "KERNEL2" | iconv -f ascii -t UTF-16LE > EFILABEL
> > +# bg_setenv -f -r 2 --kernelfile "C:KERNEL2:vmlinuz-linux" --kernelparams="root=/dev/sdX5 noinitrd"
> > +# umount /mnt
>
> It's probably better to pull the setup of the second environment after
> booting into the first one. Having both created initially makes no sense
> in practice.
>

If efibootguard is compiled for dual-FAT config, then it expects two
config partitions. This is the default. It tries to boot with one, but
then it warns about one being corrupted. To prevent this warning, both
are initialized, which is a clean starting point. Efibootguard has no
way of seeing if this is just the very first initial setup or if it is
executed after a hard disk failure were one partition is gone. It also
does not know if the only config existing is correct, so just cloning
the only thing that is there is based on an unneeded assumption.

> > +The following command create an entry for `bootx64.efi`:
> > +
> > +```
> > +bcfg boot add 0 fs0:\efi\boot\bootx64.efi "efi boot guard"
>
> In fact, that should never be needed for any non-broken BIOS. At most,
> you would have to remove any existing settings and let the BIOS fall
> back to that. I would not highlight the exceptional case here.
>

Not true. Maybe you have two harddisks and want to boot from fs0 instead
from fs1. Or you want to boot fs0: before network boot... I would say it
is not about the firmware being broken or not... it is about the NVRAM
settings the user may have.

Kind regards,
Andreas

--
Andreas Reichel
Dipl.-Phys. (Univ.)
Software Consultant

Andreas...@tngtech.com, +49-174-3180074

Jan Kiszka

unread,
Jul 21, 2017, 8:23:14 AM7/21/17
to Andreas Reichel, efibootg...@googlegroups.com
On 2017-07-21 14:00, Andreas Reichel wrote:
> On Thu, Jul 20, 2017 at 08:05:31PM +0200, Jan Kiszka wrote:
>> On 2017-07-12 14:38, [ext] Reichel Andreas wrote:
>>> Update and rework documentation for better user support.
>>>
>>
>>> +# umount /mnt
>>> +# mount /dev/sdX3 /mnt && cd /mnt
>>> +# echo -n "KERNEL2" | iconv -f ascii -t UTF-16LE > EFILABEL
>>> +# bg_setenv -f -r 2 --kernelfile "C:KERNEL2:vmlinuz-linux" --kernelparams="root=/dev/sdX5 noinitrd"
>>> +# umount /mnt
>>
>> It's probably better to pull the setup of the second environment after
>> booting into the first one. Having both created initially makes no sense
>> in practice.
>>
>
> If efibootguard is compiled for dual-FAT config, then it expects two
> config partitions. This is the default. It tries to boot with one, but
> then it warns about one being corrupted. To prevent this warning, both
> are initialized, which is a clean starting point. Efibootguard has no
> way of seeing if this is just the very first initial setup or if it is
> executed after a hard disk failure were one partition is gone. It also
> does not know if the only config existing is correct, so just cloning
> the only thing that is there is based on an unneeded assumption.

OK, I see.

That makes me wonder about the following error scenario: After running
fine, maybe rebooting a couple of times, the latest partition suddenly
becomes corrupt. Can/should the previous version then act as backup? Can
we detect this case of "late downgrade"? Should we, after successfully
updating and booting a new version also update the second partition to
the same content, just leaving its partition revision old? I'm concern
of a silent fall-back to an old, potentially vulnerable version of the
system otherwise.

>
>>> +The following command create an entry for `bootx64.efi`:
>>> +
>>> +```
>>> +bcfg boot add 0 fs0:\efi\boot\bootx64.efi "efi boot guard"
>>
>> In fact, that should never be needed for any non-broken BIOS. At most,
>> you would have to remove any existing settings and let the BIOS fall
>> back to that. I would not highlight the exceptional case here.
>>
>
> Not true. Maybe you have two harddisks and want to boot from fs0 instead
> from fs1. Or you want to boot fs0: before network boot... I would say it
> is not about the firmware being broken or not... it is about the NVRAM
> settings the user may have.

That's what I meant with "existing settings" - of course, if you don't
have a new system, you may have to remove existing settings. And, true,
if you have a system with multiple bootable media attached, you may need
to set this particular one. But the latter is an exception.

The idea is to make this section for the common case as simple as
possible - if that is possible, of course.

Andreas Reichel

unread,
Jul 21, 2017, 8:38:31 AM7/21/17
to Jan Kiszka, efibootg...@googlegroups.com
On Fri, Jul 21, 2017 at 02:23:13PM +0200, Jan Kiszka wrote:
> On 2017-07-21 14:00, Andreas Reichel wrote:
> > On Thu, Jul 20, 2017 at 08:05:31PM +0200, Jan Kiszka wrote:
> >> On 2017-07-12 14:38, [ext] Reichel Andreas wrote:
>
> OK, I see.
>
> That makes me wonder about the following error scenario: After running
> fine, maybe rebooting a couple of times, the latest partition suddenly
> becomes corrupt. Can/should the previous version then act as backup? Can
> we detect this case of "late downgrade"? Should we, after successfully
> updating and booting a new version also update the second partition to
> the same content, just leaving its partition revision old? I'm concern
> of a silent fall-back to an old, potentially vulnerable version of the
> system otherwise.
>
This is a really good point! I suggest to modify the confirm feature to
also create a backup copy of the new working environment to the (latest-1)
environment. I will add this to the TODO.md as first item. This is then
also in agreement with the initial setup of two identical (except
revision) environment data sets.

>
> The idea is to make this section for the common case as simple as
> possible - if that is possible, of course.
>
Sure. However this was the first point I fell over and I have the
feeling that this is important for users. Just to be user-friendly.
That's why I wrote (>if needed<) so the user can easily skip this
section :)

Andreas

> Jan
>
> --
> Siemens AG, Corporate Technology, CT RDA ITP SES-DE
> Corporate Competence Center Embedded Linux

Jan Kiszka

unread,
Jul 21, 2017, 8:41:45 AM7/21/17
to Andreas Reichel, efibootg...@googlegroups.com
On 2017-07-21 14:34, Andreas Reichel wrote:
> On Fri, Jul 21, 2017 at 02:23:13PM +0200, Jan Kiszka wrote:
>> On 2017-07-21 14:00, Andreas Reichel wrote:
>>> On Thu, Jul 20, 2017 at 08:05:31PM +0200, Jan Kiszka wrote:
>>>> On 2017-07-12 14:38, [ext] Reichel Andreas wrote:
>>
>> OK, I see.
>>
>> That makes me wonder about the following error scenario: After running
>> fine, maybe rebooting a couple of times, the latest partition suddenly
>> becomes corrupt. Can/should the previous version then act as backup? Can
>> we detect this case of "late downgrade"? Should we, after successfully
>> updating and booting a new version also update the second partition to
>> the same content, just leaving its partition revision old? I'm concern
>> of a silent fall-back to an old, potentially vulnerable version of the
>> system otherwise.
>>
> This is a really good point! I suggest to modify the confirm feature to
> also create a backup copy of the new working environment to the (latest-1)
> environment. I will add this to the TODO.md as first item. This is then
> also in agreement with the initial setup of two identical (except
> revision) environment data sets.

Sounds good.

>
>>
>> The idea is to make this section for the common case as simple as
>> possible - if that is possible, of course.
>>
> Sure. However this was the first point I fell over and I have the
> feeling that this is important for users. Just to be user-friendly.
> That's why I wrote (>if needed<) so the user can easily skip this
> section :)

Ack.

Reichel Andreas

unread,
Jul 21, 2017, 9:23:05 AM7/21/17
to efibootg...@googlegroups.com, Andreas Reichel
From: Andreas Reichel <andreas.r...@siemens.com>

From: Andreas Reichel <andreas.r...@siemens.com>

Fix some style issues.
---
tools/bg_utils.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/tools/bg_utils.c b/tools/bg_utils.c
index e44fc40..d042fc9 100644
--- a/tools/bg_utils.c
+++ b/tools/bg_utils.c
@@ -154,7 +154,7 @@ static FILE *open_config_file(CONFIG_PART *cfgpart, char *mode)
strncat(configfilepath, "/", 1);
strncat(configfilepath, FAT_ENV_FILENAME, strlen(FAT_ENV_FILENAME));
VERBOSE(stdout, "Probing config file at %s.\n", configfilepath);
- FILE* config = fopen(configfilepath, mode);
+ FILE *config = fopen(configfilepath, mode);
free(configfilepath);
return config;
}
@@ -227,7 +227,7 @@ bool probe_config_partitions(CONFIG_PART *cfgpart)
PedPartition *part = pd->part_list;
while (part) {
if (!part->fs_type || !part->fs_type->name ||
- strcmp(part->fs_type->name, "fat16") != 0) {
+ strcmp(part->fs_type->name, "fat12") != 0) {
part = ped_disk_next_partition(pd, part);
continue;
}
@@ -440,11 +440,14 @@ bool bgenv_write(BGENV *env)
case BGENVTYPE_FAT:
part = (CONFIG_PART *)env->desc;
if (!part) {
- VERBOSE(stderr, "Invalid config partition to store environment.\n");
+ VERBOSE(
+ stderr,
+ "Invalid config partition to store environment.\n");
return false;
}
if (!write_env(part, env->data)) {
- VERBOSE(stderr, "Could not write to %s\n", part->devpath);
+ VERBOSE(stderr, "Could not write to %s\n",
+ part->devpath);
return false;
}
return true;
--
2.13.3

Reichel Andreas

unread,
Jul 21, 2017, 9:23:05 AM7/21/17
to efibootg...@googlegroups.com, Reichel Andreas
This series addresses the following:

* Fix detection for customized device nodes:
Currently, ebgpart.c detects devices by enumerating in
/sys/block. For each found directory, it assumes, the same name
exists in /dev as device node, which may fail if a user or udev
costumized the node names. The fix compares major and minor revision
to detect any existing node in /dev/ that matches.

* Remove FAT16 restriction of environment tools:
The restriction was formerly introduced as code for manual file
system handling was used. A restriction to FAT16 is not needed anymore
and UEFI supports all FAT formats anyhow.

* Update documentation for better user support.

Signed-off-by: Andreas Reichel <andreas.r...@siemens.com>

Reichel Andreas (4):
bg_utils: Fix detection for customized loop devs
bg_utils: Minor style fixes
bg_utils: Remove FAT16 restriction for environment
docs: Update documentation.

README.md | 283 ++++---------------------------------------
docs/API.md | 74 +++++++++++
docs/COMPILE.md | 48 ++++++++
docs/TODO.md | 37 ++++++
docs/TOOLS.md | 82 +++++++++++++
docs/UPDATE.md | 99 +++++++++++++++
docs/USAGE.md | 171 ++++++++++++++++++++++++++
swupdate-adapter/swupdate.md | 101 +++++++++++----
tools/bg_utils.c | 13 +-
tools/ebgpart.c | 103 +++++++++++++---
tools/ebgpart.h | 5 +-
11 files changed, 713 insertions(+), 303 deletions(-)
create mode 100644 docs/API.md
create mode 100644 docs/COMPILE.md
create mode 100644 docs/TODO.md
create mode 100644 docs/TOOLS.md
create mode 100644 docs/UPDATE.md
create mode 100644 docs/USAGE.md

--
2.13.3

Reichel Andreas

unread,
Jul 21, 2017, 9:23:05 AM7/21/17
to efibootg...@googlegroups.com, Andreas Reichel
From: Andreas Reichel <andreas.r...@siemens.com>

From: Andreas Reichel <andreas.r...@siemens.com>

If the user manually creates a loopback device node in /dev, for example
/dev/NAME and uses it to setup a loopback device, the resulting base
name of /sys/block/loop* does not match that of /dev/NAME. On some
systems, /dev/loop* gets created automatically by using losetup, on
others not.
The solution is to read the major and minor revision out of
/sys/block/NAME/dev and look for the same in /dev. Thus, the correct
block device node in /dev can be found.

Signed-off-by: Andreas Reichel <andreas.r...@siemens.com>
---
tools/ebgpart.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++--------
tools/ebgpart.h | 5 ++-
2 files changed, 93 insertions(+), 15 deletions(-)

diff --git a/tools/ebgpart.c b/tools/ebgpart.c
index 97b5596..878cc5e 100644
--- a/tools/ebgpart.c
+++ b/tools/ebgpart.c
@@ -15,6 +15,7 @@
*/

#include "ebgpart.h"
+#include <sys/sysmacros.h>

static PedDevice *first_device = NULL;
static PedDisk g_ped_dummy_disk;
@@ -345,27 +346,101 @@ bool check_partition_table(PedDevice *dev)
return true;
}

-void ped_device_probe_all()
+int scan_devdir(unsigned int fmajor, unsigned int fminor, char *fullname,
+ unsigned int maxlen)
+static int get_major_minor(char *filename, unsigned int *major, unsigned int *minor)
+{
+ FILE *fh = fopen(filename, "r");
+ if (fh == 0) {
+ VERBOSE(stderr, "Error opening %s for read", filename);
+ return -1;
+ }
+ int res = fscanf(fh, "%u:%u", major, minor);
+ fclose(fh);
+ if (res < 2) {
+ VERBOSE(stderr,
+ "Error reading major/minor of device entry. (%s)\n",
+ strerror(errno));
+ return -1;
+ };
+ return 0;
+}
+
+ snprintf(fullname, sizeof(fullname), "/sys/block/%s/dev",
+ sysblockfile->d_name);
+ /* Get major and minor revision from /sys/block/sdX/dev */
+ unsigned int fmajor, fminor;
+ if (get_major_minor(fullname, &fmajor, &fminor) < 0) {
+ continue;
+ }
+ VERBOSE(stdout,
+ "Trying device with: Major = %d, Minor = %d, (%s)\n",
+ fmajor, fminor, fullname);
+ /* Check if this file is really in the dev directory */
+ snprintf(fullname, sizeof(fullname), "%s/%s", DEVDIR,
+ sysblockfile->d_name);
+ struct stat fstat;
+ if (stat(fullname, &fstat) == -1) {
+ /* Node with same name not found in /dev, thus search
+ * for node with identical Major and Minor revision */
+ if (scan_devdir(fmajor, fminor, fullname,
+ sizeof(fullname)) != 0) {
+ continue;
+ }
+ }
/* This is a block device, so add it to the list*/
PedDevice *dev = calloc(sizeof(PedDevice), 1);
asprintf(&dev->model, "N/A");
@@ -377,9 +452,9 @@ void ped_device_probe_all()
free(dev->path);
free(dev);
}
- } while (devfile);
+ } while (sysblockfile);

- closedir(devdir);
+ closedir(sysblockdir);
}

void ped_partition_destroy(PedPartition *p)
diff --git a/tools/ebgpart.h b/tools/ebgpart.h
index bb4c2a9..918602e 100644
--- a/tools/ebgpart.h
+++ b/tools/ebgpart.h
@@ -25,6 +25,8 @@
#define _GNU_SOURCE
#endif

+#define DEV_FILENAME_LEN 256
+
#ifndef VERBOSE
#define VERBOSE(o, ...) \
if (verbosity) fprintf(o, __VA_ARGS__)
@@ -45,7 +47,8 @@
#include <string.h>
#include <stdlib.h>

-#define DEVDIRNAME "/sys/block"
+#define SYSBLOCKDIR "/sys/block"
+#define DEVDIR "/dev"

#define LB_SIZE 512

--
2.13.3

Reichel Andreas

unread,
Jul 21, 2017, 9:23:05 AM7/21/17
to efibootg...@googlegroups.com, Andreas Reichel
From: Andreas Reichel <andreas.r...@siemens.com>

From: Andreas Reichel <andreas.r...@siemens.com>

Allow FAT12, FAT16 and FAT32 for environment data.
---
tools/bg_utils.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tools/bg_utils.c b/tools/bg_utils.c
index d042fc9..026abb9 100644
--- a/tools/bg_utils.c
+++ b/tools/bg_utils.c
@@ -227,7 +227,9 @@ bool probe_config_partitions(CONFIG_PART *cfgpart)
PedPartition *part = pd->part_list;
while (part) {
if (!part->fs_type || !part->fs_type->name ||
- strcmp(part->fs_type->name, "fat12") != 0) {
+ (strcmp(part->fs_type->name, "fat12") != 0 &&
+ strcmp(part->fs_type->name, "fat16") != 0 &&
+ strcmp(part->fs_type->name, "fat32") != 0)) {
part = ped_disk_next_partition(pd, part);
continue;
}
--
2.13.3

Reichel Andreas

unread,
Jul 21, 2017, 9:23:06 AM7/21/17
to efibootg...@googlegroups.com, Andreas Reichel
From: Andreas Reichel <andreas.r...@siemens.com>

From: Andreas Reichel <andreas.r...@siemens.com>

Update and rework documentation for better user support.

Signed-off-by: Andreas Reichel <andreas.r...@siemens.com>
---
README.md | 283 ++++---------------------------------------
docs/API.md | 74 +++++++++++
docs/COMPILE.md | 48 ++++++++
docs/TODO.md | 37 ++++++
docs/TOOLS.md | 82 +++++++++++++
docs/UPDATE.md | 99 +++++++++++++++
docs/USAGE.md | 171 ++++++++++++++++++++++++++
swupdate-adapter/swupdate.md | 101 +++++++++++----
8 files changed, 611 insertions(+), 284 deletions(-)
create mode 100644 docs/API.md
create mode 100644 docs/COMPILE.md
create mode 100644 docs/TODO.md
create mode 100644 docs/TOOLS.md
create mode 100644 docs/UPDATE.md
create mode 100644 docs/USAGE.md

index 0000000..8778b70
--- /dev/null
+++ b/docs/API.md
@@ -0,0 +1,74 @@
+# API Library #
+
+## General information ##
+
+The library `libebgenv.a` provides an API to access the environment from a
+user space program.
+The following example program creates a new environment with the latest revision
+and sets it to the testing state:
+
+```c
+#include <stdbool.h>
+#include "ebgenv.h"
+
+int main(void)
+{
+ ebg_env_create_new();
+ ebg_env_set("kernelfile", "vmlinux-new");
+ ebg_env_set("kernelparams", "root=/dev/bootdevice");
+ ebg_env_set("watchdog_timeout_sec", "30");
+ ebg_env_close();
+ return 0;
+}
+```
+
+*Note*: If no watchdog timeout value is specified, a default of 30 seconds is
+set.
+
+### Advanced Usage ###
+
+In some cases, for example in tests, access to the current environment is
+needed. The following example program opens the current environment and
+modifies the kernel file name:
+
+```c
+#include <stdbool.h>
+#include "ebgenv.h"
+
+int main(void)
+{
+ ebg_env_open_current();
+ ebg_env_set("kernelfile", "vmlinux-new");
+ ebg_env_close();
+ return 0;
+}
+```
index 0000000..40d5ba8
--- /dev/null
+++ b/docs/TODO.md
@@ -0,0 +1,37 @@
+# The following items will be implemented #
+
+
+* Tools modification
+ * Make `bg_setenv -c` and the underlying confirm mechanism to backup
+ the current working environment to the (latest-1) environment, so
+ that if the current environment breaks, there is a backup with the
+ latest values.
+ * Make `bg_setenv -f` take a path to where the `BGENV.DAT` is stored.
index 0000000..c79e5a8
--- /dev/null
+++ b/docs/TOOLS.md
@@ -0,0 +1,82 @@
+# Environment Tools #
+
+Two tools exist for handling `efibootguard`'s environment:
+* `bg_setenv`
+* `bg_printenv`.
+
+With these, the user can change environment content or display it.
+
+**NOTE**: The environment tools only work, if the correct number and type of
+config partitions is detected. This also means that the stored configuration
+data must have a valid checksum. If this is not the case, environments must be
+repaired first. To do so, follow the `Initial Setup` step section below.
+
+## Initial Setup ##
+
+Generation of a valid configuration partition is described in
+[docs/USAGE.md](USAGE.md).
+
+*NOTE*: To access configuration data on FAT partitions, the partition must
+either already be mounted, with access rights for the user using the tool, or
+the tool can mount the partition by itself. The latter is only possible if the
+tool has the `CAP_SYS_ADMIN` capability. This is the case if the user is `root`
+or the corresponding capability is set in the filesystem.
+
+## Updating a configuration ##
+
+In most cases, the user wants to update to a new environment configuration,
+which can be done with:
+
+```
+./bg_setenv --update --kernel="XXXX" --args="YYYY" --watchdog=25 --testing=1
+```
+
+The `--update` parameter tells `bg_setenv` to automatically overwrite the
+configuration with the lowest revision and sets its revision value to the
+highest. Hence, the oldest revision becomes the latest.
+
+*NOTE*: Environment variables are limited to 255 characters as stated in
+`include/envdata.h`.
+
+To overwrite a given configuration, specified by a fixed zero-based `config
+partition` number, i.e. `4`, execute:
+
+```
+./bg_setenv --part=4 [...]
+```
+
+To also specify a specific revision to set, i.e. `13`, execute:
+
+```
+./bg_setenv --part=4 --revision=13 [...]
+```
+
+This specifies the revision number of the configuration in config partition
+number 4 to be set to 13.
+
+Please note that the config partition index is zero-based as displayed with:
+
+```
+bg_printenv
+```
+
+To mark the current environment as working after having successfully booted
+with it and having tested essential features, use the `--confirm` option:
+
+```
+bg_setenv --confirm
+```
+
+To simulate a failed update, with its environment data stored in config partition 1,
+issue:
+
+```
+bg_setenv --partition=1 --bootonce --testing=1 --revision=0
+```
+
+To simulate a reboot of a recently updated configuration stored in config partition 1,
+issue:
+
+```
+bg_setenv --partition=1 --boot_once
index 0000000..7dd89ce
--- /dev/null
+++ b/docs/USAGE.md
@@ -0,0 +1,171 @@
+# Installation AND Usage #
+
+In order to use `efibootguard` one needs to
+* have a valid disk partitioning scheme
+* have the bootloader binary installed in the proper place
+* have valid configuration files
+* configure the UEFI boot sequence (may be optional)
+
+## Creating a valid partitioning scheme ##
+
+UEFI by default supports FAT file systems, which are used to store
+configuration data for `efibootguard`. The following partition type GUIDS are
+supported for GPT partition entries:
+
+GUID | description
+-------------------------------------|----------------------------------
+EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 | Microsoft default data partition
+C12A7328-F81F-11D2-BA4B-00A0C93EC93B | EFI System partition
+# mount /dev/sdX2 /mnt && cd /mnt
+# echo -n "KERNEL1" | iconv -f ascii -t UTF-16LE > EFILABEL
+# bg_setenv -f -r 1 --kernel="C:KERNEL1:vmlinuz-linux" --args="root=/dev/sdX4 noinitrd"
+# umount /mnt
+# mount /dev/sdX3 /mnt && cd /mnt
+# echo -n "KERNEL2" | iconv -f ascii -t UTF-16LE > EFILABEL
+# bg_setenv -f -r 2 --kernel="C:KERNEL2:vmlinuz-linux" --args="root=/dev/sdX5 noinitrd"
+# umount /mnt
+```
+
+## Configuring UEFI boot sequence (Optional) ##
+
+UEFI compliant firmwares fall back to a standard search path for the boot loader binary. This is
+
+```
+/EFI/BOOT/BOOT<arch>.EFI
+```
+
+In some cases, if the system does not select the correct `bootx64.efi` for
+booting automatically, use the `efibootmgr` user space tool to setup the boot
+sequence configuration.
+
+Another possibility is to boot into `UEFI shell` and use the `bcfg` command.
+
+Issue the following command to list the currently configured boot sequence:
+
+```
+bcfg boot dump
+```
+
+The following command deletes item number `n`:
+
+```
+bcfg boot rm `n`
+```
+
+The following command create an entry for `bootx64.efi`:
+
+```
+bcfg boot add 0 fs0:\efi\boot\bootx64.efi "efi boot guard"
+```
+
+where the binary is on drive `fs0:`.
+
+Exit `UEFI shell` with the `reset` command.
+
+## Kernel Location ##
+
+If you just specify a file name as `--kernelfile`, `efibootguard` loads the
+kernel from the same FAT partition as the boot loader binary itself.
+
+To load the kernel from a different FAT partition than `efibootguard`, there are
+two possible mechanisms. One directly uses the label of the FAT partition,
+created with `dosfslabel`:
+
+```
+./bg_setenv -u --kernel="L:FATLABEL:kernelfile"
+```
+
+where `FATLABEL` is the label of the FAT partition. On some older UEFI
+implementations, the label is not supported properly and a user defined label
+can be created instead, which is a file named `EFILABEL` in the root directory
+of the corresponding FAT partition. This file contains an UTF-16le encoded
+partition name and can be used as follows:
+
+```
+./bg_setenv -u --kernel="C:USERLABEL:kernelfile"
+```
+
+*NOTE*: Do not mix-up the file system label and the GPT entry label.
+
diff --git a/swupdate-adapter/swupdate.md b/swupdate-adapter/swupdate.md
index 334aa5f..7df0c43 100644
--- a/swupdate-adapter/swupdate.md
+++ b/swupdate-adapter/swupdate.md
@@ -1,5 +1,68 @@
# Update process with swupdate #

+
+## Update state mapping ##
+
+Swupdate-Suricatta works with an internal state variable, which is called `ustate`
+per default.
+
+The values of common interest are:
+
+ustate | meaning
+--------|-----------------------------------
+0 | nothing to do
+1 | update installed (reboot pending)
+2 | update testing (after reboot)
+3 | update failed
+4 | state not available
+
+`efibootguard` works with three internal variables regarding the update mechanism:
+
+* `revision`
+* `testing`
+* `boot_once`
+
+The values of these variables are mapped onto ustate according to the following matrix:
+
+*Note*: A failed revision exists, if its `revision` is `0` and at the same time,
+both `boot_once` and `testing` are set to `1`. If such a revision exists in any of
+the stored environment partitions, this is marked as [FAILED] in the matrix below.
+
+`efibootguard` | `suricatta`
+---------------------------------------------------------------------------|------------
+ (current env.)<br />testing = 0<br />boot_once = 0<br /><br />NOT [FAILED]| ustate = 0
+ (current env.)<br />testing = 1<br />boot_once = 0<br /><br />NOT [FAILED]| ustate = 1
+ (current env.)<br />testing = 1<br />boot_once = 1<br /><br />NOT [FAILED]| ustate = 2
+ [FAILED] | ustate = 3
+ Environment<br />Error | ustate = 4
@@ -93,17 +155,19 @@ Test conditions:

Function to retrieve state: `ebg_env_isinstalled()`

-## Rebooting ##
+### Rebooting ###

-efibootguard will detect the `testing` flag and set `boot_once`. This can be simulated by
+efibootguard will detect the `testing` flag and set `boot_once`. This can be
+simulated by

```
bg_setenv -p X -b
```

-where `X` is the 0-based index of the config partition to be updated. This sets the `boot_once` flag.
+where `X` is the 0-based index of the config partition to be updated. This sets
+the `boot_once` flag.

-### Resulting Environment ###
+#### Resulting Environment ####

```
Config Partition #0 Values:
@@ -131,13 +195,13 @@ Test conditions:

Function to retrieve state: `ebg_env_istesting()`

-## Confirming working update ##
+### Confirming working update ###

```
bg_setenv -c
```

-### Resulting environment ###
+#### Resulting environment ####

```
Config Partition #0 Values:
@@ -165,11 +229,12 @@ Test conditions:

Function to retrieve state: `ebg_env_isokay()`

-## Not confirming and rebooting ##
+### Not confirming and rebooting ###

After rebooting with state == INSTALLED and not confirming,
the resulting environment is:

+#### Resulting environment ####
```
Config Partition #0 Values:
revision: 15
@@ -191,7 +256,8 @@ boot once flag: set
**suricatta state: FAILED**

Test conditions:
-* ebg_env_isupdatesuccessful == FALSE (since a revision is 0 and both flags set in this config)
+* ebg_env_isupdatesuccessful == FALSE (since a revision is 0 and both flags set
+ in this config)

### Manually resetting failure state ###

@@ -202,7 +268,7 @@ bg_setenv -u -t 0
replaces the oldest environment with these and then updates the
newly set values (-t 0).*

-### Resulting environment ###
+#### Resulting environment ####

```
Config Partition #0 Values:
@@ -222,14 +288,3 @@ test flag: disabled
boot once flag: not set
```

-# State mapping #
-
-Condition A is ebg_env_isupdatesuccessful == true
-
-Suricatta state | condition | function
--------------------------------------------------------------------------------------
-FAILED | !A | !ebg_env_isupdatesuccessful()
-OK | A && testing == 0 | ebg_env_isokay()
-INSTALLED | A && testing == 1 && boot_once == 0 | ebg_env_isinstalled()
-TESTING | A && testing == 1 && boot_once == 1 | ebg_env_istesting()
-
--
2.13.3

Jan Kiszka

unread,
Jul 21, 2017, 12:28:25 PM7/21/17
to [ext] Reichel Andreas, efibootg...@googlegroups.com
On 2017-07-21 15:17, [ext] Reichel Andreas wrote:
> From: Andreas Reichel <andreas.r...@siemens.com>
>
> From: Andreas Reichel <andreas.r...@siemens.com>

"Doppelt hält besser"? ;)
Still "static" missing - I'll write a patch that fixes all cases.
Merged, thanks.

Jan Kiszka

unread,
Jul 21, 2017, 12:30:27 PM7/21/17
to [ext] Reichel Andreas, efibootg...@googlegroups.com
On 2017-07-21 15:17, [ext] Reichel Andreas wrote:
Merged them all.
Reply all
Reply to author
Forward
0 new messages