[RFC PATCH] env: Export environment config to OS devicetree

Stefano Babic sbabic at denx.de
Thu Aug 3 10:14:43 CEST 2023


Hi Frieder,

On 01.08.23 16:46, Frieder Schrempf wrote:
> From: Frieder Schrempf <frieder.schrempf at kontron.de>
> 
> There are plenty of cases where the OS wants to know where the
> persistent environment is stored in order to print or manipulate it.
> 
> In most cases a userspace tool like libubootenv [1] is used to handle
> the environment. It uses hardcoded settings in a config file in the
> rootfs to determine the exact location of the environment.
> 

...or the configuration file is generated, or it is located on a rw 
(overlayfs) partition.

> In order to make systems more flexible and let userspace tools
> detect the location of the environment currently in use, let's
> add an option to export the runtime configuration to the FDT passed
> to the OS.
> > This is especially useful when your device is able to use multiple
> locations for the environment and the rootfs containing the
> fw_env.config file should be kept universal.

Ok

One possible issue is there is a hard dependency between the properties 
set in DT and the user space tool. The tool in user space is bound to 
the list of properties, and changes / extensions for the user tool 
requires changes in DT semantics (which properties, their path, etc).

The same hard dependency is set by fw_env.config with its strong 
hardcoded and position dependent syntax. This one of the reason I 
introduced YAML support in libubootenv, so that the user tool is 
independent and can add own properties.

I am just asking if this use case requires a new interface, or it is 
already available in some way. I mean, the configuration is YAML instead 
of fw_env.config and contains multiple setup ("namespaces"), like:

mmc:
   size : 0x100000
   lockfile : /var/lock/fw_printenv.lock
   devices:
     - path : /dev/mmcblk0boot0
       offset : 0x1E0000
       sectorsize : 0x100000
     - path : /dev/mmcblk0boot0
       offset : 0x1E0000
       sectorsize : 0x100000
spi:
   size : 0x100000
   lockfile : /var/lock/fw_printenv.lock
   devices:
     - path : /dev/mtd0
       offset : 0x1E0000
       sectorsize : 0x100000
     - path : /dev/mtd0
       offset : 0x1F0000
       sectorsize : 0x100000

This configuration file can safely stored in your common rootfs and 
covers all possible storage for environment. It does not cover "runtime" 
changes, but well, I do not think this is a use case.

An advantage here is that new options, useful for the User Space tool, 
can be simply introduced for the tool without the need to synchronize 
with DT spec and U-Boot.

A mapping between location index and device path is not required. What 
is still required is a selector, and this can be implemented in DT or as 
kernel parameter.

Does this already cover your use case or do we need the introduction of 
this new interface ?

