Skip to contentSkip to conversation

efi_loader: bunch of EFI work for distro boot

RFC New Rob Clark 1 participant 12 messages Jul 27, 2017

Subsystems (2)
lore
Share & tools
lore

Rob Clark

author
#0 lore raw

cover efi_loader: bunch of EFI work for distro boot

I still need to do some testing for net boot (but I think I didn't screw anything up there), so just sending as an RFC for now.

I had to do a bunch of patch rejuggling to get this into a sensible sequence of patches. Hopefully I didn't flub anything too badly in the process.

This patchset adds better devicepath handling to make upstream grub work, reworks and cleans up bootefi's handling of boot device and boot devicepath (making it signifcantly less hacky), adds file support, and adds support to load_image() from a file.

This plus efi variable support (which I'm still working on and will send separately), gets u-boot to the point where we can boot \EFI\BOOT\BOOTAA64.efi, which can't find the boot-order related EFI variables, and then loads fallback.efi to scan the boot disk looking for boot.csv's which tell it about installed distro's) and then finally loads the first distro's shim.efi or grubaa64.efi.

(Once I have support to write variables and saveenv in ExitBootServices then it should only hit fallback.efi on first boot.)

Peter Jones (2):
efi: fill in disk signature bits of hard drive device path.
efi: add some more device path structures

Rob Clark (8):
efi_loader: add back optional efi_handler::open()
fs: add fs_readdir()
efi_loader: add device-path utils
efi_loader: use proper device-paths for partitions
efi_loader: use proper device-paths for net
efi_loader: refactor boot device and loaded_image handling
efi_loader: add file/filesys support
efi_loader: support load_image() from a file-path

 cmd/bootefi.c                     | 163 ++++---------
 disk/part_dos.c                   |  12 +-
 disk/part_efi.c                   |  20 ++
 fs/fat/fat.c                      |  59 +++--
 fs/fs.c                           |  46 ++++
 include/blk.h                     |  15 ++
 include/efi.h                     |   6 +
 include/efi_api.h                 | 114 ++++++++-
 include/efi_loader.h              |  47 +++-
 include/fat.h                     |   4 +-
 include/fs.h                      |  23 ++
 include/part_efi.h                |   4 -
 lib/efi_loader/Makefile           |   3 +-
 lib/efi_loader/efi_boottime.c     | 156 +++++++++++--
 lib/efi_loader/efi_device_path.c  | 408 ++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_disk.c         | 103 ++++++--
 lib/efi_loader/efi_file.c         | 477 ++++++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_image_loader.c |   3 +
 lib/efi_loader/efi_net.c          |  24 +-
 19 files changed, 1483 insertions(+), 204 deletions(-)
 create mode 100644 lib/efi_loader/efi_device_path.c
 create mode 100644 lib/efi_loader/efi_file.c
Signature · 1 line
2.13.0
author
#1 lore raw

RFC 1/10 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.

---
 include/efi_loader.h          | 12 +++++++++++-
 lib/efi_loader/efi_boottime.c | 30 ++++++++++++++++++++++++------
 2 files changed, 35 insertions(+), 7 deletions(-)
include/efi_loader.h +11 -1
index 037cc7c543..03c4ed5e1c 100644
@@ -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;
lib/efi_loader/efi_boottime.c +24 -6
index de338f009c..9ce550f5d2 100644
@@ -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;
 			}
 		}
Signature · 1 line
2.13.0
author
#2 lore raw

RFC 2/10 efi: fill in disk signature bits of hard drive device path.

In reply to Rob Clark

From: Peter Jones <[email protected]>

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.

---
 disk/part_dos.c    | 12 +++++++++---
 disk/part_efi.c    | 20 ++++++++++++++++++++
 include/blk.h      | 15 +++++++++++++++
 include/efi.h      |  4 ++++
 include/part_efi.h |  4 ----
 5 files changed, 48 insertions(+), 7 deletions(-)
