uboot-tools/uefi-fixes.patch

2343 lines
68 KiB
Diff

From 8cb4ccaf3be1258bfaa8069501028ad1e825489d Mon Sep 17 00:00:00 2001
From: Rob Clark <robdclark@gmail.com>
Date: Mon, 24 Jul 2017 10:15:08 -0400
Subject: [PATCH 01/13] efi_loader: add back optional efi_handler::open()
In some cases it is useful to defer creation of the protocol interface
object. So add back an optional ->open() hook that is used if
protcol_interface is NULL.
I've slightly simplified the fxn ptr signature to remove unneeded args,
and so compiler will complain if patches that used the "old way" are,
and which do not need this extra complexity, are rebased.
Signed-off-by: Rob Clark <robdclark@gmail.com>
---
include/efi_loader.h | 12 +++++++++++-
lib/efi_loader/efi_boottime.c | 30 ++++++++++++++++++++++++------
2 files changed, 35 insertions(+), 7 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 037cc7c543..03c4ed5e1c 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -70,10 +70,17 @@ extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
/*
* When the UEFI payload wants to open a protocol on an object to get its
* interface (usually a struct with callback functions), this struct maps the
- * protocol GUID to the respective protocol interface */
+ * protocol GUID to the respective protocol interface.
+ *
+ * The optional ->open() fxn can be used for cases where the protocol
+ * interface is constructed on-demand, and is called if protocol_interface
+ * is NULL.
+ */
struct efi_handler {
const efi_guid_t *guid;
void *protocol_interface;
+ efi_status_t (EFIAPI *open)(void *handle, const efi_guid_t *protocol,
+ void **protocol_interface);
};
/*
@@ -191,6 +198,9 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
int efi_memory_init(void);
/* Adds new or overrides configuration table entry to the system table */
efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table);
+efi_status_t efi_get_protocol(struct efi_object *efiobj,
+ struct efi_handler *handler,
+ void **protocol_interface);
#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
extern void *efi_bounce_buffer;
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 59479eddb9..4b78f6d556 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -1004,6 +1004,23 @@ out:
return EFI_EXIT(r);
}
+efi_status_t efi_get_protocol(struct efi_object *efiobj,
+ struct efi_handler *handler,
+ void **protocol_interface)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ if (!handler->protocol_interface) {
+ ret = handler->open(efiobj->handle,
+ handler->guid,
+ &handler->protocol_interface);
+ }
+ *protocol_interface =
+ handler->protocol_interface;
+
+ return ret;
+}
+
static efi_status_t EFIAPI efi_locate_protocol(efi_guid_t *protocol,
void *registration,
void **protocol_interface)
@@ -1026,9 +1043,10 @@ static efi_status_t EFIAPI efi_locate_protocol(efi_guid_t *protocol,
if (!handler->guid)
continue;
if (!guidcmp(handler->guid, protocol)) {
- *protocol_interface =
- handler->protocol_interface;
- return EFI_EXIT(EFI_SUCCESS);
+ efi_status_t ret;
+ ret = efi_get_protocol(efiobj, handler,
+ protocol_interface);
+ return EFI_EXIT(ret);
}
}
}
@@ -1162,12 +1180,12 @@ static efi_status_t EFIAPI efi_open_protocol(
if (!hprotocol)
continue;
if (!guidcmp(hprotocol, protocol)) {
+ r = EFI_SUCCESS;
if (attributes !=
EFI_OPEN_PROTOCOL_TEST_PROTOCOL) {
- *protocol_interface =
- handler->protocol_interface;
+ r = efi_get_protocol(efiobj, handler,
+ protocol_interface);
}
- r = EFI_SUCCESS;
goto out;
}
}
--
2.13.3
From 784e48fe7488893209f4569a2a1664890fd75d4e Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Tue, 25 Jul 2017 13:40:43 -0400
Subject: [PATCH 02/13] part: extract MBR signature from partitions
EFI client programs need the signature information from the partition
table to determine the disk a partition is on, so we need to fill that
in here.
Signed-off-by: Peter Jones <pjones@redhat.com>
[separated from efi_loader part, and fixed build-errors for non-
CONFIG_EFI_PARTITION case]
Signed-off-by: Rob Clark <robdclark@gmail.com>
---
disk/part_dos.c | 12 +++++++++---
disk/part_efi.c | 20 ++++++++++++++++++++
include/blk.h | 15 +++++++++++++++
include/efi.h | 4 ++++
include/part.h | 3 ++-
include/part_efi.h | 4 ----
6 files changed, 50 insertions(+), 8 deletions(-)
diff --git a/disk/part_dos.c b/disk/part_dos.c
index 7ede15ec26..850a538e83 100644
--- a/disk/part_dos.c
+++ b/disk/part_dos.c
@@ -89,14 +89,20 @@ static int test_block_type(unsigned char *buffer)
static int part_test_dos(struct blk_desc *dev_desc)
{
- ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
+ ALLOC_CACHE_ALIGN_BUFFER(legacy_mbr, mbr, dev_desc->blksz);
- if (blk_dread(dev_desc, 0, 1, (ulong *)buffer) != 1)
+ if (blk_dread(dev_desc, 0, 1, (ulong *)mbr) != 1)
return -1;
- if (test_block_type(buffer) != DOS_MBR)
+ if (test_block_type((unsigned char *)mbr) != DOS_MBR)
return -1;
+ if (dev_desc->sig_type == SIG_TYPE_NONE &&
+ mbr->unique_mbr_signature != 0) {
+ dev_desc->sig_type = SIG_TYPE_MBR;
+ dev_desc->mbr_sig = mbr->unique_mbr_signature;
+ }
+
return 0;
}
diff --git a/disk/part_efi.c b/disk/part_efi.c
index 1b7ba27947..71e4188455 100644
--- a/disk/part_efi.c
+++ b/disk/part_efi.c
@@ -871,11 +871,19 @@ static int is_pmbr_valid(legacy_mbr * mbr)
static int is_gpt_valid(struct blk_desc *dev_desc, u64 lba,
gpt_header *pgpt_head, gpt_entry **pgpt_pte)
{
+ ALLOC_CACHE_ALIGN_BUFFER(legacy_mbr, mbr, dev_desc->blksz);
+
if (!dev_desc || !pgpt_head) {
printf("%s: Invalid Argument(s)\n", __func__);
return 0;
}
+ /* Read MBR Header from device */
+ if (blk_dread(dev_desc, 0, 1, (ulong *)mbr) != 1) {
+ printf("*** ERROR: Can't read MBR header ***\n");
+ return 0;
+ }
+
/* Read GPT Header from device */
if (blk_dread(dev_desc, (lbaint_t)lba, 1, pgpt_head) != 1) {
printf("*** ERROR: Can't read GPT header ***\n");
@@ -885,6 +893,18 @@ static int is_gpt_valid(struct blk_desc *dev_desc, u64 lba,
if (validate_gpt_header(pgpt_head, (lbaint_t)lba, dev_desc->lba))
return 0;
+ if (dev_desc->sig_type == SIG_TYPE_NONE) {
+ efi_guid_t empty = {};
+ if (memcmp(&pgpt_head->disk_guid, &empty, sizeof(empty))) {
+ dev_desc->sig_type = SIG_TYPE_GUID;
+ memcpy(&dev_desc->guid_sig, &pgpt_head->disk_guid,
+ sizeof(empty));
+ } else if (mbr->unique_mbr_signature != 0) {
+ dev_desc->sig_type = SIG_TYPE_MBR;
+ dev_desc->mbr_sig = mbr->unique_mbr_signature;
+ }
+ }
+
/* Read and allocate Partition Table Entries */
*pgpt_pte = alloc_read_gpt_entries(dev_desc, pgpt_head);
if (*pgpt_pte == NULL) {
diff --git a/include/blk.h b/include/blk.h
index ef29a07ee2..3a5e04c00d 100644
--- a/include/blk.h
+++ b/include/blk.h
@@ -8,6 +8,8 @@
#ifndef BLK_H
#define BLK_H
+#include <efi.h>
+
#ifdef CONFIG_SYS_64BIT_LBA
typedef uint64_t lbaint_t;
#define LBAFlength "ll"
@@ -35,6 +37,14 @@ enum if_type {
IF_TYPE_COUNT, /* Number of interface types */
};
+enum sig_type {
+ SIG_TYPE_NONE,
+ SIG_TYPE_MBR,
+ SIG_TYPE_GUID,
+
+ SIG_TYPE_COUNT /* Number of signature types */
+};
+
/*
* With driver model (CONFIG_BLK) this is uclass platform data, accessible
* with dev_get_uclass_platdata(dev)
@@ -62,6 +72,11 @@ struct blk_desc {
char vendor[40+1]; /* IDE model, SCSI Vendor */
char product[20+1]; /* IDE Serial no, SCSI product */
char revision[8+1]; /* firmware revision */
+ enum sig_type sig_type; /* Partition table signature type */
+ union {
+ uint32_t mbr_sig; /* MBR integer signature */
+ efi_guid_t guid_sig; /* GPT GUID Signature */
+ };
#if CONFIG_IS_ENABLED(BLK)
/*
* For now we have a few functions which take struct blk_desc as a
diff --git a/include/efi.h b/include/efi.h
index 02b78b31b1..87b0b43f20 100644
--- a/include/efi.h
+++ b/include/efi.h
@@ -28,6 +28,10 @@
struct efi_device_path;
+typedef struct {
+ u8 b[16];
+} efi_guid_t;
+
#define EFI_BITS_PER_LONG BITS_PER_LONG
/*
diff --git a/include/part.h b/include/part.h
index 83bce05a43..ac5ee895e9 100644
--- a/include/part.h
+++ b/include/part.h
@@ -259,8 +259,9 @@ struct part_driver {
#define U_BOOT_PART_TYPE(__name) \
ll_entry_declare(struct part_driver, __name, part_driver)
-#if CONFIG_IS_ENABLED(EFI_PARTITION)
#include <part_efi.h>
+
+#if CONFIG_IS_ENABLED(EFI_PARTITION)
/* disk/part_efi.c */
/**
* write_gpt_table() - Write the GUID Partition Table to disk
diff --git a/include/part_efi.h b/include/part_efi.h
index 317c044795..31e6bc6e14 100644
--- a/include/part_efi.h
+++ b/include/part_efi.h
@@ -58,10 +58,6 @@
/* linux/include/efi.h */
typedef u16 efi_char16_t;
-typedef struct {
- u8 b[16];
-} efi_guid_t;
-
/* based on linux/include/genhd.h */
struct partition {
u8 boot_ind; /* 0x80 - active */
--
2.13.3
From a6235e1a0f298f4e1e0271e772e457988f90ac2a Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Wed, 21 Jun 2017 16:39:02 -0400
Subject: [PATCH 03/13] efi: add some more device path structures
Signed-off-by: Peter Jones <pjones@redhat.com>
---
include/efi_api.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 47 insertions(+), 2 deletions(-)
diff --git a/include/efi_api.h b/include/efi_api.h
index ec1b321e8e..85afbeb72b 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -290,22 +290,67 @@ struct efi_mac_addr {
u8 addr[32];
};
+#define DEVICE_PATH_TYPE_ACPI_DEVICE 0x02
+#define DEVICE_PATH_SUB_TYPE_ACPI_DEVICE 0x01
+
+#define EFI_PNP_ID(ID) (u32)(((ID) << 16) | 0x41D0)
+#define EISA_PNP_ID(ID) EFI_PNP_ID(ID)
+
+struct efi_device_path_acpi_path {
+ struct efi_device_path dp;
+ u32 hid;
+ u32 uid;
+} __packed;
+
#define DEVICE_PATH_TYPE_MESSAGING_DEVICE 0x03
+# define DEVICE_PATH_SUB_TYPE_MSG_USB 0x05
# define DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR 0x0b
+# define DEVICE_PATH_SUB_TYPE_MSG_SD 0x1a
+# define DEVICE_PATH_SUB_TYPE_MSG_MMC 0x1d
+
+struct efi_device_path_usb {
+ struct efi_device_path dp;
+ u8 parent_port_number;
+ u8 usb_interface;
+} __packed;
struct efi_device_path_mac_addr {
struct efi_device_path dp;
struct efi_mac_addr mac;
u8 if_type;
-};
+} __packed;
+
+struct efi_device_path_sd_mmc_path {
+ struct efi_device_path dp;
+ u8 slot_number;
+} __packed;
#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04
+# define DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH 0x01
+# define DEVICE_PATH_SUB_TYPE_CDROM_PATH 0x02
# define DEVICE_PATH_SUB_TYPE_FILE_PATH 0x04
+struct efi_device_path_hard_drive_path {
+ struct efi_device_path dp;
+ u32 partition_number;
+ u64 partition_start;
+ u64 partition_end;
+ u8 partition_signature[16];
+ u8 partmap_type;
+ u8 signature_type;
+} __packed;
+
+struct efi_device_path_cdrom_path {
+ struct efi_device_path dp;
+ u32 boot_entry;
+ u64 partition_start;
+ u64 partition_end;
+} __packed;
+
struct efi_device_path_file_path {
struct efi_device_path dp;
u16 str[32];
-};
+} __packed;
#define BLOCK_IO_GUID \
EFI_GUID(0x964e5b21, 0x6459, 0x11d2, \
--
2.13.3
From 86eed9c21403969fdc69e508261a2e091c24897b Mon Sep 17 00:00:00 2001
From: Rob Clark <robdclark@gmail.com>
Date: Sun, 23 Jul 2017 11:25:53 -0400
Subject: [PATCH 04/13] fs: add fs_readdir()
Needed to support efi file protocol. The fallback.efi loader wants
to be able to read the contents of the /EFI directory to find an OS
to boot.
Currently only implemented for FAT, but that is all that UEFI is
required to support.
Signed-off-by: Rob Clark <robdclark@gmail.com>
---
fs/fat/fat.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++-------------
fs/fs.c | 25 +++++++++++++++++++++++++
include/fat.h | 4 +++-
include/fs.h | 21 +++++++++++++++++++++
4 files changed, 95 insertions(+), 14 deletions(-)
diff --git a/fs/fat/fat.c b/fs/fat/fat.c
index 9ad18f96ff..04d8616598 100644
--- a/fs/fat/fat.c
+++ b/fs/fat/fat.c
@@ -14,6 +14,7 @@
#include <config.h>
#include <exports.h>
#include <fat.h>
+#include <fs.h>
#include <asm/byteorder.h>
#include <part.h>
#include <malloc.h>
@@ -575,17 +576,25 @@ static __u8 mkcksum(const char name[8], const char ext[3])
/*
* Get the directory entry associated with 'filename' from the directory
* starting at 'startsect'
+ *
+ * Last two args are only used for dols==LS_READDIR
*/
__u8 get_dentfromdir_block[MAX_CLUSTSIZE]
__aligned(ARCH_DMA_MINALIGN);
-static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
- char *filename, dir_entry *retdent,
- int dols)
+static dir_entry *get_dentfromdir(fsdata *mydata, char *filename,
+ dir_entry *retdent, int dols,
+ loff_t pos, struct fs_dirent *d)
{
__u16 prevcksum = 0xffff;
__u32 curclust = START(retdent);
int files = 0, dirs = 0;
+ int readdir = 0;
+
+ if (dols == LS_READDIR) {
+ readdir = 1;
+ dols = 0;
+ }
debug("get_dentfromdir: %s\n", filename);
@@ -618,7 +627,7 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
get_vfatname(mydata, curclust,
get_dentfromdir_block,
dentptr, l_name);
- if (dols) {
+ if (dols || readdir) {
int isdir;
char dirc;
int doit = 0;
@@ -637,7 +646,14 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
}
}
if (doit) {
- if (dirc == ' ') {
+ if (readdir) {
+ if ((dirs + files - 1) == pos) {
+ strcpy(d->name, l_name);
+ if (!isdir)
+ d->size = FAT2CPU32(dentptr->size);
+ return NULL;
+ }
+ } else if (dirc == ' ') {
printf(" %8u %s%c\n",
FAT2CPU32(dentptr->size),
l_name,
@@ -676,7 +692,7 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
}
get_name(dentptr, s_name);
- if (dols) {
+ if (dols || readdir) {
int isdir = (dentptr->attr & ATTR_DIR);
char dirc;
int doit = 0;
@@ -694,7 +710,14 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
}
if (doit) {
- if (dirc == ' ') {
+ if (readdir) {
+ if ((dirs + files - 1) == pos) {
+ strcpy(d->name, s_name);
+ if (!isdir)
+ d->size = FAT2CPU32(dentptr->size);
+ return NULL;
+ }
+ } else if (dirc == ' ') {
printf(" %8u %s%c\n",
FAT2CPU32(dentptr->size),
s_name, dirc);
@@ -825,7 +848,7 @@ int do_fat_read_at(const char *filename, loff_t pos, void *buffer,
__u32 cursect;
int idx, isdir = 0;
int files = 0, dirs = 0;
- int ret = -1;
+ int ret = (dols == LS_READDIR) ? -ENOTDIR : -1;
int firsttime;
__u32 root_cluster = 0;
__u32 read_blk;
@@ -906,7 +929,8 @@ root_reparse:
if (!dols)
goto exit;
- dols = LS_ROOT;
+ if (dols == LS_YES)
+ dols = LS_ROOT;
} else if ((idx = dirdelim(fnamecopy)) >= 0) {
isdir = 1;
fnamecopy[idx] = '\0';
@@ -1151,8 +1175,6 @@ rootdir_done:
firsttime = 1;
while (isdir) {
- int startsect = mydata->data_begin
- + START(dentptr) * mydata->clust_size;
dir_entry dent;
char *nextname = NULL;
@@ -1177,10 +1199,14 @@ rootdir_done:
}
}
- if (get_dentfromdir(mydata, startsect, subname, dentptr,
- isdir ? 0 : dols) == NULL) {
+ if (get_dentfromdir(mydata, subname, dentptr,
+ isdir ? 0 : dols, pos, buffer) == NULL) {
if (dols && !isdir)
*size = 0;
+ if (dols == LS_READDIR) {
+ struct fs_dirent *dent = buffer;
+ ret = dent->name[0] ? 0 : -ENOENT;
+ }
goto exit;
}
@@ -1353,6 +1379,13 @@ int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
return ret;
}
+int fat_readdir(const char *filename, loff_t offset, struct fs_dirent *dent)
+{
+ loff_t actread;
+ return do_fat_read_at(filename, offset, dent, sizeof(*dent),
+ LS_READDIR, 0, &actread);
+}
+
void fat_close(void)
{
}
diff --git a/fs/fs.c b/fs/fs.c
index 595ff1fe69..42a028a6ce 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -69,6 +69,12 @@ static inline int fs_uuid_unsupported(char *uuid_str)
return -1;
}
+static inline int fs_readdir_unsupported(const char *filename, loff_t offset,
+ struct fs_dirent *dent)
+{
+ return -ENXIO;
+}
+
struct fstype_info {
int fstype;
char *name;
@@ -92,6 +98,7 @@ struct fstype_info {
loff_t len, loff_t *actwrite);
void (*close)(void);
int (*uuid)(char *uuid_str);
+ int (*readdir)(const char *filename, loff_t offset, struct fs_dirent *dent);
};
static struct fstype_info fstypes[] = {
@@ -112,6 +119,7 @@ static struct fstype_info fstypes[] = {
.write = fs_write_unsupported,
#endif
.uuid = fs_uuid_unsupported,
+ .readdir = fat_readdir,
},
#endif
#ifdef CONFIG_FS_EXT4
@@ -131,6 +139,7 @@ static struct fstype_info fstypes[] = {
.write = fs_write_unsupported,
#endif
.uuid = ext4fs_uuid,
+ .readdir = fs_readdir_unsupported,
},
#endif
#ifdef CONFIG_SANDBOX
@@ -146,6 +155,7 @@ static struct fstype_info fstypes[] = {
.read = fs_read_sandbox,
.write = fs_write_sandbox,
.uuid = fs_uuid_unsupported,
+ .readdir = fs_readdir_unsupported,
},
#endif
#ifdef CONFIG_CMD_UBIFS
@@ -161,6 +171,7 @@ static struct fstype_info fstypes[] = {
.read = ubifs_read,
.write = fs_write_unsupported,
.uuid = fs_uuid_unsupported,
+ .readdir = fs_readdir_unsupported,
},
#endif
{
@@ -175,6 +186,7 @@ static struct fstype_info fstypes[] = {
.read = fs_read_unsupported,
.write = fs_write_unsupported,
.uuid = fs_uuid_unsupported,
+ .readdir = fs_readdir_unsupported,
},
};
@@ -334,6 +346,19 @@ int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len,
return ret;
}
+int fs_readdir(const char *filename, loff_t offset, struct fs_dirent *dent)
+{
+ struct fstype_info *info = fs_get_info(fs_type);
+ int ret;
+
+ memset(dent, 0, sizeof(*dent));
+
+ ret = info->readdir(filename, offset, dent);
+ fs_close();
+
+ return ret;
+}
+
int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
int fstype)
{
diff --git a/include/fat.h b/include/fat.h
index 71879f01ca..0ef3f5be16 100644
--- a/include/fat.h
+++ b/include/fat.h
@@ -61,8 +61,8 @@
/* Flags telling whether we should read a file or list a directory */
#define LS_NO 0
#define LS_YES 1
-#define LS_DIR 1
#define LS_ROOT 2
+#define LS_READDIR 3 /* read directory entry at specified offset */
#define ISDIRDELIM(c) ((c) == '/' || (c) == '\\')
@@ -210,5 +210,7 @@ int file_fat_write(const char *filename, void *buf, loff_t offset, loff_t len,
loff_t *actwrite);
int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
loff_t *actread);
+struct fs_dirent;
+int fat_readdir(const char *filename, loff_t offset, struct fs_dirent *dir);
void fat_close(void);
#endif /* _FAT_H_ */
diff --git a/include/fs.h b/include/fs.h
index 2f2aca8378..71051d7c66 100644
--- a/include/fs.h
+++ b/include/fs.h
@@ -79,6 +79,27 @@ int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len,
loff_t *actwrite);
/*
+ * A directory entry.
+ */
+struct fs_dirent {
+ loff_t size;
+ char name[256];
+};
+
+/*
+ * fs_readdir - Read a directory.
+ *
+ * @filename: Name of file to read from
+ * @offset: The offset into the directory to read, ie. offset of N returns
+ * the N'th directory entry
+ * @dent: on success, filled in with directory entry
+ * @return 0 on success, -ENOTDIR if specified file is not a directory,
+ * or -ENOENT if offset is beyond last directory entry, or -ENXIO if
+ * operation is not supported.
+ */
+int fs_readdir(const char *filename, loff_t offset, struct fs_dirent *dent);
+
+/*
* Common implementation for various filesystem commands, optionally limited
* to a specific filesystem type via the fstype parameter.
*/
--
2.13.3
From 9c814af51bb6b2ac462f94d7a7d5484954a95d17 Mon Sep 17 00:00:00 2001
From: Rob Clark <robdclark@gmail.com>
Date: Thu, 27 Jul 2017 14:12:21 -0400
Subject: [PATCH 05/13] efi_loader: add device-path utils
Helpers to construct device-paths from devices, partitions, files, and
for parsing and manipulating device-paths.
For non-legacy devices, this will use u-boot's device-model to construct
device-paths which include bus hierarchy to construct device-paths. For
legacy devices we still fake it, but slightly more convincingly.
Signed-off-by: Rob Clark <robdclark@gmail.com>
---
include/efi_api.h | 10 +
include/efi_loader.h | 20 ++
lib/efi_loader/Makefile | 2 +-
lib/efi_loader/efi_device_path.c | 485 +++++++++++++++++++++++++++++++++++++++
4 files changed, 516 insertions(+), 1 deletion(-)
create mode 100644 lib/efi_loader/efi_device_path.c
diff --git a/include/efi_api.h b/include/efi_api.h
index 85afbeb72b..0ebe8d0283 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -305,6 +305,7 @@ struct efi_device_path_acpi_path {
#define DEVICE_PATH_TYPE_MESSAGING_DEVICE 0x03
# define DEVICE_PATH_SUB_TYPE_MSG_USB 0x05
# define DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR 0x0b
+# define DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS 0x0f
# define DEVICE_PATH_SUB_TYPE_MSG_SD 0x1a
# define DEVICE_PATH_SUB_TYPE_MSG_MMC 0x1d
@@ -320,6 +321,15 @@ struct efi_device_path_mac_addr {
u8 if_type;
} __packed;
+struct efi_device_path_usb_class {
+ struct efi_device_path dp;
+ u16 vendor_id;
+ u16 product_id;
+ u8 device_class;
+ u8 device_subclass;
+ u8 device_protocol;
+} __packed;
+
struct efi_device_path_sd_mmc_path {
struct efi_device_path dp;
u8 slot_number;
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 03c4ed5e1c..1028bfb75d 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -207,6 +207,26 @@ extern void *efi_bounce_buffer;
#define EFI_LOADER_BOUNCE_BUFFER_SIZE (64 * 1024 * 1024)
#endif
+
+struct efi_device_path *efi_dp_next(struct efi_device_path *dp);
+int efi_dp_match(struct efi_device_path *a, struct efi_device_path *b);
+struct efi_object *efi_dp_find_obj(struct efi_device_path *dp);
+unsigned efi_dp_size(struct efi_device_path *dp);
+struct efi_device_path *efi_dp_dup(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);
+struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
+ const char *path);
+struct efi_device_path *efi_dp_from_eth(void);
+void efi_dp_split_file_path(struct efi_device_path *full_path,
+ struct efi_device_path **device_path,
+ struct efi_device_path **file_path);
+
+#define EFI_DP_TYPE(_dp, _type, _subtype) \
+ (((_dp)->type == DEVICE_PATH_TYPE_##_type) && \
+ ((_dp)->sub_type == DEVICE_PATH_SUB_TYPE_##_subtype))
+
/* Convert strings from normal C strings to uEFI strings */
static inline void ascii2unicode(u16 *unicode, const char *ascii)
{
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 30bf343a36..f35e5ce8a8 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -15,7 +15,7 @@ always := $(efiprogs-y)
obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o
-obj-y += efi_memory.o efi_device_path_to_text.o
+obj-y += efi_memory.o efi_device_path_to_text.o efi_device_path.o
obj-$(CONFIG_LCD) += efi_gop.o
obj-$(CONFIG_DM_VIDEO) += efi_gop.o
obj-$(CONFIG_PARTITIONS) += efi_disk.o
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
new file mode 100644
index 0000000000..b5acf73f98
--- /dev/null
+++ b/lib/efi_loader/efi_device_path.c
@@ -0,0 +1,485 @@
+/*
+ * EFI device path from u-boot device-model mapping
+ *
+ * (C) Copyright 2017 Rob Clark
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <dm.h>
+#include <usb.h>
+#include <mmc.h>
+#include <efi_loader.h>
+#include <inttypes.h>
+#include <part.h>
+#include <malloc.h>
+
+/* template END node: */
+const static struct efi_device_path END = {
+ .type = DEVICE_PATH_TYPE_END,
+ .sub_type = DEVICE_PATH_SUB_TYPE_END,
+ .length = sizeof(END),
+};
+
+/* template ROOT node, a fictional ACPI PNP device: */
+const static struct efi_device_path_acpi_path ROOT = {
+ .dp = {
+ .type = DEVICE_PATH_TYPE_ACPI_DEVICE,
+ .sub_type = DEVICE_PATH_SUB_TYPE_ACPI_DEVICE,
+ .length = sizeof(ROOT),
+ },
+ .hid = EISA_PNP_ID(0x1337),
+ .uid = 0,
+};
+
+
+/*
+ * Iterate to next block in device-path, terminating (returning NULL)
+ * at /End* node.
+ */
+struct efi_device_path *efi_dp_next(struct efi_device_path *dp)
+{
+ if (dp == NULL)
+ return NULL;
+ dp = ((void *)dp) + dp->length;
+ if (dp->type == DEVICE_PATH_TYPE_END)
+ return NULL;
+ return dp;
+}
+
+/*
+ * Compare two device-paths, stopping when the shorter of the two hits
+ * an End* node. This is useful to, for example, compare a device-path
+ * representing a device with one representing a file on the device, or
+ * a device with a parent device.
+ */
+int efi_dp_match(struct efi_device_path *a, struct efi_device_path *b)
+{
+ 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;
+ }
+}
+
+
+/*
+ * See UEFI spec (section 3.1.2, about short-form device-paths..
+ * tl;dr: we can have a device-path that starts with a USB WWID
+ * or USB Class node, and a few other cases which don't encode
+ * the full device path with bus hierarchy:
+ *
+ * - MESSAGING:USB_WWID
+ * - MESSAGING:USB_CLASS
+ * - MEDIA:FILE_PATH
+ * - MEDIA:HARD_DRIVE
+ * - MESSAGING:URI
+ */
+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_object *efiobj;
+
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(efiobj->protocols); i++) {
+ struct efi_handler *handler = &efiobj->protocols[i];
+ struct efi_device_path *obj_dp;
+ efi_status_t ret;
+
+ if (!handler->guid)
+ break;
+
+ if (guidcmp(handler->guid, &efi_guid_device_path))
+ continue;
+
+ ret = efi_get_protocol(efiobj, handler, (void **)&obj_dp);
+ if (ret != EFI_SUCCESS)
+ continue;
+
+ do {
+ if (efi_dp_match(dp, obj_dp) == 0)
+ return efiobj;
+
+ obj_dp = shorten_path(efi_dp_next(obj_dp));
+ } while (short_path && obj_dp);
+ }
+ }
+
+ return NULL;
+}
+
+
+/* Find an efiobj from device-path */
+struct efi_object *efi_dp_find_obj(struct efi_device_path *dp)
+{
+ struct efi_object *efiobj;
+
+ efiobj = find_obj(dp, false);
+
+ if (!efiobj)
+ efiobj = find_obj(dp, true);
+
+ return efiobj;
+}
+
+/* return size not including End node: */
+unsigned efi_dp_size(struct efi_device_path *dp)
+{
+ unsigned sz = 0;
+
+ while (dp) {
+ sz += dp->length;
+ dp = efi_dp_next(dp);
+ }
+
+ return sz;
+}
+
+struct efi_device_path *efi_dp_dup(struct efi_device_path *dp)
+{
+ struct efi_device_path *ndp;
+ unsigned sz = efi_dp_size(dp) + sizeof(struct efi_device_path);
+
+ ndp = malloc(sz);
+ memcpy(ndp, dp, sz);
+
+ return ndp;
+}
+
+#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);
+ case UCLASS_MMC:
+ return dp_size(dev->parent) + sizeof(struct efi_device_path_sd_mmc_path);
+ 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);
+ }
+}
+
+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_acpi_path *adp = buf;
+ *adp = ROOT;
+ return &adp[1];
+ }
+#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;
+ sddp->dp.sub_type = (desc->if_type == IF_TYPE_MMC) ?
+ DEVICE_PATH_SUB_TYPE_MSG_MMC :
+ DEVICE_PATH_SUB_TYPE_MSG_SD;
+ sddp->dp.length = sizeof(*sddp);
+ sddp->slot_number = 0; // XXX ???
+
+ 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:
+ debug("unhandled device class: %s (%u)\n",
+ 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 = calloc(1, dp_size(dev) + sizeof(END));
+ 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
+ dpsize = dp_size(desc->bdev->parent);
+#else
+ dpsize = sizeof(ROOT) + sizeof(struct efi_device_path_file_path);
+#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;
+}
+
+static void *dp_part_fill(void *buf, struct blk_desc *desc, int part)
+{
+ disk_partition_t info;
+
+#ifdef CONFIG_BLK
+ buf = dp_fill(buf, desc->bdev->parent);
+#else
+ struct efi_device_path_file_path *fp;
+ char devname[32] = { 0 }; /* fp->str is u16[32] long */
+
+ snprintf(devname, sizeof(devname), "%d.%d.%d", desc->if_type,
+ desc->devnum, part);
+
+ memcpy(buf, &ROOT, sizeof(ROOT));
+ buf += sizeof(ROOT);
+
+ fp = buf;
+ fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+ fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
+ fp->dp.length = sizeof(*fp);
+ ascii2unicode(fp->str, devname);
+ buf = &fp[1];
+#endif
+
+ if (part == 0) /* the actual disk, not a partition */
+ return buf;
+
+ part_get_info(desc, part, &info);
+
+ if (desc->part_type == PART_TYPE_ISO) {
+ struct efi_device_path_cdrom_path *cddp = buf;
+
+ cddp->boot_entry = part - 1;
+ 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);
+ hddp->partition_number = part - 1;
+ 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;
+ hddp->signature_type = desc->sig_type;
+ if (hddp->signature_type != 0)
+ memcpy(hddp->partition_signature, &desc->guid_sig,
+ sizeof(hddp->partition_signature));
+
+ buf = &hddp[1];
+ }
+
+ return buf;
+}
+
+
+/* Construct a device-path from a partition on a blk device: */
+struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part)
+{
+ void *buf, *start;
+
+ start = buf = calloc(1, dp_part_size(desc, part) + sizeof(END));
+
+ buf = dp_part_fill(buf, desc, part);
+
+ *((struct efi_device_path *)buf) = END;
+
+ return start;
+}
+
+/* convert path to an UEFI style path (ie. DOS style backslashes and utf16) */
+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);
+
+ // TODO efi_device_path_file_path should be variable length:
+ fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
+ dpsize += fpsize;
+
+ start = buf = calloc(1, dpsize + sizeof(END));
+
+ 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;
+}
+
+#ifdef CONFIG_NET
+struct efi_device_path *efi_dp_from_eth(void)
+{
+ struct efi_device_path_mac_addr *ndp;
+ void *buf, *start;
+ unsigned dpsize = 0;
+
+ assert(eth_get_dev());
+
+#ifdef CONFIG_DM_ETH
+ dpsize += dp_size(eth_get_dev());
+#else
+ dpsize += sizeof(ROOT);
+#endif
+ dpsize += sizeof(*ndp);
+
+ start = buf = calloc(1, dpsize + sizeof(END));
+
+#ifdef CONFIG_DM_ETH
+ buf = dp_fill(buf, eth_get_dev());
+#else
+ memcpy(buf, &ROOT, sizeof(ROOT));
+ buf += sizeof(ROOT);
+#endif
+
+ 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);
+ memcpy(ndp->mac.addr, eth_get_ethaddr(), ARP_HLEN);
+ buf = &ndp[1];
+
+ *((struct efi_device_path *)buf) = END;
+
+ return start;
+}
+#endif
+
+/*
+ * Helper to split a full device path (containing both device and file
+ * parts) into it's constituent parts.
+ */
+void efi_dp_split_file_path(struct efi_device_path *full_path,
+ struct efi_device_path **device_path,
+ struct efi_device_path **file_path)
+{
+ struct efi_device_path *p, *dp, *fp;
+
+ dp = efi_dp_dup(full_path);
+ p = dp;
+ while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH))
+ p = efi_dp_next(p);
+ fp = efi_dp_dup(p);
+
+ p->type = DEVICE_PATH_TYPE_END;
+ p->sub_type = DEVICE_PATH_SUB_TYPE_END;
+ p->length = sizeof(*p);
+
+ *device_path = dp;
+ *file_path = fp;
+}
--
2.13.3
From 5685374b81f9ff62419a53fb2fd6f140add56def Mon Sep 17 00:00:00 2001
From: Rob Clark <robdclark@gmail.com>
Date: Mon, 31 Jul 2017 09:07:49 -0400
Subject: [PATCH 06/13] efi_loader: drop redundant efi_device_path_protocol
This is really the same thing as the efi_device_path struct.
Signed-off-by: Rob Clark <robdclark@gmail.com>
---
include/efi_api.h | 12 ++----------
lib/efi_loader/efi_device_path_to_text.c | 13 ++++++++-----
2 files changed, 10 insertions(+), 15 deletions(-)
diff --git a/include/efi_api.h b/include/efi_api.h
index 0ebe8d0283..7691a054a5 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -478,22 +478,14 @@ struct efi_console_control_protocol
EFI_GUID(0x8b843e20, 0x8132, 0x4852, \
0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c)
-struct efi_device_path_protocol
-{
- uint8_t type;
- uint8_t sub_type;
- uint16_t length;
- uint8_t data[];
-};
-
struct efi_device_path_to_text_protocol
{
uint16_t *(EFIAPI *convert_device_node_to_text)(
- struct efi_device_path_protocol *device_node,
+ struct efi_device_path *device_node,
bool display_only,
bool allow_shortcuts);
uint16_t *(EFIAPI *convert_device_path_to_text)(
- struct efi_device_path_protocol *device_path,
+ struct efi_device_path *device_path,
bool display_only,
bool allow_shortcuts);
};
diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c
index 4b2f43f0c8..f9d071ac50 100644
--- a/lib/efi_loader/efi_device_path_to_text.c
+++ b/lib/efi_loader/efi_device_path_to_text.c
@@ -16,7 +16,7 @@ const efi_guid_t efi_guid_device_path_to_text_protocol =
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
static uint16_t *efi_convert_device_node_to_text(
- struct efi_device_path_protocol *device_node,
+ struct efi_device_path *device_node,
bool display_only,
bool allow_shortcuts)
{
@@ -55,15 +55,18 @@ static uint16_t *efi_convert_device_node_to_text(
break;
case DEVICE_PATH_TYPE_MEDIA_DEVICE:
switch (device_node->sub_type) {
- case DEVICE_PATH_SUB_TYPE_FILE_PATH:
+ case DEVICE_PATH_SUB_TYPE_FILE_PATH: {
+ struct efi_device_path_file_path *fp =
+ (struct efi_device_path_file_path *)device_node;
buffer_size = device_node->length - 4;
r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES,
buffer_size, (void **) &buffer);
if (r != EFI_SUCCESS)
return NULL;
- memcpy(buffer, device_node->data, buffer_size);
+ memcpy(buffer, fp->str, buffer_size);
break;
}
+ }
break;
}
@@ -89,7 +92,7 @@ static uint16_t *efi_convert_device_node_to_text(
}
static uint16_t EFIAPI *efi_convert_device_node_to_text_ext(
- struct efi_device_path_protocol *device_node,
+ struct efi_device_path *device_node,
bool display_only,
bool allow_shortcuts)
{
@@ -105,7 +108,7 @@ static uint16_t EFIAPI *efi_convert_device_node_to_text_ext(
}
static uint16_t EFIAPI *efi_convert_device_path_to_text(
- struct efi_device_path_protocol *device_path,
+ struct efi_device_path *device_path,
bool display_only,
bool allow_shortcuts)
{
--
2.13.3
From 87811659b781575024ae3499f5b5f6d1ca4aefb0 Mon Sep 17 00:00:00 2001
From: Rob Clark <robdclark@gmail.com>
Date: Mon, 31 Jul 2017 11:25:49 -0400
Subject: [PATCH 07/13] efi_loader: add guidstr helper
There are a couple places where we'll need GUID -> string. So add a
helper.
Signed-off-by: Rob Clark <robdclark@gmail.com>
---
include/efi_loader.h | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 1028bfb75d..e6c46f713e 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -239,6 +239,21 @@ static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2)
return memcmp(g1, g2, sizeof(efi_guid_t));
}
+static inline int guidstr(char *s, const efi_guid_t *g)
+{
+ /* unpacked-guid, otherwise we have to have to consider endianess */
+ struct {
+ uint32_t data1;
+ uint16_t data2;
+ uint16_t data3;
+ uint8_t data4[8];
+ } *ug = (void *)g;
+ return sprintf(s, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ ug->data1, ug->data2, ug->data3, ug->data4[0],
+ ug->data4[1], ug->data4[2], ug->data4[3], ug->data4[4],
+ ug->data4[5], ug->data4[6], ug->data4[7]);
+}
+
/*
* Use these to indicate that your code / data should go into the EFI runtime
* section and thus still be available when the OS is running
--
2.13.3
From 3cbfcc19afa80f28819630e2ff1e9b0ef71f1cce Mon Sep 17 00:00:00 2001
From: Rob Clark <robdclark@gmail.com>
Date: Mon, 31 Jul 2017 11:28:01 -0400
Subject: [PATCH 08/13] efi_loader: flesh out device-path to text
It needs to handle more device-path node types, and also multiple levels
of path hierarchy. To simplify this, initially construct utf8 string to
a temporary buffer, and then allocate the real utf16 buffer that is
returned. This should be mostly for debugging or at least not critical-
path so an extra copy won't hurt, and is saner than the alternative.
Signed-off-by: Rob Clark <robdclark@gmail.com>
---
include/efi_api.h | 1 +
include/efi_loader.h | 2 +
lib/efi_loader/efi_device_path_to_text.c | 223 ++++++++++++++++++++++---------
3 files changed, 163 insertions(+), 63 deletions(-)
diff --git a/include/efi_api.h b/include/efi_api.h
index 7691a054a5..dd79cace32 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -295,6 +295,7 @@ struct efi_mac_addr {
#define EFI_PNP_ID(ID) (u32)(((ID) << 16) | 0x41D0)
#define EISA_PNP_ID(ID) EFI_PNP_ID(ID)
+#define EISA_PNP_NUM(ID) ((ID) >> 16)
struct efi_device_path_acpi_path {
struct efi_device_path dp;
diff --git a/include/efi_loader.h b/include/efi_loader.h
index e6c46f713e..1ab4af0f88 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -59,6 +59,8 @@ extern struct efi_simple_input_interface efi_con_in;
extern const struct efi_console_control_protocol efi_console_control;
extern const struct efi_device_path_to_text_protocol efi_device_path_to_text;
+uint16_t *efi_dp_str(struct efi_device_path *dp);
+
extern const efi_guid_t efi_guid_console_control;
extern const efi_guid_t efi_guid_device_path;
extern const efi_guid_t efi_guid_loaded_image;
diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c
index f9d071ac50..d10a1319af 100644
--- a/lib/efi_loader/efi_device_path_to_text.c
+++ b/lib/efi_loader/efi_device_path_to_text.c
@@ -15,82 +15,179 @@
const efi_guid_t efi_guid_device_path_to_text_protocol =
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
-static uint16_t *efi_convert_device_node_to_text(
- struct efi_device_path *device_node,
- bool display_only,
- bool allow_shortcuts)
+static char *dp_unknown(char *s, struct efi_device_path *dp)
{
- unsigned long buffer_size;
- efi_status_t r;
- uint16_t *buffer = NULL;
- int i;
+ s += sprintf(s, "/UNKNOWN(%04x,%04x)", dp->type, dp->sub_type);
+ return s;
+}
- switch (device_node->type) {
- case DEVICE_PATH_TYPE_END:
- return NULL;
- case DEVICE_PATH_TYPE_MESSAGING_DEVICE:
- switch (device_node->sub_type) {
- case DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR: {
- struct efi_device_path_mac_addr *dp =
- (struct efi_device_path_mac_addr *)device_node;
-
- if (dp->if_type != 0 && dp->if_type != 1)
- break;
- r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES,
- 2 * MAC_OUTPUT_LEN,
- (void **)&buffer);
- if (r != EFI_SUCCESS)
- return NULL;
- sprintf((char *)buffer,
- "MAC(%02x%02x%02x%02x%02x%02x,0x%1x)",
- dp->mac.addr[0], dp->mac.addr[1],
- dp->mac.addr[2], dp->mac.addr[3],
- dp->mac.addr[4], dp->mac.addr[5],
- dp->if_type);
- for (i = MAC_OUTPUT_LEN - 1; i >= 0; --i)
- buffer[i] = ((uint8_t *)buffer)[i];
+static char *dp_acpi(char *s, struct efi_device_path *dp)
+{
+ switch (dp->sub_type) {
+ case DEVICE_PATH_SUB_TYPE_ACPI_DEVICE: {
+ struct efi_device_path_acpi_path *adp =
+ (struct efi_device_path_acpi_path *)dp;
+ s += sprintf(s, "/Acpi(PNP%04x", EISA_PNP_NUM(adp->hid));
+ if (adp->uid)
+ s += sprintf(s, ",%d", adp->uid);
+ s += sprintf(s, ")");
+ break;
+ }
+ default:
+ s = dp_unknown(s, dp);
+ break;
+ }
+ return s;
+}
+
+static char *dp_msging(char *s, struct efi_device_path *dp)
+{
+ switch (dp->sub_type) {
+ case DEVICE_PATH_SUB_TYPE_MSG_USB: {
+ struct efi_device_path_usb *udp =
+ (struct efi_device_path_usb *)dp;
+ s += sprintf(s, "/Usb(0x%x,0x%x)", udp->parent_port_number,
+ udp->usb_interface);
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR: {
+ struct efi_device_path_mac_addr *mdp =
+ (struct efi_device_path_mac_addr *)dp;
+
+ if (mdp->if_type != 0 && mdp->if_type != 1)
break;
- }
- }
+
+ s += sprintf(s, "/MAC(%02x%02x%02x%02x%02x%02x,0x%1x)",
+ mdp->mac.addr[0], mdp->mac.addr[1],
+ mdp->mac.addr[2], mdp->mac.addr[3],
+ mdp->mac.addr[4], mdp->mac.addr[5],
+ mdp->if_type);
+
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS: {
+ struct efi_device_path_usb_class *ucdp =
+ (struct efi_device_path_usb_class *)dp;
+
+ s += sprintf(s, "/USBClass(%x,%x,%x,%x,%x)",
+ ucdp->vendor_id, ucdp->product_id,
+ ucdp->device_class, ucdp->device_subclass,
+ ucdp->device_protocol);
+
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_MSG_SD:
+ case DEVICE_PATH_SUB_TYPE_MSG_MMC: {
+ const char *typename =
+ (dp->sub_type == DEVICE_PATH_SUB_TYPE_MSG_SD) ?
+ "SDCard" : "MMC";
+ struct efi_device_path_sd_mmc_path *sddp =
+ (struct efi_device_path_sd_mmc_path *)dp;
+ s += sprintf(s, "/%s(Slot%u)", typename, sddp->slot_number);
+ break;
+ }
+ default:
+ s = dp_unknown(s, dp);
break;
- case DEVICE_PATH_TYPE_MEDIA_DEVICE:
- switch (device_node->sub_type) {
- case DEVICE_PATH_SUB_TYPE_FILE_PATH: {
- struct efi_device_path_file_path *fp =
- (struct efi_device_path_file_path *)device_node;
- buffer_size = device_node->length - 4;
- r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES,
- buffer_size, (void **) &buffer);
- if (r != EFI_SUCCESS)
- return NULL;
- memcpy(buffer, fp->str, buffer_size);
+ }
+ return s;
+}
+
+static char *dp_media(char *s, struct efi_device_path *dp)
+{
+ switch (dp->sub_type) {
+ case DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH: {
+ struct efi_device_path_hard_drive_path *hddp =
+ (struct efi_device_path_hard_drive_path *)dp;
+ void *sig = hddp->partition_signature;
+
+ switch (hddp->signature_type) {
+ case SIG_TYPE_MBR:
+ s += sprintf(s, "/HD(Part%d,Sig%08x)",
+ hddp->partition_number,
+ *(uint32_t *)sig);
break;
+ case SIG_TYPE_GUID:
+ s += sprintf(s, "HD(Part%d,Sig", hddp->partition_number);
+ s += guidstr(s, (efi_guid_t *)sig);
+ s += sprintf(s, ")");
+ default:
+ s += sprintf(s, "/HD(Part%d,MBRType=%02x,SigType=%02x)",
+ hddp->partition_number, hddp->partmap_type,
+ hddp->signature_type);
}
- }
+
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_CDROM_PATH: {
+ struct efi_device_path_cdrom_path *cddp =
+ (struct efi_device_path_cdrom_path *)dp;
+ s += sprintf(s, "/CDROM(0x%x)", cddp->boot_entry);
+ break;
+ }
+ case DEVICE_PATH_SUB_TYPE_FILE_PATH: {
+ struct efi_device_path_file_path *fp =
+ (struct efi_device_path_file_path *)dp;
+ int slen = (dp->length - sizeof(*dp)) / 2;
+ s += sprintf(s, "/%-*ls", slen, fp->str);
+ break;
+ }
+ default:
+ s = dp_unknown(s, dp);
break;
}
+ return s;
+}
- /*
- * For all node types that we do not yet support return
- * 'UNKNOWN(type,subtype)'.
- */
- if (!buffer) {
- r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES,
- 2 * UNKNOWN_OUTPUT_LEN,
- (void **)&buffer);
- if (r != EFI_SUCCESS)
- return NULL;
- sprintf((char *)buffer,
- "UNKNOWN(%04x,%04x)",
- device_node->type,
- device_node->sub_type);
- for (i = UNKNOWN_OUTPUT_LEN - 1; i >= 0; --i)
- buffer[i] = ((uint8_t *)buffer)[i];
+static uint16_t *efi_convert_device_node_to_text(
+ struct efi_device_path *dp,
+ bool display_only,
+ bool allow_shortcuts)
+{
+ unsigned long len;
+ efi_status_t r;
+ char buf[512]; /* this ought be be big enough for worst case */
+ char *str = buf;
+ uint16_t *out;
+
+ while (dp) {
+ switch (dp->type) {
+ case DEVICE_PATH_TYPE_ACPI_DEVICE:
+ str = dp_acpi(str, dp);
+ break;
+ case DEVICE_PATH_TYPE_MESSAGING_DEVICE:
+ str = dp_msging(str, dp);
+ break;
+ case DEVICE_PATH_TYPE_MEDIA_DEVICE:
+ str = dp_media(str, dp);
+ break;
+ default:
+ str = dp_unknown(str, dp);
+ }
+
+ dp = efi_dp_next(dp);
}
- return buffer;
+ *str++ = '\0';
+
+ len = str - buf;
+ r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, 2 * len, (void **)&out);
+ if (r != EFI_SUCCESS)
+ return NULL;
+
+ ascii2unicode(out, buf);
+ out[len - 1] = 0;
+
+ return out;
}
+/* helper for debug prints.. efi_free_pool() the result. */
+uint16_t *efi_dp_str(struct efi_device_path *dp)
+{
+ return efi_convert_device_node_to_text(dp, true, true);
+}
+
+
static uint16_t EFIAPI *efi_convert_device_node_to_text_ext(
struct efi_device_path *device_node,
bool display_only,
--
2.13.3
From 281813fc5f8e13b20201eb4279bb32d296c99930 Mon Sep 17 00:00:00 2001
From: Rob Clark <robdclark@gmail.com>
Date: Thu, 27 Jul 2017 14:22:21 -0400
Subject: [PATCH 09/13] efi_loader: use proper device-paths for partitions
Also, create disk objects for the disk itself, in addition to the
partitions. (UEFI terminology is a bit confusing, a "disk" object is
really a partition.) This helps grub properly identify the boot device
since it is trying to match up partition "disk" object with it's parent
device.
Now instead of seeing devices like:
/File(sdhci@07864000.blk)/EndEntire
/File(usb_mass_storage.lun0)/EndEntire
You see:
/ACPI(133741d0,0)/UnknownMessaging(1d)/EndEntire
/ACPI(133741d0,0)/UnknownMessaging(1d)/HD(0,800,64000,dd904a8c00000000,1,1)/EndEntire
/ACPI(133741d0,0)/UnknownMessaging(1d)/HD(1,64800,200000,dd904a8c00000000,1,1)/EndEntire
/ACPI(133741d0,0)/UnknownMessaging(1d)/HD(2,264800,19a000,dd904a8c00000000,1,1)/EndEntire
/ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/EndEntire
/ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(0,800,60000,38ca680200000000,1,1)/EndEntire
/ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(1,61000,155000,38ca680200000000,1,1)/EndEntire
/ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(2,20fa800,1bbf8800,38ca680200000000,1,1)/EndEntire
/ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(3,1b6800,1f44000,38ca680200000000,1,1)/EndEntire
This is on a board with single USB disk and single sd-card. The
UnknownMessaging(1d) node in the device-path is the MMC device,
but grub_efi_print_device_path() hasn't been updated yet for some
of the newer device-path sub-types.
This patch is inspired by a patch originally from Peter Jones, but
re-worked to use efi_device_path, so it doesn't much resemble the
original.
Signed-off-by: Rob Clark <robdclark@gmail.com>
---
lib/efi_loader/efi_disk.c | 54 +++++++++++++++++++++++++++--------------------
1 file changed, 31 insertions(+), 23 deletions(-)
diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c
index ed06485e33..eea65a402a 100644
--- a/lib/efi_loader/efi_disk.c
+++ b/lib/efi_loader/efi_disk.c
@@ -28,11 +28,13 @@ struct efi_disk_obj {
/* EFI Interface Media descriptor struct, referenced by ops */
struct efi_block_io_media media;
/* EFI device path to this block device */
- struct efi_device_path_file_path *dp;
+ struct efi_device_path *dp;
+ /* partition # */
+ unsigned part;
/* Offset into disk for simple partitions */
lbaint_t offset;
/* Internal block device */
- const struct blk_desc *desc;
+ struct blk_desc *desc;
};
static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this,
@@ -172,26 +174,26 @@ static const struct efi_block_io block_io_disk_template = {
static void efi_disk_add_dev(const char *name,
const char *if_typename,
- const struct blk_desc *desc,
+ struct blk_desc *desc,
int dev_index,
- lbaint_t offset)
+ lbaint_t offset,
+ unsigned part)
{
struct efi_disk_obj *diskobj;
- struct efi_device_path_file_path *dp;
- int objlen = sizeof(*diskobj) + (sizeof(*dp) * 2);
/* Don't add empty devices */
if (!desc->lba)
return;
- diskobj = calloc(1, objlen);
+ diskobj = calloc(1, sizeof(*diskobj));
/* Fill in object data */
- dp = (void *)&diskobj[1];
+ diskobj->dp = efi_dp_from_part(desc, part);
+ diskobj->part = part;
diskobj->parent.protocols[0].guid = &efi_block_io_guid;
diskobj->parent.protocols[0].protocol_interface = &diskobj->ops;
diskobj->parent.protocols[1].guid = &efi_guid_device_path;
- diskobj->parent.protocols[1].protocol_interface = dp;
+ diskobj->parent.protocols[1].protocol_interface = diskobj->dp;
diskobj->parent.handle = diskobj;
diskobj->ops = block_io_disk_template;
diskobj->ifname = if_typename;
@@ -207,17 +209,6 @@ static void efi_disk_add_dev(const char *name,
diskobj->media.last_block = desc->lba - offset;
diskobj->ops.media = &diskobj->media;
- /* Fill in device path */
- diskobj->dp = dp;
- dp[0].dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
- dp[0].dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
- dp[0].dp.length = sizeof(*dp);
- ascii2unicode(dp[0].str, name);
-
- dp[1].dp.type = DEVICE_PATH_TYPE_END;
- dp[1].dp.sub_type = DEVICE_PATH_SUB_TYPE_END;
- dp[1].dp.length = sizeof(*dp);
-
/* Hook up to the device list */
list_add_tail(&diskobj->parent.link, &efi_obj_list);
}
@@ -236,14 +227,18 @@ static int efi_disk_create_eltorito(struct blk_desc *desc,
if (desc->part_type != PART_TYPE_ISO)
return 0;
+ /* and devices for each partition: */
while (!part_get_info(desc, part, &info)) {
snprintf(devname, sizeof(devname), "%s:%d", pdevname,
part);
efi_disk_add_dev(devname, if_typename, desc, diskid,
- info.start);
+ info.start, part);
part++;
disks++;
}
+
+ /* ... and add block device: */
+ efi_disk_add_dev(devname, if_typename, desc, diskid, 0, 0);
#endif
return disks;
@@ -271,9 +266,22 @@ int efi_disk_register(void)
uclass_next_device_check(&dev)) {
struct blk_desc *desc = dev_get_uclass_platdata(dev);
const char *if_typename = dev->driver->name;
+ disk_partition_t info;
+ int part = 1;
printf("Scanning disk %s...\n", dev->name);
- efi_disk_add_dev(dev->name, if_typename, desc, desc->devnum, 0);
+
+ /* add devices for each partition: */
+ while (!part_get_info(desc, part, &info)) {
+ efi_disk_add_dev(dev->name, if_typename, desc,
+ desc->devnum, 0, part);
+ part++;
+ }
+
+ /* ... and add block device: */
+ efi_disk_add_dev(dev->name, if_typename, desc,
+ desc->devnum, 0, 0);
+
disks++;
/*
@@ -309,7 +317,7 @@ int efi_disk_register(void)
snprintf(devname, sizeof(devname), "%s%d",
if_typename, i);
- efi_disk_add_dev(devname, if_typename, desc, i, 0);
+ efi_disk_add_dev(devname, if_typename, desc, i, 0, 0);
disks++;
/*
--
2.13.3
From c72b667b6ea2f3e2483a0ec0ac4f443d9e09863b Mon Sep 17 00:00:00 2001
From: Rob Clark <robdclark@gmail.com>
Date: Thu, 27 Jul 2017 16:50:45 -0400
Subject: [PATCH 10/13] efi_loader: use proper device-paths for net
Signed-off-by: Rob Clark <robdclark@gmail.com>
---
include/efi_loader.h | 2 +-
lib/efi_loader/efi_net.c | 24 +++---------------------
2 files changed, 4 insertions(+), 22 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 1ab4af0f88..c783d0da51 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -143,7 +143,7 @@ int efi_disk_register(void);
/* Called by bootefi to make GOP (graphical) interface available */
int efi_gop_register(void);
/* Called by bootefi to make the network interface available */
-int efi_net_register(void **handle);
+int efi_net_register(void);
/* Called by bootefi to make SMBIOS tables available */
void efi_smbios_register(void);
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c
index 0b949d86e8..91f1e4a69e 100644
--- a/lib/efi_loader/efi_net.c
+++ b/lib/efi_loader/efi_net.c
@@ -26,9 +26,6 @@ struct efi_net_obj {
/* EFI Interface callback struct for network */
struct efi_simple_network net;
struct efi_simple_network_mode net_mode;
- /* Device path to the network adapter */
- struct efi_device_path_mac_addr dp_mac;
- struct efi_device_path_file_path dp_end;
/* PXE struct to transmit dhcp data */
struct efi_pxe pxe;
struct efi_pxe_mode pxe_mode;
@@ -210,19 +207,9 @@ void efi_net_set_dhcp_ack(void *pkt, int len)
}
/* This gets called from do_bootefi_exec(). */
-int efi_net_register(void **handle)
+int efi_net_register(void)
{
struct efi_net_obj *netobj;
- struct efi_device_path_mac_addr dp_net = {
- .dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE,
- .dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR,
- .dp.length = sizeof(dp_net),
- };
- struct efi_device_path_file_path dp_end = {
- .dp.type = DEVICE_PATH_TYPE_END,
- .dp.sub_type = DEVICE_PATH_SUB_TYPE_END,
- .dp.length = sizeof(dp_end),
- };
if (!eth_get_dev()) {
/* No eth device active, don't expose any */
@@ -236,7 +223,8 @@ int efi_net_register(void **handle)
netobj->parent.protocols[0].guid = &efi_net_guid;
netobj->parent.protocols[0].protocol_interface = &netobj->net;
netobj->parent.protocols[1].guid = &efi_guid_device_path;
- netobj->parent.protocols[1].protocol_interface = &netobj->dp_mac;
+ netobj->parent.protocols[1].protocol_interface =
+ efi_dp_from_eth();
netobj->parent.protocols[2].guid = &efi_pxe_guid;
netobj->parent.protocols[2].protocol_interface = &netobj->pxe;
netobj->parent.handle = &netobj->net;
@@ -255,9 +243,6 @@ int efi_net_register(void **handle)
netobj->net.receive = efi_net_receive;
netobj->net.mode = &netobj->net_mode;
netobj->net_mode.state = EFI_NETWORK_STARTED;
- netobj->dp_mac = dp_net;
- netobj->dp_end = dp_end;
- memcpy(netobj->dp_mac.mac.addr, eth_get_ethaddr(), 6);
memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6);
netobj->net_mode.max_packet_size = PKTSIZE;
@@ -268,8 +253,5 @@ int efi_net_register(void **handle)
/* Hook net up to the device list */
list_add_tail(&netobj->parent.link, &efi_obj_list);
- if (handle)
- *handle = &netobj->net;
-
return 0;
}
--
2.13.3
From 52f916744dc8ac73f00480a906c4239160a007ad Mon Sep 17 00:00:00 2001
From: Rob Clark <robdclark@gmail.com>
Date: Thu, 27 Jul 2017 15:18:18 -0400
Subject: [PATCH 11/13] efi_loader: refactor boot device and loaded_image
handling
Get rid of the hacky fake boot-device and duplicate device-path
constructing (which needs to match what efi_disk and efi_net do).
Instead convert over to use efi_device_path helpers to construct
device-paths, and use that to look up the actual boot device.
Also, extract out a helper to plug things in properly to the
loaded_image. In a following patch we'll want to re-use this in
efi_load_image() to handle the case of loading an image from a
file_path.
Signed-off-by: Rob Clark <robdclark@gmail.com>
---
cmd/bootefi.c | 201 +++++++++++++-----------------------------
include/efi_loader.h | 3 +
lib/efi_loader/efi_boottime.c | 35 ++++++++
3 files changed, 97 insertions(+), 142 deletions(-)
diff --git a/cmd/bootefi.c b/cmd/bootefi.c
index d20775eccd..b9e1e5e131 100644
--- a/cmd/bootefi.c
+++ b/cmd/bootefi.c
@@ -22,97 +22,14 @@ DECLARE_GLOBAL_DATA_PTR;
static uint8_t efi_obj_list_initalized;
-/*
- * When booting using the "bootefi" command, we don't know which
- * physical device the file came from. So we create a pseudo-device
- * called "bootefi" with the device path /bootefi.
- *
- * In addition to the originating device we also declare the file path
- * of "bootefi" based loads to be /bootefi.
- */
-static struct efi_device_path_file_path bootefi_image_path[] = {
- {
- .dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
- .dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
- .dp.length = sizeof(bootefi_image_path[0]),
- .str = { 'b','o','o','t','e','f','i' },
- }, {
- .dp.type = DEVICE_PATH_TYPE_END,
- .dp.sub_type = DEVICE_PATH_SUB_TYPE_END,
- .dp.length = sizeof(bootefi_image_path[0]),
- }
-};
-
-static struct efi_device_path_file_path bootefi_device_path[] = {
- {
- .dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
- .dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
- .dp.length = sizeof(bootefi_image_path[0]),
- .str = { 'b','o','o','t','e','f','i' },
- }, {
- .dp.type = DEVICE_PATH_TYPE_END,
- .dp.sub_type = DEVICE_PATH_SUB_TYPE_END,
- .dp.length = sizeof(bootefi_image_path[0]),
- }
-};
-
-/* The EFI loaded_image interface for the image executed via "bootefi" */
-static struct efi_loaded_image loaded_image_info = {
- .device_handle = bootefi_device_path,
- .file_path = bootefi_image_path,
-};
-
-/* The EFI object struct for the image executed via "bootefi" */
-static struct efi_object loaded_image_info_obj = {
- .handle = &loaded_image_info,
- .protocols = {
- {
- /*
- * When asking for the loaded_image interface, just
- * return handle which points to loaded_image_info
- */
- .guid = &efi_guid_loaded_image,
- .protocol_interface = &loaded_image_info,
- },
- {
- /*
- * When asking for the device path interface, return
- * bootefi_device_path
- */
- .guid = &efi_guid_device_path,
- .protocol_interface = bootefi_device_path,
- },
- {
- .guid = &efi_guid_console_control,
- .protocol_interface = (void *) &efi_console_control
- },
- {
- .guid = &efi_guid_device_path_to_text_protocol,
- .protocol_interface = (void *) &efi_device_path_to_text
- },
- },
-};
-
-/* The EFI object struct for the device the "bootefi" image was loaded from */
-static struct efi_object bootefi_device_obj = {
- .handle = bootefi_device_path,
- .protocols = {
- {
- /* When asking for the device path interface, return
- * bootefi_device_path */
- .guid = &efi_guid_device_path,
- .protocol_interface = bootefi_device_path
- }
- },
-};
+static struct efi_device_path *bootefi_image_path;
+static struct efi_device_path *bootefi_device_path;
/* Initialize and populate EFI object list */
static void efi_init_obj_list(void)
{
efi_obj_list_initalized = 1;
- list_add_tail(&loaded_image_info_obj.link, &efi_obj_list);
- list_add_tail(&bootefi_device_obj.link, &efi_obj_list);
efi_console_register();
#ifdef CONFIG_PARTITIONS
efi_disk_register();
@@ -121,13 +38,7 @@ static void efi_init_obj_list(void)
efi_gop_register();
#endif
#ifdef CONFIG_NET
- void *nethandle = loaded_image_info.device_handle;
- efi_net_register(&nethandle);
-
- if (!memcmp(bootefi_device_path[0].str, "N\0e\0t", 6))
- loaded_image_info.device_handle = nethandle;
- else
- loaded_image_info.device_handle = bootefi_device_path;
+ efi_net_register();
#endif
#ifdef CONFIG_GENERATE_SMBIOS_TABLE
efi_smbios_register();
@@ -210,14 +121,27 @@ static unsigned long efi_run_in_el2(asmlinkage ulong (*entry)(
* Load an EFI payload into a newly allocated piece of memory, register all
* EFI objects it would want to access and jump to it.
*/
-static unsigned long do_bootefi_exec(void *efi, void *fdt)
+static unsigned long do_bootefi_exec(void *efi, void *fdt,
+ struct efi_device_path *device_path,
+ struct efi_device_path *image_path)
{
+ struct efi_loaded_image loaded_image_info = {};
+ struct efi_object loaded_image_info_obj = {};
+ ulong ret;
+
ulong (*entry)(void *image_handle, struct efi_system_table *st)
asmlinkage;
ulong fdt_pages, fdt_size, fdt_start, fdt_end;
const efi_guid_t fdt_guid = EFI_FDT_GUID;
bootm_headers_t img = { 0 };
+ /* Initialize and populate EFI object list */
+ if (!efi_obj_list_initalized)
+ efi_init_obj_list();
+
+ efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj,
+ device_path, image_path);
+
/*
* gd lives in a fixed register which may get clobbered while we execute
* the payload. So save it here and restore it on every callback entry
@@ -252,18 +176,18 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt)
/* Load the EFI payload */
entry = efi_load_pe(efi, &loaded_image_info);
- if (!entry)
- return -ENOENT;
-
- /* Initialize and populate EFI object list */
- if (!efi_obj_list_initalized)
- efi_init_obj_list();
+ if (!entry) {
+ ret = -ENOENT;
+ goto exit;
+ }
/* Call our payload! */
debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry);
if (setjmp(&loaded_image_info.exit_jmp)) {
- return loaded_image_info.exit_status;
+ ret = loaded_image_info.exit_status;
+ EFI_EXIT(ret);
+ goto exit;
}
#ifdef CONFIG_ARM64
@@ -282,7 +206,13 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt)
}
#endif
- return efi_do_enter(&loaded_image_info, &systab, entry);
+ ret = efi_do_enter(&loaded_image_info, &systab, entry);
+
+exit:
+ /* image has returned, loaded-image obj goes *poof*: */
+ list_del(&loaded_image_info_obj.link);
+
+ return ret;
}
@@ -315,7 +245,8 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
printf("## Starting EFI application at %08lx ...\n", addr);
- r = do_bootefi_exec((void *)addr, (void*)fdt_addr);
+ r = do_bootefi_exec((void *)addr, (void*)fdt_addr,
+ bootefi_device_path, bootefi_image_path);
printf("## Application terminated, r = %lu\n",
r & ~EFI_ERROR_MASK);
@@ -344,58 +275,44 @@ U_BOOT_CMD(
bootefi_help_text
);
-void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
+static int parse_partnum(const char *devnr)
{
- __maybe_unused struct blk_desc *desc;
- char devname[32] = { 0 }; /* dp->str is u16[32] long */
- char *colon, *s;
-
-#if defined(CONFIG_BLK) || CONFIG_IS_ENABLED(ISO_PARTITION)
- desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10));
-#endif
-
-#ifdef CONFIG_BLK
- if (desc) {
- snprintf(devname, sizeof(devname), "%s", desc->bdev->name);
- } else
-#endif
-
- {
- /* Assemble the condensed device name we use in efi_disk.c */
- snprintf(devname, sizeof(devname), "%s%s", dev, devnr);
+ const char *str = strchr(devnr, ':');
+ if (str) {
+ str++;
+ return simple_strtoul(str, NULL, 16);
}
+ return 0;
+}
- colon = strchr(devname, ':');
-
-#if CONFIG_IS_ENABLED(ISO_PARTITION)
- /* For ISOs we create partition block devices */
- if (desc && (desc->type != DEV_TYPE_UNKNOWN) &&
- (desc->part_type == PART_TYPE_ISO)) {
- if (!colon)
- snprintf(devname, sizeof(devname), "%s:1", devname);
+void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
+{
+ char filename[32] = { 0 }; /* dp->str is u16[32] long */
+ char *s;
- colon = NULL;
- }
-#endif
+ if (strcmp(dev, "Net")) {
+ struct blk_desc *desc;
+ int part;
- if (colon)
- *colon = '\0';
+ desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10));
+ part = parse_partnum(devnr);
- /* Patch bootefi_device_path to the target device */
- memset(bootefi_device_path[0].str, 0, sizeof(bootefi_device_path[0].str));
- ascii2unicode(bootefi_device_path[0].str, devname);
+ bootefi_device_path = efi_dp_from_part(desc, part);
+ } else {
+#ifdef CONFIG_NET
+ bootefi_device_path = efi_dp_from_eth();
+#endif
+ }
- /* Patch bootefi_image_path to the target file path */
- memset(bootefi_image_path[0].str, 0, sizeof(bootefi_image_path[0].str));
if (strcmp(dev, "Net")) {
/* Add leading / to fs paths, because they're absolute */
- snprintf(devname, sizeof(devname), "/%s", path);
+ snprintf(filename, sizeof(filename), "/%s", path);
} else {
- snprintf(devname, sizeof(devname), "%s", path);
+ snprintf(filename, sizeof(filename), "%s", path);
}
/* DOS style file path: */
- s = devname;
+ s = filename;
while ((s = strchr(s, '/')))
*s++ = '\\';
- ascii2unicode(bootefi_image_path[0].str, devname);
+ bootefi_image_path = efi_dp_from_file(NULL, 0, filename);
}
diff --git a/include/efi_loader.h b/include/efi_loader.h
index c783d0da51..479fb6b63d 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -203,6 +203,9 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table
efi_status_t efi_get_protocol(struct efi_object *efiobj,
struct efi_handler *handler,
void **protocol_interface);
+void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *obj,
+ struct efi_device_path *device_path,
+ struct efi_device_path *file_path);
#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
extern void *efi_bounce_buffer;
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 4b78f6d556..4a19fabdb3 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -714,6 +714,41 @@ static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid,
return EFI_EXIT(efi_install_configuration_table(guid, table));
}
+/* Initialize a loaded_image_info + loaded_image_info object with correct
+ * protocols, boot-device, etc.
+ */
+void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *obj,
+ struct efi_device_path *device_path,
+ struct efi_device_path *file_path)
+{
+ obj->handle = info;
+
+ /*
+ * When asking for the device path interface, return
+ * bootefi_device_path
+ */
+ obj->protocols[0].guid = &efi_guid_device_path;
+ obj->protocols[0].protocol_interface = device_path;
+
+ /*
+ * When asking for the loaded_image interface, just
+ * return handle which points to loaded_image_info
+ */
+ obj->protocols[1].guid = &efi_guid_loaded_image;
+ obj->protocols[1].protocol_interface = info;
+
+ obj->protocols[2].guid = &efi_guid_console_control;
+ obj->protocols[2].protocol_interface = (void *)&efi_console_control;
+
+ obj->protocols[3].guid = &efi_guid_device_path_to_text_protocol;
+ obj->protocols[3].protocol_interface = (void *)&efi_guid_device_path_to_text_protocol;
+
+ info->file_path = file_path;
+ info->device_handle = efi_dp_find_obj(device_path);
+
+ list_add_tail(&obj->link, &efi_obj_list);
+}
+
static efi_status_t EFIAPI efi_load_image(bool boot_policy,
efi_handle_t parent_image,
struct efi_device_path *file_path,
--
2.13.3