summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/README.uefi2
-rw-r--r--include/efi_api.h1
-rw-r--r--include/efi_loader.h19
-rw-r--r--include/efi_selftest.h2
-rw-r--r--include/log.h2
-rw-r--r--lib/efi_loader/Kconfig2
-rw-r--r--lib/efi_loader/efi_boottime.c4
-rw-r--r--lib/efi_loader/efi_device_path.c124
-rw-r--r--lib/efi_loader/efi_device_path_utilities.c133
-rw-r--r--lib/efi_selftest/Makefile1
-rw-r--r--lib/efi_selftest/efi_selftest.c14
-rw-r--r--lib/efi_selftest/efi_selftest_devicepath.c2
-rw-r--r--lib/efi_selftest/efi_selftest_devicepath_util.c286
13 files changed, 556 insertions, 36 deletions
diff --git a/doc/README.uefi b/doc/README.uefi
index 7403be3614..bb89b7ac2f 100644
--- a/doc/README.uefi
+++ b/doc/README.uefi
@@ -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)
diff --git a/include/efi_api.h b/include/efi_api.h
index ae93061160..64c27e494b 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -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 {
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 17f9d3d1ef..8d21ba74b1 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -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);
diff --git a/include/efi_selftest.h b/include/efi_selftest.h
index 08dd8e43ad..c23bc24bed 100644
--- a/include/efi_selftest.h
+++ b/include/efi_selftest.h
@@ -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;
};
diff --git a/include/log.h b/include/log.h
index 20dc5289c7..3cf08de6d0 100644
--- a/include/log.h
+++ b/include/log.h
@@ -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,
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 83d75c4fdc..d38780b604 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -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
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 7a9449f59c..1cfdabf6eb 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -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
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
index e965f1d88e..634dacf41b 100644
--- a/lib/efi_loader/efi_device_path.c
+++ b/lib/efi_loader/efi_device_path.c
@@ -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.
diff --git a/lib/efi_loader/efi_device_path_utilities.c b/lib/efi_loader/efi_device_path_utilities.c
index bc97eeee31..0ada2111db 100644
--- a/lib/efi_loader/efi_device_path_utilities.c
+++ b/lib/efi_loader/efi_device_path_utilities.c
@@ -12,69 +12,180 @@
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 = {
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile
index 31b444fc8b..0e4980c8a0 100644
--- a/lib/efi_selftest/Makefile
+++ b/lib/efi_selftest/Makefile
@@ -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 \
diff --git a/lib/efi_selftest/efi_selftest.c b/lib/efi_selftest/efi_selftest.c
index fc5ef254a1..fd4fee726f 100644
--- a/lib/efi_selftest/efi_selftest.c
+++ b/lib/efi_selftest/efi_selftest.c
@@ -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);
diff --git a/lib/efi_selftest/efi_selftest_devicepath.c b/lib/efi_selftest/efi_selftest_devicepath.c
index 92940c7ab6..da68102eb2 100644
--- a/lib/efi_selftest/efi_selftest_devicepath.c
+++ b/lib/efi_selftest/efi_selftest_devicepath.c
@@ -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
index 0000000000..2b5384f21b
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_devicepath_util.c
@@ -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,
+};