Merge tag 'signed-efi-2018.05' of git://github.com/agraf/u-boot
authorTom Rini <trini@konsulko.com>
Mon, 23 Apr 2018 20:17:36 +0000 (16:17 -0400)
committerTom Rini <trini@konsulko.com>
Mon, 23 Apr 2018 20:17:36 +0000 (16:17 -0400)
Patch queue for efi - 2018-04-23

Some last minute fixes for 2018.05. Most of them are minor fixes. On
top we have some functional improvements for the device path logic
which should also help us be more compatible.

13 files changed:
doc/README.uefi
include/efi_api.h
include/efi_loader.h
include/efi_selftest.h
include/log.h
lib/efi_loader/Kconfig
lib/efi_loader/efi_boottime.c
lib/efi_loader/efi_device_path.c
lib/efi_loader/efi_device_path_utilities.c
lib/efi_selftest/Makefile
lib/efi_selftest/efi_selftest.c
lib/efi_selftest/efi_selftest_devicepath.c
lib/efi_selftest/efi_selftest_devicepath_util.c [new file with mode: 0644]

index 7403be3..bb89b7a 100644 (file)
@@ -324,6 +324,8 @@ This driver is only available if U-Boot is configured with
   * persistence
   * runtime support
 
+* support bootefi booting ARMv7 in non-secure mode (CONFIG_ARMV7_NONSEC=y)
+
 ## Links
 
 * [1](http://uefi.org/specifications)
index ae93061..64c27e4 100644 (file)
@@ -343,6 +343,7 @@ struct efi_loaded_image {
                 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
 
 #define DEVICE_PATH_TYPE_END                   0x7f
+#  define DEVICE_PATH_SUB_TYPE_INSTANCE_END    0x01
 #  define DEVICE_PATH_SUB_TYPE_END             0xff
 
 struct efi_device_path {
index 17f9d3d..8d21ba7 100644 (file)
@@ -324,13 +324,28 @@ int efi_dp_match(const struct efi_device_path *a,
                 const struct efi_device_path *b);
 struct efi_object *efi_dp_find_obj(struct efi_device_path *dp,
                                   struct efi_device_path **rem);
-unsigned efi_dp_size(const struct efi_device_path *dp);
+/* get size of the first device path instance excluding end node */
+efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp);
+/* size of multi-instance device path excluding end node */
+efi_uintn_t efi_dp_size(const struct efi_device_path *dp);
 struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp);
 struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
                                      const struct efi_device_path *dp2);
 struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
                                           const struct efi_device_path *node);
-
+/* Create a device path node of given type, sub-type, length */
+struct efi_device_path *efi_dp_create_device_node(const u8 type,
+                                                 const u8 sub_type,
+                                                 const u16 length);
+/* Append device path instance */
+struct efi_device_path *efi_dp_append_instance(
+               const struct efi_device_path *dp,
+               const struct efi_device_path *dpi);
+/* Get next device path instance */
+struct efi_device_path *efi_dp_get_next_instance(struct efi_device_path **dp,
+                                                efi_uintn_t *size);
+/* Check if a device path contains muliple instances */
+bool efi_dp_is_multi_instance(const struct efi_device_path *dp);
 
 struct efi_device_path *efi_dp_from_dev(struct udevice *dev);
 struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part);
index 08dd8e4..c23bc24 100644 (file)
@@ -114,6 +114,7 @@ u16 efi_st_get_key(void);
  * @setup:     set up the unit test
  * @teardown:  tear down the unit test
  * @execute:   execute the unit test
+ * @setup_ok:  setup was successful (set at runtime)
  * @on_request:        test is only executed on request
  */
 struct efi_unit_test {
@@ -123,6 +124,7 @@ struct efi_unit_test {
                     const struct efi_system_table *systable);
        int (*execute)(void);
        int (*teardown)(void);
+       int setup_ok;
        bool on_request;
 };
 