disk/part_dos.c +9 -3
index 7ede15ec26..850a538e83 100644
@@ -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;
 }
 
disk/part_efi.c +20 -0
index 1b7ba27947..f880d4736f 100644
@@ -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 = { 0, };
+		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) {
include/blk.h +15 -0
index ef29a07ee2..3a5e04c00d 100644
@@ -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 */
+	};
 #ifdef CONFIG_BLK
 	/*
 	 * For now we have a few functions which take struct blk_desc as a
include/efi.h +4 -0
index 02b78b31b1..87b0b43f20 100644
@@ -28,6 +28,10 @@
 
 struct efi_device_path;
 
+typedef struct {
+	u8 b[16];
+} efi_guid_t;
+
 #define EFI_BITS_PER_LONG	BITS_PER_LONG
 
 /*
include/part_efi.h +0 -4
index 317c044795..31e6bc6e14 100644
@@ -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 */
Signature · 1 line
2.13.0
author
#3 lore raw

RFC 3/10 efi: add some more device path structures

In reply to Rob Clark

From: Peter Jones <[email protected]>

---
 include/efi_api.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 47 insertions(+), 2 deletions(-)
include/efi_api.h +47 -2
index ec1b321e8e..85afbeb72b 100644
@@ -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, \
Signature · 1 line
2.13.0
author
#4 lore raw

RFC 4/10 fs: add fs_readdir()

In reply to Rob Clark

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.

---
 fs/fat/fat.c  | 59 ++++++++++++++++++++++++++++++++++++++++++++++-------------
 fs/fs.c       | 25 +++++++++++++++++++++++++
 include/fat.h |  4 +++-
 include/fs.h  | 21 +++++++++++++++++++++
 4 files changed, 95 insertions(+), 14 deletions(-)
fs/fat/fat.c +46 -13
index 9ad18f96ff..04d8616598 100644
@@ -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)
 {
 }
fs/fs.c +25 -0
index 595ff1fe69..42a028a6ce 100644
@@ -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)
 {
include/fat.h +3 -1
index 71879f01ca..0ef3f5be16 100644
@@ -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_ */
include/fs.h +21 -0
index 2f2aca8378..71051d7c66 100644
@@ -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.
  */
Signature · 1 line
2.13.0
author
#5 lore raw

RFC 5/10 efi_loader: add device-path utils

In reply to Rob Clark

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.

---
 include/efi_loader.h             |  17 ++
 lib/efi_loader/Makefile          |   2 +-
 lib/efi_loader/efi_device_path.c | 408 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 426 insertions(+), 1 deletion(-)
 create mode 100644 lib/efi_loader/efi_device_path.c
include/efi_loader.h +17 -0
index 03c4ed5e1c..f09d0a8003 100644
@@ -207,6 +207,23 @@ 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);
+
+#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)
 {
lib/efi_loader/Makefile +1 -1
index 30bf343a36..f35e5ce8a8 100644
@@ -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
lib/efi_loader/efi_device_path.c new file +408 -0

408 lines not on this page · raw message

Signature · 1 line
2.13.0
author
#6 lore raw

RFC 6/10 efi_loader: use proper device-paths for partitions

In reply to Rob Clark

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 at 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.

---
 lib/efi_loader/efi_disk.c | 52 +++++++++++++++++++++++++++--------------------
 1 file changed, 30 insertions(+), 22 deletions(-)
lib/efi_loader/efi_disk.c +30 -22
index ed06485e33..efa70fc460 100644
@@ -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++;
 
 		/*
Signature · 1 line
2.13.0
author
#7 lore raw

RFC 7/10 efi_loader: use proper device-paths for net

In reply to Rob Clark
---
 include/efi_loader.h     |  2 +-
 lib/efi_loader/efi_net.c | 24 +++---------------------
 2 files changed, 4 insertions(+), 22 deletions(-)
include/efi_loader.h +1 -1
index f09d0a8003..c5cc15fc4c 100644
@@ -141,7 +141,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);
 
lib/efi_loader/efi_net.c +3 -21
index 0b949d86e8..91f1e4a69e 100644
@@ -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;
 }
Signature · 1 line
2.13.0
author
#8 lore raw

RFC 8/10 efi_loader: refactor boot device and loaded_image handling

In reply to Rob Clark

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.

---
 cmd/bootefi.c                 | 163 +++++++++++++-----------------------------
 include/efi_loader.h          |   3 +
 lib/efi_loader/efi_boottime.c |  35 +++++++++
 3 files changed, 87 insertions(+), 114 deletions(-)
cmd/bootefi.c +49 -114
index d20775eccd..5b97958e4d 100644
@@ -43,76 +43,16 @@ static struct efi_device_path_file_path bootefi_image_path[] = {
 	}
 };
 
-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
-		}
-	},
-};
+/* if we have CONFIG_DM we construct a proper device-path from the
+ * boot device, otherwise fallback to using bootefi_device_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 +61,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();
@@ -212,12 +146,24 @@ static unsigned long efi_run_in_el2(asmlinkage ulong (*entry)(
  */
 static unsigned long do_bootefi_exec(void *efi, void *fdt)
 {
+	static struct efi_loaded_image loaded_image_info = {0};
+	static struct efi_object loaded_image_info_obj = {0};
+	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,
+			       bootefi_device_path,
+			       (struct efi_device_path *)bootefi_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
@@ -255,10 +201,6 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt)
 	if (!entry)
 		return -ENOENT;
 
-	/* Initialize and populate EFI object list */
-	if (!efi_obj_list_initalized)
-		efi_init_obj_list();
-
 	/* Call our payload! */
 	debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry);
 