> 
> Currently the general information like location/type, size, offset
> and redundancy are exported. Userspace tools might already be able
> to guess the correct device to access based on this information.
> 
> For further device-specific information two additional properties
> 'id1' and 'id2' are used. The current implementation uses them for
> MMC and SPI FLASH like this:
> 
> | Type       | id1                        | id2                        |
> | ---------- | -------------------------- | -------------------------- |
> | MMC        | MMC device index in U-Boot | MMC hwpart index in U-Boot |
> | SPI FLASH  | SPI bus index in U-Boot    | SPI CS index in U-Boot     |
> 
> Further extensions for other environment devices are possible.
> 
> We add the necessary information to the '/chosen' node. The following
> shows two examples:
> 
> Redundant environment in MMC:
> 
>    /chosen {
>      u-boot,env-config {
>        location = <5>;              # according to 'enum env_location'
>        offset = <0x1E0000>;
>        size = <0x100000>;
>        sect_size = <0x100000>;
>        id1 = <1>;                   # MMC device index
>        id2 = <0>;                   # MMC HW partition index
>      };
>      u-boot,env-redund-config {
>        offset = <0x1F0000>;
>      };
>    };
> 
> Redundant environment in SPI NOR:
> 
>    /chosen {
>      u-boot,env-config {
>        location = <10>;             # according to 'enum env_location'
>        offset = <0x1E0000>;
>        size = <0x100000>;
>        sect_size = <0x100000>;
>        id1 = <0>;                   # SPI bus
>        id2 = <0>;                   # SPI CS
>      };
>      u-boot,env-redund-config {
>        offset = <0x1F0000>;
>      };
>    };
> 
> See [2] for an example parser implementation for libubootenv.
> 
> [1] https://github.com/sbabic/libubootenv
> [2] https://github.com/fschrempf/libubootenv/commit/726a7ac9b1b1020354ee8fe750f4cad3df01f5e7
> 
> Cc: Stefano Babic <sbabic at denx.de>
> Cc: Michael Walle <michael at walle.cc>
> Signed-off-by: Frieder Schrempf <frieder.schrempf at kontron.de>
> ---
>   boot/Kconfig     |  9 ++++++++
>   boot/image-fdt.c |  8 ++++++++
>   env/env.c        | 53 ++++++++++++++++++++++++++++++++++++++++++++++++
>   include/env.h    |  4 ++++
>   4 files changed, 74 insertions(+)
> 
> diff --git a/boot/Kconfig b/boot/Kconfig
> index e8fb03b801..16a94f9b35 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -702,6 +702,15 @@ config OF_BOARD_SETUP
>   	  board-specific information in the device tree for use by the OS.
>   	  The device tree is then passed to the OS.
>   
> +config OF_EXPORT_ENV_CONFIG
> +	bool "Export environment config to device tree before boot"
> +	depends on OF_LIBFDT
> +	help
> +	  This causes U-Boot to call fdt_environment_config() before booting into
> +	  the operating system. This function exports the currently in use
> +	  environemnt configuration to the "chosen" node of the fdt. This allows
> +	  the OS to determine where the environment is located.
> +
>   config OF_SYSTEM_SETUP
>   	bool "Set up system-specific details in device tree before boot"
>   	depends on OF_LIBFDT
> diff --git a/boot/image-fdt.c b/boot/image-fdt.c
> index f10200f647..c02c8f33ef 100644
> --- a/boot/image-fdt.c
> +++ b/boot/image-fdt.c
> @@ -643,6 +643,14 @@ int image_setup_libfdt(struct bootm_headers *images, void *blob,
>   	/* Append PStore configuration */
>   	fdt_fixup_pstore(blob);
>   #endif
> +	if (IS_ENABLED(CONFIG_OF_EXPORT_ENV_CONFIG)) {
> +		fdt_ret = fdt_environment_config(blob);
> +		if (fdt_ret) {
> +			printf("ERROR: environment config fdt fixup failed: %s\n",
> +			       fdt_strerror(fdt_ret));
> +			goto err;
> +		}
> +	}
>   	if (IS_ENABLED(CONFIG_OF_BOARD_SETUP)) {
>   		const char *skip_board_fixup;
>   
> diff --git a/env/env.c b/env/env.c
> index 2aa52c98f8..a640977205 100644
> --- a/env/env.c
> +++ b/env/env.c
> @@ -8,6 +8,7 @@
>   #include <env.h>
>   #include <env_internal.h>
>   #include <log.h>
> +#include <mmc.h>
>   #include <asm/global_data.h>
>   #include <linux/bitops.h>
>   #include <linux/bug.h>
> @@ -156,6 +157,58 @@ __weak enum env_location env_get_location(enum env_operation op, int prio)
>   	return arch_env_get_location(op, prio);
>   }
>   
> +#ifdef CONFIG_OF_EXPORT_ENV_CONFIG
> +int fdt_environment_config(void *blob)
> +{
> +	struct mmc *mmc;
> +	u32 location;
> +	int chosen_offs, offs;
> +	int ret;
> +
> +	if (CONFIG_IS_ENABLED(ENV_IS_NOWHERE))
> +		return 0;
> +
> +	chosen_offs = fdt_path_offset(blob, "/chosen");
> +	if (chosen_offs < 0) {
> +		printf("Could not find chosen node.\n");
> +		return chosen_offs;
> +	}
> +
> +	offs = fdt_add_subnode(blob, chosen_offs, "u-boot,env-config");
> +	if (offs < 0) {
> +		printf("Could not create env-config node.\n");
> +		return offs;
> +	}
> +
> +	location = env_get_location(0, 0);
> +	fdt_setprop_u32(blob, offs, "location", location);
> +	fdt_setprop_u32(blob, offs, "offset", CONFIG_ENV_OFFSET);
> +	fdt_setprop_u32(blob, offs, "size", CONFIG_ENV_SIZE);
> +	fdt_setprop_u32(blob, offs, "sect_size", CONFIG_ENV_SECT_SIZE);
> +

My major concern is regarding the dependency that is introduced. These 
properties are then compiled into U-Boot. If a new property will be 
required in future, this could required an update for the bootloader in 
field. Even if yes, bootloader's updates are done in field, they are 
often risky and not power-cut safe.

If the whole configuration, new properties, etc, remains as much as 
possible in User Space, to get the new feature we need an update of 
rootfs (in your example), that is (mostly) safe (and then it is not, the 
update procedure should be revisited). A single rootfs for multiple 
hardware using different locations and maybe different options (if 
needed or if they will be introduced) is possible, the yaml 
configuration will contain all of them and some detection (in user 
space) can do the trick to select the right one.

What do you think ?

Regards,
Stefano

> +	if (CONFIG_IS_ENABLED(ENV_IS_IN_MMC) && location == ENVL_MMC) {
> +		fdt_setprop_u32(blob, offs, "id1", mmc_get_env_dev());
> +		mmc = find_mmc_device(mmc_get_env_dev());
> +		if (mmc)
> +			fdt_setprop_u32(blob, offs, "id2", mmc_get_env_part(mmc));
> +	} else if (CONFIG_IS_ENABLED(ENV_IS_IN_SPI_FLASH) && location == ENVL_SPI_FLASH) {
> +		fdt_setprop_u32(blob, offs, "id1", CONFIG_ENV_SPI_BUS);
> +		fdt_setprop_u32(blob, offs, "id2", CONFIG_ENV_SPI_CS);
> +	}
> +
> +	if (CONFIG_IS_ENABLED(SYS_REDUNDAND_ENVIRONMENT)) {
> +		offs = fdt_add_subnode(blob, chosen_offs, "u-boot,env-redund-config");
> +		if (offs < 0) {
> +			printf("Could not create env-redund-config node.\n");
> +			return offs;
> +		}
> +		fdt_setprop_u32(blob, offs, "offset", CONFIG_ENV_OFFSET_REDUND);
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
>   /**
>    * env_driver_lookup() - Finds the most suited environment location
>    * @op: operations performed on the environment
> diff --git a/include/env.h b/include/env.h
> index 1480efa59e..f9b45a4925 100644
> --- a/include/env.h
> +++ b/include/env.h
> @@ -377,4 +377,8 @@ void env_import_fdt(void);
>   static inline void env_import_fdt(void) {}
>   #endif
>   
> +#ifdef CONFIG_OF_EXPORT_ENV_CONFIG
> +int fdt_environment_config(void *blob);
> +#endif
> +
>   #endif

-- 
=====================================================================
DENX Software Engineering GmbH,        Managing Director: Erika Unter
HRB 165235 Munich,   Office: Kirchenstr.5, 82194 Groebenzell, Germany
Phone: +49-8142-66989-53 Fax: +49-8142-66989-80 Email: sbabic at denx.de
=====================================================================



More information about the U-Boot mailing list