index 20dc528..3cf08de 100644 (file)
@@ -46,7 +46,7 @@ enum log_category_t {
        LOGC_CORE,
        LOGC_DM,        /* Core driver-model */
        LOGC_DT,        /* Device-tree */
-       LOGL_EFI,       /* EFI implementation */
+       LOGC_EFI,       /* EFI implementation */
 
        LOGC_COUNT,
        LOGC_END,
index 83d75c4..d38780b 100644 (file)
@@ -1,6 +1,8 @@
 config EFI_LOADER
        bool "Support running EFI Applications in U-Boot"
        depends on (ARM || X86) && OF_LIBFDT
+       # We do not support bootefi booting ARMv7 in non-secure mode
+       depends on !ARMV7_NONSEC
        # We need EFI_STUB_64BIT to be set on x86_64 with EFI_STUB
        depends on !EFI_STUB || !X86_64 || EFI_STUB_64BIT
        # We need EFI_STUB_32BIT to be set on x86_32 with EFI_STUB
index 7a9449f..1cfdabf 100644 (file)
@@ -2219,7 +2219,7 @@ static efi_status_t EFIAPI efi_locate_device_path(
        }
 
        /* Find end of device path */
-       len = efi_dp_size(*device_path);
+       len = efi_dp_instance_size(*device_path);
 
        /* Get all handles implementing the protocol */
        ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL, protocol, NULL,
@@ -2234,7 +2234,7 @@ static efi_status_t EFIAPI efi_locate_device_path(
                if (ret != EFI_SUCCESS)
                        continue;
                dp = (struct efi_device_path *)handler->protocol_interface;
-               len_dp = efi_dp_size(dp);
+               len_dp = efi_dp_instance_size(dp);
                /*
                 * This handle can only be a better fit
                 * if its device path length is longer than the best fit and
index e965f1d..634dacf 100644 (file)
@@ -149,7 +149,7 @@ static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path,
                                   struct efi_device_path **rem)
 {
        struct efi_object *efiobj;
-       unsigned int dp_size = efi_dp_size(dp);
+       efi_uintn_t dp_size = efi_dp_instance_size(dp);
 
        list_for_each_entry(efiobj, &efi_obj_list, link) {
                struct efi_handler *handler;
@@ -170,11 +170,12 @@ static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path,
                                         * the caller.
                                         */
                                        *rem = ((void *)dp) +
-                                               efi_dp_size(obj_dp);
+                                               efi_dp_instance_size(obj_dp);
                                        return efiobj;
                                } else {
                                        /* Only return on exact matches */
-                                       if (efi_dp_size(obj_dp) == dp_size)
+                                       if (efi_dp_instance_size(obj_dp) ==
+                                           dp_size)
                                                return efiobj;
                                }
                        }
@@ -229,11 +230,13 @@ const struct efi_device_path *efi_dp_last_node(const struct efi_device_path *dp)
        return ret;
 }
 
