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

Simon Glass sjg at chromium.org
Wed Aug 9 04:03:49 CEST 2023


Hi Frieder,

On Mon, 7 Aug 2023 at 06:19, Frieder Schrempf
<frieder.schrempf at kontron.de> wrote:
>
> Hi Simon, hi Stefano,
>
> On 04.08.23 15:39, Stefano Babic wrote:
> > Hi Simon,
> >
> > On 04.08.23 05:42, Simon Glass wrote:
> >> Hi,
> >>
> >> On Thu, 3 Aug 2023 at 07:21, Stefano Babic <sbabic at denx.de> wrote:
> >>>
> >>> Hi Frieder,
> >>>
> >>> On 03.08.23 14:51, Frieder Schrempf wrote:
> >>>> Hi Stefano,
> >>>>
> >>>> On 03.08.23 10:14, Stefano Babic wrote:
> >>>>> 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 ?
> >>>>
> >>>> Thanks for the feedback! I've seen the YAML configuration feature in
> >>>> libubootenv but I missed the fact, that it already supports something
> >>>> like the namespace parameter for selecting different configurations.
> >>>>
> >>>> Indeed this would solve one part of the issue. And yes, I think we
> >>>> could
> >>>> use the DT or a kernel parameter to pass a selector from U-Boot to
> >>>> userspace.
> >>>
> >>> Exactly.
> >>>
> >>>>
> >>>> Do you think we could add something to libubootenv to automatically
> >>>> select the default namespace based on some DT property or kernel
> >>>> parameter?
> >>>
> >>> Yes, we just need to add a way to set up a default "namespace". There is
> >>> currently a default "uboot" namespace, and we just need a way to pass
> >>> the information to the library. Maybe with an env ?
> >>>
> >>>>
> >>>> If yes, I think this would be a viable solution for me.
> >>>
> >>> Fine !
> >>>
> >>>>
> >>>>>
> >>>>>>
> >>>>>> 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 ?
> >>>>
> >>>> Yeah, I get your point. My idea was that the bootloader exports
> >>>> "everything" it knows about the environment to the DT. But indeed,
> >>>> there
> >>>> is some (minimal?) risk that changes to the exported data are needed
> >>>> which would require the bootloader to be updated.
> >>>
> >>> Right - the idea with libubootenv is that it is hardware-independent,
> >>> and setup is done just via configuration files.
> >>>
> >>>>
> >>>> Using the YAML config with multiple namespaces sounds like a good
> >>>> alternative. But detecting the right namespace to use can only be done
> >>>> based on information passed by the bootloader.
> >>>
> >>> Agree.
> >>>
> >>>> So if we could build a
> >>>> default namespace selection by DT property or kernel parameter into
> >>>> libubootenv that would be great.
> >>>
> >>> Of course, we can !
> >>>
> >>> Best regards,
> >>> Stefano
> >>>
> >>>>
> >>>> Thanks
> >>>> Frieder
> >>
> >> It seems to me that U-Boot controls where the env areas are and which
> >> ones are used. How can we have a file in the root fs in that case? If
> >> we update the firmware, who updates the file?
> >
> > I think it is adifferent topic. This is raises a compatibility issue
> > between User Space and bootloader, and the update concept must of course
> > take care of this, that means, if U-Boot is updated, the SW (rootfs)
> > must be updated as well if not compatible anymore. This already happens
> > any time there is compatibility issue between U-Boot and User Space
> > (switch from legacy image to fitImage for example, etc.).
> >
> > Some sort of automatic detection in user space can also be possible,
> >
> >>
> >> Re the DT binding, we should send that upstream. I think it would be
> >> better if we can indicate which device holds each env, e.g. with a
> >> phandle from that device, or a phandle the other way? I suspect Rob
> >> Herring will have some ideas there.
> >
> > My point is that, apart the added dependencies between U-Boot and User
> > Space, we still need a sort of intermediate layer in User Space to map
> > between such as device index / phandle, and the device node. A location
> > = <X> does not help a lot when we access from User Space, and such sort
> > of mapping can be simply avoided by configuring tools in USer Space with
> > the names provided by the kernel, that can change from board to board
> > (for example depends on the order MTD devices are detected).
>
> A rather sophisticated solution could maybe work without needing a
> separate mapping between U-Boot env device and kernel device.
>
> Given that U-Boot is able to match the env device with a node in the
> devicetree passed to the kernel it could store a phandle to that node
> (e.g. /chosen/u-boot,env = <&mmc1>).

Yes.

>
> For specifying a partition/offset we would need a representation of the
> partition in the devicetree. This would work for MTD devices. I'm not
> sure about eMMC with hardware partitions (boot0, boot1, etc.). If they
> could be represented in DT this could work, too. No idea what about
> other potential env devices.
>
> I also see that there is already some binding for MTD devices to specify
> the U-Boot env partition in [1].
>
> Anyway this is way beyond what I can do at the moment and our primary
> goal to create a root FS that supports multiple env devices and is able
> to determine the one that is currently in use, can be achieved with
> minor extensions of U-Boot and libubootenv as proposed above by Stefano.
> So I think I will try to go down that road for now.

OK.

>
> Best regards
> Frieder
>
> [1]
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/mtd/partitions/u-boot.yaml

Regards,
Simon


More information about the U-Boot mailing list