2018-05-06 21:58:06 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2017-09-13 22:05:28 +00:00
|
|
|
/*
|
|
|
|
* EFI device path from u-boot device-model mapping
|
|
|
|
*
|
|
|
|
* (C) Copyright 2017 Rob Clark
|
|
|
|
*/
|
|
|
|
|
2018-01-20 20:02:18 +00:00
|
|
|
#define LOG_CATEGORY LOGL_ERR
|
|
|
|
|
2017-09-13 22:05:28 +00:00
|
|
|
#include <common.h>
|
|
|
|
#include <blk.h>
|
|
|
|
#include <dm.h>
|
|
|
|
#include <usb.h>
|
|
|
|
#include <mmc.h>
|
|
|
|
#include <efi_loader.h>
|
|
|
|
#include <part.h>
|
|
|
|
|
|
|
|
/* template END node: */
|
|
|
|
static const struct efi_device_path END = {
|
|
|
|
.type = DEVICE_PATH_TYPE_END,
|
|
|
|
.sub_type = DEVICE_PATH_SUB_TYPE_END,
|
|
|
|
.length = sizeof(END),
|
|
|
|
};
|
|
|
|
|
|
|
|
/* template ROOT node: */
|
|
|
|
static const struct efi_device_path_vendor ROOT = {
|
|
|
|
.dp = {
|
|
|
|
.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE,
|
|
|
|
.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
|
|
|
|
.length = sizeof(ROOT),
|
|
|
|
},
|
|
|
|
.guid = U_BOOT_GUID,
|
|
|
|
};
|
|
|
|
|
2017-12-11 11:56:39 +00:00
|
|
|
#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
|
|
|
|
/*
|
|
|
|
* Determine if an MMC device is an SD card.
|
|
|
|
*
|
|
|
|
* @desc block device descriptor
|
|
|
|
* @return true if the device is an SD card
|
|
|
|
*/
|
|
|
|
static bool is_sd(struct blk_desc *desc)
|
|
|
|
{
|
|
|
|
struct mmc *mmc = find_mmc_device(desc->devnum);
|
|
|
|
|
|
|
|
if (!mmc)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return IS_SD(mmc) != 0U;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-09-13 22:05:28 +00:00
|
|
|
static void *dp_alloc(size_t sz)
|
|
|
|
{
|
|
|
|
void *buf;
|
|
|
|
|
2018-01-19 19:24:37 +00:00
|
|
|
if (efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, sz, &buf) !=
|
|
|
|
EFI_SUCCESS) {
|
|
|
|
debug("EFI: ERROR: out of memory in %s\n", __func__);
|
2017-09-13 22:05:28 +00:00
|
|
|
return NULL;
|
2018-01-19 19:24:37 +00:00
|
|
|
}
|
2017-09-13 22:05:28 +00:00
|
|
|
|
2018-03-25 17:54:03 +00:00
|
|
|
memset(buf, 0, sz);
|
2017-09-13 22:05:28 +00:00
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Iterate to next block in device-path, terminating (returning NULL)
|
|
|
|
* at /End* node.
|
|
|
|
*/
|
|
|
|
struct efi_device_path *efi_dp_next(const struct efi_device_path *dp)
|
|
|
|
{
|
|
|
|
if (dp == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (dp->type == DEVICE_PATH_TYPE_END)
|
|
|
|
return NULL;
|
|
|
|
dp = ((void *)dp) + dp->length;
|
|
|
|
if (dp->type == DEVICE_PATH_TYPE_END)
|
|
|
|
return NULL;
|
|
|
|
return (struct efi_device_path *)dp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compare two device-paths, stopping when the shorter of the two hits
|
2018-10-17 19:55:24 +00:00
|
|
|
* an End* node. This is useful to, for example, compare a device-path
|
2017-09-13 22:05:28 +00:00
|
|
|
* representing a device with one representing a file on the device, or
|
|
|
|
* a device with a parent device.
|
|
|
|
*/
|
2017-10-26 17:25:48 +00:00
|
|
|
int efi_dp_match(const struct efi_device_path *a,
|
|
|
|
const struct efi_device_path *b)
|
2017-09-13 22:05:28 +00:00
|
|
|
{
|
|
|
|
while (1) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = memcmp(&a->length, &b->length, sizeof(a->length));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = memcmp(a, b, a->length);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
a = efi_dp_next(a);
|
|
|
|
b = efi_dp_next(b);
|
|
|
|
|
|
|
|
if (!a || !b)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-10-17 19:55:24 +00:00
|
|
|
* We can have device paths that start with a USB WWID or a USB Class node,
|
|
|
|
* and a few other cases which don't encode the full device path with bus
|
|
|
|
* hierarchy:
|
2017-09-13 22:05:28 +00:00
|
|
|
*
|
|
|
|
* - MESSAGING:USB_WWID
|
|
|
|
* - MESSAGING:USB_CLASS
|
|
|
|
* - MEDIA:FILE_PATH
|
|
|
|
* - MEDIA:HARD_DRIVE
|
|
|
|
* - MESSAGING:URI
|
2018-10-17 19:55:24 +00:00
|
|
|
*
|
|
|
|
* See UEFI spec (section 3.1.2, about short-form device-paths)
|
2017-09-13 22:05:28 +00:00
|
|
|
*/
|
|
|
|
static struct efi_device_path *shorten_path(struct efi_device_path *dp)
|
|
|
|
{
|
|
|
|
while (dp) {
|
|
|
|
/*
|
|
|
|
* TODO: Add MESSAGING:USB_WWID and MESSAGING:URI..
|
|
|
|
* in practice fallback.efi just uses MEDIA:HARD_DRIVE
|
|
|
|
* so not sure when we would see these other cases.
|
|
|
|
*/
|
|
|
|
if (EFI_DP_TYPE(dp, MESSAGING_DEVICE, MSG_USB_CLASS) ||
|
|
|
|
EFI_DP_TYPE(dp, MEDIA_DEVICE, HARD_DRIVE_PATH) ||
|
|
|
|
EFI_DP_TYPE(dp, MEDIA_DEVICE, FILE_PATH))
|
|
|
|
return dp;
|
|
|
|
|
|
|
|
dp = efi_dp_next(dp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path,
|
|
|
|
struct efi_device_path **rem)
|
|
|
|
{
|
|
|
|
struct efi_object *efiobj;
|
2018-04-16 05:59:08 +00:00
|
|
|
efi_uintn_t dp_size = efi_dp_instance_size(dp);
|
2017-09-13 22:05:28 +00:00
|
|
|
|
|
|
|
list_for_each_entry(efiobj, &efi_obj_list, link) {
|
2017-11-26 13:05:16 +00:00
|
|
|
struct efi_handler *handler;
|
|
|
|
struct efi_device_path *obj_dp;
|
|
|
|
efi_status_t ret;
|
|
|
|
|
2018-09-26 03:27:55 +00:00
|
|
|
ret = efi_search_protocol(efiobj,
|
2017-11-26 13:05:16 +00:00
|
|
|
&efi_guid_device_path, &handler);
|
|
|
|
if (ret != EFI_SUCCESS)
|
|
|
|
continue;
|
|
|
|
obj_dp = handler->protocol_interface;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (efi_dp_match(dp, obj_dp) == 0) {
|
|
|
|
if (rem) {
|
efi_loader: Ensure efi_dp_find_obj() finds exact matches
When calling efi_dp_find_obj(), we usually want to find the *exact* match
of an object for a given device path. However, I ran into a nasty corner case
where I had the following objects with paths available:
Handle 0x9feffa70
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/EndEntire
Handle 0x9feffb58
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/HD(1,800,32000,2de808cb00000000,1,1)/EndEntire
and was searching for
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/HD(1,800,32000,2de808cb00000000,1,1)/EndEntire
But because our device path search looked for any substring match, it would
return
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/EndEntire
because that path is a full substring of the path we were searching for.
So this patch adapts the device path search logic to always look for exact
matches first. The way we distinguish between those cases is by looking at
whether our caller actually deals with remainders.
As a side effect, the code as is from all I can tell now never does a
substring match anymore, because it always gets called with rem=NULL, so
we always only do exact matches now.
Reported-by: Jonathan Gray <jsg@jsg.id.au>
Signed-off-by: Alexander Graf <agraf@suse.de>
2017-12-11 13:29:46 +00:00
|
|
|
/*
|
|
|
|
* Allow partial matches, but inform
|
|
|
|
* the caller.
|
|
|
|
*/
|
2017-11-26 13:05:16 +00:00
|
|
|
*rem = ((void *)dp) +
|
2018-04-16 05:59:08 +00:00
|
|
|
efi_dp_instance_size(obj_dp);
|
efi_loader: Ensure efi_dp_find_obj() finds exact matches
When calling efi_dp_find_obj(), we usually want to find the *exact* match
of an object for a given device path. However, I ran into a nasty corner case
where I had the following objects with paths available:
Handle 0x9feffa70
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/EndEntire
Handle 0x9feffb58
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/HD(1,800,32000,2de808cb00000000,1,1)/EndEntire
and was searching for
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/HD(1,800,32000,2de808cb00000000,1,1)/EndEntire
But because our device path search looked for any substring match, it would
return
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/EndEntire
because that path is a full substring of the path we were searching for.
So this patch adapts the device path search logic to always look for exact
matches first. The way we distinguish between those cases is by looking at
whether our caller actually deals with remainders.
As a side effect, the code as is from all I can tell now never does a
substring match anymore, because it always gets called with rem=NULL, so
we always only do exact matches now.
Reported-by: Jonathan Gray <jsg@jsg.id.au>
Signed-off-by: Alexander Graf <agraf@suse.de>
2017-12-11 13:29:46 +00:00
|
|
|
return efiobj;
|
|
|
|
} else {
|
|
|
|
/* Only return on exact matches */
|
2018-04-16 05:59:08 +00:00
|
|
|
if (efi_dp_instance_size(obj_dp) ==
|
|
|
|
dp_size)
|
efi_loader: Ensure efi_dp_find_obj() finds exact matches
When calling efi_dp_find_obj(), we usually want to find the *exact* match
of an object for a given device path. However, I ran into a nasty corner case
where I had the following objects with paths available:
Handle 0x9feffa70
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/EndEntire
Handle 0x9feffb58
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/HD(1,800,32000,2de808cb00000000,1,1)/EndEntire
and was searching for
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/HD(1,800,32000,2de808cb00000000,1,1)/EndEntire
But because our device path search looked for any substring match, it would
return
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/EndEntire
because that path is a full substring of the path we were searching for.
So this patch adapts the device path search logic to always look for exact
matches first. The way we distinguish between those cases is by looking at
whether our caller actually deals with remainders.
As a side effect, the code as is from all I can tell now never does a
substring match anymore, because it always gets called with rem=NULL, so
we always only do exact matches now.
Reported-by: Jonathan Gray <jsg@jsg.id.au>
Signed-off-by: Alexander Graf <agraf@suse.de>
2017-12-11 13:29:46 +00:00
|
|
|
return efiobj;
|
2017-09-13 22:05:28 +00:00
|
|
|
}
|
2017-11-26 13:05:16 +00:00
|
|
|
}
|
2017-09-13 22:05:28 +00:00
|
|
|
|
2017-11-26 13:05:16 +00:00
|
|
|
obj_dp = shorten_path(efi_dp_next(obj_dp));
|
|
|
|
} while (short_path && obj_dp);
|
2017-09-13 22:05:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find an efiobj from device-path, if 'rem' is not NULL, returns the
|
|
|
|
* remaining part of the device path after the matched object.
|
|
|
|
*/
|
|
|
|
struct efi_object *efi_dp_find_obj(struct efi_device_path *dp,
|
|
|
|
struct efi_device_path **rem)
|
|
|
|
{
|
|
|
|
struct efi_object *efiobj;
|
|
|
|
|
efi_loader: Ensure efi_dp_find_obj() finds exact matches
When calling efi_dp_find_obj(), we usually want to find the *exact* match
of an object for a given device path. However, I ran into a nasty corner case
where I had the following objects with paths available:
Handle 0x9feffa70
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/EndEntire
Handle 0x9feffb58
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/HD(1,800,32000,2de808cb00000000,1,1)/EndEntire
and was searching for
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/HD(1,800,32000,2de808cb00000000,1,1)/EndEntire
But because our device path search looked for any substring match, it would
return
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/EndEntire
because that path is a full substring of the path we were searching for.
So this patch adapts the device path search logic to always look for exact
matches first. The way we distinguish between those cases is by looking at
whether our caller actually deals with remainders.
As a side effect, the code as is from all I can tell now never does a
substring match anymore, because it always gets called with rem=NULL, so
we always only do exact matches now.
Reported-by: Jonathan Gray <jsg@jsg.id.au>
Signed-off-by: Alexander Graf <agraf@suse.de>
2017-12-11 13:29:46 +00:00
|
|
|
/* Search for an exact match first */
|
|
|
|
efiobj = find_obj(dp, false, NULL);
|
2017-09-13 22:05:28 +00:00
|
|
|
|
efi_loader: Ensure efi_dp_find_obj() finds exact matches
When calling efi_dp_find_obj(), we usually want to find the *exact* match
of an object for a given device path. However, I ran into a nasty corner case
where I had the following objects with paths available:
Handle 0x9feffa70
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/EndEntire
Handle 0x9feffb58
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/HD(1,800,32000,2de808cb00000000,1,1)/EndEntire
and was searching for
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/HD(1,800,32000,2de808cb00000000,1,1)/EndEntire
But because our device path search looked for any substring match, it would
return
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]/USB(6,0)/EndEntire
because that path is a full substring of the path we were searching for.
So this patch adapts the device path search logic to always look for exact
matches first. The way we distinguish between those cases is by looking at
whether our caller actually deals with remainders.
As a side effect, the code as is from all I can tell now never does a
substring match anymore, because it always gets called with rem=NULL, so
we always only do exact matches now.
Reported-by: Jonathan Gray <jsg@jsg.id.au>
Signed-off-by: Alexander Graf <agraf@suse.de>
2017-12-11 13:29:46 +00:00
|
|
|
/* Then for a fuzzy match */
|
|
|
|
if (!efiobj)
|
|
|
|
efiobj = find_obj(dp, false, rem);
|
|
|
|
|
|
|
|
/* And now for a fuzzy short match */
|
2017-09-13 22:05:28 +00:00
|
|
|
if (!efiobj)
|
|
|
|
efiobj = find_obj(dp, true, rem);
|
|
|
|
|
|
|
|
return efiobj;
|
|
|
|
}
|
|
|
|
|
2018-01-19 19:24:49 +00:00
|
|
|
/*
|
|
|
|
* Determine the last device path node that is not the end node.
|
|
|
|
*
|
|
|
|
* @dp device path
|
|
|
|
* @return last node before the end node if it exists
|
|
|
|
* otherwise NULL
|
|
|
|
*/
|
|
|
|
const struct efi_device_path *efi_dp_last_node(const struct efi_device_path *dp)
|
|
|
|
{
|
|
|
|
struct efi_device_path *ret;
|
|
|
|
|
|
|
|
if (!dp || dp->type == DEVICE_PATH_TYPE_END)
|
|
|
|
return NULL;
|
|
|
|
while (dp) {
|
|
|
|
ret = (struct efi_device_path *)dp;
|
|
|
|
dp = efi_dp_next(dp);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-04-16 05:59:08 +00:00
|
|
|
/* get size of the first device path instance excluding end node */
|
|
|
|
efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp)
|
2017-09-13 22:05:28 +00:00
|
|
|
{
|
2018-04-16 05:59:08 +00:00
|
|
|
efi_uintn_t sz = 0;
|
2017-09-13 22:05:28 +00:00
|
|
|
|
2018-04-16 05:59:07 +00:00
|
|
|
if (!dp || dp->type == DEVICE_PATH_TYPE_END)
|
|
|
|
return 0;
|
2017-09-13 22:05:28 +00:00
|
|
|
while (dp) {
|
|
|
|
sz += dp->length;
|
|
|
|
dp = efi_dp_next(dp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return sz;
|
|
|
|
}
|
|
|
|
|
2018-04-16 05:59:08 +00:00
|
|
|
/* 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 */
|
2017-09-13 22:05:28 +00:00
|
|
|
struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
|
|
|
|
{
|
|
|
|
struct efi_device_path *ndp;
|
2018-04-16 05:59:08 +00:00
|
|
|
size_t sz = efi_dp_size(dp) + sizeof(END);
|
2017-09-13 22:05:28 +00:00
|
|
|
|
|
|
|
if (!dp)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ndp = dp_alloc(sz);
|
2018-01-19 19:24:37 +00:00
|
|
|
if (!ndp)
|
|
|
|
return NULL;
|
2017-09-13 22:05:28 +00:00
|
|
|
memcpy(ndp, dp, sz);
|
|
|
|
|
|
|
|
return ndp;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
|
|
|
|
const struct efi_device_path *dp2)
|
|
|
|
{
|
|
|
|
struct efi_device_path *ret;
|
|
|
|
|
2018-04-16 05:59:06 +00:00
|
|
|
if (!dp1 && !dp2) {
|
|
|
|
/* return an end node */
|
|
|
|
ret = efi_dp_dup(&END);
|
|
|
|
} else if (!dp1) {
|
2017-09-13 22:05:28 +00:00
|
|
|
ret = efi_dp_dup(dp2);
|
|
|
|
} else if (!dp2) {
|
|
|
|
ret = efi_dp_dup(dp1);
|
|
|
|
} else {
|
|
|
|
/* both dp1 and dp2 are non-null */
|
|
|
|
unsigned sz1 = efi_dp_size(dp1);
|
|
|
|
unsigned sz2 = efi_dp_size(dp2);
|
|
|
|
void *p = dp_alloc(sz1 + sz2 + sizeof(END));
|
2018-01-19 19:24:37 +00:00
|
|
|
if (!p)
|
|
|
|
return NULL;
|
2017-09-13 22:05:28 +00:00
|
|
|
memcpy(p, dp1, sz1);
|
2018-04-16 05:59:06 +00:00
|
|
|
/* the end node of the second device path has to be retained */
|
|
|
|
memcpy(p + sz1, dp2, sz2 + sizeof(END));
|
2017-09-13 22:05:28 +00:00
|
|
|
ret = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
|
|
|
|
const struct efi_device_path *node)
|
|
|
|
{
|
|
|
|
struct efi_device_path *ret;
|
|
|
|
|
|
|
|
if (!node && !dp) {
|
|
|
|
ret = efi_dp_dup(&END);
|
|
|
|
} else if (!node) {
|
|
|
|
ret = efi_dp_dup(dp);
|
|
|
|
} else if (!dp) {
|
2018-04-16 05:59:08 +00:00
|
|
|
size_t sz = node->length;
|
2017-09-13 22:05:28 +00:00
|
|
|
void *p = dp_alloc(sz + sizeof(END));
|
2018-01-19 19:24:37 +00:00
|
|
|
if (!p)
|
|
|
|
return NULL;
|
2017-09-13 22:05:28 +00:00
|
|
|
memcpy(p, node, sz);
|
|
|
|
memcpy(p + sz, &END, sizeof(END));
|
|
|
|
ret = p;
|
|
|
|
} else {
|
|
|
|
/* both dp and node are non-null */
|
2018-04-16 05:59:08 +00:00
|
|
|
size_t sz = efi_dp_size(dp);
|
2017-09-13 22:05:28 +00:00
|
|
|
void *p = dp_alloc(sz + node->length + sizeof(END));
|
2018-01-19 19:24:37 +00:00
|
|
|
if (!p)
|
|
|
|
return NULL;
|
2017-09-13 22:05:28 +00:00
|
|
|
memcpy(p, dp, sz);
|
|
|
|
memcpy(p + sz, node, node->length);
|
|
|
|
memcpy(p + sz + node->length, &END, sizeof(END));
|
|
|
|
ret = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-04-16 05:59:05 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-04-16 05:59:09 +00:00
|
|
|
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;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-09-13 22:05:28 +00:00
|
|
|
#ifdef CONFIG_DM
|
|
|
|
/* size of device-path not including END node for device and all parents
|
|
|
|
* up to the root device.
|
|
|
|
*/
|
|
|
|
static unsigned dp_size(struct udevice *dev)
|
|
|
|
{
|
|
|
|
if (!dev || !dev->driver)
|
|
|
|
return sizeof(ROOT);
|
|
|
|
|
|
|
|
switch (dev->driver->id) {
|
|
|
|
case UCLASS_ROOT:
|
|
|
|
case UCLASS_SIMPLE_BUS:
|
|
|
|
/* stop traversing parents at this point: */
|
|
|
|
return sizeof(ROOT);
|
2018-01-20 20:02:18 +00:00
|
|
|
case UCLASS_ETH:
|
|
|
|
return dp_size(dev->parent) +
|
|
|
|
sizeof(struct efi_device_path_mac_addr);
|
2017-12-11 11:56:44 +00:00
|
|
|
#ifdef CONFIG_BLK
|
|
|
|
case UCLASS_BLK:
|
|
|
|
switch (dev->parent->uclass->uc_drv->id) {
|
|
|
|
#ifdef CONFIG_IDE
|
|
|
|
case UCLASS_IDE:
|
|
|
|
return dp_size(dev->parent) +
|
|
|
|
sizeof(struct efi_device_path_atapi);
|
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_SCSI) && defined(CONFIG_DM_SCSI)
|
|
|
|
case UCLASS_SCSI:
|
|
|
|
return dp_size(dev->parent) +
|
|
|
|
sizeof(struct efi_device_path_scsi);
|
2018-01-20 20:02:18 +00:00
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
|
|
|
|
case UCLASS_MMC:
|
|
|
|
return dp_size(dev->parent) +
|
|
|
|
sizeof(struct efi_device_path_sd_mmc_path);
|
2017-12-11 11:56:44 +00:00
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
return dp_size(dev->parent);
|
|
|
|
}
|
|
|
|
#endif
|
2018-01-20 20:02:18 +00:00
|
|
|
#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
|
2017-09-13 22:05:28 +00:00
|
|
|
case UCLASS_MMC:
|
|
|
|
return dp_size(dev->parent) +
|
|
|
|
sizeof(struct efi_device_path_sd_mmc_path);
|
2018-01-20 20:02:18 +00:00
|
|
|
#endif
|
2017-09-13 22:05:28 +00:00
|
|
|
case UCLASS_MASS_STORAGE:
|
|
|
|
case UCLASS_USB_HUB:
|
|
|
|
return dp_size(dev->parent) +
|
|
|
|
sizeof(struct efi_device_path_usb_class);
|
|
|
|
default:
|
|
|
|
/* just skip over unknown classes: */
|
|
|
|
return dp_size(dev->parent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-11 11:56:44 +00:00
|
|
|
/*
|
|
|
|
* Recursively build a device path.
|
|
|
|
*
|
|
|
|
* @buf pointer to the end of the device path
|
|
|
|
* @dev device
|
|
|
|
* @return pointer to the end of the device path
|
|
|
|
*/
|
2017-09-13 22:05:28 +00:00
|
|
|
static void *dp_fill(void *buf, struct udevice *dev)
|
|
|
|
{
|
|
|
|
if (!dev || !dev->driver)
|
|
|
|
return buf;
|
|
|
|
|
|
|
|
switch (dev->driver->id) {
|
|
|
|
case UCLASS_ROOT:
|
|
|
|
case UCLASS_SIMPLE_BUS: {
|
|
|
|
/* stop traversing parents at this point: */
|
|
|
|
struct efi_device_path_vendor *vdp = buf;
|
|
|
|
*vdp = ROOT;
|
|
|
|
return &vdp[1];
|
|
|
|
}
|
2018-01-20 20:02:18 +00:00
|
|
|
#ifdef CONFIG_DM_ETH
|
|
|
|
case UCLASS_ETH: {
|
|
|
|
struct efi_device_path_mac_addr *dp =
|
|
|
|
dp_fill(buf, dev->parent);
|
|
|
|
struct eth_pdata *pdata = dev->platdata;
|
|
|
|
|
|
|
|
dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
|
|
|
|
dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR;
|
|
|
|
dp->dp.length = sizeof(*dp);
|
|
|
|
memset(&dp->mac, 0, sizeof(dp->mac));
|
|
|
|
/* We only support IPv4 */
|
|
|
|
memcpy(&dp->mac, &pdata->enetaddr, ARP_HLEN);
|
|
|
|
/* Ethernet */
|
|
|
|
dp->if_type = 1;
|
|
|
|
return &dp[1];
|
|
|
|
}
|
|
|
|
#endif
|
2017-12-11 11:56:44 +00:00
|
|
|
#ifdef CONFIG_BLK
|
|
|
|
case UCLASS_BLK:
|
|
|
|
switch (dev->parent->uclass->uc_drv->id) {
|
|
|
|
#ifdef CONFIG_IDE
|
|
|
|
case UCLASS_IDE: {
|
|
|
|
struct efi_device_path_atapi *dp =
|
|
|
|
dp_fill(buf, dev->parent);
|
|
|
|
struct blk_desc *desc = dev_get_uclass_platdata(dev);
|
|
|
|
|
|
|
|
dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
|
|
|
|
dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_ATAPI;
|
|
|
|
dp->dp.length = sizeof(*dp);
|
|
|
|
dp->logical_unit_number = desc->devnum;
|
|
|
|
dp->primary_secondary = IDE_BUS(desc->devnum);
|
|
|
|
dp->slave_master = desc->devnum %
|
|
|
|
(CONFIG_SYS_IDE_MAXDEVICE /
|
|
|
|
CONFIG_SYS_IDE_MAXBUS);
|
|
|
|
return &dp[1];
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_SCSI) && defined(CONFIG_DM_SCSI)
|
|
|
|
case UCLASS_SCSI: {
|
|
|
|
struct efi_device_path_scsi *dp =
|
|
|
|
dp_fill(buf, dev->parent);
|
|
|
|
struct blk_desc *desc = dev_get_uclass_platdata(dev);
|
|
|
|
|
|
|
|
dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
|
|
|
|
dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SCSI;
|
|
|
|
dp->dp.length = sizeof(*dp);
|
|
|
|
dp->logical_unit_number = desc->lun;
|
|
|
|
dp->target_id = desc->target;
|
|
|
|
return &dp[1];
|
|
|
|
}
|
2018-01-20 20:02:18 +00:00
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
|
|
|
|
case UCLASS_MMC: {
|
|
|
|
struct efi_device_path_sd_mmc_path *sddp =
|
|
|
|
dp_fill(buf, dev->parent);
|
|
|
|
struct blk_desc *desc = dev_get_uclass_platdata(dev);
|
|
|
|
|
|
|
|
sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
|
|
|
|
sddp->dp.sub_type = is_sd(desc) ?
|
|
|
|
DEVICE_PATH_SUB_TYPE_MSG_SD :
|
|
|
|
DEVICE_PATH_SUB_TYPE_MSG_MMC;
|
|
|
|
sddp->dp.length = sizeof(*sddp);
|
|
|
|
sddp->slot_number = dev->seq;
|
|
|
|
return &sddp[1];
|
|
|
|
}
|
2017-12-11 11:56:44 +00:00
|
|
|
#endif
|
|
|
|
default:
|
2018-01-20 20:02:18 +00:00
|
|
|
debug("%s(%u) %s: unhandled parent class: %s (%u)\n",
|
|
|
|
__FILE__, __LINE__, __func__,
|
|
|
|
dev->name, dev->parent->uclass->uc_drv->id);
|
2017-12-11 11:56:44 +00:00
|
|
|
return dp_fill(buf, dev->parent);
|
|
|
|
}
|
|
|
|
#endif
|
2017-09-13 22:05:28 +00:00
|
|
|
#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC)
|
|
|
|
case UCLASS_MMC: {
|
|
|
|
struct efi_device_path_sd_mmc_path *sddp =
|
|
|
|
dp_fill(buf, dev->parent);
|
|
|
|
struct mmc *mmc = mmc_get_mmc_dev(dev);
|
|
|
|
struct blk_desc *desc = mmc_get_blk_desc(mmc);
|
|
|
|
|
|
|
|
sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
|
2017-12-11 11:56:39 +00:00
|
|
|
sddp->dp.sub_type = is_sd(desc) ?
|
|
|
|
DEVICE_PATH_SUB_TYPE_MSG_SD :
|
|
|
|
DEVICE_PATH_SUB_TYPE_MSG_MMC;
|
2017-09-13 22:05:28 +00:00
|
|
|
sddp->dp.length = sizeof(*sddp);
|
|
|
|
sddp->slot_number = dev->seq;
|
|
|
|
|
|
|
|
return &sddp[1];
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
case UCLASS_MASS_STORAGE:
|
|
|
|
case UCLASS_USB_HUB: {
|
|
|
|
struct efi_device_path_usb_class *udp =
|
|
|
|
dp_fill(buf, dev->parent);
|
|
|
|
struct usb_device *udev = dev_get_parent_priv(dev);
|
|
|
|
struct usb_device_descriptor *desc = &udev->descriptor;
|
|
|
|
|
|
|
|
udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
|
|
|
|
udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS;
|
|
|
|
udp->dp.length = sizeof(*udp);
|
|
|
|
udp->vendor_id = desc->idVendor;
|
|
|
|
udp->product_id = desc->idProduct;
|
|
|
|
udp->device_class = desc->bDeviceClass;
|
|
|
|
udp->device_subclass = desc->bDeviceSubClass;
|
|
|
|
udp->device_protocol = desc->bDeviceProtocol;
|
|
|
|
|
|
|
|
return &udp[1];
|
|
|
|
}
|
|
|
|
default:
|
2018-01-20 20:02:18 +00:00
|
|
|
debug("%s(%u) %s: unhandled device class: %s (%u)\n",
|
|
|
|
__FILE__, __LINE__, __func__,
|
2017-09-13 22:05:28 +00:00
|
|
|
dev->name, dev->driver->id);
|
|
|
|
return dp_fill(buf, dev->parent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Construct a device-path from a device: */
|
|
|
|
struct efi_device_path *efi_dp_from_dev(struct udevice *dev)
|
|
|
|
{
|
|
|
|
void *buf, *start;
|
|
|
|
|
|
|
|
start = buf = dp_alloc(dp_size(dev) + sizeof(END));
|
2018-01-19 19:24:37 +00:00
|
|
|
if (!buf)
|
|
|
|
return NULL;
|
2017-09-13 22:05:28 +00:00
|
|
|
buf = dp_fill(buf, dev);
|
|
|
|
*((struct efi_device_path *)buf) = END;
|
|
|
|
|
|
|
|
return start;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static unsigned dp_part_size(struct blk_desc *desc, int part)
|
|
|
|
{
|
|
|
|
unsigned dpsize;
|
|
|
|
|
|
|
|
#ifdef CONFIG_BLK
|
2017-12-11 11:56:43 +00:00
|
|
|
{
|
|
|
|
struct udevice *dev;
|
|
|
|
int ret = blk_find_device(desc->if_type, desc->devnum, &dev);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
dev = desc->bdev->parent;
|
|
|
|
dpsize = dp_size(dev);
|
|
|
|
}
|
2017-09-13 22:05:28 +00:00
|
|
|
#else
|
|
|
|
dpsize = sizeof(ROOT) + sizeof(struct efi_device_path_usb);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (part == 0) /* the actual disk, not a partition */
|
|
|
|
return dpsize;
|
|
|
|
|
|
|
|
if (desc->part_type == PART_TYPE_ISO)
|
|
|
|
dpsize += sizeof(struct efi_device_path_cdrom_path);
|
|
|
|
else
|
|
|
|
dpsize += sizeof(struct efi_device_path_hard_drive_path);
|
|
|
|
|
|
|
|
return dpsize;
|
|
|
|
}
|
|
|
|
|
2017-12-11 11:56:42 +00:00
|
|
|
/*
|
2018-01-19 19:24:46 +00:00
|
|
|
* Create a device node for a block device partition.
|
2017-12-11 11:56:42 +00:00
|
|
|
*
|
2018-10-17 19:55:24 +00:00
|
|
|
* @buf buffer to which the device path is written
|
2017-12-11 11:56:42 +00:00
|
|
|
* @desc block device descriptor
|
|
|
|
* @part partition number, 0 identifies a block device
|
|
|
|
*/
|
2018-01-19 19:24:46 +00:00
|
|
|
static void *dp_part_node(void *buf, struct blk_desc *desc, int part)
|
2017-09-13 22:05:28 +00:00
|
|
|
{
|
|
|
|
disk_partition_t info;
|
|
|
|
|
|
|
|
part_get_info(desc, part, &info);
|
|
|
|
|
|
|
|
if (desc->part_type == PART_TYPE_ISO) {
|
|
|
|
struct efi_device_path_cdrom_path *cddp = buf;
|
|
|
|
|
2017-12-11 11:56:40 +00:00
|
|
|
cddp->boot_entry = part;
|
2017-09-13 22:05:28 +00:00
|
|
|
cddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
|
|
|
|
cddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CDROM_PATH;
|
|
|
|
cddp->dp.length = sizeof(*cddp);
|
|
|
|
cddp->partition_start = info.start;
|
|
|
|
cddp->partition_end = info.size;
|
|
|
|
|
|
|
|
buf = &cddp[1];
|
|
|
|
} else {
|
|
|
|
struct efi_device_path_hard_drive_path *hddp = buf;
|
|
|
|
|
|
|
|
hddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
|
|
|
|
hddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH;
|
|
|
|
hddp->dp.length = sizeof(*hddp);
|
2017-12-11 11:56:40 +00:00
|
|
|
hddp->partition_number = part;
|
2017-09-13 22:05:28 +00:00
|
|
|
hddp->partition_start = info.start;
|
|
|
|
hddp->partition_end = info.size;
|
|
|
|
if (desc->part_type == PART_TYPE_EFI)
|
|
|
|
hddp->partmap_type = 2;
|
|
|
|
else
|
|
|
|
hddp->partmap_type = 1;
|
2017-11-22 03:18:59 +00:00
|
|
|
|
|
|
|
switch (desc->sig_type) {
|
|
|
|
case SIG_TYPE_NONE:
|
|
|
|
default:
|
|
|
|
hddp->signature_type = 0;
|
|
|
|
memset(hddp->partition_signature, 0,
|
|
|
|
sizeof(hddp->partition_signature));
|
|
|
|
break;
|
|
|
|
case SIG_TYPE_MBR:
|
|
|
|
hddp->signature_type = 1;
|
|
|
|
memset(hddp->partition_signature, 0,
|
|
|
|
sizeof(hddp->partition_signature));
|
|
|
|
memcpy(hddp->partition_signature, &desc->mbr_sig,
|
|
|
|
sizeof(desc->mbr_sig));
|
|
|
|
break;
|
|
|
|
case SIG_TYPE_GUID:
|
|
|
|
hddp->signature_type = 2;
|
2017-09-13 22:05:28 +00:00
|
|
|
memcpy(hddp->partition_signature, &desc->guid_sig,
|
|
|
|
sizeof(hddp->partition_signature));
|
2017-11-22 03:18:59 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-09-13 22:05:28 +00:00
|
|
|
|
|
|
|
buf = &hddp[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2018-01-19 19:24:46 +00:00
|
|
|
/*
|
|
|
|
* Create a device path for a block device or one of its partitions.
|
|
|
|
*
|
2018-10-17 19:55:24 +00:00
|
|
|
* @buf buffer to which the device path is written
|
2018-01-19 19:24:46 +00:00
|
|
|
* @desc block device descriptor
|
|
|
|
* @part partition number, 0 identifies a block device
|
|
|
|
*/
|
|
|
|
static void *dp_part_fill(void *buf, struct blk_desc *desc, int part)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_BLK
|
|
|
|
{
|
|
|
|
struct udevice *dev;
|
|
|
|
int ret = blk_find_device(desc->if_type, desc->devnum, &dev);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
dev = desc->bdev->parent;
|
|
|
|
buf = dp_fill(buf, dev);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
/*
|
|
|
|
* We *could* make a more accurate path, by looking at if_type
|
|
|
|
* and handling all the different cases like we do for non-
|
2018-10-17 19:55:24 +00:00
|
|
|
* legacy (i.e. CONFIG_BLK=y) case. But most important thing
|
2018-01-19 19:24:46 +00:00
|
|
|
* is just to have a unique device-path for if_type+devnum.
|
|
|
|
* So map things to a fictitious USB device.
|
|
|
|
*/
|
|
|
|
struct efi_device_path_usb *udp;
|
|
|
|
|
|
|
|
memcpy(buf, &ROOT, sizeof(ROOT));
|
|
|
|
buf += sizeof(ROOT);
|
|
|
|
|
|
|
|
udp = buf;
|
|
|
|
udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
|
|
|
|
udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB;
|
|
|
|
udp->dp.length = sizeof(*udp);
|
|
|
|
udp->parent_port_number = desc->if_type;
|
|
|
|
udp->usb_interface = desc->devnum;
|
|
|
|
buf = &udp[1];
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (part == 0) /* the actual disk, not a partition */
|
|
|
|
return buf;
|
|
|
|
|
|
|
|
return dp_part_node(buf, desc, part);
|
|
|
|
}
|
2017-09-13 22:05:28 +00:00
|
|
|
|
2018-10-17 19:55:24 +00:00
|
|
|
/* Construct a device-path from a partition on a block device: */
|
2017-09-13 22:05:28 +00:00
|
|
|
struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part)
|
|
|
|
{
|
|
|
|
void *buf, *start;
|
|
|
|
|
|
|
|
start = buf = dp_alloc(dp_part_size(desc, part) + sizeof(END));
|
2018-01-19 19:24:37 +00:00
|
|
|
if (!buf)
|
|
|
|
return NULL;
|
2017-09-13 22:05:28 +00:00
|
|
|
|
|
|
|
buf = dp_part_fill(buf, desc, part);
|
|
|
|
|
|
|
|
*((struct efi_device_path *)buf) = END;
|
|
|
|
|
|
|
|
return start;
|
|
|
|
}
|
|
|
|
|
2018-01-19 19:24:46 +00:00
|
|
|
/*
|
|
|
|
* Create a device node for a block device partition.
|
|
|
|
*
|
2018-10-17 19:55:24 +00:00
|
|
|
* @buf buffer to which the device path is written
|
2018-01-19 19:24:46 +00:00
|
|
|
* @desc block device descriptor
|
|
|
|
* @part partition number, 0 identifies a block device
|
|
|
|
*/
|
|
|
|
struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part)
|
|
|
|
{
|
|
|
|
efi_uintn_t dpsize;
|
|
|
|
void *buf;
|
|
|
|
|
|
|
|
if (desc->part_type == PART_TYPE_ISO)
|
|
|
|
dpsize = sizeof(struct efi_device_path_cdrom_path);
|
|
|
|
else
|
|
|
|
dpsize = sizeof(struct efi_device_path_hard_drive_path);
|
|
|
|
buf = dp_alloc(dpsize);
|
|
|
|
|
|
|
|
dp_part_node(buf, desc, part);
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2018-10-17 19:55:24 +00:00
|
|
|
/* convert path to an UEFI style path (i.e. DOS style backslashes and UTF-16) */
|
2017-09-13 22:05:28 +00:00
|
|
|
static void path_to_uefi(u16 *uefi, const char *path)
|
|
|
|
{
|
|
|
|
while (*path) {
|
|
|
|
char c = *(path++);
|
|
|
|
if (c == '/')
|
|
|
|
c = '\\';
|
|
|
|
*(uefi++) = c;
|
|
|
|
}
|
|
|
|
*uefi = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If desc is NULL, this creates a path with only the file component,
|
|
|
|
* otherwise it creates a full path with both device and file components
|
|
|
|
*/
|
|
|
|
struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
|
|
|
|
const char *path)
|
|
|
|
{
|
|
|
|
struct efi_device_path_file_path *fp;
|
|
|
|
void *buf, *start;
|
|
|
|
unsigned dpsize = 0, fpsize;
|
|
|
|
|
|
|
|
if (desc)
|
|
|
|
dpsize = dp_part_size(desc, part);
|
|
|
|
|
|
|
|
fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
|
|
|
|
dpsize += fpsize;
|
|
|
|
|
|
|
|
start = buf = dp_alloc(dpsize + sizeof(END));
|
2018-01-19 19:24:37 +00:00
|
|
|
if (!buf)
|
|
|
|
return NULL;
|
2017-09-13 22:05:28 +00:00
|
|
|
|
|
|
|
if (desc)
|
|
|
|
buf = dp_part_fill(buf, desc, part);
|
|
|
|
|
|
|
|
/* add file-path: */
|
|
|
|
fp = buf;
|
|
|
|
fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
|
|
|
|
fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
|
|
|
|
fp->dp.length = fpsize;
|
|
|
|
path_to_uefi(fp->str, path);
|
|
|
|
buf += fpsize;
|
|
|
|
|
|
|
|
*((struct efi_device_path *)buf) = END;
|
|
|
|
|
|
|
|
return start;
|
|
|
|
}
|
|
|
|
|
2018-04-13 20:26:39 +00:00
|
|
|
#ifdef CONFIG_NET
|
2017-09-13 22:05:28 +00:00
|
|
|
struct efi_device_path *efi_dp_from_eth(void)
|
|
|
|
{
|
efi_loader: Fix network DP with DM_ETH
When CONFIG_DM_ETH is set, we assemble the device path properly with a
full device hierarchy. Our helper function dp_fill() even put the MAC
node itself in it for us.
However, for non-DM compatibility we also have code in that added the
MAC node manually. That code now runs on top of the existing MAC node:
Handle 0x3db2f6b0
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]
/USBClass(0,0,9,0,0)/USBClass(424,9514,9,0,2)/MacAddr(b8:27:eb:e1:81:47,1)
/MacAddr(b8:27:eb:e1:81:47,57)/EndEntire
We obviously don't need the additional node and in fact, grub chokes on
it and fails to match the DP against the ethernet device node. So this
patch moves the additional MAC node into the non-DM code path:
Handle 0x3db3fde0
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]
/USBClass(0,0,9,0,0)/USBClass(424,9514,9,0,2)/MacAddr(b8:27:eb:e1:81:47,1)
/EndEntire
While at it, we also mark the non-DM MAC node as ethernet.
Fixes: b66c60dde9d ("efi_loader: add device-path utils")
Signed-off-by: Alexander Graf <agraf@suse.de>
2018-03-15 16:33:38 +00:00
|
|
|
#ifndef CONFIG_DM_ETH
|
2017-09-13 22:05:28 +00:00
|
|
|
struct efi_device_path_mac_addr *ndp;
|
efi_loader: Fix network DP with DM_ETH
When CONFIG_DM_ETH is set, we assemble the device path properly with a
full device hierarchy. Our helper function dp_fill() even put the MAC
node itself in it for us.
However, for non-DM compatibility we also have code in that added the
MAC node manually. That code now runs on top of the existing MAC node:
Handle 0x3db2f6b0
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]
/USBClass(0,0,9,0,0)/USBClass(424,9514,9,0,2)/MacAddr(b8:27:eb:e1:81:47,1)
/MacAddr(b8:27:eb:e1:81:47,57)/EndEntire
We obviously don't need the additional node and in fact, grub chokes on
it and fails to match the DP against the ethernet device node. So this
patch moves the additional MAC node into the non-DM code path:
Handle 0x3db3fde0
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]
/USBClass(0,0,9,0,0)/USBClass(424,9514,9,0,2)/MacAddr(b8:27:eb:e1:81:47,1)
/EndEntire
While at it, we also mark the non-DM MAC node as ethernet.
Fixes: b66c60dde9d ("efi_loader: add device-path utils")
Signed-off-by: Alexander Graf <agraf@suse.de>
2018-03-15 16:33:38 +00:00
|
|
|
#endif
|
2017-09-13 22:05:28 +00:00
|
|
|
void *buf, *start;
|
|
|
|
unsigned dpsize = 0;
|
|
|
|
|
|
|
|
assert(eth_get_dev());
|
|
|
|
|
|
|
|
#ifdef CONFIG_DM_ETH
|
|
|
|
dpsize += dp_size(eth_get_dev());
|
|
|
|
#else
|
|
|
|
dpsize += sizeof(ROOT);
|
|
|
|
dpsize += sizeof(*ndp);
|
efi_loader: Fix network DP with DM_ETH
When CONFIG_DM_ETH is set, we assemble the device path properly with a
full device hierarchy. Our helper function dp_fill() even put the MAC
node itself in it for us.
However, for non-DM compatibility we also have code in that added the
MAC node manually. That code now runs on top of the existing MAC node:
Handle 0x3db2f6b0
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]
/USBClass(0,0,9,0,0)/USBClass(424,9514,9,0,2)/MacAddr(b8:27:eb:e1:81:47,1)
/MacAddr(b8:27:eb:e1:81:47,57)/EndEntire
We obviously don't need the additional node and in fact, grub chokes on
it and fails to match the DP against the ethernet device node. So this
patch moves the additional MAC node into the non-DM code path:
Handle 0x3db3fde0
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]
/USBClass(0,0,9,0,0)/USBClass(424,9514,9,0,2)/MacAddr(b8:27:eb:e1:81:47,1)
/EndEntire
While at it, we also mark the non-DM MAC node as ethernet.
Fixes: b66c60dde9d ("efi_loader: add device-path utils")
Signed-off-by: Alexander Graf <agraf@suse.de>
2018-03-15 16:33:38 +00:00
|
|
|
#endif
|
2017-09-13 22:05:28 +00:00
|
|
|
|
|
|
|
start = buf = dp_alloc(dpsize + sizeof(END));
|
2018-01-19 19:24:37 +00:00
|
|
|
if (!buf)
|
|
|
|
return NULL;
|
2017-09-13 22:05:28 +00:00
|
|
|
|
|
|
|
#ifdef CONFIG_DM_ETH
|
|
|
|
buf = dp_fill(buf, eth_get_dev());
|
|
|
|
#else
|
|
|
|
memcpy(buf, &ROOT, sizeof(ROOT));
|
|
|
|
buf += sizeof(ROOT);
|
|
|
|
|
|
|
|
ndp = buf;
|
|
|
|
ndp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
|
|
|
|
ndp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR;
|
|
|
|
ndp->dp.length = sizeof(*ndp);
|
efi_loader: Fix network DP with DM_ETH
When CONFIG_DM_ETH is set, we assemble the device path properly with a
full device hierarchy. Our helper function dp_fill() even put the MAC
node itself in it for us.
However, for non-DM compatibility we also have code in that added the
MAC node manually. That code now runs on top of the existing MAC node:
Handle 0x3db2f6b0
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]
/USBClass(0,0,9,0,0)/USBClass(424,9514,9,0,2)/MacAddr(b8:27:eb:e1:81:47,1)
/MacAddr(b8:27:eb:e1:81:47,57)/EndEntire
We obviously don't need the additional node and in fact, grub chokes on
it and fails to match the DP against the ethernet device node. So this
patch moves the additional MAC node into the non-DM code path:
Handle 0x3db3fde0
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]
/USBClass(0,0,9,0,0)/USBClass(424,9514,9,0,2)/MacAddr(b8:27:eb:e1:81:47,1)
/EndEntire
While at it, we also mark the non-DM MAC node as ethernet.
Fixes: b66c60dde9d ("efi_loader: add device-path utils")
Signed-off-by: Alexander Graf <agraf@suse.de>
2018-03-15 16:33:38 +00:00
|
|
|
ndp->if_type = 1; /* Ethernet */
|
2017-09-13 22:05:28 +00:00
|
|
|
memcpy(ndp->mac.addr, eth_get_ethaddr(), ARP_HLEN);
|
|
|
|
buf = &ndp[1];
|
efi_loader: Fix network DP with DM_ETH
When CONFIG_DM_ETH is set, we assemble the device path properly with a
full device hierarchy. Our helper function dp_fill() even put the MAC
node itself in it for us.
However, for non-DM compatibility we also have code in that added the
MAC node manually. That code now runs on top of the existing MAC node:
Handle 0x3db2f6b0
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]
/USBClass(0,0,9,0,0)/USBClass(424,9514,9,0,2)/MacAddr(b8:27:eb:e1:81:47,1)
/MacAddr(b8:27:eb:e1:81:47,57)/EndEntire
We obviously don't need the additional node and in fact, grub chokes on
it and fails to match the DP against the ethernet device node. So this
patch moves the additional MAC node into the non-DM code path:
Handle 0x3db3fde0
/HardwareVendor(e61d73b9-a384-4acc-aeab-82e828f3628b)[0: ]
/USBClass(0,0,9,0,0)/USBClass(424,9514,9,0,2)/MacAddr(b8:27:eb:e1:81:47,1)
/EndEntire
While at it, we also mark the non-DM MAC node as ethernet.
Fixes: b66c60dde9d ("efi_loader: add device-path utils")
Signed-off-by: Alexander Graf <agraf@suse.de>
2018-03-15 16:33:38 +00:00
|
|
|
#endif
|
2017-09-13 22:05:28 +00:00
|
|
|
|
|
|
|
*((struct efi_device_path *)buf) = END;
|
|
|
|
|
|
|
|
return start;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-10-10 12:23:06 +00:00
|
|
|
/* Construct a device-path for memory-mapped image */
|
|
|
|
struct efi_device_path *efi_dp_from_mem(uint32_t memory_type,
|
|
|
|
uint64_t start_address,
|
|
|
|
uint64_t end_address)
|
|
|
|
{
|
|
|
|
struct efi_device_path_memory *mdp;
|
|
|
|
void *buf, *start;
|
|
|
|
|
|
|
|
start = buf = dp_alloc(sizeof(*mdp) + sizeof(END));
|
2018-01-19 19:24:37 +00:00
|
|
|
if (!buf)
|
|
|
|
return NULL;
|
2017-10-10 12:23:06 +00:00
|
|
|
|
|
|
|
mdp = buf;
|
|
|
|
mdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
|
|
|
|
mdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MEMORY;
|
|
|
|
mdp->dp.length = sizeof(*mdp);
|
|
|
|
mdp->memory_type = memory_type;
|
|
|
|
mdp->start_address = start_address;
|
|
|
|
mdp->end_address = end_address;
|
|
|
|
buf = &mdp[1];
|
|
|
|
|
|
|
|
*((struct efi_device_path *)buf) = END;
|
|
|
|
|
|
|
|
return start;
|
|
|
|
}
|
|
|
|
|
2019-02-04 11:49:43 +00:00
|
|
|
/**
|
|
|
|
* efi_dp_split_file_path() - split of relative file path from device path
|
|
|
|
*
|
|
|
|
* Given a device path indicating a file on a device, separate the device
|
|
|
|
* path in two: the device path of the actual device and the file path
|
|
|
|
* relative to this device.
|
|
|
|
*
|
|
|
|
* @full_path: device path including device and file path
|
|
|
|
* @device_path: path of the device
|
|
|
|
* @file_path: relative path of the file
|
|
|
|
* Return: status code
|
2017-09-13 22:05:28 +00:00
|
|
|
*/
|
2018-01-19 19:24:37 +00:00
|
|
|
efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path,
|
|
|
|
struct efi_device_path **device_path,
|
|
|
|
struct efi_device_path **file_path)
|
2017-09-13 22:05:28 +00:00
|
|
|
{
|
|
|
|
struct efi_device_path *p, *dp, *fp;
|
|
|
|
|
2018-01-19 19:24:37 +00:00
|
|
|
*device_path = NULL;
|
|
|
|
*file_path = NULL;
|
2017-09-13 22:05:28 +00:00
|
|
|
dp = efi_dp_dup(full_path);
|
2018-01-19 19:24:37 +00:00
|
|
|
if (!dp)
|
|
|
|
return EFI_OUT_OF_RESOURCES;
|
2017-09-13 22:05:28 +00:00
|
|
|
p = dp;
|
2018-01-19 19:24:37 +00:00
|
|
|
while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) {
|
2017-09-13 22:05:28 +00:00
|
|
|
p = efi_dp_next(p);
|
2018-01-19 19:24:37 +00:00
|
|
|
if (!p)
|
2019-02-04 11:49:43 +00:00
|
|
|
return EFI_INVALID_PARAMETER;
|
2018-01-19 19:24:37 +00:00
|
|
|
}
|
2017-09-13 22:05:28 +00:00
|
|
|
fp = efi_dp_dup(p);
|
2018-01-19 19:24:37 +00:00
|
|
|
if (!fp)
|
|
|
|
return EFI_OUT_OF_RESOURCES;
|
2017-09-13 22:05:28 +00:00
|
|
|
p->type = DEVICE_PATH_TYPE_END;
|
|
|
|
p->sub_type = DEVICE_PATH_SUB_TYPE_END;
|
|
|
|
p->length = sizeof(*p);
|
|
|
|
|
|
|
|
*device_path = dp;
|
|
|
|
*file_path = fp;
|
2018-01-19 19:24:37 +00:00
|
|
|
return EFI_SUCCESS;
|
2017-09-13 22:05:28 +00:00
|
|
|
}
|
2018-10-17 07:32:03 +00:00
|
|
|
|
|
|
|
efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
|
|
|
|
const char *path,
|
|
|
|
struct efi_device_path **device,
|
|
|
|
struct efi_device_path **file)
|
|
|
|
{
|
|
|
|
int is_net;
|
|
|
|
struct blk_desc *desc = NULL;
|
|
|
|
disk_partition_t fs_partition;
|
|
|
|
int part = 0;
|
|
|
|
char filename[32] = { 0 }; /* dp->str is u16[32] long */
|
|
|
|
char *s;
|
|
|
|
|
2018-11-05 09:06:40 +00:00
|
|
|
if (path && !file)
|
2018-10-17 07:32:03 +00:00
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
is_net = !strcmp(dev, "Net");
|
|
|
|
if (!is_net) {
|
|
|
|
part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition,
|
|
|
|
1);
|
|
|
|
if (part < 0)
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
|
2018-11-05 09:06:40 +00:00
|
|
|
if (device)
|
|
|
|
*device = efi_dp_from_part(desc, part);
|
2018-10-17 07:32:03 +00:00
|
|
|
} else {
|
|
|
|
#ifdef CONFIG_NET
|
2018-11-05 09:06:40 +00:00
|
|
|
if (device)
|
|
|
|
*device = efi_dp_from_eth();
|
2018-10-17 07:32:03 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!path)
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
|
|
|
|
if (!is_net) {
|
|
|
|
/* Add leading / to fs paths, because they're absolute */
|
|
|
|
snprintf(filename, sizeof(filename), "/%s", path);
|
|
|
|
} else {
|
|
|
|
snprintf(filename, sizeof(filename), "%s", path);
|
|
|
|
}
|
|
|
|
/* DOS style file path: */
|
|
|
|
s = filename;
|
|
|
|
while ((s = strchr(s, '/')))
|
|
|
|
*s++ = '\\';
|
2018-11-05 09:06:40 +00:00
|
|
|
*file = efi_dp_from_file(((!is_net && device) ? desc : NULL),
|
|
|
|
part, filename);
|
2018-10-17 07:32:03 +00:00
|
|
|
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|