-/* return size not including End node: */
-unsigned efi_dp_size(const struct efi_device_path *dp)
+/* get size of the first device path instance excluding end node */
+efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp)
 {
-       unsigned sz = 0;
+       efi_uintn_t sz = 0;
 
+       if (!dp || dp->type == DEVICE_PATH_TYPE_END)
+               return 0;
        while (dp) {
                sz += dp->length;
                dp = efi_dp_next(dp);
@@ -242,10 +245,25 @@ unsigned efi_dp_size(const struct efi_device_path *dp)
        return sz;
 }
 
+/* get size of multi-instance device path excluding end node */
+efi_uintn_t efi_dp_size(const struct efi_device_path *dp)
+{
+       const struct efi_device_path *p = dp;
+
+       if (!p)
+               return 0;
+       while (p->type != DEVICE_PATH_TYPE_END ||
+              p->sub_type != DEVICE_PATH_SUB_TYPE_END)
+               p = (void *)p + p->length;
+
+       return (void *)p - (void *)dp;
+}
+
+/* copy multi-instance device path */
 struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
 {
        struct efi_device_path *ndp;
-       unsigned sz = efi_dp_size(dp) + sizeof(END);
+       size_t sz = efi_dp_size(dp) + sizeof(END);
 
        if (!dp)
                return NULL;
@@ -263,7 +281,10 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
 {
        struct efi_device_path *ret;
 
-       if (!dp1) {
+       if (!dp1 && !dp2) {
+               /* return an end node */
+               ret = efi_dp_dup(&END);
+       } else if (!dp1) {
                ret = efi_dp_dup(dp2);
        } else if (!dp2) {
                ret = efi_dp_dup(dp1);
@@ -275,8 +296,8 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
                if (!p)
                        return NULL;
                memcpy(p, dp1, sz1);
-               memcpy(p + sz1, dp2, sz2);
-               memcpy(p + sz1 + sz2, &END, sizeof(END));
+               /* the end node of the second device path has to be retained */
+               memcpy(p + sz1, dp2, sz2 + sizeof(END));
                ret = p;
        }
 
@@ -293,7 +314,7 @@ struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
        } else if (!node) {
                ret = efi_dp_dup(dp);
        } else if (!dp) {
-               unsigned sz = node->length;
+               size_t sz = node->length;
                void *p = dp_alloc(sz + sizeof(END));
                if (!p)
                        return NULL;
@@ -302,7 +323,7 @@ struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
                ret = p;
        } else {
                /* both dp and node are non-null */
-               unsigned sz = efi_dp_size(dp);
+               size_t sz = efi_dp_size(dp);
                void *p = dp_alloc(sz + node->length + sizeof(END));
                if (!p)
                        return NULL;
@@ -315,6 +336,85 @@ struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
        return ret;
 }
 
+struct efi_device_path *efi_dp_create_device_node(const u8 type,
+                                                 const u8 sub_type,
+                                                 const u16 length)
+{
+       struct efi_device_path *ret;
+
+       ret = dp_alloc(length);
+       if (!ret)
+               return ret;
+       ret->type = type;
+       ret->sub_type = sub_type;
+       ret->length = length;
+       return ret;
+}
+
+struct efi_device_path *efi_dp_append_instance(
+               const struct efi_device_path *dp,
+               const struct efi_device_path *dpi)
+{
+       size_t sz, szi;
+       struct efi_device_path *p, *ret;
+
+       if (!dpi)
+               return NULL;
+       if (!dp)
+               return efi_dp_dup(dpi);
+       sz = efi_dp_size(dp);
+       szi = efi_dp_instance_size(dpi);
+       p = dp_alloc(sz + szi + 2 * sizeof(END));
+       if (!p)
+               return NULL;
+       ret = p;
+       memcpy(p, dp, sz + sizeof(END));
+       p = (void *)p + sz;
+       p->sub_type = DEVICE_PATH_SUB_TYPE_INSTANCE_END;
+       p = (void *)p + sizeof(END);
+       memcpy(p, dpi, szi);
+       p = (void *)p + szi;
+       memcpy(p, &END, sizeof(END));
+       return ret;
+}
+
+struct efi_device_path *efi_dp_get_next_instance(struct efi_device_path **dp,
+                                                efi_uintn_t *size)
+{
+       size_t sz;
+       struct efi_device_path *p;
+
+       if (size)
+               *size = 0;
+       if (!dp || !*dp)
+               return NULL;
+       p = *dp;
+       sz = efi_dp_instance_size(*dp);
+       p = dp_alloc(sz + sizeof(END));
+       if (!p)
+               return NULL;
+       memcpy(p, *dp, sz + sizeof(END));
+       *dp = (void *)*dp + sz;
+       if ((*dp)->sub_type == DEVICE_PATH_SUB_TYPE_INSTANCE_END)
+               *dp = (void *)*dp + sizeof(END);
+       else
+               *dp = NULL;
+       if (size)
+               *size = sz + sizeof(END);
+       return p;
+}
+
+bool efi_dp_is_multi_instance(const struct efi_device_path *dp)
+{
+       const struct efi_device_path *p = dp;
+
+       if (!p)
+               return false;
+       while (p->type != DEVICE_PATH_TYPE_END)
+               p = (void *)p + p->length;
+       return p->sub_type == DEVICE_PATH_SUB_TYPE_INSTANCE_END;
+}
+
 #ifdef CONFIG_DM
 /* size of device-path not including END node for device and all parents
  * up to the root device.
index bc97eee..0ada211 100644 (file)
 const efi_guid_t efi_guid_device_path_utilities_protocol =
                EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID;
 
+/*
+ * Get size of a device path.
+ *
+ * This function implements the GetDevicePathSize service of the device path
+ * utilities protocol. The device path length includes the end of path tag
+ * which may be an instance end.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path                device path
+ * @return             size in bytes
+ */
 static efi_uintn_t EFIAPI get_device_path_size(
        const struct efi_device_path *device_path)
 {
        efi_uintn_t sz = 0;
 
-       EFI_ENTRY("%p", device_path);
+       EFI_ENTRY("%pD", device_path);
        /* size includes the END node: */
        if (device_path)
                sz = efi_dp_size(device_path) + sizeof(struct efi_device_path);
        return EFI_EXIT(sz);
 }
 
+/*
+ * Duplicate a device path.
+ *
+ * This function implements the DuplicateDevicePath service of the device path
+ * utilities protocol.
+ *
+ * The UEFI spec does not indicate what happens to the end tag. We follow the
+ * EDK2 logic: In case the device path ends with an end of instance tag, the
+ * copy will also end with an end of instance tag.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path                device path
+ * @return             copy of the device path
+ */
 static struct efi_device_path * EFIAPI duplicate_device_path(
        const struct efi_device_path *device_path)
 {
-       EFI_ENTRY("%p", device_path);
+       EFI_ENTRY("%pD", device_path);
        return EFI_EXIT(efi_dp_dup(device_path));
 }
 
+/*
+ * Append device path.
+ *
+ * This function implements the AppendDevicePath service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @src1               1st device path
+ * @src2               2nd device path
+ * @return             concatenated device path
+ */
 static struct efi_device_path * EFIAPI append_device_path(
        const struct efi_device_path *src1,
        const struct efi_device_path *src2)
 {
-       EFI_ENTRY("%p, %p", src1, src2);
+       EFI_ENTRY("%pD, %pD", src1, src2);
        return EFI_EXIT(efi_dp_append(src1, src2));
 }
 
+/*
+ * Append device path node.
+ *
+ * This function implements the AppendDeviceNode service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path                device path
+ * @device_node                device node
+ * @return             concatenated device path
+ */
 static struct efi_device_path * EFIAPI append_device_node(
        const struct efi_device_path *device_path,
        const struct efi_device_path *device_node)
 {
-       EFI_ENTRY("%p, %p", device_path, device_node);
+       EFI_ENTRY("%pD, %p", device_path, device_node);
        return EFI_EXIT(efi_dp_append_node(device_path, device_node));
 }
 
+/*
+ * Append device path instance.
+ *
+ * This function implements the AppendDevicePathInstance service of the device
+ * path utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path                        1st device path
+ * @device_path_instance       2nd device path
+ * @return                     concatenated device path
+ */
 static struct efi_device_path * EFIAPI append_device_path_instance(
        const struct efi_device_path *device_path,
        const struct efi_device_path *device_path_instance)
 {
-       EFI_ENTRY("%p, %p", device_path, device_path_instance);
-       return EFI_EXIT(NULL);
+       EFI_ENTRY("%pD, %pD", device_path, device_path_instance);
+       return EFI_EXIT(efi_dp_append_instance(device_path,
+                                              device_path_instance));
 }
 
+/*
+ * Get next device path instance.
+ *
+ * This function implements the GetNextDevicePathInstance service of the device
+ * path utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path_instance       next device path instance
+ * @device_path_instance_size  size of the device path instance
+ * @return                     concatenated device path
+ */
 static struct efi_device_path * EFIAPI get_next_device_path_instance(
        struct efi_device_path **device_path_instance,
        efi_uintn_t *device_path_instance_size)
 {
-       EFI_ENTRY("%p, %p", device_path_instance, device_path_instance_size);
-       return EFI_EXIT(NULL);
+       EFI_ENTRY("%pD, %p", device_path_instance, device_path_instance_size);
+       return EFI_EXIT(efi_dp_get_next_instance(device_path_instance,
+                                                device_path_instance_size));
 }
 
+/*
+ * Check if a device path contains more than one instance.
+ *
+ * This function implements the AppendDeviceNode service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path                device path
+ * @device_node                device node
+ * @return             concatenated device path
+ */
 static bool EFIAPI is_device_path_multi_instance(
        const struct efi_device_path *device_path)
 {
-       EFI_ENTRY("%p", device_path);
-       return EFI_EXIT(false);
+       EFI_ENTRY("%pD", device_path);
+       return EFI_EXIT(efi_dp_is_multi_instance(device_path));
 }
 
+/*
+ * Create device node.
+ *
+ * This function implements the CreateDeviceNode service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @node_type          node type
+ * @node_sub_type      node sub type
+ * @node_length                node length
+ * @return             device path node
+ */
 static struct efi_device_path * EFIAPI create_device_node(
        uint8_t node_type, uint8_t node_sub_type, uint16_t node_length)
 {
        EFI_ENTRY("%u, %u, %u", node_type, node_sub_type, node_length);
-       return EFI_EXIT(NULL);
+       return EFI_EXIT(efi_dp_create_device_node(node_type, node_sub_type,
+                       node_length));
 }
 
 const struct efi_device_path_utilities_protocol efi_device_path_utilities = {
index 31b444f..0e4980c 100644 (file)
@@ -18,6 +18,7 @@ efi_selftest_bitblt.o \
 efi_selftest_controllers.o \
 efi_selftest_console.o \
 efi_selftest_devicepath.o \
+efi_selftest_devicepath_util.o \
 efi_selftest_events.o \
 efi_selftest_event_groups.o \
 efi_selftest_exitbootservices.o \
index fc5ef25..fd4fee7 100644 (file)
@@ -77,20 +77,20 @@ void efi_st_exit_boot_services(void)
  */
 static int setup(struct efi_unit_test *test, unsigned int *failures)
 {
-       int ret;
-
-       if (!test->setup)
+       if (!test->setup) {
+               test->setup_ok = EFI_ST_SUCCESS;
                return EFI_ST_SUCCESS;
+       }
        efi_st_printc(EFI_LIGHTBLUE, "\nSetting up '%s'\n", test->name);
-       ret = test->setup(handle, systable);
-       if (ret != EFI_ST_SUCCESS) {
+       test->setup_ok = test->setup(handle, systable);
+       if (test->setup_ok != EFI_ST_SUCCESS) {
                efi_st_error("Setting up '%s' failed\n", test->name);
                ++*failures;
        } else {
                efi_st_printc(EFI_LIGHTGREEN,
                              "Setting up '%s' succeeded\n", test->name);
        }
-       return ret;
+       return test->setup_ok;
 }
 
 /*
@@ -200,7 +200,7 @@ void efi_st_do_tests(const u16 *testname, unsigned int phase,
                        continue;
                if (steps & EFI_ST_SETUP)
                        setup(test, failures);
-               if (steps & EFI_ST_EXECUTE)
+               if (steps & EFI_ST_EXECUTE && test->setup_ok == EFI_ST_SUCCESS)
                        execute(test, failures);
                if (steps & EFI_ST_TEARDOWN)
                        teardown(test, failures);
index 92940c7..da68102 100644 (file)
@@ -52,7 +52,7 @@ struct efi_device_path_to_text_protocol *device_path_to_text;
  * Setup unit test.
  *
  * Create three handles. Install a new protocol on two of them and
- * provice device paths.
+ * provide device paths.
  *
  * handle1
  *   guid interface
diff --git a/lib/efi_selftest/efi_selftest_devicepath_util.c b/lib/efi_selftest/efi_selftest_devicepath_util.c
new file mode 100644 (file)
index 0000000..2b5384f
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * efi_selftest_devicepath_util
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * This unit test checks the device path utilities protocol.
+ */
+
+#include <efi_selftest.h>
+
+static struct efi_boot_services *boottime;
+
+static efi_guid_t guid_device_path_utilities_protocol =
+       EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID;
+
+struct efi_device_path_utilities_protocol *dpu;
+
+/*
+ * Setup unit test.
+ *
+ * Locate the device path utilities protocol.
+ *
+ * @handle:    handle of the loaded image
+ * @systable:  system table
+ */
+static int setup(const efi_handle_t img_handle,
+                const struct efi_system_table *systable)
+{
+       int ret;
+
+       boottime = systable->boottime;
+
+       ret = boottime->locate_protocol(&guid_device_path_utilities_protocol,
+                                       NULL, (void **)&dpu);
+       if (ret != EFI_SUCCESS) {
+               dpu = NULL;
+               efi_st_error(
+                       "Device path to text protocol is not available.\n");
+               return EFI_ST_FAILURE;
+       }
+
+       return EFI_ST_SUCCESS;
+}
+
+/*
+ * Create a device path consisting of a single media device node followed by an
+ * end node.
+ *
+ * @length:    length of the media device node
+ * @dp:                device path
+ * @return:    status code
+ */
+static int create_single_node_device_path(unsigned int length,
+                                         struct efi_device_path **dp)
+{
+       struct efi_device_path *node;
+       efi_uintn_t len;
+       int ret;
+
+       node = dpu->create_device_node(DEVICE_PATH_TYPE_MEDIA_DEVICE,
+                                      DEVICE_PATH_SUB_TYPE_FILE_PATH, length);
+       if (!node) {
+               efi_st_error("CreateDeviceNode failed\n");
+               return EFI_ST_FAILURE;
+       }
+       *dp = dpu->append_device_node(NULL, node);
+       if (!*dp) {
+               efi_st_error("AppendDeviceNode failed\n");
+               return EFI_ST_FAILURE;
+       }
+       ret = boottime->free_pool(node);
+       if (ret != EFI_ST_SUCCESS) {
+               efi_st_error("FreePool failed\n");
+               return EFI_ST_FAILURE;
+       }
+       len = dpu->get_device_path_size(*dp);
+       if (len != length + 4) {
+               efi_st_error("Wrong device path length %u, expected %u\n",
+                            (unsigned int)len, length);
+               return EFI_ST_FAILURE;
+       }
+       return EFI_ST_SUCCESS;
+}
+
+/*
+ * Execute unit test.
+ *
+ * In the test device paths are created, copied, and concatenated. The device
+ * path length is used as a measure of success.
+ */
+static int execute(void)
+{
+       struct efi_device_path *dp1;
+       struct efi_device_path *dp2;
+       struct efi_device_path *dp3;
+
+       efi_uintn_t len;
+       int ret;
+
+       /* IsDevicePathMultiInstance(NULL) */
+       if (dpu->is_device_path_multi_instance(NULL)) {
+               efi_st_error("IsDevicePathMultiInstance(NULL) returned true\n");
+               return EFI_ST_FAILURE;
+       }
+       /* GetDevicePathSize(NULL) */
+       len = dpu->get_device_path_size(NULL);
+       if (len) {
+               efi_st_error("Wrong device path length %u, expected 0\n",
+                            (unsigned int)len);
+               return EFI_ST_FAILURE;
+       }
+       /* DuplicateDevicePath(NULL) */
+       dp1 = dpu->duplicate_device_path(NULL);
+       if (dp1) {
+               efi_st_error("DuplicateDevicePath(NULL) failed\n");
+               return EFI_ST_FAILURE;
+       }
+       /* AppendDevicePath(NULL, NULL) */
+       dp1 = dpu->append_device_path(NULL, NULL);
+       if (!dp1) {
+               efi_st_error("AppendDevicePath(NULL, NULL) failed\n");
+               return EFI_ST_FAILURE;
+       }
+       len = dpu->get_device_path_size(dp1);
+       if (len != 4) {
+               efi_st_error("Wrong device path length %u, expected 4\n",
+                            (unsigned int)len);
+               return EFI_ST_FAILURE;
+       }
+       ret = boottime->free_pool(dp1);
+       if (ret != EFI_ST_SUCCESS) {
+               efi_st_error("FreePool failed\n");
+               return EFI_ST_FAILURE;
+       }
+       /* CreateDeviceNode */
+       ret = create_single_node_device_path(21, &dp1);
+       if (ret != EFI_ST_SUCCESS)
+               return ret;
+       ret = create_single_node_device_path(17, &dp2);
+       if (ret != EFI_ST_SUCCESS)
+               return ret;
+       /* AppendDevicePath */
+       dp3 = dpu->append_device_path(dp1, dp2);
+       if (!dp3) {
+               efi_st_error("AppendDevicePath failed\n");
+               return EFI_ST_FAILURE;
+       }
+       if (dp3 == dp1 || dp3 == dp2) {
+               efi_st_error("AppendDevicePath reused buffer\n");
+               return EFI_ST_FAILURE;
+       }
+       len = dpu->get_device_path_size(dp3);
+       /* 21 + 17 + 4 */
+       if (len != 42) {
+               efi_st_error("Wrong device path length %u, expected 42\n",
+                            (unsigned int)len);
+               return EFI_ST_FAILURE;
+       }
+       ret = boottime->free_pool(dp2);
+       if (ret != EFI_ST_SUCCESS) {
+               efi_st_error("FreePool failed\n");
+               return EFI_ST_FAILURE;
+       }
+       /* AppendDeviceNode */
+       dp2 = dpu->append_device_node(dp1, dp3);
+       if (!dp2) {
+               efi_st_error("AppendDevicePath failed\n");
+               return EFI_ST_FAILURE;
+       }
+       len = dpu->get_device_path_size(dp2);
+       /* 21 + 21 + 4 */
+       if (len != 46) {
+               printf("%s(%d) %s\n", __FILE__, __LINE__, __func__);
+               efi_st_error("Wrong device path length %u, expected 46\n",
+                            (unsigned int)len);
+               return EFI_ST_FAILURE;
+       }
+       ret = boottime->free_pool(dp1);
+       if (ret != EFI_ST_SUCCESS) {
+               efi_st_error("FreePool failed\n");
+               return EFI_ST_FAILURE;
+       }
+       /* IsDevicePathMultiInstance */
+       if (dpu->is_device_path_multi_instance(dp2)) {
+               printf("%s(%d) %s\n", __FILE__, __LINE__, __func__);
+               efi_st_error("IsDevicePathMultiInstance returned true\n");
+               return EFI_ST_FAILURE;
+       }
+       /* AppendDevicePathInstance */
+       dp1 = dpu->append_device_path_instance(dp2, dp3);
+       if (!dp1) {
+               efi_st_error("AppendDevicePathInstance failed\n");
+               return EFI_ST_FAILURE;
+       }
+       len = dpu->get_device_path_size(dp1);
+       /* 46 + 42 */
+       if (len != 88) {
+               efi_st_error("Wrong device path length %u, expected 88\n",
+                            (unsigned int)len);
+               return EFI_ST_FAILURE;
+       }
+       /* IsDevicePathMultiInstance */
+       if (!dpu->is_device_path_multi_instance(dp1)) {
+               efi_st_error("IsDevicePathMultiInstance returned false\n");
+               return EFI_ST_FAILURE;
+       }
+       ret = boottime->free_pool(dp2);
+       if (ret != EFI_ST_SUCCESS) {
+               efi_st_error("FreePool failed\n");
+               return EFI_ST_FAILURE;
+       }
+       ret = boottime->free_pool(dp3);
+       if (ret != EFI_ST_SUCCESS) {
+               efi_st_error("FreePool failed\n");
+               return EFI_ST_FAILURE;
+       }
+       /* GetNextDevicePathInstance */
+       dp3 = dp1;
+       dp2 = dpu->get_next_device_path_instance(&dp1, &len);
+       if (!dp2) {
+               efi_st_error("GetNextDevicePathInstance failed\n");
+               return EFI_ST_FAILURE;
+       }
+       if (!dp1) {
+               efi_st_error("GetNextDevicePathInstance no 2nd instance\n");
+               return EFI_ST_FAILURE;
+       }
+       if (len != 46) {
+               efi_st_error("Wrong device path length %u, expected 46\n",
+                            (unsigned int)len);
+               return EFI_ST_FAILURE;
+       }
+       len = dpu->get_device_path_size(dp1);
+       if (len != 42) {
+               efi_st_error("Wrong device path length %u, expected 42\n",
+                            (unsigned int)len);
+               return EFI_ST_FAILURE;
+       }
+       ret = boottime->free_pool(dp2);
+       if (ret != EFI_ST_SUCCESS) {
+               efi_st_error("FreePool failed\n");
+               return EFI_ST_FAILURE;
+       }
+       dp2 = dpu->get_next_device_path_instance(&dp1, &len);
+       if (!dp2) {
+               efi_st_error("GetNextDevicePathInstance failed\n");
+               return EFI_ST_FAILURE;
+       }
+       if (len != 42) {
+               efi_st_error("Wrong device path length %u, expected 46\n",
+                            (unsigned int)len);
+               return EFI_ST_FAILURE;
+       }
+       if (dp1) {
+               efi_st_error("GetNextDevicePathInstance did not signal end\n");
+               return EFI_ST_FAILURE;
+       }
+       ret = boottime->free_pool(dp2);
+       if (ret != EFI_ST_SUCCESS) {
+               efi_st_error("FreePool failed\n");
+               return EFI_ST_FAILURE;
+       }
+
+       /* Clean up */
+       ret = boottime->free_pool(dp2);
+       if (ret != EFI_ST_SUCCESS) {
+               efi_st_error("FreePool failed\n");
+               return EFI_ST_FAILURE;
+       }
+       ret = boottime->free_pool(dp3);
+       if (ret != EFI_ST_SUCCESS) {
+               efi_st_error("FreePool failed\n");
+               return EFI_ST_FAILURE;
+       }
+
+       return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(dputil) = {
+       .name = "device path utilities protocol",
+       .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+       .setup = setup,
+       .execute = execute,
+};