Merge branch '2022-08-11-verified-boot-for-embedded-initial-support'
authorTom Rini <trini@konsulko.com>
Fri, 12 Aug 2022 16:51:14 +0000 (12:51 -0400)
committerTom Rini <trini@konsulko.com>
Fri, 12 Aug 2022 16:51:14 +0000 (12:51 -0400)
To quote Simon:
This adds the concept of a VBE method to U-Boot, along with an
implementation of the 'VBE simple' method, basically a simple way of
updating firmware in MMC from userspace and monitoring it from U-Boot.

VBE simple is implemented in fwupd. U-Boot's role is to set up the
device tree with the required firmware-update properties and provide the
developer with information about the current VBE state. To that end this
series includes a new 'vbe' command that allows VBE methods to be listed
and examined.

As part of this work, support for doing FDT fixups via the event interface
is provided, along with the ability to write to the device tree via the
ofnode interface.

Another (significant) change is that bootmeths now have a 'global' flag,
to allow the implementation of EFI bootmgr (and VBE) to be cleaned up.
The 'system' bootdev is no-longer needed and these bootmeths are scanned
first.

Further work is needed to pull everything together, but this is a step
along the way.

64 files changed:
arch/sandbox/dts/sandbox.dtsi
arch/sandbox/dts/test.dts
arch/sandbox/include/asm/test.h
arch/x86/lib/bios.c
arch/x86/lib/coreboot_table.c
arch/x86/lib/fsp/fsp_graphics.c
boot/Kconfig
boot/Makefile
boot/bootdev-uclass.c
boot/bootflow.c
boot/bootmeth-uclass.c
boot/bootmeth_distro.c
boot/bootmeth_efi_mgr.c
boot/bootstd-uclass.c
boot/image-fdt.c
boot/system_bootdev.c [deleted file]
boot/vbe.c [new file with mode: 0644]
boot/vbe_simple.c [new file with mode: 0644]
cmd/Kconfig
cmd/Makefile
cmd/bootflow.c
cmd/bootmeth.c
cmd/elf.c
cmd/read.c
cmd/vbe.c [new file with mode: 0644]
common/event.c
configs/sandbox_flattree_defconfig
doc/develop/bootstd.rst
doc/develop/driver-model/livetree.rst
doc/develop/index.rst
doc/develop/vbe.rst [new file with mode: 0644]
doc/usage/cmd/bootmeth.rst
drivers/bios_emulator/atibios.c
drivers/core/of_access.c
drivers/core/ofnode.c
drivers/pci/pci_rom.c
drivers/video/broadwell_igd.c
drivers/video/coreboot.c
drivers/video/efi.c
drivers/video/ivybridge_igd.c
drivers/video/vesa.c
include/bios_emul.h
include/bootflow.h
include/bootmeth.h
include/bootstd.h
include/dm/of_access.h
include/dm/ofnode.h
include/dm/ofnode_decl.h [new file with mode: 0644]
include/event.h
include/of_live.h
include/test/test.h
include/vbe.h
include/vesa.h [new file with mode: 0644]
lib/efi_loader/Kconfig
lib/elf.c
lib/of_live.c
test/boot/Makefile
test/boot/bootflow.c
test/boot/bootmeth.c
test/boot/vbe_simple.c [new file with mode: 0644]
test/dm/ofnode.c
test/dm/test-fdt.c
test/py/tests/test_event_dump.py
test/test-main.c

index aa22b87..56e6b38 100644 (file)
 
        chosen {
                stdout-path = "/serial";
+
+               fwupd {
+                       compatible = "simple-bus";
+                       firmware {
+                               compatible = "fwupd,vbe-simple";
+                               cur-version = "1.2.3";
+                               bootloader-version = "2022.01";
+                               storage = "mmc1";
+                               area-start = <0x0>;
+                               area-size = <0x1000000>;
+                               skip-offset = <0x8000>;
+                       };
+               };
        };
 
        audio: audio-codec {
index d1a8cc7..2761588 100644 (file)
                        compatible = "denx,u-boot-fdt-test";
                        reg = <9 1>;
                };
+
+               fwupd {
+                       compatible = "simple-bus";
+                       firmware0 {
+                               compatible = "fwupd,vbe-simple";
+                               storage = "mmc1";
+                               area-start = <0x400>;
+                               area-size = <0x1000>;
+                               skip-offset = <0x200>;
+                               state-offset = <0x400>;
+                               state-size = <0x40>;
+                               version-offset = <0x800>;
+                               version-size = <0x100>;
+                       };
+               };
        };
 
        translation-test@8000 {
index 015e96d..53a036b 100644 (file)
@@ -304,4 +304,15 @@ int sandbox_cros_ec_get_pwm_duty(struct udevice *dev, uint index, uint *duty);
  */
 int sandbox_sdl_set_bpp(struct udevice *dev, enum video_log2_bpp l2bpp);
 
+/**
+ * sandbox_set_fake_efi_mgr_dev() - Control EFI bootmgr producing valid bootflow
+ *
+ * This is only used for testing.
+ *
+ * @dev: efi_mgr bootmeth device
+ * @fake_dev: true to produce a valid bootflow when requested, false to produce
+ * an error
+ */
+void sandbox_set_fake_efi_mgr_dev(struct udevice *dev, bool fake_dev);
+
 #endif
index 98cc05d..94349ba 100644 (file)
@@ -10,7 +10,7 @@
 #include <bios_emul.h>
 #include <irq_func.h>
 #include <log.h>
-#include <vbe.h>
+#include <vesa.h>
 #include <linux/linkage.h>
 #include <asm/cache.h>
 #include <asm/processor.h>
@@ -190,7 +190,7 @@ static void setup_realmode_idt(void)
 }
 
 #ifdef CONFIG_FRAMEBUFFER_SET_VESA_MODE
-static u8 vbe_get_mode_info(struct vbe_mode_info *mi)
+static u8 vbe_get_mode_info(struct vesa_state *mi)
 {
        u16 buffer_seg;
        u16 buffer_adr;
@@ -204,13 +204,13 @@ static u8 vbe_get_mode_info(struct vbe_mode_info *mi)
 
        realmode_interrupt(0x10, VESA_GET_MODE_INFO, 0x0000, mi->video_mode,
                           0x0000, buffer_seg, buffer_adr);
-       memcpy(mi->mode_info_block, buffer, sizeof(struct vbe_mode_info));
+       memcpy(mi->mode_info_block, buffer, sizeof(struct vesa_state));
        mi->valid = true;
 
        return 0;
 }
 
-static u8 vbe_set_mode(struct vbe_mode_info *mi)
+static u8 vbe_set_mode(struct vesa_state *mi)
 {
        int video_mode = mi->video_mode;
 
@@ -225,7 +225,7 @@ static u8 vbe_set_mode(struct vbe_mode_info *mi)
        return 0;
 }
 
-static void vbe_set_graphics(int vesa_mode, struct vbe_mode_info *mode_info)
+static void vbe_set_graphics(int vesa_mode, struct vesa_state *mode_info)
 {
        unsigned char *framebuffer;
 
@@ -249,7 +249,7 @@ static void vbe_set_graphics(int vesa_mode, struct vbe_mode_info *mode_info)
 #endif /* CONFIG_FRAMEBUFFER_SET_VESA_MODE */
 
 void bios_run_on_x86(struct udevice *dev, unsigned long addr, int vesa_mode,
-                    struct vbe_mode_info *mode_info)
+                    struct vesa_state *mode_info)
 {
        pci_dev_t pcidev = dm_pci_get_bdf(dev);
        u32 num_dev;
index 6eab045..05519d8 100644 (file)
@@ -6,7 +6,7 @@
 #include <common.h>
 #include <malloc.h>
 #include <net.h>
-#include <vbe.h>
+#include <vesa.h>
 #include <acpi/acpi_s3.h>
 #include <asm/coreboot_tables.h>
 #include <asm/e820.h>
index 02fd05c..b07c666 100644 (file)
@@ -9,7 +9,7 @@
 #include <dm.h>
 #include <init.h>
 #include <log.h>
-#include <vbe.h>
+#include <vesa.h>
 #include <video.h>
 #include <acpi/acpi_table.h>
 #include <asm/fsp/fsp_support.h>
@@ -106,7 +106,7 @@ static int fsp_video_probe(struct udevice *dev)
        vesa->phys_base_ptr = dm_pci_read_bar32(dev, 2);
        gd->fb_base = vesa->phys_base_ptr;
 
-       ret = vbe_setup_video_priv(vesa, uc_priv, plat);
+       ret = vesa_setup_video_priv(vesa, uc_priv, plat);
        if (ret)
                goto err;
 
index a294ad7..6b3b8f0 100644 (file)
@@ -349,6 +349,13 @@ config BOOTSTD_BOOTCOMMAND
          standard boot does not support all of the features of distro boot
          yet.
 
+config BOOTMETH_GLOBAL
+       bool
+       help
+         Add support for global bootmeths. This feature is used by VBE and
+         EFI bootmgr, since they take full control over which bootdevs are
+         selected to boot.
+
 config BOOTMETH_DISTRO
        bool "Bootdev support for distro boot"
        select PXE_UTILS
@@ -391,6 +398,28 @@ config BOOTMETH_EFILOADER
 
          This provides a way to try out standard boot on an existing boot flow.
 
+config BOOTMETH_VBE
+       bool "Bootdev support for Verified Boot for Embedded"
+       depends on FIT
+       default y
+       select BOOTMETH_GLOBAL
+       help
+         Enables support for VBE boot. This is a standard boot method which
+         supports selection of various firmware components, seleciton of an OS to
+         boot as well as updating these using fwupd.
+
+if BOOTMETH_VBE
+
+config BOOTMETH_VBE_SIMPLE
+       bool "Bootdev support for VBE 'simple' method"
+       default y
+       help
+         Enables support for VBE 'simple' boot. This allows updating a single
+         firmware image in boot media such as MMC. It does not support any sort
+         of rollback, recovery or A/B boot.
+
+endif # BOOTMETH_VBE
+
 config BOOTMETH_SANDBOX
        def_bool y
        depends on SANDBOX
index 124065a..67e3352 100644 (file)
@@ -18,7 +18,7 @@ obj-y += image.o image-board.o
 obj-$(CONFIG_ANDROID_AB) += android_ab.o
 obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o image-android-dt.o
 
-obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootdev-uclass.o system_bootdev.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootdev-uclass.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootflow.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootmeth-uclass.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootstd-uclass.o
@@ -46,3 +46,6 @@ obj-$(CONFIG_CMD_ADTIMG) += image-android-dt.o
 ifdef CONFIG_SPL_BUILD
 obj-$(CONFIG_SPL_LOAD_FIT) += common_fit.o
 endif
+
+obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE) += vbe.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_VBE_SIMPLE) += vbe_simple.o
index 1ede933..13ac69e 100644 (file)
@@ -36,7 +36,6 @@ enum {
 
 int bootdev_add_bootflow(struct bootflow *bflow)
 {
-       struct bootdev_uc_plat *ucp = dev_get_uclass_plat(bflow->dev);
        struct bootstd_priv *std;
        struct bootflow *new;
        int ret;
@@ -52,7 +51,11 @@ int bootdev_add_bootflow(struct bootflow *bflow)
        memcpy(new, bflow, sizeof(*bflow));
 
        list_add_tail(&new->glob_node, &std->glob_head);
-       list_add_tail(&new->bm_node, &ucp->bootflow_head);
+       if (bflow->dev) {
+               struct bootdev_uc_plat *ucp = dev_get_uclass_plat(bflow->dev);
+
+               list_add_tail(&new->bm_node, &ucp->bootflow_head);
+       }
 
        return 0;
 }
@@ -604,14 +607,14 @@ int bootdev_setup_iter_order(struct bootflow_iter *iter, struct udevice **devp)
                log_debug("Expected %d bootdevs, found %d using aliases\n",
                          count, upto);
 
-       count = build_order(bootstd, order, upto);
-       if (count < 0) {
+       ret = build_order(bootstd, order, upto);
+       if (ret < 0) {
                free(order);
-               return log_msg_ret("build", count);
+               return log_msg_ret("build", ret);
        }
 
+       iter->num_devs = ret;
        iter->dev_order = order;
-       iter->num_devs = count;
        iter->cur_dev = 0;
 
        dev = *order;
index 24ba3c3..5d94a27 100644 (file)
@@ -86,6 +86,7 @@ int bootflow_next_glob(struct bootflow **bflowp)
 void bootflow_iter_init(struct bootflow_iter *iter, int flags)
 {
        memset(iter, '\0', sizeof(*iter));
+       iter->first_glob_method = -1;
        iter->flags = flags;
 }
 
@@ -115,11 +116,17 @@ int bootflow_iter_drop_bootmeth(struct bootflow_iter *iter,
 static void bootflow_iter_set_dev(struct bootflow_iter *iter,
                                  struct udevice *dev)
 {
+       struct bootmeth_uc_plat *ucp = dev_get_uclass_plat(iter->method);
+
        iter->dev = dev;
        if ((iter->flags & (BOOTFLOWF_SHOW | BOOTFLOWF_SINGLE_DEV)) ==
            BOOTFLOWF_SHOW) {
                if (dev)
                        printf("Scanning bootdev '%s':\n", dev->name);
+               else if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) &&
+                        ucp->flags & BOOTMETHF_GLOBAL)
+                       printf("Scanning global bootmeth '%s':\n",
+                              iter->method->name);
                else
                        printf("No more bootdevs\n");
        }
@@ -133,8 +140,12 @@ static void bootflow_iter_set_dev(struct bootflow_iter *iter,
 static int iter_incr(struct bootflow_iter *iter)
 {
        struct udevice *dev;
+       bool inc_dev = true;
+       bool global;
        int ret;
 
+       global = iter->doing_global;
+
        if (iter->err == BF_NO_MORE_DEVICES)
                return BF_NO_MORE_DEVICES;
 
@@ -144,6 +155,21 @@ static int iter_incr(struct bootflow_iter *iter)
                        iter->method = iter->method_order[iter->cur_method];
                        return 0;
                }
+
+               /*
+                * If we have finished scanning the global bootmeths, start the
+                * normal bootdev scan
+                */
+               if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && global) {
+                       iter->num_methods = iter->first_glob_method;
+                       iter->doing_global = false;
+
+                       /*
+                        * Don't move to the next dev as we haven't tried this
+                        * one yet!
+                        */
+                       inc_dev = false;
+               }
        }
 
        /* No more bootmeths; start at the first one, and... */
@@ -169,14 +195,18 @@ static int iter_incr(struct bootflow_iter *iter)
        /* ...select next bootdev */
        if (iter->flags & BOOTFLOWF_SINGLE_DEV) {
                ret = -ENOENT;
-       } else if (++iter->cur_dev == iter->num_devs) {
-               ret = -ENOENT;
-               bootflow_iter_set_dev(iter, NULL);
        } else {
-               dev = iter->dev_order[iter->cur_dev];
-               ret = device_probe(dev);
-               if (!log_msg_ret("probe", ret))
-                       bootflow_iter_set_dev(iter, dev);
+               if (inc_dev)
+                       iter->cur_dev++;
+               if (iter->cur_dev == iter->num_devs) {
+                       ret = -ENOENT;
+                       bootflow_iter_set_dev(iter, NULL);
+               } else {
+                       dev = iter->dev_order[iter->cur_dev];
+                       ret = device_probe(dev);
+                       if (!log_msg_ret("probe", ret))
+                               bootflow_iter_set_dev(iter, dev);
+               }
        }
 
        /* if there are no more bootdevs, give up */
@@ -199,6 +229,15 @@ static int bootflow_check(struct bootflow_iter *iter, struct bootflow *bflow)
        struct udevice *dev;
        int ret;
 
+       if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && iter->doing_global) {
+               bootflow_iter_set_dev(iter, NULL);
+               ret = bootmeth_get_bootflow(iter->method, bflow);
+               if (ret)
+                       return log_msg_ret("glob", ret);
+
+               return 0;
+       }
+
        dev = iter->dev;
        ret = bootdev_get_bootflow(dev, iter, bflow);
 