@@ -282,7 +224,12 @@ 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);
+
+	/* image has returned, loaded-image obj goes *poof*: */
+	list_del(&loaded_image_info_obj.link);
+
+	return ret;
 }
 
 
@@ -344,58 +291,46 @@ 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")) {
+		static struct blk_desc *desc;
+		static 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);
+	ascii2unicode(bootefi_image_path[0].str, filename);
 }
include/efi_loader.h +3 -0
index c5cc15fc4c..208beffa78 100644
@@ -201,6 +201,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;
lib/efi_loader/efi_boottime.c +35 -0
index 9ce550f5d2..aa342670c2 100644
@@ -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,
Signature · 1 line
2.13.0
author
#9 lore raw

RFC 9/10 efi_loader: add file/filesys support

In reply to Rob Clark

fallback.efi (and probably other things) use UEFI's simple-file-system protocol and file support to search for OS's to boot.

---
 fs/fs.c                           |  21 ++
 include/efi.h                     |   2 +
 include/efi_api.h                 |  65 ++++++
 include/efi_loader.h              |  13 ++
 include/fs.h                      |   2 +
 lib/efi_loader/Makefile           |   1 +
 lib/efi_loader/efi_disk.c         |  51 ++++
 lib/efi_loader/efi_file.c         | 477 ++++++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_image_loader.c |   3 +
 9 files changed, 635 insertions(+)
 create mode 100644 lib/efi_loader/efi_file.c
fs/fs.c +21 -0
index 42a028a6ce..a269619b51 100644
@@ -247,6 +247,27 @@ int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
 	return -1;
 }
 
