[PATCH 0/4] Add features and improve docs

13 views
Skip to first unread message

Reichel Andreas

unread,
Jul 11, 2017, 9:41:33 AM7/11/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 | 97 ++++++++++++---
tools/ebgpart.h | 3 +-
12 files changed, 689 insertions(+), 301 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 11, 2017, 9:41:33 AM7/11/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 | 97 ++++++++++++++++++++++++++++++++++++++++++++++++---------
tools/ebgpart.h | 3 +-
2 files changed, 85 insertions(+), 15 deletions(-)

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

-void ped_device_probe_all()
+void scan_devdir(unsigned int fmajor, unsigned int fminor, char *fullname,
+ unsigned int maxlen)
{
+ DIR *devdir = opendir(DEVDIR);
+ if (!devdir) {
+ VERBOSE(stderr, "Failed to open %s\n", DEVDIR);
+ return;
+ }
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);
+ break;
+ }
+ fullname[0] = 0;
+ } while (devfile);
+ closedir(devdir);
+}
+
+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[256];

- DIR *devdir = opendir(DEVDIRNAME);
- if (!devdir) {
- VERBOSE(stderr, "Could not open %s\n", DEVDIRNAME);
+ 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)
- break;
- if (strcmp(devfile->d_name, ".") == 0 ||
- strcmp(devfile->d_name, "..") == 0)
+ sysblockfile = readdir(sysblockdir);
+ if (!sysblockfile) break;
+ if (strcmp(sysblockfile->d_name, ".") == 0 ||
+ strcmp(sysblockfile->d_name, "..") == 0)
continue;
-
- snprintf(fullname, 255, "/dev/%s", devfile->d_name);
+ snprintf(fullname, 255, "/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) == -1) {
+ 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, 255, "%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 */
+ scan_devdir(fmajor, fminor, fullname, 255);
+ }
+ if (strlen(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");
@@ -385,9 +454,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..679a1d7 100644
--- a/tools/ebgpart.h
+++ b/tools/ebgpart.h
@@ -41,7 +41,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 11, 2017, 9:41:33 AM7/11/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

Claudius Heine

unread,
Jul 11, 2017, 2:36:48 PM7/11/17
to Reichel Andreas, efibootg...@googlegroups.com
scan_devdir should have an explicit return value about the success of
it. Don't do it over the fullname length.

> {
> + DIR *devdir = opendir(DEVDIR);
> + if (!devdir) {
> + VERBOSE(stderr, "Failed to open %s\n", DEVDIR);

Shouldn't that also set `fullname[0] = 0`? If that is the case, then
this shows why a return value is better for this stuff.

> + return;
> + }
> 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);
> + break;
> + }
> + fullname[0] = 0;

Hmm, you might have to check if maxlen >= 1 before doing that ;)
Go with a decent return value instead, please.
255/256 is a constant, give it a name. define or just constant, for me
thats not that important. But maybe define in the header might be the
right way to do.

Also PRINTF(3):

The functions snprintf() and vsnprintf() write at most size bytes
(including the terminating null byte ('\0')) to str.

So correct me if I am wrong, but shouldn't there be 256 instead of 255?
Maybe just use sizeof(fullname)?

> + sysblockfile->d_name);
> + /* Get major and minor revision from
> /sys/block/sdX/dev */
> + unsigned int fmajor, fminor;
> + if (get_major_minor(fullname, &fmajor, &fminor) ==
> -1) {

Minor issue: I would prefer < 0 in these cases. If a function is
expanded later to cover multiple different error values, you don't need
to fix this.

> + 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, 255, "%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 */
> + scan_devdir(fmajor, fminor, fullname, 255);
> + }

If you have a scan_devdir return value, you can easily (and cleanly)
add additional ways to get to the block device path later. mknod if
that is enabled via command line switch/enabled on compile time.
--
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
Reply all
Reply to author
Forward
0 new messages