@@ -231,19 +270,22 @@ int bootflow_scan_bootdev(struct udevice *dev, struct bootflow_iter *iter,
 {
        int ret;
 
+       if (dev)
+               flags |= BOOTFLOWF_SKIP_GLOBAL;
        bootflow_iter_init(iter, flags);
 
        ret = bootdev_setup_iter_order(iter, &dev);
        if (ret)
                return log_msg_ret("obdev", -ENODEV);
-       bootflow_iter_set_dev(iter, dev);
 
-       ret = bootmeth_setup_iter_order(iter);
+       ret = bootmeth_setup_iter_order(iter, !(flags & BOOTFLOWF_SKIP_GLOBAL));
        if (ret)
                return log_msg_ret("obmeth", -ENODEV);
 
        /* Find the first bootmeth (there must be at least one!) */
        iter->method = iter->method_order[iter->cur_method];
+       if (!IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) || !iter->doing_global)
+               bootflow_iter_set_dev(iter, dev);
 
        ret = bootflow_check(iter, bflow);
        if (ret) {
@@ -307,7 +349,8 @@ void bootflow_free(struct bootflow *bflow)
 
 void bootflow_remove(struct bootflow *bflow)
 {
-       list_del(&bflow->bm_node);
+       if (bflow->dev)
+               list_del(&bflow->bm_node);
        list_del(&bflow->glob_node);
 
        bootflow_free(bflow);
index c040d5f..2d7652e 100644 (file)
 
 DECLARE_GLOBAL_DATA_PTR;
 
+int bootmeth_get_state_desc(struct udevice *dev, char *buf, int maxsize)
+{
+       const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
+
+       if (!ops->get_state_desc)
+               return -ENOSYS;
+
+       return ops->get_state_desc(dev, buf, maxsize);
+}
+
 int bootmeth_check(struct udevice *dev, struct bootflow_iter *iter)
 {
        const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
@@ -61,18 +71,21 @@ int bootmeth_read_file(struct udevice *dev, struct bootflow *bflow,
        return ops->read_file(dev, bflow, file_path, addr, sizep);
 }
 
-/**
- * bootmeth_setup_iter_order() - Set up the ordering of bootmeths to scan
- *
- * This sets up the ordering information in @iter, based on the selected
- * ordering of the bootmethds in bootstd_priv->bootmeth_order. If there is no
- * ordering there, then all bootmethods are added
- *
- * @iter: Iterator to update with the order
- * Return: 0 if OK, -ENOENT if no bootdevs, -ENOMEM if out of memory, other -ve
- *     on other error
- */
-int bootmeth_setup_iter_order(struct bootflow_iter *iter)
+int bootmeth_get_bootflow(struct udevice *dev, struct bootflow *bflow)
+{
+       const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
+
+       if (!ops->read_bootflow)
+               return -ENOSYS;
+       memset(bflow, '\0', sizeof(*bflow));
+       bflow->dev = NULL;
+       bflow->method = dev;
+       bflow->state = BOOTFLOWST_BASE;
+
+       return ops->read_bootflow(dev, bflow);
+}
+
+int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global)
 {
        struct bootstd_priv *std;
        struct udevice **order;
@@ -95,29 +108,77 @@ int bootmeth_setup_iter_order(struct bootflow_iter *iter)
 
        /* If we have an ordering, copy it */
        if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && std->bootmeth_count) {
+               int i;
+
+               /*
+                * We don't support skipping global bootmeths. Instead, the user
+                * should omit them from the ordering
+                */
+               if (!include_global)
+                       return log_msg_ret("glob", -EPERM);
                memcpy(order, std->bootmeth_order,
                       count * sizeof(struct bootmeth *));
+
+               if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL)) {
+                       for (i = 0; i < count; i++) {
+                               struct udevice *dev = order[i];
+                               struct bootmeth_uc_plat *ucp;
+                               bool is_global;
+
+                               ucp = dev_get_uclass_plat(dev);
+                               is_global = ucp->flags &
+                                       BOOTMETHF_GLOBAL;
+                               if (is_global) {
+                                       iter->first_glob_method = i;
+                                       break;
+                               }
+                       }
+               }
        } else {
                struct udevice *dev;
-               int i, upto;
+               int i, upto, pass;
 
                /*
-                * Get a list of bootmethods, in seq order (i.e. using aliases).
-                * There may be gaps so try to count up high enough to find them
-                * all.
+                * Do two passes, one to find the normal bootmeths and another
+                * to find the global ones, if required, The global ones go at
+                * the end.
                 */
-               for (i = 0, upto = 0; upto < count && i < 20 + count * 2; i++) {
-                       ret = uclass_get_device_by_seq(UCLASS_BOOTMETH, i,
-                                                      &dev);
-                       if (!ret)
-                               order[upto++] = dev;
+               for (pass = 0, upto = 0; pass < 1 + include_global; pass++) {
+                       if (pass)
+                               iter->first_glob_method = upto;
+                       /*
+                        * Get a list of bootmethods, in seq order (i.e. using
+                        * aliases). There may be gaps so try to count up high
+                        * enough to find them all.
+                        */
+                       for (i = 0; upto < count && i < 20 + count * 2; i++) {
+                               struct bootmeth_uc_plat *ucp;
+                               bool is_global;
+
+                               ret = uclass_get_device_by_seq(UCLASS_BOOTMETH,
+                                                              i, &dev);
+                               if (ret)
+                                       continue;
+                               ucp = dev_get_uclass_plat(dev);
+                               is_global =
+                                       IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) &&
+                                       (ucp->flags & BOOTMETHF_GLOBAL);
+                               if (pass ? is_global : !is_global)
+                                       order[upto++] = dev;
+                       }
                }
                count = upto;
        }
+       if (!count)
+               return log_msg_ret("count2", -ENOENT);
 
+       if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && include_global &&
+           iter->first_glob_method != -1 && iter->first_glob_method != count) {
+               iter->cur_method = iter->first_glob_method;
+               iter->doing_global = true;
+       }
        iter->method_order = order;
        iter->num_methods = count;
-       iter->cur_method = 0;
 
        return 0;
 }
index 2b41e65..fea09b2 100644 (file)
 #include <mmc.h>
 #include <pxe_utils.h>
 
+static int distro_get_state_desc(struct udevice *dev, char *buf, int maxsize)
+{
+       if (IS_ENABLED(CONFIG_SANDBOX)) {
+               int len;
+
+               len = snprintf(buf, maxsize, "OK");
+
+               return len + 1 < maxsize ? 0 : -ENOSPC;
+       }
+
+       return 0;
+}
+
 static int disto_getfile(struct pxe_context *ctx, const char *file_path,
                         char *file_addr, ulong *sizep)
 {
@@ -123,6 +136,7 @@ static int distro_bootmeth_bind(struct udevice *dev)
 }
 
 static struct bootmeth_ops distro_bootmeth_ops = {
+       .get_state_desc = distro_get_state_desc,
        .check          = distro_check,
        .read_bootflow  = distro_read_bootflow,
        .read_file      = bootmeth_common_read_file,
index a691446..2f327c1 100644 (file)
 #include <command.h>
 #include <dm.h>
 
+/**
+ * struct efi_mgr_priv - private info for the efi-mgr driver
+ *
+ * @fake_bootflow: Fake a valid bootflow for testing
+ */
+struct efi_mgr_priv {
+       bool fake_dev;
+};
+
+void sandbox_set_fake_efi_mgr_dev(struct udevice *dev, bool fake_dev)
+{
+       struct efi_mgr_priv *priv = dev_get_priv(dev);
+
+       priv->fake_dev = fake_dev;
+}
+
 static int efi_mgr_check(struct udevice *dev, struct bootflow_iter *iter)
 {
        int ret;
@@ -29,13 +45,16 @@ static int efi_mgr_check(struct udevice *dev, struct bootflow_iter *iter)
 
 static int efi_mgr_read_bootflow(struct udevice *dev, struct bootflow *bflow)
 {
-       /*
-        * Just assume there is something to boot since we don't have any way
-        * of knowing in advance
-        */
-       bflow->state = BOOTFLOWST_READY;
+       struct efi_mgr_priv *priv = dev_get_priv(dev);
 
-       return 0;
+       if (priv->fake_dev) {
+               bflow->state = BOOTFLOWST_READY;
+               return 0;
+       }
+
+       /* To be implemented */
+
+       return -EINVAL;
 }
 
 static int efi_mgr_read_file(struct udevice *dev, struct bootflow *bflow,
@@ -61,6 +80,7 @@ static int bootmeth_efi_mgr_bind(struct udevice *dev)
        struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
 
        plat->desc = "EFI bootmgr flow";
+       plat->flags = BOOTMETHF_GLOBAL;
 
        return 0;
 }
@@ -77,10 +97,11 @@ static const struct udevice_id efi_mgr_bootmeth_ids[] = {
        { }
 };
 
-U_BOOT_DRIVER(bootmeth_zefi_mgr) = {
+U_BOOT_DRIVER(bootmeth_efi_mgr) = {
        .name           = "bootmeth_efi_mgr",
        .id             = UCLASS_BOOTMETH,
        .of_match       = efi_mgr_bootmeth_ids,
        .ops            = &efi_mgr_bootmeth_ops,
        .bind           = bootmeth_efi_mgr_bind,
+       .priv_auto      = sizeof(struct efi_mgr_priv),
 };
index 3c6c32a..565c22a 100644 (file)
@@ -133,12 +133,7 @@ int dm_scan_other(bool pre_reloc_only)
                return 0;
 
        for (i = 0; i < n_ents; i++, drv++) {
-               /*
-                * Disable EFI Manager for now as no one uses it so it is
-                * confusing
-                */
-               if (drv->id == UCLASS_BOOTMETH &&
-                   strcmp("efi_mgr_bootmeth", drv->name)) {
+               if (drv->id == UCLASS_BOOTMETH) {
                        const char *name = drv->name;
 
                        if (!strncmp("bootmeth_", name, 9))
@@ -150,12 +145,6 @@ int dm_scan_other(bool pre_reloc_only)
                }
        }
 
-       /* Create the system bootdev too */
-       ret = device_bind_driver(bootstd, "system_bootdev", "system-bootdev",
-                                &dev);
-       if (ret)
-               return log_msg_ret("sys", ret);
-
        return 0;
 }
 
index 9db2cee..5e5b246 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/libfdt.h>
 #include <mapmem.h>
 #include <asm/io.h>
+#include <dm/ofnode.h>
 #include <tee/optee.h>
 
 #ifndef CONFIG_SYS_FDT_PAD
@@ -668,6 +669,16 @@ int image_setup_libfdt(bootm_headers_t *images, void *blob,
                        goto err;
                }
        }
+       if (CONFIG_IS_ENABLED(EVENT)) {
+               struct event_ft_fixup fixup;
+
+               fixup.tree = oftree_default();
+               ret = event_notify(EVT_FT_FIXUP, &fixup, sizeof(fixup));
+               if (ret) {
+                       printf("ERROR: fdt fixup event failed: %d\n", ret);
+                       goto err;
+               }
+       }
 
        /* Delete the old LMB reservation */
        if (lmb)
diff --git a/boot/system_bootdev.c b/boot/system_bootdev.c
deleted file mode 100644 (file)
index 432d203..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Bootdevice for system, used for bootmeths not tied to any partition device
- *
- * Copyright 2021 Google LLC
- * Written by Simon Glass <sjg@chromium.org>
- */
-
-#define LOG_CATEGORY UCLASS_BOOTSTD
-
-#include <common.h>
-#include <bootdev.h>
-#include <bootflow.h>
-#include <bootmeth.h>
-#include <command.h>
-#include <distro.h>
-#include <dm.h>
-#include <log.h>
-#include <net.h>
-
-static int system_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
-                              struct bootflow *bflow)
-{
-       int ret;
-
-       /* Must be an bootstd device */
-       ret = bootflow_iter_uses_system(iter);
-       if (ret)
-               return log_msg_ret("net", ret);
-
-       ret = bootmeth_check(bflow->method, iter);
-       if (ret)
-               return log_msg_ret("check", ret);
-
-       ret = bootmeth_read_bootflow(bflow->method, bflow);
-       if (ret)
-               return log_msg_ret("method", ret);
-
-       return 0;
-}
-
-static int system_bootdev_bind(struct udevice *dev)
-{
-       struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
-
-       ucp->prio = BOOTDEVP_6_SYSTEM;
-
-       return 0;
-}
-
-struct bootdev_ops system_bootdev_ops = {
-       .get_bootflow   = system_get_bootflow,
-};
-
-static const struct udevice_id system_bootdev_ids[] = {
-       { .compatible = "u-boot,bootdev-system" },
-       { }
-};
-
-U_BOOT_DRIVER(system_bootdev) = {
-       .name           = "system_bootdev",
-       .id             = UCLASS_BOOTDEV,
-       .ops            = &system_bootdev_ops,
-       .bind           = system_bootdev_bind,
-       .of_match       = system_bootdev_ids,
-};
diff --git a/boot/vbe.c b/boot/vbe.c
new file mode 100644 (file)
index 0000000..e6ee087
--- /dev/null
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Verified Boot for Embedded (VBE) access functions
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <bootmeth.h>
+#include <bootstd.h>
+#include <dm.h>
+#include <image.h>
+#include <vbe.h>
+#include <dm/uclass-internal.h>
+
+/**
+ * is_vbe() - Check if a device is a VBE method
+ *
+ * @dev: Device to check
+ * @return true if this is a VBE bootmth device, else false
+ */
+static bool is_vbe(struct udevice *dev)
+{
+       return !strncmp("vbe", dev->driver->name, 3);
+}
+
+int vbe_find_next_device(struct udevice **devp)
+{
+       for (uclass_find_next_device(devp);
+            *devp;
+            uclass_find_next_device(devp)) {
+               if (is_vbe(*devp))
+                       return 0;
+       }
+
+       return 0;
+}
+
+int vbe_find_first_device(struct udevice **devp)
+{
+       uclass_find_first_device(UCLASS_BOOTMETH, devp);
+       if (*devp && is_vbe(*devp))
+               return 0;
+
+       return vbe_find_next_device(devp);
+}
+
+int vbe_list(void)
+{
+       struct bootstd_priv *std;
+       struct udevice *dev;
+       int ret;
+
+       ret = bootstd_get_priv(&std);
+       if (ret)
+               return ret;
+
+       printf("%3s  %-3s  %-15s  %-15s %s\n", "#", "Sel", "Device", "Driver",
+              "Description");
+       printf("%3s  %-3s  %-15s  %-15s %s\n", "---", "---", "--------------",
+              "--------------", "-----------");
+       for (ret = vbe_find_first_device(&dev); dev;
+            ret = vbe_find_next_device(&dev)) {
+               const struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
+
+               printf("%3d  %-3s  %-15s  %-15s %s\n", dev_seq(dev),
+                      std->vbe_bootmeth == dev ? "*" : "", dev->name,
+                      dev->driver->name, plat->desc);
+       }
+       printf("%3s  %-3s  %-15s  %-15s %s\n", "---", "---", "--------------",
+              "--------------", "-----------");
+
+       return 0;
+}
+
+int vbe_select(struct udevice *dev)
+{
+       struct bootstd_priv *std;
+       int ret;
+
+       ret = bootstd_get_priv(&std);
+       if (ret)
+               return ret;
+       std->vbe_bootmeth = dev;
+
+       return 0;
+}
+
+int vbe_find_by_any(const char *name, struct udevice **devp)
+{
+       struct udevice *dev;
+       int ret, seq;
+       char *endp;
+
+       seq = simple_strtol(name, &endp, 16);
+
+       /* Select by name */
+       if (*endp) {
+               ret = uclass_get_device_by_name(UCLASS_BOOTMETH, name, &dev);
+               if (ret) {
+                       printf("Cannot probe VBE bootmeth '%s' (err=%d)\n", name,
+                              ret);
+                       return ret;
+               }
+
+       /* select by number */
+       } else {
+               ret = uclass_get_device_by_seq(UCLASS_BOOTMETH, seq, &dev);
+               if (ret) {
+                       printf("Cannot find '%s' (err=%d)\n", name, ret);
+                       return ret;
+               }
+       }
+
+       *devp = dev;
+
+       return 0;
+}
diff --git a/boot/vbe_simple.c b/boot/vbe_simple.c
new file mode 100644 (file)
index 0000000..a395bc2
--- /dev/null
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Verified Boot for Embedded (VBE) 'simple' method
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <log.h>
+#include <memalign.h>
+#include <part.h>
+#include <bootflow.h>
+#include <bootmeth.h>
+#include <dm.h>
+#include <mmc.h>
+#include <vbe.h>
+#include <version_string.h>
+#include <dm/device-internal.h>
+#include <dm/ofnode.h>
+#include <u-boot/crc.h>
+
+enum {
+       MAX_VERSION_LEN         = 256,
+
+       NVD_HDR_VER_SHIFT       = 0,
+       NVD_HDR_VER_MASK        = 0xf,
+       NVD_HDR_SIZE_SHIFT      = 4,
+       NVD_HDR_SIZE_MASK       = 0xf << NVD_HDR_SIZE_SHIFT,
+
+       /* Firmware key-version is in the top 16 bits of fw_ver */
+       FWVER_KEY_SHIFT         = 16,
+       FWVER_FW_MASK           = 0xffff,
+
+       NVD_HDR_VER_CUR         = 1,    /* current version */
+};
+
+/** struct simple_priv - information read from the device tree */
+struct simple_priv {
+       u32 area_start;
+       u32 area_size;
+       u32 skip_offset;
+       u32 state_offset;
+       u32 state_size;
+       u32 version_offset;
+       u32 version_size;
+       const char *storage;
+};
+
+/** struct simple_state - state information read from media
+ *
+ * @fw_version: Firmware version string
+ * @fw_vernum: Firmware version number
+ */
+struct simple_state {
+       char fw_version[MAX_VERSION_LEN];
+       u32 fw_vernum;
+};
+
+/** struct simple_nvdata - storage format for non-volatile data */
+struct simple_nvdata {
+       u8 crc8;
+       u8 hdr;
+       u16 spare1;
+       u32 fw_vernum;
+       u8 spare2[0x38];
+};
+
+static int simple_read_version(struct udevice *dev, struct blk_desc *desc,
+                              u8 *buf, struct simple_state *state)
+{
+       struct simple_priv *priv = dev_get_priv(dev);
+       int start;
+
+       if (priv->version_size > MMC_MAX_BLOCK_LEN)
+               return log_msg_ret("ver", -E2BIG);
+
+       start = priv->area_start + priv->version_offset;
+       if (start & (MMC_MAX_BLOCK_LEN - 1))
+               return log_msg_ret("get", -EBADF);
+       start /= MMC_MAX_BLOCK_LEN;
+
+       if (blk_dread(desc, start, 1, buf) != 1)
+               return log_msg_ret("read", -EIO);
+       strlcpy(state->fw_version, buf, MAX_VERSION_LEN);
+       log_debug("version=%s\n", state->fw_version);
+
+       return 0;
+}
+
+static int simple_read_nvdata(struct udevice *dev, struct blk_desc *desc,
+                             u8 *buf, struct simple_state *state)
+{
+       struct simple_priv *priv = dev_get_priv(dev);
+       uint hdr_ver, hdr_size, size, crc;
+       const struct simple_nvdata *nvd;
+       int start;
+
+       if (priv->state_size > MMC_MAX_BLOCK_LEN)
+               return log_msg_ret("state", -E2BIG);
+
+       start = priv->area_start + priv->state_offset;
+       if (start & (MMC_MAX_BLOCK_LEN - 1))
+               return log_msg_ret("get", -EBADF);
+       start /= MMC_MAX_BLOCK_LEN;
+
+       if (blk_dread(desc, start, 1, buf) != 1)
+               return log_msg_ret("read", -EIO);
+       nvd = (struct simple_nvdata *)buf;
+       hdr_ver = (nvd->hdr & NVD_HDR_VER_MASK) >> NVD_HDR_VER_SHIFT;
+       hdr_size = (nvd->hdr & NVD_HDR_SIZE_MASK) >> NVD_HDR_SIZE_SHIFT;
+       if (hdr_ver != NVD_HDR_VER_CUR)
+               return log_msg_ret("hdr", -EPERM);
+       size = 1 << hdr_size;
+       if (size > sizeof(*nvd))
+               return log_msg_ret("sz", -ENOEXEC);
+
+       crc = crc8(0, buf + 1, size - 1);
+       if (crc != nvd->crc8)
+               return log_msg_ret("crc", -EPERM);
+       state->fw_vernum = nvd->fw_vernum;
+
+       log_debug("version=%s\n", state->fw_version);
+
+       return 0;
+}
+
+static int simple_read_state(struct udevice *dev, struct simple_state *state)
+{
+       ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN);
+       struct simple_priv *priv = dev_get_priv(dev);
+       struct blk_desc *desc;
+       char devname[16];
+       const char *end;
+       int devnum;
+       int ret;
+
+       /* First figure out the block device */
+       log_debug("storage=%s\n", priv->storage);
+       devnum = trailing_strtoln_end(priv->storage, NULL, &end);
+       if (devnum == -1)
+               return log_msg_ret("num", -ENODEV);
+       if (end - priv->storage >= sizeof(devname))
+               return log_msg_ret("end", -E2BIG);
+       strlcpy(devname, priv->storage, end - priv->storage + 1);
+       log_debug("dev=%s, %x\n", devname, devnum);
+
+       desc = blk_get_dev(devname, devnum);
+       if (!desc)
+               return log_msg_ret("get", -ENXIO);
+
+       ret = simple_read_version(dev, desc, buf, state);
+       if (ret)
+               return log_msg_ret("ver", ret);
+
+       ret = simple_read_nvdata(dev, desc, buf, state);
+       if (ret)
+               return log_msg_ret("nvd", ret);
+
+       return 0;
+}
+
+static int vbe_simple_get_state_desc(struct udevice *dev, char *buf,
+                                    int maxsize)
+{
+       struct simple_state state;
+       int ret;
+
+       ret = simple_read_state(dev, &state);
+       if (ret)
+               return log_msg_ret("read", ret);
+
+       if (maxsize < 30)
+               return -ENOSPC;
+       snprintf(buf, maxsize, "Version: %s\nVernum: %x/%x", state.fw_version,
+                state.fw_vernum >> FWVER_KEY_SHIFT,
+                state.fw_vernum & FWVER_FW_MASK);
+
+       return 0;
+}
+
+static int vbe_simple_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+{
+       /* To be implemented */
+
+       return -EINVAL;
+}
+
+static struct bootmeth_ops bootmeth_vbe_simple_ops = {
+       .get_state_desc = vbe_simple_get_state_desc,
+       .read_bootflow  = vbe_simple_read_bootflow,
+       .read_file      = bootmeth_common_read_file,
+};
+
+int vbe_simple_fixup_node(ofnode node, struct simple_state *state)
+{
+       char *version;
+       int ret;
+
+       version = strdup(state->fw_version);
+       if (!version)
+               return log_msg_ret("ver", -ENOMEM);
+
+       ret = ofnode_write_string(node, "cur-version", version);
+       if (ret)
+               return log_msg_ret("ver", ret);
+       ret = ofnode_write_u32(node, "cur-vernum", state->fw_vernum);
+       if (ret)
+               return log_msg_ret("ver", ret);
+       ret = ofnode_write_string(node, "bootloader-version", version_string);
+       if (ret)
+               return log_msg_ret("fix", ret);
+
+       return 0;
+}
+
+/**
+ * bootmeth_vbe_simple_ft_fixup() - Write out all VBE simple data to the DT
+ *
+ * @ctx: Context for event
+ * @event: Event to process
+ * @return 0 if OK, -ve on error
+ */
+static int bootmeth_vbe_simple_ft_fixup(void *ctx, struct event *event)
+{
+       oftree tree = event->data.ft_fixup.tree;
+       struct udevice *dev;
+       ofnode node;
+       int ret;
+
+       /*
+        * Ideally we would have driver model support for fixups, but that does
+        * not exist yet. It is a step too far to try to do this before VBE is
+        * in place.
+        */
+       for (ret = vbe_find_first_device(&dev); dev;
+            ret = vbe_find_next_device(&dev)) {
+               struct simple_state state;
+
+               if (strcmp("vbe_simple", dev->driver->name))
+                       continue;
+
+               /* Check if there is a node to fix up */
+               node = ofnode_path_root(tree, "/chosen/fwupd");
+               if (!ofnode_valid(node))
+                       continue;
+               node = ofnode_find_subnode(node, dev->name);
+               if (!ofnode_valid(node))
+                       continue;
+
+               log_debug("Fixing up: %s\n", dev->name);
+               ret = device_probe(dev);
+               if (ret)
+                       return log_msg_ret("probe", ret);
+               ret = simple_read_state(dev, &state);
+               if (ret)
+                       return log_msg_ret("read", ret);
+
+               ret = vbe_simple_fixup_node(node, &state);
+               if (ret)
+                       return log_msg_ret("fix", ret);
+       }
+
+       return 0;
+}
+EVENT_SPY(EVT_FT_FIXUP, bootmeth_vbe_simple_ft_fixup);
+
+static int bootmeth_vbe_simple_probe(struct udevice *dev)
+{
+       struct simple_priv *priv = dev_get_priv(dev);
+
+       memset(priv, '\0', sizeof(*priv));
+       if (dev_read_u32(dev, "area-start", &priv->area_start) ||
+           dev_read_u32(dev, "area-size", &priv->area_size) ||
+           dev_read_u32(dev, "version-offset", &priv->version_offset) ||
+           dev_read_u32(dev, "version-size", &priv->version_size) ||
+           dev_read_u32(dev, "state-offset", &priv->state_offset) ||
+           dev_read_u32(dev, "state-size", &priv->state_size))
+               return log_msg_ret("read", -EINVAL);
+       dev_read_u32(dev, "skip-offset", &priv->skip_offset);
+       priv->storage = strdup(dev_read_string(dev, "storage"));
+       if (!priv->storage)
+               return log_msg_ret("str", -EINVAL);
+
+       return 0;
+}
+
+static int bootmeth_vbe_simple_bind(struct udevice *dev)
+{
+       struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
+
+       plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ?
+               "VBE simple" : "vbe-simple";
+       plat->flags = BOOTMETHF_GLOBAL;
+
+       return 0;
+}
+
+#if CONFIG_IS_ENABLED(OF_REAL)
+static const struct udevice_id generic_simple_vbe_simple_ids[] = {
+       { .compatible = "fwupd,vbe-simple" },
+       { }
+};
+#endif
+
+U_BOOT_DRIVER(vbe_simple) = {
+       .name   = "vbe_simple",
+       .id     = UCLASS_BOOTMETH,
+       .of_match = of_match_ptr(generic_simple_vbe_simple_ids),
+       .ops    = &bootmeth_vbe_simple_ops,
+       .bind   = bootmeth_vbe_simple_bind,
+       .probe  = bootmeth_vbe_simple_probe,
+       .flags  = DM_FLAG_PRE_RELOC,
+       .priv_auto      = sizeof(struct simple_priv),
+};
index 7d19706..211ebe9 100644 (file)
@@ -330,6 +330,16 @@ config BOOTM_RTEMS
        help
          Support booting RTEMS images via the bootm command.
 