+/* set current blk device w/ blk_desc + partition # */
+int fs_set_blk_dev2(struct blk_desc *desc, int part)
+{
+	struct fstype_info *info;
+	int ret, i;
+
+	ret = part_get_info(desc, part, &fs_partition);
+	if (ret)
+		return ret;
+	fs_dev_desc = desc;
+
+	for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
+		if (!info->probe(fs_dev_desc, &fs_partition)) {
+			fs_type = info->fstype;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
 static void fs_close(void)
 {
 	struct fstype_info *info = fs_get_info(fs_type);
include/efi.h +2 -0
index 87b0b43f20..ddd2b96417 100644
@@ -81,6 +81,8 @@ typedef struct {
 #define EFI_IP_ADDRESS_CONFLICT		(EFI_ERROR_MASK | 34)
 #define EFI_HTTP_ERROR			(EFI_ERROR_MASK | 35)
 
+#define EFI_WARN_DELETE_FAILURE	2
+
 typedef unsigned long efi_status_t;
 typedef u64 efi_physical_addr_t;
 typedef u64 efi_virtual_addr_t;
include/efi_api.h +65 -0

69 lines not on this page · raw message

include/efi_loader.h +13 -0
index 208beffa78..f946abbc88 100644
@@ -63,6 +63,8 @@ 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;
 extern const efi_guid_t efi_guid_device_path_to_text_protocol;
+extern const efi_guid_t efi_simple_file_system_protocol_guid;
+extern const efi_guid_t efi_file_info_guid;
 
 extern unsigned int __efi_runtime_start, __efi_runtime_stop;
 extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
@@ -145,6 +147,9 @@ int efi_net_register(void);
 /* Called by bootefi to make SMBIOS tables available */
 void efi_smbios_register(void);
 
+struct efi_simple_file_system_protocol *
+efi_fs_from_path(struct efi_device_path *fp);
+
 /* Called by networking code to memorize the dhcp ack package */
 void efi_net_set_dhcp_ack(void *pkt, int len);
 
@@ -173,6 +178,14 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
 /* Call this to signal an event */
 void efi_signal_event(struct efi_event *event);
 
+/* open file system: */
+struct efi_simple_file_system_protocol * efi_simple_file_system(
+		struct blk_desc *desc, int part, struct efi_device_path *dp);
+
+/* open file from device-path: */
+struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp);
+
+
 /* Generic EFI memory allocator, call this to get memory */
 void *efi_alloc(uint64_t len, int memory_type);
 /* More specific EFI memory allocator, called by EFI payloads */
include/fs.h +2 -0
index 71051d7c66..7e920916e3 100644
@@ -26,6 +26,8 @@
  */
 int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype);
 
+int fs_set_blk_dev2(struct blk_desc *desc, int part);
+
 /*
  * Print the list of files on the partition previously set by fs_set_blk_dev(),
  * in directory "dirname".
lib/efi_loader/Makefile +1 -0
index f35e5ce8a8..1724b1b4f9 100644
@@ -16,6 +16,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 efi_device_path.o
+obj-y += efi_variable.o
 obj-$(CONFIG_LCD) += efi_gop.o
 obj-$(CONFIG_DM_VIDEO) += efi_gop.o
 obj-$(CONFIG_PARTITIONS) += efi_disk.o
lib/efi_loader/efi_disk.c +51 -0

69 lines not on this page · raw message

lib/efi_loader/efi_file.c new file +477 -0

477 lines not on this page · raw message

lib/efi_loader/efi_image_loader.c +3 -0
index f961407f50..469acae082 100644
@@ -17,6 +17,9 @@ DECLARE_GLOBAL_DATA_PTR;
 
 const efi_guid_t efi_guid_device_path = DEVICE_PATH_GUID;
 const efi_guid_t efi_guid_loaded_image = LOADED_IMAGE_GUID;
+const efi_guid_t efi_simple_file_system_protocol_guid =
+		EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
+const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
 
 static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
 			unsigned long rel_size, void *efi_reloc)
Signature · 1 line
2.13.0
author
#10 lore raw

RFC 10/10 efi_loader: support load_image() from a file-path

In reply to Rob Clark

Previously we only supported the case when the EFI application loaded the image into memory for us. But fallback.efi does not do this.

---
 lib/efi_loader/efi_boottime.c | 91 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 76 insertions(+), 15 deletions(-)
lib/efi_loader/efi_boottime.c +76 -15
index aa342670c2..326a428a57 100644
@@ -749,6 +749,45 @@ void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *ob
 	list_add_tail(&obj->link, &efi_obj_list);
 }
 
+static efi_status_t load_image_from_path(struct efi_device_path *file_path,
+					 void **buffer)
+{
+	struct efi_file_info *info = NULL;
+	struct efi_file_handle *f;
+	static efi_status_t ret;
+	uint64_t bs;
+
+	f = efi_file_from_path(file_path);
+	if (!f)
+		return EFI_DEVICE_ERROR;
+
+	bs = sizeof(info);
+	EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, &bs, info));
+	if (ret == EFI_BUFFER_TOO_SMALL) {
+		info = malloc(bs);
+		EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, &bs, info));
+	}
+	if (ret != EFI_SUCCESS)
+		goto error;
+
+	ret = efi_allocate_pool(EFI_LOADER_DATA, info->file_size, buffer);
+	if (ret)
+		goto error;
+
+	EFI_CALL(ret = f->read(f, &info->file_size, *buffer));
+
+error:
+	free(info);
+	EFI_CALL(f->close(f));
+
+	if (ret != EFI_SUCCESS) {
+		efi_free_pool(*buffer);
+		*buffer = NULL;
+	}
+
+	return ret;
+}
+
 static efi_status_t EFIAPI efi_load_image(bool boot_policy,
 					  efi_handle_t parent_image,
 					  struct efi_device_path *file_path,
@@ -756,25 +795,48 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
 					  unsigned long source_size,
 					  efi_handle_t *image_handle)
 {
-	static struct efi_object loaded_image_info_obj = {
-		.protocols = {
-			{
-				.guid = &efi_guid_loaded_image,
-			},
-		},
-	};
 	struct efi_loaded_image *info;
 	struct efi_object *obj;
 
 	EFI_ENTRY("%d, %p, %p, %p, %ld, %p", boot_policy, parent_image,
 		  file_path, source_buffer, source_size, image_handle);
-	info = malloc(sizeof(*info));
-	loaded_image_info_obj.protocols[0].protocol_interface = info;
-	obj = malloc(sizeof(loaded_image_info_obj));
-	memset(info, 0, sizeof(*info));
-	memcpy(obj, &loaded_image_info_obj, sizeof(loaded_image_info_obj));
-	obj->handle = info;
-	info->file_path = file_path;
+
+	info = calloc(1, sizeof(*info));
+	obj = calloc(1, sizeof(*obj));
+
+	if (!source_buffer) {
+		struct efi_device_path *dp, *fp, *p;
+		efi_status_t ret;
+
+		ret = load_image_from_path(file_path, &source_buffer);
+		if (ret != EFI_SUCCESS) {
+			free(info);
+			free(obj);
+			return EFI_EXIT(ret);
+		}
+
+		/*
+		 * split file_path which contains both the device and
+		 * file parts:
+		 */
+		dp = efi_dp_dup(file_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);
+
+		efi_setup_loaded_image(info, obj, dp, fp);
+	} else {
+		/* In this case, file_path is the "device" path, ie.
+		 * something like a HARDWARE_DEVICE:MEMORY_MAPPED
+		 */
+		efi_setup_loaded_image(info, obj, file_path, NULL);
+	}
+
 	info->reserved = efi_load_pe(source_buffer, info);
 	if (!info->reserved) {
 		free(info);
@@ -783,7 +845,6 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
 	}
 
 	*image_handle = info;
-	list_add_tail(&obj->link, &efi_obj_list);
 
 	return EFI_EXIT(EFI_SUCCESS);
 }