+config CMD_VBE
+       bool "vbe - Verified Boot for Embedded"
+       depends on BOOTMETH_VBE
+       default y
+       help
+         Provides various subcommands related to VBE, such as listing the
+         available methods, looking at the state and changing which method
+         is used to boot. Updating the parameters is not currently
+         supported.
+
 config BOOTM_VXWORKS
        bool "Support booting VxWorks OS images"
        depends on CMD_BOOTM
index 5e43a1e..6e87522 100644 (file)
@@ -179,6 +179,7 @@ obj-$(CONFIG_CMD_FS_UUID) += fs_uuid.o
 obj-$(CONFIG_CMD_USB_MASS_STORAGE) += usb_mass_storage.o
 obj-$(CONFIG_CMD_USB_SDP) += usb_gadget_sdp.o
 obj-$(CONFIG_CMD_THOR_DOWNLOAD) += thordown.o
+obj-$(CONFIG_CMD_VBE) += vbe.o
 obj-$(CONFIG_CMD_XIMG) += ximg.o
 obj-$(CONFIG_CMD_YAFFS2) += yaffs2.o
 obj-$(CONFIG_CMD_SPL) += spl.o
index af4b9c3..313103d 100644 (file)
@@ -69,8 +69,8 @@ static void show_bootflow(int index, struct bootflow *bflow, bool errors)
 {
        printf("%3x  %-11s  %-6s  %-9.9s %4x  %-25.25s %s\n", index,
               bflow->method->name, bootflow_state_get_name(bflow->state),
-              dev_get_uclass_name(dev_get_parent(bflow->dev)), bflow->part,
-              bflow->name, bflow->fname);
+              bflow->dev ? dev_get_uclass_name(dev_get_parent(bflow->dev)) :
+              "(none)", bflow->part, bflow->name, bflow->fname);
        if (errors)
                report_bootflow_err(bflow, bflow->err);
 }
@@ -95,7 +95,8 @@ static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
        struct bootflow_iter iter;
        struct udevice *dev;
        struct bootflow bflow;
-       bool all = false, boot = false, errors = false, list = false;
+       bool all = false, boot = false, errors = false, no_global = false;
+       bool list = false;
        int num_valid = 0;
        bool has_args;
        int ret, i;
@@ -112,6 +113,7 @@ static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
                        all = strchr(argv[1], 'a');
                        boot = strchr(argv[1], 'b');
                        errors = strchr(argv[1], 'e');
+                       no_global = strchr(argv[1], 'G');
                        list = strchr(argv[1], 'l');
                        argc--;
                        argv++;
@@ -137,6 +139,8 @@ static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
                flags |= BOOTFLOWF_SHOW;
        if (all)
                flags |= BOOTFLOWF_ALL;
+       if (no_global)
+               flags |= BOOTFLOWF_SKIP_GLOBAL;
 
        /*
         * If we have a device, just scan for bootflows attached to that device
@@ -383,7 +387,7 @@ static int do_bootflow_boot(struct cmd_tbl *cmdtp, int flag, int argc,
 #ifdef CONFIG_SYS_LONGHELP
 static char bootflow_help_text[] =
 #ifdef CONFIG_CMD_BOOTFLOW_FULL
-       "scan [-abel] [bdev]   - scan for valid bootflows (-l list, -a all, -e errors, -b boot)\n"
+       "scan [-abeGl] [bdev]  - scan for valid bootflows (-l list, -a all, -e errors, -b boot, -G no global)\n"
        "bootflow list [-e]             - list scanned bootflows (-e errors)\n"
        "bootflow select [<num>|<name>] - select a bootflow\n"
        "bootflow info [-d]             - show info on current bootflow (-d dump bootflow)\n"
index c9a27fe..9fbcccd 100644 (file)
@@ -69,7 +69,9 @@ static int do_bootmeth_list(struct cmd_tbl *cmdtp, int flag, int argc,
                        }
                }
 
-               if (order == -1)
+               if (ucp->flags & BOOTMETHF_GLOBAL)
+                       printf("%5s", "glob");
+               else if (order == -1)
                        printf("%5s", "-");
                else
                        printf("%5x", order);
index 2b33c50..ce40d3f 100644 (file)
--- a/cmd/elf.c
+++ b/cmd/elf.c
@@ -14,7 +14,7 @@
 #include <net.h>
 #include <vxworks.h>
 #ifdef CONFIG_X86
-#include <vbe.h>
+#include <vesa.h>
 #include <asm/cache.h>
 #include <asm/e820.h>
 #include <linux/linkage.h>
index 99c7e38..fecfada 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <common.h>
 #include <command.h>
+#include <mapmem.h>
 #include <part.h>
 
 int do_read(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
@@ -45,7 +46,7 @@ int do_read(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
                return 1;
        }
 
-       addr = (void *)hextoul(argv[3], NULL);
+       addr = map_sysmem(hextoul(argv[3], NULL), 0);
        blk = hextoul(argv[4], NULL);
        cnt = hextoul(argv[5], NULL);
 
diff --git a/cmd/vbe.c b/cmd/vbe.c
new file mode 100644 (file)
index 0000000..a5737ed
--- /dev/null
+++ b/cmd/vbe.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Verified Boot for Embedded (VBE) command
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <bootmeth.h>
+#include <bootstd.h>
+#include <command.h>
+#include <vbe.h>
+
+static int do_vbe_list(struct cmd_tbl *cmdtp, int flag, int argc,
+                      char *const argv[])
+{
+       vbe_list();
+
+       return 0;
+}
+
+static int do_vbe_select(struct cmd_tbl *cmdtp, int flag, int argc,
+                        char *const argv[])
+{
+       struct bootstd_priv *std;
+       struct udevice *dev;
+       int ret;
+
+       ret = bootstd_get_priv(&std);
+       if (ret)
+               return CMD_RET_FAILURE;
+       if (argc < 2) {
+               std->vbe_bootmeth = NULL;
+               return 0;
+       }
+       if (vbe_find_by_any(argv[1], &dev))
+               return CMD_RET_FAILURE;
+
+       std->vbe_bootmeth = dev;
+
+       return 0;
+}
+
+static int do_vbe_info(struct cmd_tbl *cmdtp, int flag, int argc,
+                      char *const argv[])
+{
+       struct bootstd_priv *std;
+       char buf[256];
+       int ret, len;
+
+       ret = bootstd_get_priv(&std);
+       if (ret)
+               return CMD_RET_FAILURE;
+       if (!std->vbe_bootmeth) {
+               printf("No VBE bootmeth selected\n");
+               return CMD_RET_FAILURE;
+       }
+       ret = bootmeth_get_state_desc(std->vbe_bootmeth, buf, sizeof(buf));
+       if (ret) {
+               printf("Failed (err=%d)\n", ret);
+               return CMD_RET_FAILURE;
+       }
+       len = strnlen(buf, sizeof(buf));
+       if (len >= sizeof(buf)) {
+               printf("Buffer overflow\n");
+               return CMD_RET_FAILURE;
+       }
+
+       puts(buf);
+       if (buf[len] != '\n')
+               putc('\n');
+
+       return 0;
+}
+
+#ifdef CONFIG_SYS_LONGHELP
+static char vbe_help_text[] =
+       "list   - list VBE bootmeths\n"
+       "vbe select - select a VBE bootmeth by sequence or name\n"
+       "vbe info   - show information about a VBE bootmeth";
+#endif
+
+U_BOOT_CMD_WITH_SUBCMDS(vbe, "Verified Boot for Embedded", vbe_help_text,
+       U_BOOT_SUBCMD_MKENT(list, 1, 1, do_vbe_list),
+       U_BOOT_SUBCMD_MKENT(select, 2, 1, do_vbe_select),
+       U_BOOT_SUBCMD_MKENT(info, 2, 1, do_vbe_info));
index af1ed41..3e34550 100644 (file)
@@ -35,6 +35,9 @@ const char *const type_name[] = {
 
        /* init hooks */
        "misc_init_f",
+
+       /* fdt hooks */
+       "ft_fixup",
 };
 
 _Static_assert(ARRAY_SIZE(type_name) == EVT_COUNT, "event type_name size");
index a71ce77..6d62fee 100644 (file)
@@ -11,6 +11,7 @@ CONFIG_DISTRO_DEFAULTS=y
 CONFIG_FIT=y
 CONFIG_FIT_SIGNATURE=y
 CONFIG_FIT_VERBOSE=y
+# CONFIG_BOOTMETH_VBE is not set
 CONFIG_BOOTSTAGE=y
 CONFIG_BOOTSTAGE_REPORT=y
 CONFIG_BOOTSTAGE_FDT=y
@@ -206,6 +207,7 @@ CONFIG_RSA_VERIFY_WITH_PKEY=y
 CONFIG_TPM=y
 CONFIG_LZ4=y
 CONFIG_ERRNO_STR=y
+# CONFIG_CMD_BOOTEFI_BOOTMGR is not set
 CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y
 CONFIG_EFI_CAPSULE_ON_DISK=y
 CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y
index 5e9c0d2..b8773f8 100644 (file)
@@ -32,6 +32,7 @@ way to boot with U-Boot. The feature is extensible to different Operating
 Systems (such as Chromium OS) and devices (beyond just block and network
 devices). It supports EFI boot and EFI bootmgr too.
 
+Finally, standard boot supports the operation of :doc:`vbe`.
 
 Bootflow
 --------
@@ -89,6 +90,12 @@ bootflows.
 Note: it is possible to have a bootmeth that uses a partition or a whole device
 directly, but it is more common to use a filesystem.
 
+Note that some bootmeths are 'global', meaning that they select the bootdev
+themselves. Examples include VBE and EFI boot manager. In this case, they
+provide a `read_bootflow()` method which checks whatever bootdevs it likes, then
+returns the bootflow, if found. Some of these bootmeths may be very slow, if
+they scan a lot of devices.
+
 
 Boot process
 ------------
@@ -112,6 +119,9 @@ the following command::
 which scans for available bootflows, optionally listing each find it finds (-l)
 and trying to boot it (-b).
 
+When global bootmeths are available, these are typically checked before the
+above bootdev scanning.
+
 
 Controlling ordering
 --------------------
@@ -269,18 +279,8 @@ Standard boot requires a single instance of the bootstd device to make things
 work. This includes global information about the state of standard boot. See
 `struct bootstd_priv` for this structure, accessed with `bootstd_get_priv()`.
 
-Within the devicetree, if you add bootmeth devices or a system bootdev, they
-should be children of the bootstd device. See `arch/sandbox/dts/test.dts` for
-an example of this.
-
-
-The system bootdev
-------------------
-
-Some bootmeths don't operate on individual bootdevs, but on the whole system.
-For example, the EFI boot manager does its own device scanning and does not
-make use of the bootdev devices. Such bootmeths can make use of the system
-bootdev, typically considered last, after everything else has been tried.
+Within the devicetree, if you add bootmeth devices, they should be children of
+the bootstd device. See `arch/sandbox/dts/test.dts` for an example of this.
 
 
 .. _`Automatic Devices`:
@@ -291,12 +291,11 @@ Automatic devices
 It is possible to define all the required devices in the devicetree manually,
 but it is not necessary. The bootstd uclass includes a `dm_scan_other()`
 function which creates the bootstd device if not found. If no bootmeth devices
-are found at all, it creates one for each available bootmeth driver as well as a
-system bootdev.
+are found at all, it creates one for each available bootmeth driver.
 
 If your devicetree has any bootmeth device it must have all of them that you
-want to use, as well as the system bootdev if needed, since no bootmeth devices
-will be created automatically in that case.
+want to use, since no bootmeth devices will be created automatically in that
+case.
 
 
 Using devicetree
@@ -347,6 +346,7 @@ Bootmeth drivers are provided for:
    - distro boot from a disk (syslinux)
    - distro boot from a network (PXE)
    - EFI boot using bootefi
+   - VBE
    - EFI boot using boot manager
 
 
@@ -433,18 +433,23 @@ case, the iterator ends up with a `dev_order` array containing the bootdevs that
 are going to be used, with `num_devs` set to the number of bootdevs and
 `cur_dev` starting at 0.
 
-Next, the ordering of bootdevs is determined, by `bootmeth_setup_iter_order()`.
+Next, the ordering of bootmeths is determined, by `bootmeth_setup_iter_order()`.
 By default the ordering is again by sequence number, i.e. the `/aliases` node,
 or failing that the order in the devicetree. But the `bootmeth order` command
 or `bootmeths` environment variable can be used to set up an ordering. If that
 has been done, the ordering is in `struct bootstd_priv`, so that ordering is
 simply copied into the iterator. Either way, the `method_order` array it set up,
-along with `num_methods`. Then `cur_method` is set to 0.
+along with `num_methods`.
+
+Note that global bootmeths are always put at the end of the ordering. If any are
+present, `cur_method` is set to the first one, so that global bootmeths are done
+first. Once all have been used, these bootmeths are dropped from the iteration.
+When there are no global bootmeths, `cur_method` is set to 0.
 
 At this point the iterator is ready to use, with the first bootdev and bootmeth
-selected. All the other fields are 0. This means that the current partition is
-0, which is taken to mean the whole device, since partition numbers start at 1.
-It also means that `max_part` is 0, i.e. the maximum partition number we know
+selected. Most of the other fields are 0. This means that the current partition
+is 0, which is taken to mean the whole device, since partition numbers start at
+1. It also means that `max_part` is 0, i.e. the maximum partition number we know
 about is 0, meaning that, as far as we know, there is no partition table on this
 bootdev.
 
@@ -455,6 +460,10 @@ If the `BOOTFLOWF_ALL` iterator flag is set, even errors are returned as
 incomplete bootflows, but normally an error results in moving onto the next
 iteration.
 
+Note that `bootflow_check()` handles global bootmeths explicitly, but calling
+`bootmeth_get_bootflow()` on each one. The `doing_global` flag indicates when
+the iterator is in that state.
+
 The `bootflow_scan_next()` function handles moving onto the next iteration and
 checking it. In fact it sits in a loop doing that repeatedly until it finds
 something it wants to return.
@@ -473,9 +482,10 @@ the least-sigificant digit on the right, counting like this:
    0           0          2
    0           1          0
    0           1          1
-   0           1          1
+   0           1          2
    1           0          0
    1           0          1
+   ...
    ========    =======    =======
 
 The maximum value for `method` is `num_methods - 1` so when it exceeds that, it
@@ -487,6 +497,31 @@ exceeds its maximum, then the next bootdev is used. In this way, iter_incr()
 works its way through all possibilities, moving forward one each time it is
 called.
 
+Note that global bootmeths introduce a subtlety into the above description.
+When `doing_global` is true, the iteration takes place only among the bootmeths,
+i.e. the last column above. The global bootmeths are at the end of the list.
+Assuming that they are entries 3 and 4 in the list, the iteration then looks
+like this:
+
+   ========    =======    =======   =======================================
+   bootdev     part       method    notes
+   ========    =======    =======   =======================================
+   .           .          3         doing_global = true, method_count = 5
+   .           .          4
+   0           0          0         doing_global = false, method_count = 3
+   0           0          1
+   0           0          2
+   0           1          0
+   0           1          1
+   0           1          2
+   1           0          0
+   1           0          1
+   ...
+   ========    =======    =======   =======================================
+
+The changeover of the value of `doing_global` from true to false is handled in
+`iter_incr()` as well.
+
 There is no expectation that iteration will actually finish. Quite often a
 valid bootflow is found early on. With `bootflow scan -b`, that causes the
 bootflow to be immediately booted. Assuming it is successful, the iteration never
@@ -516,17 +551,19 @@ method `bootdev_get_bootflow()` to ask the bootdev to return a bootflow. It
 passes the iterator to the bootdev method, so that function knows what we are
 talking about. At first, the bootflow is set up in the state `BOOTFLOWST_BASE`,
 with just the `method` and `dev` intiialised. But the bootdev may fill in more,
-e.g. updating the state, depending on what it finds.
+e.g. updating the state, depending on what it finds. For global bootmeths the
+`bootmeth_get_bootflow()` function is called instead of
+`bootdev_get_bootflow()`.
 
-Based on what the bootdev responds with, `bootflow_check()` either
+Based on what the bootdev or bootmeth responds with, `bootflow_check()` either
 returns a valid bootflow, or a partial one with an error. A partial bootflow
 is one that has some fields set up, but did not reach the `BOOTFLOWST_READY`
 state. As noted before, if the `BOOTFLOWF_ALL` iterator flag is set, then all
 bootflows are returned, even partial ones. This can help with debugging.
 
 So at this point you can see that total control over whether a bootflow can
-be generated from a particular iteration, or not, rests with the bootdev.
-Each one can adopt its own approach.
+be generated from a particular iteration, or not, rests with the bootdev (or
+global bootmeth). Each one can adopt its own approach.
 
 Going down a level, what does the bootdev do in its `get_bootflow()` method?
 Let us consider the MMC bootdev. In that case the call to
index 9f654f3..faf3eb5 100644 (file)
@@ -211,9 +211,61 @@ using it in new code.
 Modifying the livetree
 ----------------------
 