Signature · 1 line
2.13.0
author
#11 lore raw

RFC 10/10 efi_loader: support load_image() from a file-path

Quoted 19 lines · Rob Clark

Previously we only supported the case when the EFI application loaded the image into memory for us. But fallback.efi does not do this.

---
 lib/efi_loader/efi_boottime.c | 91 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 76 insertions(+), 15 deletions(-)
lib/efi_loader/efi_boottime.c +3 -0
index aa342670c2..326a428a57 100644
@@ -749,6 +749,45 @@ void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *ob
        list_add_tail(&obj->link, &efi_obj_list);
 }
 
+static efi_status_t load_image_from_path(struct efi_device_path *file_path,
+                                        void **buffer)
+{

lib/efi_loader/efi_boottime.c void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *ob · line 764

Quoted 10 lines
lib/efi_loader/efi_boottime.c +10 -0
+       struct efi_file_info *info = NULL;
+       struct efi_file_handle *f;
+       static efi_status_t ret;
+       uint64_t bs;
+
+       f = efi_file_from_path(file_path);
+       if (!f)
+               return EFI_DEVICE_ERROR;
+
+       bs = sizeof(info);

oh, this should actually be: bs = 0;

already fixed up locally

BR,
-R

Quoted 103 lines
lib/efi_loader/efi_boottime.c +63 -16
+       EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, &bs, info));
+       if (ret == EFI_BUFFER_TOO_SMALL) {
+               info = malloc(bs);
+               EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, &bs, info));
+       }
+       if (ret != EFI_SUCCESS)
+               goto error;
+
+       ret = efi_allocate_pool(EFI_LOADER_DATA, info->file_size, buffer);
+       if (ret)
+               goto error;
+
+       EFI_CALL(ret = f->read(f, &info->file_size, *buffer));
+
+error:
+       free(info);
+       EFI_CALL(f->close(f));
+
+       if (ret != EFI_SUCCESS) {
+               efi_free_pool(*buffer);
+               *buffer = NULL;
+       }
+
+       return ret;
+}
+
 static efi_status_t EFIAPI efi_load_image(bool boot_policy,
                                          efi_handle_t parent_image,
                                          struct efi_device_path *file_path,
@@ -756,25 +795,48 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
                                          unsigned long source_size,
                                          efi_handle_t *image_handle)
 {
-       static struct efi_object loaded_image_info_obj = {
-               .protocols = {
-                       {
-                               .guid = &efi_guid_loaded_image,
-                       },
-               },
-       };
        struct efi_loaded_image *info;
        struct efi_object *obj;
 
        EFI_ENTRY("%d, %p, %p, %p, %ld, %p", boot_policy, parent_image,
                  file_path, source_buffer, source_size, image_handle);
-       info = malloc(sizeof(*info));
-       loaded_image_info_obj.protocols[0].protocol_interface = info;
-       obj = malloc(sizeof(loaded_image_info_obj));
-       memset(info, 0, sizeof(*info));
-       memcpy(obj, &loaded_image_info_obj, sizeof(loaded_image_info_obj));
-       obj->handle = info;
-       info->file_path = file_path;
+
+       info = calloc(1, sizeof(*info));
+       obj = calloc(1, sizeof(*obj));
+
+       if (!source_buffer) {
+               struct efi_device_path *dp, *fp, *p;
+               efi_status_t ret;
+
+               ret = load_image_from_path(file_path, &source_buffer);
+               if (ret != EFI_SUCCESS) {
+                       free(info);
+                       free(obj);
+                       return EFI_EXIT(ret);
+               }
+
+               /*
+                * split file_path which contains both the device and
+                * file parts:
+                */
+               dp = efi_dp_dup(file_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);
+
+               efi_setup_loaded_image(info, obj, dp, fp);
+       } else {
+               /* In this case, file_path is the "device" path, ie.
+                * something like a HARDWARE_DEVICE:MEMORY_MAPPED
+                */
+               efi_setup_loaded_image(info, obj, file_path, NULL);
+       }
+
        info->reserved = efi_load_pe(source_buffer, info);
        if (!info->reserved) {
                free(info);
@@ -783,7 +845,6 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
        }
 
        *image_handle = info;
-       list_add_tail(&obj->link, &efi_obj_list);
 
        return EFI_EXIT(EFI_SUCCESS);
 }
--

2.13.0