-This is not currently supported. Once implemented it should provide a much
-more efficient implementation for modification of the device tree than using
-the flat tree.
+This is supported in a limited way, with ofnode_write_prop() and related
+functions.
+
+The unflattening algorithm results in a single block of memory being
+allocated for the whole tree. When writing new properties, these are
+allocated new memory outside that block. When the block is freed, the
+allocated properties remain. This can result in a memory leak.
+
+The solution to this leak would be to add a flag for properties (and nodes when
+support is provided for adding those) that indicates that they should be
+freed. Then the tree can be scanned for these 'separately allocated' nodes and
+properties before freeing the memory block.
+
+The ofnode_write\_...() functions also support writing to the flat tree. Care
+should be taken however, since this can change the position of node names and
+properties in the flat tree, thus affecting the live tree. Generally this does
+not matter, since when we fire up the live tree we don't ever use the flat tree
+again. But in the case of tests, this can cause a problem.
+
+The sandbox tests typically run with OF_LIVE enabled but with the actual live
+tree either present or absent. This is to make sure that the flat tree functions
+work correctly even with OF_LIVE is enabled. But if a test modifies the flat
+device tree, then the live tree can become invalid. Any live tree tests that run
+after that point will use a corrupted tree, e.g. with an incorrect property name
+or worse. To deal with this we use a flag UT_TESTF_LIVE_OR_FLAT then ensures
+that tests which write to the flat tree are not run if OF_LIVE is enabled. Only
+the live tree version of the test is run, when OF_LIVE is enabled, with
+sandbox_flattree running the flat tree version.
+
+This is of course a work-around, even if a reasonable one. One solution to this
+problem would be to make a copy of the flat tree before the test and restore it
+afterwards, in the same memory location, so that the live tree pointers work
+again. Another would be to regenerate the live tree if a test modified the flat
+tree.
+
+Neither of these solutions is currently implemented, since the situation that
+causes the problem can only occur in sandbox tests, is somewhat esoteric and
+the UT_TESTF_LIVE_OR_FLAT flag deals with it in a reasonable way.
+
+
+Multiple livetrees
+------------------
+
+The livetree implementation was originally designed for use with the control
+FDT. This means that the FDT fix-ups (ft_board_setup() and the like, must use
+a flat tree.
+
+It would be helpful to use livetree for fixups, since adding a lot of nodes and
+properties would involve less memory copying and be more efficient. As a step
+towards this, an `oftree` type has been introduced. It is normally set to
+oftree_default() but can be set to other values. Eventually this should allow
+the use of FDT fixups using the ofnode interface, instead of the low-level
+libfdt one.
+
+See dm_test_ofnode_root() for some examples.
 
 
 Internal implementation
@@ -281,6 +333,6 @@ Live tree support was introduced in U-Boot 2017.07. There is still quite a bit
 of work to do to flesh this out:
 
 - tests for all access functions
-- support for livetree modification
+- more support for livetree modification
 - addition of more access functions as needed
 - support for livetree in SPL and before relocation (if desired)
index 7c41e3f..c94c7fe 100644 (file)
@@ -39,6 +39,7 @@ Implementation
    smbios
    spl
    uefi/index
+   vbe
    version
 
 Debugging
diff --git a/doc/develop/vbe.rst b/doc/develop/vbe.rst
new file mode 100644 (file)
index 0000000..8f147fd
--- /dev/null
@@ -0,0 +1,26 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Verified Boot for Embedded (VBE)
+================================
+
+Introduction
+------------
+
+VBE provides a standard boot mechanism for embedded systems. If defines
+how firmware and Operating Systems are located, updated and verified.
+
+Within U-Boot, one or more VBE bootmeths implement the boot logic. For example,
+the vbe-simple bootmeth handles finding the firmware (e.g. in MMC) and starting
+it. Typically the bootmeth is started up in VPL and controls which SPL and
+U-Boot binaries are loaded.
+
+A 'vbe' command provides access to various aspects of VBE's operation, including
+listing methods and getting the status for a method.
+
+For a detailed overview of VBE, see vbe-intro_. A fuller description of
+bootflows is at vbe-bootflows_ and the firmware-update mechanism is described at
+vbe-fwupdate_.
+
+.. _vbe-intro: https://docs.google.com/document/d/e/2PACX-1vQjXLPWMIyVktaTMf8edHZYDrEvMYD_iNzIj1FgPmKF37fpglAC47Tt5cvPBC5fvTdoK-GA5Zv1wifo/pub
+.. _vbe-bootflows: https://docs.google.com/document/d/e/2PACX-1vR0OzhuyRJQ8kdeOibS3xB1rVFy3J4M_QKTM5-3vPIBNcdvR0W8EXu9ymG-yWfqthzWoM4JUNhqwydN/pub
+.. _vbe-fwupdate: https://docs.google.com/document/d/e/2PACX-1vTnlIL17vVbl6TVoTHWYMED0bme7oHHNk-g5VGxblbPiKIdGDALE1HKId8Go5f0g1eziLsv4h9bocbk/pub
index 9fc7ebf..29d8215 100644 (file)
@@ -31,7 +31,9 @@ scanning bootdevs, each bootmeth is tried in turn to see if it can find a valid
 bootflow. You can use this command to adjust the order or even to omit some
 boomeths.
 
-The argument is a quoted list of bootmeths to use, by name.
+The argument is a quoted list of bootmeths to use, by name. If global bootmeths
+are included, they must be at the end, otherwise the scanning mechanism will not
+work correctly.
 
 
 bootmeth list
@@ -47,14 +49,15 @@ Order  Seq  Name                Description
     1    1  efi                 EFI boot from an .efi file
     2    2  pxe                 PXE boot from a network device
     3    3  sandbox             Sandbox boot for testing
   4    4  efi_mgr             EFI bootmgr flow
glob    4  efi_mgr             EFI bootmgr flow
 =====  ===  ==================  =================================
 
 The fields are as follows:
 
 Order:
     The order in which these bootmeths are invoked for each bootdev. If this
-    shows as a hyphen, then the bootmeth is not in the current ordering.
+    shows as a hyphen, then the bootmeth is not in the current ordering. If it
+    shows as 'glob', then this is a global bootmeth and should be at the end.
 
 Seq:
     The sequence number of the bootmeth, i.e. the normal ordering if none is set
index cdc5ba6..7ebead6 100644 (file)
@@ -51,7 +51,7 @@
 #include <errno.h>
 #include <log.h>
 #include <malloc.h>
-#include <vbe.h>
+#include <vesa.h>
 #include <linux/delay.h>
 #include "biosemui.h"
 
@@ -83,13 +83,13 @@ static const void *bios_ptr(const void *buf, BE_VGAInfo *vga_info,
 }
 
 static int atibios_debug_mode(BE_VGAInfo *vga_info, RMREGS *regs,
-                             int vesa_mode, struct vbe_mode_info *mode_info)
+                             int vesa_mode, struct vesa_state *mode_info)
 {
        void *buffer = (void *)(M.mem_base + vbe_offset);
        u16 buffer_seg = (((unsigned long)vbe_offset) >> 4) & 0xff00;
        u16 buffer_adr = ((unsigned long)vbe_offset) & 0xffff;
        struct vesa_mode_info *vm;
-       struct vbe_info *info;
+       struct vesa_bios_ext_info *info;
        const u16 *modes_bios, *ptr;
        u16 *modes;
        int size;
@@ -140,7 +140,7 @@ static int atibios_debug_mode(BE_VGAInfo *vga_info, RMREGS *regs,
                int attr;
 
                debug("Mode %x: ", mode);
-               memset(buffer, '\0', sizeof(struct vbe_mode_info));
+               memset(buffer, '\0', sizeof(struct vesa_state));
                regs->e.eax = VESA_GET_MODE_INFO;
                regs->e.ebx = 0;
                regs->e.ecx = mode;
@@ -174,7 +174,7 @@ static int atibios_debug_mode(BE_VGAInfo *vga_info, RMREGS *regs,
 }
 
 static int atibios_set_vesa_mode(RMREGS *regs, int vesa_mode,
-                                struct vbe_mode_info *mode_info)
+                                struct vesa_state *mode_info)
 {
        void *buffer = (void *)(M.mem_base + vbe_offset);
        u16 buffer_seg = (((unsigned long)vbe_offset) >> 4) & 0xff00;
@@ -192,7 +192,7 @@ static int atibios_set_vesa_mode(RMREGS *regs, int vesa_mode,
                return -ENOSYS;
        }
 
-       memset(buffer, '\0', sizeof(struct vbe_mode_info));
+       memset(buffer, '\0', sizeof(struct vesa_state));
        debug("VBE: Geting info for VESA mode %#04x\n", vesa_mode);
        regs->e.eax = VESA_GET_MODE_INFO;
        regs->e.ecx = vesa_mode;
@@ -231,7 +231,7 @@ at this stage the controller has its I/O and memory space enabled and
 that all other controllers are in a disabled state.
 ****************************************************************************/
 static void PCI_doBIOSPOST(struct udevice *pcidev, BE_VGAInfo *vga_info,
-                          int vesa_mode, struct vbe_mode_info *mode_info)
+                          int vesa_mode, struct vesa_state *mode_info)
 {
        RMREGS regs;
        RMSREGS sregs;
@@ -416,7 +416,7 @@ image we can extract over the PCI bus.
 ****************************************************************************/
 static int PCI_postController(struct udevice *pcidev, uchar *bios_rom,
                              int bios_len, BE_VGAInfo *vga_info,
-                             int vesa_mode, struct vbe_mode_info *mode_info)
+                             int vesa_mode, struct vesa_state *mode_info)
 {
        u32 bios_image_len;
        uchar *mapped_bios;
@@ -496,7 +496,7 @@ void biosemu_set_interrupt_handler(int intnum, int (*int_func)(void))
 
 int biosemu_run(struct udevice *pcidev, uchar *bios_rom, int bios_len,
                BE_VGAInfo *vga_info, int clean_up, int vesa_mode,
-               struct vbe_mode_info *mode_info)
+               struct vesa_state *mode_info)
 {
        /*Post all the display controller BIOS'es*/
        if (!PCI_postController(pcidev, bios_rom, bios_len, vga_info,
index c20b19c..a52f5a6 100644 (file)
@@ -343,24 +343,30 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent,
 #define for_each_property_of_node(dn, pp) \
        for (pp = dn->properties; pp != NULL; pp = pp->next)
 
-struct device_node *of_find_node_opts_by_path(const char *path,
+struct device_node *of_find_node_opts_by_path(struct device_node *root,
+                                             const char *path,
                                              const char **opts)
 {
        struct device_node *np = NULL;
        struct property *pp;
        const char *separator = strchr(path, ':');
 
+       if (!root)
+               root = gd->of_root;
        if (opts)
                *opts = separator ? separator + 1 : NULL;
 
        if (strcmp(path, "/") == 0)
-               return of_node_get(gd->of_root);
+               return of_node_get(root);
 
        /* The path could begin with an alias */
        if (*path != '/') {
                int len;
                const char *p = separator;
 
+               /* Only allow alias processing on the control FDT */
+               if (root != gd->of_root)
+                       return NULL;
                if (!p)
                        p = strchrnul(path, '/');
                len = p - path;
@@ -383,7 +389,7 @@ struct device_node *of_find_node_opts_by_path(const char *path,
 
        /* Step down the tree matching path components */
        if (!np)
-               np = of_node_get(gd->of_root);
+               np = of_node_get(root);
        while (np && *path == '/') {
                struct device_node *tmp = np;
 
@@ -791,7 +797,7 @@ int of_alias_scan(void)
 
                name = of_get_property(of_chosen, "stdout-path", NULL);
                if (name)
-                       of_stdout = of_find_node_opts_by_path(name,
+                       of_stdout = of_find_node_opts_by_path(NULL, name,
                                                        &of_stdout_options);
        }
 
@@ -881,3 +887,46 @@ struct device_node *of_get_stdout(void)
 {
        return of_stdout;
 }
+
+int of_write_prop(struct device_node *np, const char *propname, int len,
+                 const void *value)
+{
+       struct property *pp;
+       struct property *pp_last = NULL;
+       struct property *new;
+
+       if (!np)
+               return -EINVAL;
+
+       for (pp = np->properties; pp; pp = pp->next) {
+               if (strcmp(pp->name, propname) == 0) {
+                       /* Property exists -> change value */
+                       pp->value = (void *)value;
+                       pp->length = len;
+                       return 0;
+               }
+               pp_last = pp;
+       }
+
+       if (!pp_last)
+               return -ENOENT;
+
+       /* Property does not exist -> append new property */
+       new = malloc(sizeof(struct property));
+       if (!new)
+               return -ENOMEM;
+
+       new->name = strdup(propname);
+       if (!new->name) {
+               free(new);
+               return -ENOMEM;
+       }
+
+       new->value = (void *)value;
+       new->length = len;
+       new->next = NULL;
+
+       pp_last->next = new;
+
+       return 0;
+}
index a59832e..45ea84e 100644 (file)
@@ -552,6 +552,17 @@ ofnode ofnode_path(const char *path)
                return offset_to_ofnode(fdt_path_offset(gd->fdt_blob, path));
 }
 
+ofnode ofnode_path_root(oftree tree, const char *path)
+{
+       if (of_live_active())
+               return np_to_ofnode(of_find_node_opts_by_path(tree.np, path,
+                                                             NULL));
+       else if (*path != '/' && tree.fdt != gd->fdt_blob)
+               return ofnode_null();  /* Aliases only on control FDT */
+       else
+               return offset_to_ofnode(fdt_path_offset(tree.fdt, path));
+}
+
 const void *ofnode_read_chosen_prop(const char *propname, int *sizep)
 {
        ofnode chosen_node;
@@ -1094,70 +1105,44 @@ ofnode ofnode_by_prop_value(ofnode from, const char *propname,
        }
 }
 
-int ofnode_write_prop(ofnode node, const char *propname, int len,
-                     const void *value)
+int ofnode_write_prop(ofnode node, const char *propname, const void *value,
+                     int len)
 {
-       const struct device_node *np = ofnode_to_np(node);
-       struct property *pp;
-       struct property *pp_last = NULL;
-       struct property *new;
-
-       if (!of_live_active())
-               return -ENOSYS;
-
-       if (!np)
-               return -EINVAL;
-
-       for (pp = np->properties; pp; pp = pp->next) {
-               if (strcmp(pp->name, propname) == 0) {
-                       /* Property exists -> change value */
-                       pp->value = (void *)value;
-                       pp->length = len;
-                       return 0;
-               }
-               pp_last = pp;
-       }
-
-       if (!pp_last)
-               return -ENOENT;
-
-       /* Property does not exist -> append new property */
-       new = malloc(sizeof(struct property));
-       if (!new)
-               return -ENOMEM;
+       if (of_live_active())
+               return of_write_prop(ofnode_to_npw(node), propname, len, value);
+       else
+               return fdt_setprop((void *)gd->fdt_blob, ofnode_to_offset(node),
+                                  propname, value, len);
 
-       new->name = strdup(propname);
-       if (!new->name) {
-               free(new);
-               return -ENOMEM;
-       }
+       return 0;
+}
 
-       new->value = (void *)value;
-       new->length = len;
-       new->next = NULL;
+int ofnode_write_string(ofnode node, const char *propname, const char *value)
+{
+       assert(ofnode_valid(node));
 
-       pp_last->next = new;
+       debug("%s: %s = %s", __func__, propname, value);
 
-       return 0;
+       return ofnode_write_prop(node, propname, value, strlen(value) + 1);
 }
 
-int ofnode_write_string(ofnode node, const char *propname, const char *value)
+int ofnode_write_u32(ofnode node, const char *propname, u32 value)
 {
-       if (!of_live_active())
-               return -ENOSYS;
+       fdt32_t *val;
 
        assert(ofnode_valid(node));
 
-       debug("%s: %s = %s", __func__, propname, value);
+       log_debug("%s = %x", propname, value);
+       val = malloc(sizeof(*val));
+       if (!val)
+               return -ENOMEM;
+       *val = cpu_to_fdt32(value);
 
-       return ofnode_write_prop(node, propname, strlen(value) + 1, value);
+       return ofnode_write_prop(node, propname, val, sizeof(value));
 }
 
 int ofnode_set_enabled(ofnode node, bool value)
 {
-       if (!of_live_active())
-               return -ENOSYS;
-
        assert(ofnode_valid(node));
 
        if (value)
index 73d15e7..27a24da 100644 (file)
@@ -34,7 +34,7 @@
 #include <malloc.h>
 #include <pci.h>
 #include <pci_rom.h>
-#include <vbe.h>
+#include <vesa.h>
 #include <video.h>
 #include <acpi/acpi_s3.h>
 #include <asm/global_data.h>
@@ -202,7 +202,7 @@ static int pci_rom_load(struct pci_rom_header *rom_header,
        return 0;
 }
 
-struct vbe_mode_info mode_info;
+struct vesa_state mode_info;
 
 void setup_video(struct screen_info *screen_info)
 {
@@ -326,9 +326,9 @@ err:
 }
 
 #ifdef CONFIG_DM_VIDEO
-int vbe_setup_video_priv(struct vesa_mode_info *vesa,
-                        struct video_priv *uc_priv,
-                        struct video_uc_plat *plat)
+int vesa_setup_video_priv(struct vesa_mode_info *vesa,
+                         struct video_priv *uc_priv,
+                         struct video_uc_plat *plat)
 {
        if (!vesa->x_resolution)
                return log_msg_ret("No x resolution", -ENXIO);
@@ -358,7 +358,7 @@ int vbe_setup_video_priv(struct vesa_mode_info *vesa,
        return 0;
 }
 
-int vbe_setup_video(struct udevice *dev, int (*int15_handler)(void))
+int vesa_setup_video(struct udevice *dev, int (*int15_handler)(void))
 {
        struct video_uc_plat *plat = dev_get_uclass_plat(dev);
        struct video_priv *uc_priv = dev_get_uclass_priv(dev);
@@ -378,7 +378,7 @@ int vbe_setup_video(struct udevice *dev, int (*int15_handler)(void))
                return ret;
        }
 
-       ret = vbe_setup_video_priv(&mode_info.vesa, uc_priv, plat);
+       ret = vesa_setup_video_priv(&mode_info.vesa, uc_priv, plat);
        if (ret) {
                if (ret == -ENFILE) {
                        /*
index 2551f16..6aa4e27 100644 (file)
@@ -11,7 +11,7 @@
 #include <dm.h>
 #include <init.h>
 #include <log.h>
-#include <vbe.h>
+#include <vesa.h>
 #include <video.h>
 #include <asm/cpu.h>
 #include <asm/global_data.h>
@@ -681,7 +681,7 @@ static int broadwell_igd_probe(struct udevice *dev)
        debug("%s: is_broadwell=%d\n", __func__, is_broadwell);
        ret = igd_pre_init(dev, is_broadwell);
        if (!ret) {
-               ret = vbe_setup_video(dev, broadwell_igd_int15_handler);
+               ret = vesa_setup_video(dev, broadwell_igd_int15_handler);
                if (ret)
                        debug("failed to run video BIOS: %d\n", ret);
        }
index 7237542..d2d87c7 100644 (file)
@@ -6,7 +6,7 @@
 #include <common.h>
 #include <dm.h>
 #include <init.h>
-#include <vbe.h>
+#include <vesa.h>
 #include <video.h>
 #include <asm/cb_sysinfo.h>
 
@@ -57,7 +57,7 @@ static int coreboot_video_probe(struct udevice *dev)
                goto err;
        }
 
-       ret = vbe_setup_video_priv(vesa, uc_priv, plat);
+       ret = vesa_setup_video_priv(vesa, uc_priv, plat);
        if (ret) {
                ret = log_msg_ret("setup", ret);
                goto err;
index 5f9031f..b11e42c 100644 (file)
@@ -9,7 +9,7 @@
 #include <dm.h>
 #include <efi_api.h>
 #include <log.h>
-#include <vbe.h>
+#include <vesa.h>
 #include <video.h>
 
 struct pixel {
@@ -149,7 +149,7 @@ static int efi_video_probe(struct udevice *dev)
        if (ret)
                goto err;
 
-       ret = vbe_setup_video_priv(vesa, uc_priv, plat);
+       ret = vesa_setup_video_priv(vesa, uc_priv, plat);
        if (ret)
                goto err;
 
index 1aa5317..9264dd6 100644 (file)
@@ -10,7 +10,7 @@
 #include <fdtdec.h>
 #include <log.h>
 #include <pci_rom.h>
-#include <vbe.h>
+#include <vesa.h>
 #include <video.h>
 #include <asm/global_data.h>
 #include <asm/intel_regs.h>
@@ -762,7 +762,7 @@ static int bd82x6x_video_probe(struct udevice *dev)
        rev = gma_func0_init(dev);
        if (rev < 0)
                return rev;
-       ret = vbe_setup_video(dev, int15_handler);
+       ret = vesa_setup_video(dev, int15_handler);
        if (ret)
                return ret;
 
index 869e546..cac3bb0 100644 (file)
@@ -7,7 +7,7 @@
 #include <dm.h>
 #include <log.h>
 #include <pci.h>
-#include <vbe.h>
+#include <vesa.h>
 #include <video.h>
 #include <asm/mtrr.h>
 
@@ -17,7 +17,7 @@ static int vesa_video_probe(struct udevice *dev)
        ulong fbbase;
        int ret;
 
-       ret = vbe_setup_video(dev, NULL);
+       ret = vesa_setup_video(dev, NULL);
        if (ret)
                return log_ret(ret);
 
index 72410dc..a7e6d73 100644 (file)
@@ -36,14 +36,14 @@ typedef struct {
        u8 LowMem[1536];
 } BE_VGAInfo;
 
-struct vbe_mode_info;
+struct vesa_state;
 
 int BootVideoCardBIOS(struct udevice *pcidev, BE_VGAInfo **pVGAInfo,
                      int clean_up);
 
 /* Run a BIOS ROM natively (only supported on x86 machines) */
 void bios_run_on_x86(struct udevice *dev, unsigned long addr, int vesa_mode,
-                    struct vbe_mode_info *mode_info);
+                    struct vesa_state *mode_info);
 
 /**
  * bios_set_interrupt_handler() - Install an interrupt handler for the BIOS
@@ -61,6 +61,6 @@ int biosemu_setup(struct udevice *pcidev, BE_VGAInfo **pVGAInfo);
 
 int biosemu_run(struct udevice *dev, uchar *bios_rom, int bios_len,
                BE_VGAInfo *vga_info, int clean_up, int vesa_mode,
-               struct vbe_mode_info *mode_info);
+               struct vesa_state *mode_info);
 
 #endif
index c30ba04..6aa3d1f 100644 (file)
@@ -77,12 +77,14 @@ struct bootflow {
  * @BOOTFLOWF_SHOW: Show each bootdev before scanning it
  * @BOOTFLOWF_ALL: Return bootflows with errors as well
  * @BOOTFLOWF_SINGLE_DEV: Just scan one bootmeth
+ * @BOOTFLOWF_SKIP_GLOBAL: Don't scan global bootmeths
  */
 enum bootflow_flags_t {
        BOOTFLOWF_FIXED         = 1 << 0,
        BOOTFLOWF_SHOW          = 1 << 1,
        BOOTFLOWF_ALL           = 1 << 2,
        BOOTFLOWF_SINGLE_DEV    = 1 << 3,
+       BOOTFLOWF_SKIP_GLOBAL   = 1 << 4,
 };
 
 /**
@@ -102,8 +104,10 @@ enum bootflow_flags_t {
  * updated to a larger value, no less than the number of available partitions.
  * This ensures that iteration works through all partitions on the bootdev.
  *
- * @flags: Flags to use (see enum bootflow_flags_t)
- * @dev: Current bootdev
+ * @flags: Flags to use (see enum bootflow_flags_t). If BOOTFLOWF_GLOBAL_FIRST is
+ *     enabled then the global bootmeths are being scanned, otherwise we have
+ *     moved onto the bootdevs
+ * @dev: Current bootdev, NULL if none
  * @part: Current partition number (0 for whole device)
  * @method: Current bootmeth
  * @max_part: Maximum hardware partition number in @dev, 0 if there is no
@@ -117,7 +121,11 @@ enum bootflow_flags_t {
  *     with the first one on the list
  * @num_methods: Number of bootmeth devices in @method_order
  * @cur_method: Current method number, an index into @method_order
- * @method_order: List of bootmeth devices to use, in order
+ * @first_glob_method: First global method, if any, else -1
+ * @method_order: List of bootmeth devices to use, in order. The normal methods
+ *     appear first, then the global ones, if any
+ * @doing_global: true if we are iterating through the global bootmeths (which
+ *     happens before the normal ones)
  */
 struct bootflow_iter {
        int flags;
@@ -131,7 +139,9 @@ struct bootflow_iter {
        struct udevice **dev_order;
        int num_methods;
        int cur_method;
+       int first_glob_method;
        struct udevice **method_order;
+       bool doing_global;
 };
 
 /**
@@ -169,9 +179,9 @@ int bootflow_iter_drop_bootmeth(struct bootflow_iter *iter,
  * If @flags includes BOOTFLOWF_ALL then bootflows with errors are returned too
  *
  * @dev:       Boot device to scan, NULL to work through all of them until it
- *     finds one that can supply a bootflow
+ *     finds one that can supply a bootflow
  * @iter:      Place to store private info (inited by this call)
- * @flags:     Flags for bootdev (enum bootflow_flags_t)
+ * @flags:     Flags for iterator (enum bootflow_flags_t)
  * @bflow:     Place to put the bootflow if found
  * Return: 0 if found,  -ENODEV if no device, other -ve on other error
  *     (iteration can continue)
index 484e503..50ded05 100644 (file)
@@ -13,18 +13,47 @@ struct bootflow_iter;
 struct udevice;
 
 /**
+ * enum bootmeth_flags - Flags for bootmeths
+ *
+ * @BOOTMETHF_GLOBAL: bootmeth handles bootdev selection automatically
+ */
+enum bootmeth_flags {
+       BOOTMETHF_GLOBAL        = BIT(0),
+};
+
+/**
  * struct bootmeth_uc_plat - information the uclass keeps about each bootmeth
  *
  * @desc: A long description of the bootmeth
+ * @flags: Flags for this bootmeth (enum bootmeth_flags)
  */
 struct bootmeth_uc_plat {
        const char *desc;
+       int flags;
 };
 
 /** struct bootmeth_ops - Operations for boot methods */
 struct bootmeth_ops {
        /**
-        * check_supported() - check if a bootmeth supports this bootflow
+        * get_state_desc() - get detailed state information
+        *
+        * Prodecues a textual description of the state of the bootmeth. This
+        * can include newline characters if it extends to multiple lines. It
+        * must be a nul-terminated string.
+        *
+        * This may involve reading state from the system, e.g. some data in
+        * the firmware area.
+        *
+        * @dev:        Bootmethod device to check
+        * @buf:        Buffer to place the info in (terminator must fit)
+        * @maxsize:    Size of buffer
+        * Returns: 0 if OK, -ENOSPC is buffer is too small, other -ve error if
+        * something else went wrong
+        */
+       int (*get_state_desc)(struct udevice *dev, char *buf, int maxsize);
+
+       /**
+        * check_supported() - check if a bootmeth supports this bootdev
         *
         * This is optional. If not provided, the bootdev is assumed to be
         * supported
@@ -92,6 +121,24 @@ struct bootmeth_ops {
 #define bootmeth_get_ops(dev)  ((struct bootmeth_ops *)(dev)->driver->ops)
 
 /**
+ * bootmeth_get_state_desc() - get detailed state information
+ *
+ * Prodecues a textual description of the state of the bootmeth. This
+ * can include newline characters if it extends to multiple lines. It
+ * must be a nul-terminated string.
+ *
+ * This may involve reading state from the system, e.g. some data in
+ * the firmware area.
+ *
+ * @dev:       Bootmethod device to check
+ * @buf:       Buffer to place the info in (terminator must fit)
+ * @maxsize:   Size of buffer
+ * Returns: 0 if OK, -ENOSPC is buffer is too small, other -ve error if
+ * something else went wrong
+ */
+int bootmeth_get_state_desc(struct udevice *dev, char *buf, int maxsize);
+
+/**
  * bootmeth_check() - check if a bootmeth supports this bootflow
  *
  * This is optional. If not provided, the bootdev is assumed to be
@@ -162,10 +209,12 @@ int bootmeth_boot(struct udevice *dev, struct bootflow *bflow);
  * ordering there, then all bootmethods are added
  *
  * @iter: Iterator to update with the order
+ * @include_global: true to add the global bootmeths, in which case they appear
+ * first
  * Return: 0 if OK, -ENOENT if no bootdevs, -ENOMEM if out of memory, other -ve
  *     on other error
  */
-int bootmeth_setup_iter_order(struct bootflow_iter *iter);
+int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global);
 
 /**
  * bootmeth_set_order() - Set the bootmeth order
@@ -231,4 +280,16 @@ int bootmeth_alloc_file(struct bootflow *bflow, uint size_limit, uint align);
 int bootmeth_common_read_file(struct udevice *dev, struct bootflow *bflow,
                              const char *file_path, ulong addr, ulong *sizep);
 
+/**
+ * bootmeth_get_bootflow() - Get a bootflow from a global bootmeth
+ *
+ * Check the bootmeth for a bootflow which can be used. In this case the
+ * bootmeth handles all bootdev selection, etc.
+ *
+ * @dev: bootmeth device to read from
+ * @bflow: Bootflow information
+ * @return 0 on success, -ve if a bootflow could not be found or had an error
+ */
+int bootmeth_get_bootflow(struct udevice *dev, struct bootflow *bflow);
+
 #endif
index b002365..01be249 100644 (file)
@@ -26,6 +26,7 @@ struct udevice;
  * @glob_head: Head for the global list of all bootflows across all bootdevs
  * @bootmeth_count: Number of bootmeth devices in @bootmeth_order
  * @bootmeth_order: List of bootmeth devices to use, in order, NULL-terminated
+ * @vbe_bootmeth: Currently selected VBE bootmeth, NULL if none
  */
 struct bootstd_priv {
        const char **prefixes;
@@ -35,6 +36,7 @@ struct bootstd_priv {
        struct list_head glob_head;
        int bootmeth_count;
        struct udevice **bootmeth_order;
+       struct udevice *vbe_bootmeth;
 };
 
 /**
index ec6e6e2..5b7821d 100644 (file)
@@ -197,6 +197,11 @@ struct device_node *of_get_parent(const struct device_node *np);
 /**
  * of_find_node_opts_by_path() - Find a node matching a full OF path
  *
+ * Note that alias processing is only available on the control FDT (gd->of_root).
+ * For other trees it is skipped, so any attempt to obtain an alias will result
+ * in returning NULL.
+ *
+ * @root: Root node of the tree to use. If this is NULL, then gd->of_root is used
  * @path: Either the full path to match, or if the path does not start with
  *     '/', the name of a property of the /aliases node (an alias). In the
  *     case of an alias, the node matching the alias' value will be returned.
@@ -210,12 +215,13 @@ struct device_node *of_get_parent(const struct device_node *np);
  *
  * Return: a node pointer or NULL if not found
  */
-struct device_node *of_find_node_opts_by_path(const char *path,
+struct device_node *of_find_node_opts_by_path(struct device_node *root,
+                                             const char *path,
                                              const char **opts);
 
 static inline struct device_node *of_find_node_by_path(const char *path)
 {
-       return of_find_node_opts_by_path(path, NULL);
+       return of_find_node_opts_by_path(NULL, path, NULL);
 }
 
 /**
@@ -513,4 +519,16 @@ int of_alias_get_highest_id(const char *stem);
  */
 struct device_node *of_get_stdout(void);
 
+/**
+ * of_write_prop() - Write a property to the device tree
+ *
+ * @np:                device node to which the property value is to be written
+ * @propname:  name of the property to write
+ * @value:     value of the property
+ * @len:       length of the property in bytes
+ * Returns: 0 if OK, -ve on error
+ */
+int of_write_prop(struct device_node *np, const char *propname, int len,
+                 const void *value);
+
 #endif
index bb60433..7ce1e4c 100644 (file)
 
 struct resource;
 
-/**
- * typedef union ofnode_union ofnode - reference to a device tree node
- *
- * This union can hold either a straightforward pointer to a struct device_node
- * in the live device tree, or an offset within the flat device tree. In the
- * latter case, the pointer value is just the integer offset within the flat DT.
- *
- * Thus we can reference nodes in both the live tree (once available) and the
- * flat tree (until then). Functions are available to translate between an
- * ofnode and either an offset or a `struct device_node *`.
- *
- * The reference can also hold a null offset, in which case the pointer value
- * here is NULL. This corresponds to a struct device_node * value of
- * NULL, or an offset of -1.
- *
- * There is no ambiguity as to whether ofnode holds an offset or a node
- * pointer: when the live tree is active it holds a node pointer, otherwise it
- * holds an offset. The value itself does not need to be unique and in theory
- * the same value could point to a valid device node or a valid offset. We
- * could arrange for a unique value to be used (e.g. by making the pointer
- * point to an offset within the flat device tree in the case of an offset) but
- * this increases code size slightly due to the subtraction. Since it offers no
- * real benefit, the approach described here seems best.
- *
- * For now these points use constant types, since we don't allow writing
- * the DT.
- *
- * @np: Pointer to device node, used for live tree
- * @of_offset: Pointer into flat device tree, used for flat tree. Note that this
- *     is not a really a pointer to a node: it is an offset value. See above.
- */
-typedef union ofnode_union {
-       const struct device_node *np;
-       long of_offset;
-} ofnode;
+#include <dm/ofnode_decl.h>
 
 struct ofnode_phandle_args {
        ofnode node;
@@ -62,45 +28,38 @@ struct ofnode_phandle_args {
 };
 
 /**
- * struct ofprop - reference to a property of a device tree node
- *
- * This struct hold the reference on one property of one node,
- * using struct ofnode and an offset within the flat device tree or either
- * a pointer to a struct property in the live device tree.
- *
- * Thus we can reference arguments in both the live tree and the flat tree.
+ * ofnode_to_np() - convert an ofnode to a live DT node pointer
  *
- * The property reference can also hold a null reference. This corresponds to
- * a struct property NULL pointer or an offset of -1.
+ * This cannot be called if the reference contains an offset.
  *
- * @node: Pointer to device node
- * @offset: Pointer into flat device tree, used for flat tree.
- * @prop: Pointer to property, used for live treee.
+ * @node: Reference containing struct device_node * (possibly invalid)
+ * Return: pointer to device node (can be NULL)
  */
-
-struct ofprop {
-       ofnode node;
-       union {
-               int offset;
-               const struct property *prop;
-       };
-};
+static inline const struct device_node *ofnode_to_np(ofnode node)
+{
+#ifdef OF_CHECKS
+       if (!of_live_active())
+               return NULL;
+#endif
+       return node.np;
+}
 
 /**
- * ofnode_to_np() - convert an ofnode to a live DT node pointer
+ * ofnode_to_npw() - convert an ofnode to a writeable live DT node pointer
  *
  * This cannot be called if the reference contains an offset.
  *
  * @node: Reference containing struct device_node * (possibly invalid)
  * Return: pointer to device node (can be NULL)
  */
-static inline const struct device_node *ofnode_to_np(ofnode node)
+static inline struct device_node *ofnode_to_npw(ofnode node)
 {
 #ifdef OF_CHECKS
        if (!of_live_active())
                return NULL;
 #endif
-       return node.np;
+       /* Drop constant */
+       return (struct device_node *)node.np;
 }
 
 /**
@@ -236,6 +195,23 @@ static inline ofnode ofnode_root(void)
 }
 
 /**
+ * oftree_default() - Returns the default device tree (U-Boot's control FDT)
+ *
+ * Returns: reference to the control FDT
+ */
+static inline oftree oftree_default(void)
+{
+       oftree tree;
+
+       if (of_live_active())
+               tree.np = gd_of_root();
+       else
+               tree.fdt = (void *)gd->fdt_blob;
+
+       return tree;
+}
+
+/**
  * ofnode_name_eq() - Check if the node name is equivalent to a given name
  *                    ignoring the unit address
  *
@@ -699,12 +675,23 @@ int ofnode_count_phandle_with_args(ofnode node, const char *list_name,
 /**
  * ofnode_path() - find a node by full path
  *
+ * This uses the control FDT.
+ *
  * @path: Full path to node, e.g. "/bus/spi@1"
  * Return: reference to the node found. Use ofnode_valid() to check if it exists
  */
 ofnode ofnode_path(const char *path);
 
 /**
+ * ofnode_path_root() - find a node by full path from a root node
+ *
+ * @tree: Device tree to use
+ * @path: Full path to node, e.g. "/bus/spi@1"
+ * Return: reference to the node found. Use ofnode_valid() to check if it exists
+ */
+ofnode ofnode_path_root(oftree tree, const char *path);
+
+/**
  * ofnode_read_chosen_prop() - get the value of a chosen property
  *
  * This looks for a property within the /chosen node and returns its value
@@ -1140,17 +1127,18 @@ int ofnode_device_is_compatible(ofnode node, const char *compat);
  * ofnode_write_prop() - Set a property of a ofnode
  *
  * Note that the value passed to the function is *not* allocated by the
- * function itself, but must be allocated by the caller if necessary.
+ * function itself, but must be allocated by the caller if necessary. However
+ * it does allocate memory for the property struct and name.
  *
  * @node:      The node for whose property should be set
  * @propname:  The name of the property to set
- * @len:       The length of the new value of the property
  * @value:     The new value of the property (must be valid prior to calling
  *             the function)
+ * @len:       The length of the new value of the property
  * Return: 0 if successful, -ve on error
  */
-int ofnode_write_prop(ofnode node, const char *propname, int len,
-                     const void *value);
+int ofnode_write_prop(ofnode node, const char *propname, const void *value,
+                     int len);
 
 /**
  * ofnode_write_string() - Set a string property of a ofnode
@@ -1167,6 +1155,16 @@ int ofnode_write_prop(ofnode node, const char *propname, int len,
 int ofnode_write_string(ofnode node, const char *propname, const char *value);
 
 /**
+ * ofnode_write_u32() - Set an integer property of an ofnode
+ *
+ * @node:      The node for whose string property should be set
+ * @propname:  The name of the string property to set
+ * @value:     The new value of the 32-bit integer property
+ * Return: 0 if successful, -ve on error
+ */
+int ofnode_write_u32(ofnode node, const char *propname, u32 value);
+
+/**
  * ofnode_set_enabled() - Enable or disable a device tree node given by its
  *                       ofnode
  *
diff --git a/include/dm/ofnode_decl.h b/include/dm/ofnode_decl.h
new file mode 100644 (file)
index 0000000..266253d
--- /dev/null
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#ifndef _DM_OFNODE_DECL_H
+#define _DM_OFNODE_DECL_H
+
+/**
+ * typedef union ofnode_union ofnode - reference to a device tree node
+ *
+ * This union can hold either a straightforward pointer to a struct device_node
+ * in the live device tree, or an offset within the flat device tree. In the
+ * latter case, the pointer value is just the integer offset within the flat DT.
+ *
+ * Thus we can reference nodes in both the live tree (once available) and the
+ * flat tree (until then). Functions are available to translate between an
+ * ofnode and either an offset or a `struct device_node *`.
+ *
+ * The reference can also hold a null offset, in which case the pointer value
+ * here is NULL. This corresponds to a struct device_node * value of
+ * NULL, or an offset of -1.
+ *
+ * There is no ambiguity as to whether ofnode holds an offset or a node
+ * pointer: when the live tree is active it holds a node pointer, otherwise it
+ * holds an offset. The value itself does not need to be unique and in theory
+ * the same value could point to a valid device node or a valid offset. We
+ * could arrange for a unique value to be used (e.g. by making the pointer
+ * point to an offset within the flat device tree in the case of an offset) but
+ * this increases code size slightly due to the subtraction. Since it offers no
+ * real benefit, the approach described here seems best.
+ *
+ * For now these points use constant types, since we don't allow writing
+ * the DT.
+ *
+ * @np: Pointer to device node, used for live tree
+ * @of_offset: Pointer into flat device tree, used for flat tree. Note that this
+ *     is not a really a pointer to a node: it is an offset value. See above.
+ */
+typedef union ofnode_union {
+       const struct device_node *np;
+       long of_offset;
+} ofnode;
+
+/**
+ * struct ofprop - reference to a property of a device tree node
+ *
+ * This struct hold the reference on one property of one node,
+ * using struct ofnode and an offset within the flat device tree or either
+ * a pointer to a struct property in the live device tree.
+ *
+ * Thus we can reference arguments in both the live tree and the flat tree.
+ *
+ * The property reference can also hold a null reference. This corresponds to
+ * a struct property NULL pointer or an offset of -1.
+ *
+ * @node: Pointer to device node
+ * @offset: Pointer into flat device tree, used for flat tree.
+ * @prop: Pointer to property, used for live treee.
+ */
+
+struct ofprop {
+       ofnode node;
+       union {
+               int offset;
+               const struct property *prop;
+       };
+};
+
+/**
+ * union oftree_union - reference to a tree of device tree nodes
+ *
+ * One or other of the members is used, depending on of_live_active()
+ *
+ * @np: Pointer to roott device node, used for live tree
+ * @fdt: Pointer to the flat device tree, used for flat tree
+ */
+typedef union oftree_union {
+       struct device_node *np;
+       void *fdt;
+} oftree;
+
+#endif
+
index c00c4fb..e8f2f55 100644 (file)
@@ -10,6 +10,8 @@
 #ifndef __event_h
 #define __event_h
 
+#include <dm/ofnode_decl.h>
+
 /**
  * enum event_t - Types of events supported by U-Boot
  *
@@ -29,6 +31,9 @@ enum event_t {
        /* Init hooks */
        EVT_MISC_INIT_F,
 
+       /* Device tree fixups before booting */
+       EVT_FT_FIXUP,
+
        EVT_COUNT
 };
 
@@ -50,6 +55,15 @@ union event_data {
        struct event_dm {
                struct udevice *dev;
        } dm;
+
+       /**
+        * struct event_ft_fixup - FDT fixup before booting
+        *
+        * @tree: tree to update
+        */
+       struct event_ft_fixup {
+               oftree tree;
+       } ft_fixup;
 };
 
 /**
@@ -123,10 +137,13 @@ static inline const char *event_spy_id(struct evspy_info *spy)
  * The only solution I can think of is to mark linker-list entries as 'used'
  * using an attribute. This should be safe, since we don't actually want to drop
  * any of these. However this does slightly limit LTO's optimisation choices.
+ *
+ * Another issue has come up, only with clang: using 'static' makes it throw
+ * away the linker-list entry sometimes, e.g. with the EVT_FT_FIXUP entry in
+ * vbe_simple.c - so for now, make it global.
  */
 #define EVENT_SPY(_type, _func) \
-       static __attribute__((used)) ll_entry_declare(struct evspy_info, \
-                                                     _type, evspy_info) = \
+       __used ll_entry_declare(struct evspy_info, _type, evspy_info) = \
        _ESPY_REC(_type, _func)
 
 /**
index b2b9679..f59d6af 100644 (file)
@@ -20,4 +20,20 @@ struct device_node;
  */
 int of_live_build(const void *fdt_blob, struct device_node **rootp);
 
+/**
+ * unflatten_device_tree() - create tree of device_nodes from flat blob
+ *
+ * Note that this allocates a single block of memory, pointed to by *mynodes.
+ * To free the tree, use free(*mynodes)
+ *
+ * unflattens a device-tree, creating the
+ * tree of struct device_node. It also fills the "name" and "type"
+ * pointers of the nodes so the normal device-tree walking functions
+ * can be used.
+ * @blob: The blob to expand
+ * @mynodes: The device_node tree created by the call
+ * Return: 0 if OK, -ve on error
+ */
+int unflatten_device_tree(const void *blob, struct device_node **mynodes);
+
 #endif
index 0104e18..c888d68 100644 (file)
@@ -46,6 +46,8 @@ enum {
        UT_TESTF_CONSOLE_REC    = BIT(5),       /* needs console recording */
        /* do extra driver model init and uninit */
        UT_TESTF_DM             = BIT(6),
+       /* live or flat device tree, but not both in the same executable */
+       UT_TESTF_LIVE_OR_FLAT   = BIT(4),
 };
 
 /**
index 1631260..b83f6f0 100644 (file)
-/* SPDX-License-Identifier: BSD-2-Clause */
-/******************************************************************************
- * Copyright (c) 2004, 2008 IBM Corporation
- * Copyright (c) 2009 Pattrick Hueper <phueper@hueper.net>
- * All rights reserved.
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Verified Boot for Embedded (VBE) support
+ * See doc/develop/vbe.rst
  *
- * Contributors:
- *     IBM Corporation - initial implementation
- *****************************************************************************/
-#ifndef _VBE_H
-#define _VBE_H
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
 
-/* these structs are for input from and output to OF */
-struct __packed vbe_screen_info {
-       u8 display_type;        /* 0=NONE, 1= analog, 2=digital */
-       u16 screen_width;
-       u16 screen_height;
-       /* bytes per line in framebuffer, may be more than screen_width */
-       u16 screen_linebytes;
-       u8 color_depth; /* color depth in bits per pixel */
-       u32 framebuffer_address;
-       u8 edid_block_zero[128];
-};
+#ifndef __VBE_H
+#define __VBE_H
 
-struct __packed vbe_screen_info_input {
-       u8 signature[4];
-       u16 size_reserved;
-       u8 monitor_number;
-       u16 max_screen_width;
-       u8 color_depth;
-};
-
-/* these structs only store the required a subset of the VBE-defined fields */
-struct __packed vbe_info {
-       char signature[4];
-       u16 version;
-       u32 oem_string_ptr;
-       u32 capabilities;
-       u32 modes_ptr;
-       u16 total_memory;
-       u16 oem_version;
-       u32 vendor_name_ptr;
-       u32 product_name_ptr;
-       u32 product_rev_ptr;
-};
-
-struct __packed vesa_mode_info {
-       u16 mode_attributes;    /* 00 */
-       u8 win_a_attributes;    /* 02 */
-       u8 win_b_attributes;    /* 03 */
-       u16 win_granularity;    /* 04 */
-       u16 win_size;           /* 06 */
-       u16 win_a_segment;      /* 08 */
-       u16 win_b_segment;      /* 0a */
-       u32 win_func_ptr;       /* 0c */
-       u16 bytes_per_scanline; /* 10 */
-       u16 x_resolution;       /* 12 */
-       u16 y_resolution;       /* 14 */
-       u8 x_charsize;          /* 16 */
-       u8 y_charsize;          /* 17 */
-       u8 number_of_planes;    /* 18 */
-       u8 bits_per_pixel;      /* 19 */
-       u8 number_of_banks;     /* 20 */
-       u8 memory_model;        /* 21 */
-       u8 bank_size;           /* 22 */
-       u8 number_of_image_pages; /* 23 */
-       u8 reserved_page;
-       u8 red_mask_size;
-       u8 red_mask_pos;
-       u8 green_mask_size;
-       u8 green_mask_pos;
-       u8 blue_mask_size;
-       u8 blue_mask_pos;
-       u8 reserved_mask_size;
-       u8 reserved_mask_pos;
-       u8 direct_color_mode_info;
-       u32 phys_base_ptr;
-       u32 offscreen_mem_offset;
-       u16 offscreen_mem_size;
-       u8 reserved[206];
-};
-
-struct vbe_mode_info {
-       u16 video_mode;
-       bool valid;
-       union {
-               struct vesa_mode_info vesa;
-               u8 mode_info_block[256];
-       };
-};
-
-struct vbe_ddc_info {
-       u8 port_number; /* i.e. monitor number */
-       u8 edid_transfer_time;
-       u8 ddc_level;
-       u8 edid_block_zero[128];
-};
-
-#define VESA_GET_INFO          0x4f00
-#define VESA_GET_MODE_INFO     0x4f01
-#define VESA_SET_MODE          0x4f02
-#define VESA_GET_CUR_MODE      0x4f03
+/**
+ * vbe_list() - List the VBE bootmeths
+ *
+ * This shows a list of the VBE bootmeth devices
+ *
+ * @return 0 (always)
+ */
+int vbe_list(void);
 
-extern struct vbe_mode_info mode_info;
+/**
+ * vbe_find_by_any() - Find a VBE bootmeth by name or sequence
+ *
+ * @name: name (e.g. "vbe-simple"), or sequence ("2") to find
+ * @devp: returns the device found, on success
+ * Return: 0 if OK, -ve on error
+ */
+int vbe_find_by_any(const char *name, struct udevice **devp);
+
+/**
+ * vbe_find_first_device() - Find the first VBE bootmeth
+ *
+ * @devp: Returns first available VBE bootmeth, or NULL if none
+ * Returns: 0 (always)
+ */
+int vbe_find_first_device(struct udevice **devp);
 
-struct video_priv;
-struct video_uc_plat;
-int vbe_setup_video_priv(struct vesa_mode_info *vesa,
-                        struct video_priv *uc_priv,
-                        struct video_uc_plat *plat);
-int vbe_setup_video(struct udevice *dev, int (*int15_handler)(void));
+/**
+ * vbe_find_next_device() - Find the next available VBE bootmeth
+ *
+ * @devp: Previous device to start from. Returns next available VBE bootmeth,
+ * or NULL if none
+ * Returns: 0 (always)
+ */
+int vbe_find_next_device(struct udevice **devp);
 
 #endif
diff --git a/include/vesa.h b/include/vesa.h
new file mode 100644 (file)
index 0000000..a42c179
--- /dev/null
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * Copyright (c) 2009 Pattrick Hueper <phueper@hueper.net>
+ * All rights reserved.
+ *
+ * Contributors:
+ *     IBM Corporation - initial implementation
+ *****************************************************************************/
+#ifndef _VESA_H
+#define _VESA_H
+
+/* these structs are for input from and output to OF */
+struct __packed vesa_screen_info {
+       u8 display_type;        /* 0=NONE, 1= analog, 2=digital */
+       u16 screen_width;
+       u16 screen_height;
+       /* bytes per line in framebuffer, may be more than screen_width */
+       u16 screen_linebytes;
+       u8 color_depth; /* color depth in bits per pixel */
+       u32 framebuffer_address;
+       u8 edid_block_zero[128];
+};
+
+struct __packed vesa_screen_info_input {
+       u8 signature[4];
+       u16 size_reserved;
+       u8 monitor_number;
+       u16 max_screen_width;
+       u8 color_depth;
+};
+
+/*
+ * These structs only store the required subset of fields  in Vesa BIOS
+ * Extensions
+ */
+struct __packed vesa_bios_ext_info {
+       char signature[4];
+       u16 version;
+       u32 oem_string_ptr;
+       u32 capabilities;
+       u32 modes_ptr;
+       u16 total_memory;
+       u16 oem_version;
+       u32 vendor_name_ptr;
+       u32 product_name_ptr;
+       u32 product_rev_ptr;
+};
+
+struct __packed vesa_mode_info {
+       u16 mode_attributes;    /* 00 */
+       u8 win_a_attributes;    /* 02 */
+       u8 win_b_attributes;    /* 03 */
+       u16 win_granularity;    /* 04 */
+       u16 win_size;           /* 06 */
+       u16 win_a_segment;      /* 08 */
+       u16 win_b_segment;      /* 0a */
+       u32 win_func_ptr;       /* 0c */
+       u16 bytes_per_scanline; /* 10 */
+       u16 x_resolution;       /* 12 */
+       u16 y_resolution;       /* 14 */
+       u8 x_charsize;          /* 16 */
+       u8 y_charsize;          /* 17 */
+       u8 number_of_planes;    /* 18 */
+       u8 bits_per_pixel;      /* 19 */
+       u8 number_of_banks;     /* 20 */
+       u8 memory_model;        /* 21 */
+       u8 bank_size;           /* 22 */
+       u8 number_of_image_pages; /* 23 */
+       u8 reserved_page;
+       u8 red_mask_size;
+       u8 red_mask_pos;
+       u8 green_mask_size;
+       u8 green_mask_pos;
+       u8 blue_mask_size;
+       u8 blue_mask_pos;
+       u8 reserved_mask_size;
+       u8 reserved_mask_pos;
+       u8 direct_color_mode_info;
+       u32 phys_base_ptr;
+       u32 offscreen_mem_offset;
+       u16 offscreen_mem_size;
+       u8 reserved[206];
+};
+
+struct vesa_state {
+       u16 video_mode;
+       bool valid;
+       union {
+               struct vesa_mode_info vesa;
+               u8 mode_info_block[256];
+       };
+};
+
+struct vesa_ddc_info {
+       u8 port_number; /* i.e. monitor number */
+       u8 edid_transfer_time;
+       u8 ddc_level;
+       u8 edid_block_zero[128];
+};
+
+#define VESA_GET_INFO          0x4f00
+#define VESA_GET_MODE_INFO     0x4f01
+#define VESA_SET_MODE          0x4f02
+#define VESA_GET_CUR_MODE      0x4f03
+
+extern struct vesa_state mode_info;
+
+struct video_priv;
+struct video_uc_plat;
+int vesa_setup_video_priv(struct vesa_mode_info *vesa,
+                         struct video_priv *uc_priv,
+                         struct video_uc_plat *plat);
+int vesa_setup_video(struct udevice *dev, int (*int15_handler)(void));
+
+#endif
index e3f2402..5cfff8c 100644 (file)
@@ -37,6 +37,7 @@ if EFI_LOADER
 config CMD_BOOTEFI_BOOTMGR
        bool "UEFI Boot Manager"
        default y
+       select BOOTMETH_GLOBAL if BOOTSTD
        help
          Select this option if you want to select the UEFI binary to be booted
          via UEFI variables Boot####, BootOrder, and BootNext. This enables the
index d074e4e..0476b26 100644 (file)
--- a/lib/elf.c
+++ b/lib/elf.c
@@ -11,7 +11,7 @@
 #include <net.h>
 #include <vxworks.h>
 #ifdef CONFIG_X86
-#include <vbe.h>
+#include <vesa.h>
 #include <asm/e820.h>
 #include <linux/linkage.h>
 #endif
index 2cb0dd9..30cae9a 100644 (file)
@@ -248,19 +248,7 @@ static void *unflatten_dt_node(const void *blob, void *mem, int *poffset,
        return mem;
 }
 
-/**
- * unflatten_device_tree() - create tree of device_nodes from flat blob
- *
- * unflattens a device-tree, creating the
- * tree of struct device_node. It also fills the "name" and "type"
- * pointers of the nodes so the normal device-tree walking functions
- * can be used.
- * @blob: The blob to expand
- * @mynodes: The device_node tree created by the call
- * Return: 0 if OK, -ve on error
- */
-static int unflatten_device_tree(const void *blob,
-                                struct device_node **mynodes)
+int unflatten_device_tree(const void *blob, struct device_node **mynodes)
 {
        unsigned long size;
        int start;
index 1730792..9e9d5ae 100644 (file)
@@ -3,3 +3,7 @@
 # Copyright 2021 Google LLC
 
 obj-$(CONFIG_BOOTSTD) += bootdev.o bootstd_common.o bootflow.o bootmeth.o
+
+ifdef CONFIG_OF_LIVE
+obj-$(CONFIG_BOOTMETH_VBE_SIMPLE) += vbe_simple.o
+endif
index 1ebb789..8530523 100644 (file)
@@ -9,13 +9,33 @@
 #include <common.h>
 #include <bootdev.h>
 #include <bootflow.h>
+#include <bootmeth.h>
 #include <bootstd.h>
 #include <dm.h>
+#ifdef CONFIG_SANDBOX
+#include <asm/test.h>
+#endif
 #include <dm/lists.h>
 #include <test/suites.h>
 #include <test/ut.h>
 #include "bootstd_common.h"
 
+static int inject_response(struct unit_test_state *uts)
+{
+       /*
+        * The image being booted presents a menu of options:
+        *
+        * Fedora-Workstation-armhfp-31-1.9 Boot Options.
+        * 1:   Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
+        * Enter choice:
+        *
+        * Provide input for this, to avoid waiting two seconds for a timeout.
+        */
+       ut_asserteq(2, console_in_puts("1\n"));
+
+       return 0;
+}
+
 /* Check 'bootflow scan/list' commands */
 static int bootflow_cmd(struct unit_test_state *uts)
 {
@@ -73,7 +93,7 @@ static int bootflow_cmd_glob(struct unit_test_state *uts)
        ut_assertok(bootstd_test_drop_bootdev_order(uts));
 
        console_record_reset_enable();
-       ut_assertok(run_command("bootflow scan -l", 0));
+       ut_assertok(run_command("bootflow scan -lG", 0));
        ut_assert_nextline("Scanning for bootflows in all bootdevs");
        ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
        ut_assert_nextlinen("---");
@@ -105,7 +125,7 @@ static int bootflow_cmd_scan_e(struct unit_test_state *uts)
        ut_assertok(bootstd_test_drop_bootdev_order(uts));
 
        console_record_reset_enable();
-       ut_assertok(run_command("bootflow scan -ale", 0));
+       ut_assertok(run_command("bootflow scan -aleG", 0));
        ut_assert_nextline("Scanning for bootflows in all bootdevs");
        ut_assert_nextline("Seq  Method       State   Uclass    Part  Name                      Filename");
        ut_assert_nextlinen("---");
@@ -188,6 +208,7 @@ BOOTSTD_TEST(bootflow_cmd_info, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
 static int bootflow_scan_boot(struct unit_test_state *uts)
 {
        console_record_reset_enable();
+       ut_assertok(inject_response(uts));
        ut_assertok(run_command("bootflow scan -b", 0));
        ut_assert_nextline(
                "** Booting bootflow 'mmc1.bootdev.part_1' with syslinux");
@@ -215,7 +236,7 @@ static int bootflow_iter(struct unit_test_state *uts)
 
        /* The first device is mmc2.bootdev which has no media */
        ut_asserteq(-EPROTONOSUPPORT,
-                   bootflow_scan_first(&iter, BOOTFLOWF_ALL, &bflow));
+                   bootflow_scan_first(&iter, BOOTFLOWF_ALL | BOOTFLOWF_SKIP_GLOBAL, &bflow));
        ut_asserteq(2, iter.num_methods);
        ut_asserteq(0, iter.cur_method);
        ut_asserteq(0, iter.part);
@@ -224,7 +245,7 @@ static int bootflow_iter(struct unit_test_state *uts)
        ut_asserteq(0, bflow.err);
 
        /*
-        * This shows MEDIA even though there is none, since int
+        * This shows MEDIA even though there is none, since in
         * bootdev_find_in_blk() we call part_get_info() which returns
         * -EPROTONOSUPPORT. Ideally it would return -EEOPNOTSUPP and we would
         * know.
@@ -302,34 +323,31 @@ static int bootflow_iter(struct unit_test_state *uts)
 }
 BOOTSTD_TEST(bootflow_iter, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
 
+#if defined(CONFIG_SANDBOX) && defined(CONFIG_BOOTMETH_GLOBAL)
 /* Check using the system bootdev */
 static int bootflow_system(struct unit_test_state *uts)
 {
-       struct udevice *bootstd, *dev;
+       struct udevice *dev;
 
-       /* Add the EFI bootmgr driver */
-       ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
-       ut_assertok(device_bind_driver(bootstd, "bootmeth_efi_mgr", "bootmgr",
-                                      &dev));
-
-       /* Add the system bootdev that it uses */
-       ut_assertok(device_bind_driver(bootstd, "system_bootdev",
-                                      "system-bootdev", &dev));
-
-       ut_assertok(bootstd_test_drop_bootdev_order(uts));
+       ut_assertok(uclass_get_device_by_name(UCLASS_BOOTMETH, "efi_mgr",
+                                             &dev));
+       sandbox_set_fake_efi_mgr_dev(dev, true);
 
        /* We should get a single 'bootmgr' method right at the end */
        bootstd_clear_glob();
        console_record_reset_enable();
        ut_assertok(run_command("bootflow scan -l", 0));
-       ut_assert_skip_to_line("  1  bootmgr      ready   bootstd      0  <NULL>                    <NULL>");
-       ut_assert_nextline("No more bootdevs");
-       ut_assert_skip_to_line("(2 bootflows, 2 valid)");
+       ut_assert_skip_to_line(
+               "  0  efi_mgr      ready   (none)       0  <NULL>                    <NULL>");
+       ut_assert_skip_to_line("No more bootdevs");
+       ut_assert_skip_to_line("(5 bootflows, 5 valid)");
        ut_assert_console_end();
 
        return 0;
 }
-BOOTSTD_TEST(bootflow_system, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+BOOTSTD_TEST(bootflow_system, UT_TESTF_DM | UT_TESTF_SCAN_PDATA |
+            UT_TESTF_SCAN_FDT);
+#endif
 
 /* Check disabling a bootmethod if it requests it */
 static int bootflow_iter_disable(struct unit_test_state *uts)
@@ -344,13 +362,11 @@ static int bootflow_iter_disable(struct unit_test_state *uts)
        ut_assertok(device_bind_driver(bootstd, "bootmeth_sandbox", "sandbox",
                                       &dev));
 
-       /* Add the system bootdev that it uses */
-       ut_assertok(device_bind_driver(bootstd, "system_bootdev",
-                                      "system-bootdev", &dev));
-
        ut_assertok(bootstd_test_drop_bootdev_order(uts));
 
        bootstd_clear_glob();
+       console_record_reset_enable();
+       ut_assertok(inject_response(uts));
        ut_assertok(run_command("bootflow scan -lb", 0));
 
        /* Try to boot the bootmgr flow, which will fail */
@@ -358,6 +374,7 @@ static int bootflow_iter_disable(struct unit_test_state *uts)
        ut_assertok(bootflow_scan_first(&iter, 0, &bflow));
        ut_asserteq(3, iter.num_methods);
        ut_asserteq_str("sandbox", iter.method->name);
+       ut_assertok(inject_response(uts));
        ut_asserteq(-ENOTSUPP, bootflow_run_boot(&iter, &bflow));
 
        ut_assert_skip_to_line("Boot method 'sandbox' failed and will not be retried");
@@ -372,6 +389,47 @@ static int bootflow_iter_disable(struct unit_test_state *uts)
 }
 BOOTSTD_TEST(bootflow_iter_disable, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
 
+/* Check 'bootflow scan' with a bootmeth ordering including a global bootmeth */
+static int bootflow_scan_glob_bootmeth(struct unit_test_state *uts)
+{
+       if (!IS_ENABLED(CONFIG_BOOTMETH_GLOBAL))
+               return 0;
+
+       ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+       /*
+        * Make sure that the -G flag makes the scan fail, since this is not
+        * supported when an ordering is provided
+        */
+       console_record_reset_enable();
+       ut_assertok(bootmeth_set_order("efi firmware0"));
+       ut_assertok(run_command("bootflow scan -lG", 0));
+       ut_assert_nextline("Scanning for bootflows in all bootdevs");
+       ut_assert_nextline(
+               "Seq  Method       State   Uclass    Part  Name                      Filename");
+       ut_assert_nextlinen("---");
+       ut_assert_nextlinen("---");
+       ut_assert_nextline("(0 bootflows, 0 valid)");
+       ut_assert_console_end();
+
+       ut_assertok(run_command("bootflow scan -l", 0));
+       ut_assert_nextline("Scanning for bootflows in all bootdevs");
+       ut_assert_nextline(
+               "Seq  Method       State   Uclass    Part  Name                      Filename");
+       ut_assert_nextlinen("---");
+       ut_assert_nextline("Scanning global bootmeth 'firmware0':");
+       ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
+       ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
+       ut_assert_nextline("Scanning bootdev 'mmc0.bootdev':");
+       ut_assert_nextline("No more bootdevs");
+       ut_assert_nextlinen("---");
+       ut_assert_nextline("(0 bootflows, 0 valid)");
+       ut_assert_console_end();
+
+       return 0;
+}
+BOOTSTD_TEST(bootflow_scan_glob_bootmeth, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
 /* Check 'bootflow boot' to boot a selected bootflow */
 static int bootflow_cmd_boot(struct unit_test_state *uts)
 {
@@ -382,6 +440,8 @@ static int bootflow_cmd_boot(struct unit_test_state *uts)
        ut_assert_console_end();
        ut_assertok(run_command("bootflow select 0", 0));
        ut_assert_console_end();
+
+       ut_assertok(inject_response(uts));
        ut_asserteq(1, run_command("bootflow boot", 0));
        ut_assert_nextline(
                "** Booting bootflow 'mmc1.bootdev.part_1' with syslinux");
index 07776c5..fb62731 100644 (file)
@@ -1,13 +1,15 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Test for bootdev functions. All start with 'bootdev'
+ * Test for bootdev functions. All start with 'bootmeth'
  *
  * Copyright 2021 Google LLC
  * Written by Simon Glass <sjg@chromium.org>
  */
 
 #include <common.h>
+#include <bootmeth.h>
 #include <bootstd.h>
+#include <dm.h>
 #include <test/suites.h>
 #include <test/ut.h>
 #include "bootstd_common.h"
@@ -21,8 +23,11 @@ static int bootmeth_cmd_list(struct unit_test_state *uts)
        ut_assert_nextlinen("---");
        ut_assert_nextline("    0    0  syslinux            Syslinux boot from a block device");
        ut_assert_nextline("    1    1  efi                 EFI boot from an .efi file");
+       if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL))
+               ut_assert_nextline(" glob    2  firmware0           VBE simple");
        ut_assert_nextlinen("---");
-       ut_assert_nextline("(2 bootmeths)");
+       ut_assert_nextline(IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) ?
+                "(3 bootmeths)" : "(2 bootmeths)");
        ut_assert_console_end();
 
        return 0;
@@ -54,8 +59,11 @@ static int bootmeth_cmd_order(struct unit_test_state *uts)
        ut_assert_nextlinen("---");
        ut_assert_nextline("    0    0  syslinux            Syslinux boot from a block device");
        ut_assert_nextline("    -    1  efi                 EFI boot from an .efi file");
+       if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL))
+               ut_assert_nextline(" glob    2  firmware0           VBE simple");
        ut_assert_nextlinen("---");
-       ut_assert_nextline("(2 bootmeths)");
+       ut_assert_nextline(IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) ?
+                "(3 bootmeths)" : "(2 bootmeths)");
        ut_assert_console_end();
 
        /* Check the -a flag with the reverse order */
@@ -66,8 +74,11 @@ static int bootmeth_cmd_order(struct unit_test_state *uts)
        ut_assert_nextlinen("---");
        ut_assert_nextline("    1    0  syslinux            Syslinux boot from a block device");
        ut_assert_nextline("    0    1  efi                 EFI boot from an .efi file");
+       if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL))
+               ut_assert_nextline(" glob    2  firmware0           VBE simple");
        ut_assert_nextlinen("---");
-       ut_assert_nextline("(2 bootmeths)");
+       ut_assert_nextline(IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) ?
+                "(3 bootmeths)" : "(2 bootmeths)");
        ut_assert_console_end();
 
        /* Now reset the order to empty, which should show all of them again */
@@ -75,7 +86,8 @@ static int bootmeth_cmd_order(struct unit_test_state *uts)
        ut_assert_console_end();
        ut_assertnull(env_get("bootmeths"));
        ut_assertok(run_command("bootmeth list", 0));
-       ut_assert_skip_to_line("(2 bootmeths)");
+       ut_assert_skip_to_line(IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) ?
+                "(3 bootmeths)" : "(2 bootmeths)");
 
        /* Try reverse order */
        ut_assertok(run_command("bootmeth order \"efi syslinux\"", 0));
@@ -91,6 +103,23 @@ static int bootmeth_cmd_order(struct unit_test_state *uts)
        ut_asserteq_str("efi syslinux", env_get("bootmeths"));
        ut_assert_console_end();
 
+       /* Try with global bootmeths */
+       if (!IS_ENABLED(CONFIG_BOOTMETH_GLOBAL))
+               return 0;
+
+       ut_assertok(run_command("bootmeth order \"efi firmware0\"", 0));
+       ut_assert_console_end();
+       ut_assertok(run_command("bootmeth list", 0));
+       ut_assert_nextline("Order  Seq  Name                Description");
+       ut_assert_nextlinen("---");
+       ut_assert_nextline("    0    1  efi                 EFI boot from an .efi file");
+       ut_assert_nextline(" glob    2  firmware0           VBE simple");
+       ut_assert_nextlinen("---");
+       ut_assert_nextline("(2 bootmeths)");
+       ut_assertnonnull(env_get("bootmeths"));
+       ut_asserteq_str("efi firmware0", env_get("bootmeths"));
+       ut_assert_console_end();
+
        return 0;
 }
 BOOTSTD_TEST(bootmeth_cmd_order, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
@@ -120,3 +149,19 @@ static int bootmeth_env(struct unit_test_state *uts)
        return 0;
 }
 BOOTSTD_TEST(bootmeth_env, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check the get_state_desc() method */
+static int bootmeth_state(struct unit_test_state *uts)
+{
+       struct udevice *dev;
+       char buf[50];
+
+       ut_assertok(uclass_first_device(UCLASS_BOOTMETH, &dev));
+       ut_assertnonnull(dev);
+
+       ut_assertok(bootmeth_get_state_desc(dev, buf, sizeof(buf)));
+       ut_asserteq_str("OK", buf);
+
+       return 0;
+}
+BOOTSTD_TEST(bootmeth_state, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
diff --git a/test/boot/vbe_simple.c b/test/boot/vbe_simple.c
new file mode 100644 (file)
index 0000000..2f6979c
--- /dev/null
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for vbe-simple bootmeth. All start with 'vbe_simple'
+ *
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <bootmeth.h>
+#include <dm.h>
+#include <image.h>
+#include <memalign.h>
+#include <mmc.h>
+#include <of_live.h>
+#include <vbe.h>
+#include <version_string.h>
+#include <linux/log2.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include <u-boot/crc.h>
+#include "bootstd_common.h"
+
+#define NVDATA_START_BLK       ((0x400 + 0x400) / MMC_MAX_BLOCK_LEN)
+#define VERSION_START_BLK      ((0x400 + 0x800) / MMC_MAX_BLOCK_LEN)
+#define TEST_VERSION           "U-Boot v2022.04-local2"
+#define TEST_VERNUM            0x00010002
+
+/* Basic test of reading nvdata and updating a fwupd node in the device tree */
+static int vbe_simple_test_base(struct unit_test_state *uts)
+{
+       ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN);
+       const char *version, *bl_version;
+       struct event_ft_fixup fixup;
+       struct udevice *dev, *mmc;
+       struct device_node *np;
+       struct blk_desc *desc;
+       char fdt_buf[0x400];
+       char info[100];
+       int node_ofs;
+       ofnode node;
+       u32 vernum;
+
+       /* Set up the version string */
+       ut_assertok(uclass_get_device(UCLASS_MMC, 1, &mmc));
+       desc = blk_get_by_device(mmc);
+       ut_assertnonnull(desc);
+
+       memset(buf, '\0', MMC_MAX_BLOCK_LEN);
+       strcpy(buf, TEST_VERSION);
+       if (blk_dwrite(desc, VERSION_START_BLK, 1, buf) != 1)
+               return log_msg_ret("write", -EIO);
+
+       /* Set up the nvdata */
+       memset(buf, '\0', MMC_MAX_BLOCK_LEN);
+       buf[1] = ilog2(0x40) << 4 | 1;
+       *(u32 *)(buf + 4) = TEST_VERNUM;
+       buf[0] = crc8(0, buf + 1, 0x3f);
+       if (blk_dwrite(desc, NVDATA_START_BLK, 1, buf) != 1)
+               return log_msg_ret("write", -EIO);
+
+       /* Read the version back */
+       ut_assertok(vbe_find_by_any("firmware0", &dev));
+       ut_assertok(bootmeth_get_state_desc(dev, info, sizeof(info)));
+       ut_asserteq_str("Version: " TEST_VERSION "\nVernum: 1/2", info);
+
+       ut_assertok(fdt_create_empty_tree(fdt_buf, sizeof(fdt_buf)));
+       node_ofs = fdt_add_subnode(fdt_buf, 0, "chosen");
+       ut_assert(node_ofs > 0);
+
+       node_ofs = fdt_add_subnode(fdt_buf, node_ofs, "fwupd");
+       ut_assert(node_ofs > 0);
+
+       node_ofs = fdt_add_subnode(fdt_buf, node_ofs, "firmware0");
+       ut_assert(node_ofs > 0);
+
+       /*
+        * This can only work on the live tree, since the ofnode interface for
+        * flat tree assumes that ofnode points to the control FDT
+        */
+       ut_assertok(unflatten_device_tree(fdt_buf, &np));
+
+       /*
+        * It would be better to call image_setup_libfdt() here, but that
+        * function does not allow passing an ofnode. We can pass fdt_buf but
+        * when it comes to send the evenr, it creates an ofnode that uses the
+        * control FDT, since it has no way of accessing the live tree created
+        * here.
+        *
+        * Two fix this we need:
+        * - image_setup_libfdt() is updated to use ofnode
+        * - ofnode updated to support access to an FDT other than the control
+        *   FDT. This is partially implemented with live tree, but not with
+        *   flat tree
+        */
+       fixup.tree.np = np;
+       ut_assertok(event_notify(EVT_FT_FIXUP, &fixup, sizeof(fixup)));
+
+       node = ofnode_path_root(fixup.tree, "/chosen/fwupd/firmware0");
+
+       version = ofnode_read_string(node, "cur-version");
+       ut_assertnonnull(version);
+       ut_asserteq_str(TEST_VERSION, version);
+
+       ut_assertok(ofnode_read_u32(node, "cur-vernum", &vernum));
+       ut_asserteq(TEST_VERNUM, vernum);
+
+       bl_version = ofnode_read_string(node, "bootloader-version");
+       ut_assertnonnull(bl_version);
+       ut_asserteq_str(version_string, bl_version);
+
+       return 0;
+}
+BOOTSTD_TEST(vbe_simple_test_base, UT_TESTF_DM | UT_TESTF_SCAN_FDT |
+            UT_TESTF_LIVE_TREE);
index 61ae1db..f80993f 100644 (file)
@@ -3,8 +3,13 @@
 #include <common.h>
 #include <dm.h>
 #include <log.h>
+#include <of_live.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
 #include <dm/of_extra.h>
+#include <dm/root.h>
 #include <dm/test.h>
+#include <dm/uclass-internal.h>
 #include <test/test.h>
 #include <test/ut.h>
 
@@ -469,3 +474,130 @@ static int dm_test_ofnode_get_phy(struct unit_test_state *uts)
        return 0;
 }
 DM_TEST(dm_test_ofnode_get_phy, 0);
+
+/**
+ * make_ofnode_fdt() - Create an FDT for testing with ofnode
+ *
+ * The size is set to the minimum needed
+ *
+ * @uts: Test state
+ * @fdt: Place to write FDT
+ * @size: Maximum size of space for fdt
+ */
+static int make_ofnode_fdt(struct unit_test_state *uts, void *fdt, int size)
+{
+       ut_assertok(fdt_create(fdt, size));
+       ut_assertok(fdt_finish_reservemap(fdt));
+       ut_assert(fdt_begin_node(fdt, "") >= 0);
+
+       ut_assert(fdt_begin_node(fdt, "aliases") >= 0);
+       ut_assertok(fdt_property_string(fdt, "mmc0", "/new-mmc"));
+       ut_assertok(fdt_end_node(fdt));
+
+       ut_assert(fdt_begin_node(fdt, "new-mmc") >= 0);
+       ut_assertok(fdt_end_node(fdt));
+
+       ut_assertok(fdt_end_node(fdt));
+       ut_assertok(fdt_finish(fdt));
+
+       return 0;
+}
+
+static int dm_test_ofnode_root(struct unit_test_state *uts)
+{
+       struct device_node *root = NULL;
+       char fdt[256];
+       oftree tree;
+       ofnode node;
+
+       /* Check that aliases work on the control FDT */
+       node = ofnode_get_aliases_node("ethernet3");
+       ut_assert(ofnode_valid(node));
+       ut_asserteq_str("sbe5", ofnode_get_name(node));
+
+       ut_assertok(make_ofnode_fdt(uts, fdt, sizeof(fdt)));
+       if (of_live_active()) {
+               ut_assertok(unflatten_device_tree(fdt, &root));
+               tree.np = root;
+       } else {
+               tree.fdt = fdt;
+       }
+
+       /* Make sure they don't work on this new tree */
+       node = ofnode_path_root(tree, "mmc0");
+       ut_assert(!ofnode_valid(node));
+
+       /* It should appear in the new tree */
+       node = ofnode_path_root(tree, "/new-mmc");
+       ut_assert(ofnode_valid(node));
+
+       /* ...and not in the control FDT */
+       node = ofnode_path_root(oftree_default(), "/new-mmc");
+       ut_assert(!ofnode_valid(node));
+
+       free(root);
+
+       return 0;
+}
+DM_TEST(dm_test_ofnode_root, UT_TESTF_SCAN_FDT);
+
+static int dm_test_ofnode_livetree_writing(struct unit_test_state *uts)
+{
+       struct udevice *dev;
+       ofnode node;
+
+       /* Test enabling devices */
+       node = ofnode_path("/usb@2");
+
+       ut_assert(!ofnode_is_enabled(node));
+       ut_assertok(ofnode_set_enabled(node, true));
+       ut_asserteq(true, ofnode_is_enabled(node));
+
+       device_bind_driver_to_node(dm_root(), "usb_sandbox", "usb@2", node,
+                                  &dev);
+       ut_assertok(uclass_find_device_by_seq(UCLASS_USB, 2, &dev));
+
+       /* Test string property setting */
+       ut_assert(device_is_compatible(dev, "sandbox,usb"));
+       ofnode_write_string(node, "compatible", "gdsys,super-usb");
+       ut_assert(device_is_compatible(dev, "gdsys,super-usb"));
+       ofnode_write_string(node, "compatible", "sandbox,usb");
+       ut_assert(device_is_compatible(dev, "sandbox,usb"));
+
+       /* Test setting generic properties */
+
+       /* Non-existent in DTB */
+       ut_asserteq_64(FDT_ADDR_T_NONE, dev_read_addr(dev));
+       /* reg = 0x42, size = 0x100 */
+       ut_assertok(ofnode_write_prop(node, "reg",
+                                     "\x00\x00\x00\x42\x00\x00\x01\x00", 8));
+       ut_asserteq(0x42, dev_read_addr(dev));
+
+       /* Test disabling devices */
+       device_remove(dev, DM_REMOVE_NORMAL);
+       device_unbind(dev);
+
+       ut_assert(ofnode_is_enabled(node));
+       ut_assertok(ofnode_set_enabled(node, false));
+       ut_assert(!ofnode_is_enabled(node));
+
+       return 0;
+}
+DM_TEST(dm_test_ofnode_livetree_writing,
+       UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_LIVE_OR_FLAT);
+
+static int dm_test_ofnode_u32(struct unit_test_state *uts)
+{
+       ofnode node;
+
+       node = ofnode_path("/lcd");
+       ut_assert(ofnode_valid(node));
+       ut_asserteq(1366, ofnode_read_u32_default(node, "xres", 123));
+       ut_assertok(ofnode_write_u32(node, "xres", 1367));
+       ut_asserteq(1367, ofnode_read_u32_default(node, "xres", 123));
+       ut_assertok(ofnode_write_u32(node, "xres", 1366));
+
+       return 0;
+}
+DM_TEST(dm_test_ofnode_u32,
+       UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_LIVE_OR_FLAT);
index f9e8174..6118ad4 100644 (file)
@@ -17,7 +17,6 @@
 #include <dm/devres.h>
 #include <dm/uclass-internal.h>
 #include <dm/util.h>
-#include <dm/lists.h>
 #include <dm/of_access.h>
 #include <linux/ioport.h>
 #include <test/test.h>
@@ -735,58 +734,6 @@ static int dm_test_fdt_remap_addr_name_live(struct unit_test_state *uts)
 DM_TEST(dm_test_fdt_remap_addr_name_live,
        UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 
-static int dm_test_fdt_livetree_writing(struct unit_test_state *uts)
-{
-       struct udevice *dev;
-       ofnode node;
-
-       if (!of_live_active()) {
-               printf("Live tree not active; ignore test\n");
-               return 0;
-       }
-
-       /* Test enabling devices */
-
-       node = ofnode_path("/usb@2");
-
-       ut_assert(!of_device_is_available(ofnode_to_np(node)));
-       ofnode_set_enabled(node, true);
-       ut_assert(of_device_is_available(ofnode_to_np(node)));
-
-       device_bind_driver_to_node(dm_root(), "usb_sandbox", "usb@2", node,
-                                  &dev);
-       ut_assertok(uclass_find_device_by_seq(UCLASS_USB, 2, &dev));
-
-       /* Test string property setting */
-
-       ut_assert(device_is_compatible(dev, "sandbox,usb"));
-       ofnode_write_string(node, "compatible", "gdsys,super-usb");
-       ut_assert(device_is_compatible(dev, "gdsys,super-usb"));
-       ofnode_write_string(node, "compatible", "sandbox,usb");
-       ut_assert(device_is_compatible(dev, "sandbox,usb"));
-
-       /* Test setting generic properties */
-
-       /* Non-existent in DTB */
-       ut_asserteq_64(FDT_ADDR_T_NONE, dev_read_addr(dev));
-       /* reg = 0x42, size = 0x100 */
-       ut_assertok(ofnode_write_prop(node, "reg", 8,
-                                     "\x00\x00\x00\x42\x00\x00\x01\x00"));
-       ut_asserteq(0x42, dev_read_addr(dev));
-
-       /* Test disabling devices */
-
-       device_remove(dev, DM_REMOVE_NORMAL);
-       device_unbind(dev);
-
-       ut_assert(of_device_is_available(ofnode_to_np(node)));
-       ofnode_set_enabled(node, false);
-       ut_assert(!of_device_is_available(ofnode_to_np(node)));
-
-       return 0;
-}
-DM_TEST(dm_test_fdt_livetree_writing, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
-
 static int dm_test_fdt_disable_enable_by_path(struct unit_test_state *uts)
 {
        ofnode node;
index b753e80..bc54149 100644 (file)
@@ -16,5 +16,6 @@ def test_event_dump(u_boot_console):
     out = util.run_and_log(cons, ['scripts/event_dump.py', sandbox])
     expect = '''.*Event type            Id                              Source location
 --------------------  ------------------------------  ------------------------------
+EVT_FT_FIXUP          bootmeth_vbe_simple_ft_fixup    .*boot/vbe_simple.c:.*
 EVT_MISC_INIT_F       sandbox_misc_init_f             .*arch/sandbox/cpu/start.c:'''
     assert re.match(expect, out, re.MULTILINE) is not None
index ee38d1f..31837e5 100644 (file)
@@ -228,8 +228,10 @@ static int test_pre_run(struct unit_test_state *uts, struct unit_test *test)
 
        uts->start = mallinfo();
 
-       if (test->flags & UT_TESTF_SCAN_PDATA)
+       if (test->flags & UT_TESTF_SCAN_PDATA) {
                ut_assertok(dm_scan_plat(false));
+               ut_assertok(dm_scan_other(false));
+       }
 
        if (test->flags & UT_TESTF_PROBE_TEST)
                ut_assertok(do_autoprobe(uts));
@@ -338,7 +340,8 @@ static int ut_run_test_live_flat(struct unit_test_state *uts,
        /* Run with the live tree if possible */
        runs = 0;
        if (CONFIG_IS_ENABLED(OF_LIVE)) {
-               if (!(test->flags & UT_TESTF_FLAT_TREE)) {
+               if (!(test->flags &
+                   (UT_TESTF_FLAT_TREE | UT_TESTF_LIVE_OR_FLAT))) {
                        uts->of_live = true;
                        ut_assertok(ut_run_test(uts, test, test->name));
                        runs++;