Skip to contentSkip to conversation

efi_loader: enough UEFI for standard distro boot

PATCH In review Rob Clark 8 participants 72 messages Sep 13, 2017 – Oct 28, 2017

lore
Share & tools
lore

Rob Clark

author
#0 lore raw

v3 cover efi_loader: enough UEFI for standard distro boot

This patchset fleshes out EFI_LOADER enough to support booting an upstream \EFI\BOOT\bootaa64.efi (which then loads fallback.efi and then eventually the per-distro shim.efi which loads the per-distro grubaa64.efi) without resorting to hacks to hard-code u-boot to load a particular distro's grub, or other hacks like setting up the distro installation as live-media.

Background: with a normal UEFI implementation, the boot process is:

a) firmware (u-boot) looks at BootOrder and the BootXXXX variables to try to determine what to boot.
b) the firmware will look at the BootXXXX variables (which contain an EFI_LOAD_OPTION "struct" in order specified by BootOrder, and will boot the first bootable option.
c) The EFI_LOAD_OPTION specifies a device-path which identifies the device and file path of the .efi payload to exectute.

This is implemented with the 'bootefi bootmgr' command.

If there are no bootable options the firmware falls back to loading \EFI\BOOT\bootaa64.efi (exact name varies depending on arch), which for distro's using fallback/shim (which should include most modern linux distros) then loads fallback.efi which uses the EFI_SIMPLE_FILE_SYSTEM_PROTCOL and EFI_FILE_PROTOCOL to search for any \EFI\*\boot.csv, and will then set BootOrder and BootXXXX EFI variables accordingly so that on next boot fallback.efi is not necessary.

The last 5 patches are a bit unrelated, just pulling forward some of the patches I have from the next patchset, to get Shell.efi and SCT working.

v2: updates for review comments
v3: rebase on latest master and additional comments

Peter Jones (2):
part: extract MBR signature from partitions
efi: add some more device path structures

Rob Clark (19):
part: move efi_guid_t
efi: add some missing __packed
efi_loader: add device-path utils
efi_loader: drop redundant efi_device_path_protocol
efi_loader: flesh out device-path to text
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
efi_loader: make pool allocations cacheline aligned
efi_loader: efi variable support
efi_loader: add bootmgr
efi_loader: file_path should be variable length
efi_loader: set loaded image code/data type properly
efi_loader: print GUIDs
efi_loader: split out escape sequence based size query
efi_loader: Correctly figure out size for vidconsole
efi_loader: Some console improvements for vidconsole

 cmd/bootefi.c                            | 249 ++++++--------
 disk/part_dos.c                          |  12 +-
 disk/part_efi.c                          |  20 ++
 include/blk.h                            |  18 +
 include/config_distro_bootcmd.h          |   5 +
 include/efi.h                            |  25 ++
 include/efi_api.h                        | 162 ++++++++-
 include/efi_loader.h                     |  62 +++-
 include/part.h                           |   3 +-
 include/part_efi.h                       |   4 -
 include/pe.h                             |   6 +
 lib/efi_loader/Makefile                  |   3 +-
 lib/efi_loader/efi_bootmgr.c             | 180 ++++++++++
 lib/efi_loader/efi_boottime.c            | 162 +++++++--
 lib/efi_loader/efi_console.c             | 123 ++++---
 lib/efi_loader/efi_device_path.c         | 563 +++++++++++++++++++++++++++++++
 lib/efi_loader/efi_device_path_to_text.c | 242 +++++++++----
 lib/efi_loader/efi_disk.c                |  86 +++--
 lib/efi_loader/efi_file.c                | 560 ++++++++++++++++++++++++++++++
 lib/efi_loader/efi_image_loader.c        |  26 ++
 lib/efi_loader/efi_memory.c              |   5 +-
 lib/efi_loader/efi_net.c                 |  24 +-
 lib/efi_loader/efi_runtime.c             |  17 +-
 lib/efi_loader/efi_variable.c            | 335 ++++++++++++++++++
 24 files changed, 2538 insertions(+), 354 deletions(-)
 create mode 100644 lib/efi_loader/efi_bootmgr.c
 create mode 100644 lib/efi_loader/efi_device_path.c
 create mode 100644 lib/efi_loader/efi_file.c
 create mode 100644 lib/efi_loader/efi_variable.c
Signature · 1 line
2.13.5
author
#1 lore raw

PATCH v3 1/21 part: move efi_guid_t

Prep work for next patch.

---
 include/efi.h      | 4 ++++
 include/part.h     | 3 ++-
 include/part_efi.h | 4 ----
 3 files changed, 6 insertions(+), 5 deletions(-)
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.h +2 -1
index 86117a7ce5..b2e820ef8a 100644
@@ -280,8 +280,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
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.5
#2 lore raw

PATCH v3 1/21 part: move efi_guid_t

Quoted 45 lines · Rob Clark

Prep work for next patch.

---
 include/efi.h      | 4 ++++
 include/part.h     | 3 ++-
 include/part_efi.h | 4 ----
 3 files changed, 6 insertions(+), 5 deletions(-)
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.h +2 -1
index 86117a7ce5..b2e820ef8a 100644
@@ -280,8 +280,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
include/part_efi.h +0 -0
index 317c044795..31e6bc6e14 100644

include/part_efi.h line 63

Quoted 10 lines
include/part_efi.h +0 -4
@@ -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 */

Compiled and tested with and without CONFIG_EFI_PARTITION.

#3 lore raw

PATCH v3 1/21 part: move efi_guid_t

Hello Alex,

I guess it makes most sense if you pick up this patch in your efi-next branch as it touches include/efi.h and otherwise we would have to load this onto Tom.

Best regards

Heinrich

Quoted 61 lines · Heinrich Schuchardt
Quoted 45 lines · Rob Clark

Prep work for next patch.

---
 include/efi.h      | 4 ++++
 include/part.h     | 3 ++-
 include/part_efi.h | 4 ----
 3 files changed, 6 insertions(+), 5 deletions(-)
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.h +2 -1
index 86117a7ce5..b2e820ef8a 100644
@@ -280,8 +280,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
include/part_efi.h +0 -0
index 317c044795..31e6bc6e14 100644

include/part_efi.h line 63

Quoted 10 lines
include/part_efi.h +0 -4
@@ -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 */

Compiled and tested with and without CONFIG_EFI_PARTITION.

author
#5 lore raw

PATCH v3 2/21 part: extract MBR signature from partitions

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.

[separated from efi_loader part, and fixed build-errors for non-
CONFIG_EFI_PARTITION case]

---
 disk/part_dos.c | 12 +++++++++---
 disk/part_efi.c | 20 ++++++++++++++++++++
 include/blk.h   | 18 ++++++++++++++++++
 3 files changed, 47 insertions(+), 3 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 2973d52f6a..208bb14ee8 100644
@@ -923,11 +923,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");
@@ -937,6 +945,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) {
include/blk.h +18 -0
index 1965812a9d..41b4d7efa8 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"
@@ -41,6 +43,17 @@ enum if_type {
 #define BLK_REV_SIZE		8
 
 /*
+ * Identifies the partition table type (ie. MBR vs GPT GUID) signature
+ */
+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)
  */
@@ -67,6 +80,11 @@ struct blk_desc {
 	char		vendor[BLK_VEN_SIZE + 1]; /* device vendor string */
 	char		product[BLK_PRD_SIZE + 1]; /* device product number */
 	char		revision[BLK_REV_SIZE + 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
Signature · 1 line
2.13.5
#6 lore raw

PATCH v3 2/21 part: extract MBR signature from partitions

Quoted 10 lines

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.

[separated from efi_loader part, and fixed build-errors for non-
CONFIG_EFI_PARTITION case]

Thanks, applied to efi-next

Alex

#7 lore raw

PATCH v3 2/21 part: extract MBR signature from partitions

In reply to Rob Clark
Quoted 10 lines · 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.

[separated from efi_loader part, and fixed build-errors for non-
CONFIG_EFI_PARTITION case]

This is commit ff98cb90514d9b78 in mainline now and breaks all mx6 SPL boots:

U-Boot SPL 2017.09-00221-g0d6ab32 (Oct 02 2017 - 15:13:19) Trying to boot from MMC1
(hangs here)

Does anyone know how to fix this problem?

#8 lore raw

PATCH v3 2/21 part: extract MBR signature from partitions

Quoted 12 lines · Fabio Estevam
Quoted 10 lines · 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.

[separated from efi_loader part, and fixed build-errors for non-
CONFIG_EFI_PARTITION case]

Quoted 7 lines

This is commit ff98cb90514d9b78 in mainline now and breaks all mx6 SPL boots:

U-Boot SPL 2017.09-00221-g0d6ab32 (Oct 02 2017 - 15:13:19) Trying to boot from MMC1
(hangs here)

Does anyone know how to fix this problem?

That would explain what I'm seeing as we pull this patch set in to 2017.09 for EFI boot support on aarch64 platforms.

Peter

#9 lore raw

PATCH v3 2/21 part: extract MBR signature from partitions

Quoted 2 lines · Peter Robinson

That would explain what I'm seeing as we pull this patch set in to 2017.09 for EFI boot support on aarch64 platforms.

Locally I am applying the following hack, so that I can continue to work on mx6 with SPL:
https://pastebin.com/MX8tK76X

#10 lore raw

PATCH v3 2/21 part: extract MBR signature from partitions

Quoted 8 lines · Fabio Estevam
Quoted 2 lines · Peter Robinson

That would explain what I'm seeing as we pull this patch set in to 2017.09 for EFI boot support on aarch64 platforms.

Locally I am applying the following hack, so that I can continue to work on mx6 with SPL:
https://pastebin.com/MX8tK76X

FWIW, on cubox the current status is looping, and your kludge I just get no output :( But there may be some other problems here too as I'm seeing some oddities as I try out SDP on it.

Signature · 8 lines
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20171002/12ce5dd7/attachment.sig>
#11 lore raw

PATCH v3 2/21 part: extract MBR signature from partitions

Hi Tom,

Quoted 2 lines · Tom Rini

FWIW, on cubox the current status is looping, and your kludge I just get no output :( But there may be some other problems here too as I'm

Just tried it on 2017.11-rc1 plus the attached hack and it boots fine:

U-Boot SPL 2017.11-rc1-dirty (Oct 02 2017 - 21:27:10) Trying to boot from MMC1

U-Boot 2017.11-rc1-dirty (Oct 02 2017 - 21:27:10 -0300)

CPU:   Freescale i.MX6Q rev1.5 996 MHz (running at 792 MHz)
CPU:   Extended Commercial temperature grade (-20C to 105C) at 26C
Reset cause: POR
Board: MX6 Cubox-i
DRAM:  2 GiB
MMC:   FSL_SDHC: 0
No panel detected: default to HDMI
Display: HDMI (1024x768)
In:    serial
Out:   serial
Err:   serial
Net:   FEC
Hit any key to stop autoboot:  0

=>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-workaround.patch
Type: text/x-patch
Size: 966 bytes
Desc: not available
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20171002/7bd3f52d/attachment.bin>

author
#12 lore raw

PATCH v3 2/21 part: extract MBR signature from partitions

In reply to Fabio Estevam
Quoted 12 lines · Fabio Estevam
Quoted 10 lines · 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.

[separated from efi_loader part, and fixed build-errors for non-
CONFIG_EFI_PARTITION case]

Quoted 7 lines

This is commit ff98cb90514d9b78 in mainline now and breaks all mx6 SPL boots:

U-Boot SPL 2017.09-00221-g0d6ab32 (Oct 02 2017 - 15:13:19) Trying to boot from MMC1
(hangs here)

Does anyone know how to fix this problem?

not sure to what extent debug()/printf() works from SPL, but if it does maybe adding this patch with and without that commit and comparing logs would give some hint:

--------------

disk/part.c +3 -1
index aa9183d696..8e0bf54b3e 100644
@@ -5,6 +5,8 @@
  * SPDX-License-Identifier:    GPL-2.0+
  */
 
+#define DEBUG
+
 #include <common.h>
 #include <command.h>
 #include <errno.h>
@@ -13,7 +15,7 @@
 #include <part.h>
 #include <ubifs_uboot.h>
 
-#undef    PART_DEBUG
+#define    PART_DEBUG
 
 #ifdef    PART_DEBUG
 #define    PRINTF(fmt,args...)    printf (fmt ,##args)

--------------

I don't really see why this commit would cause problems, but I guess either something unexpected going on w/ partitions or block device driver on this platform..

BR,
-R

#13 lore raw

PATCH v3 2/21 part: extract MBR signature from partitions

Hi Rob,

Quoted 3 lines · Rob Clark

not sure to what extent debug()/printf() works from SPL, but if it does maybe adding this patch with and without that commit and comparing logs would give some hint:

Unfortunately I don't get any logs with this debug patch.

Quoted 3 lines

I don't really see why this commit would cause problems, but I guess either something unexpected going on w/ partitions or block device driver on this platform..

I have just sent a RFC patch that fixes the boot here.

author
#14 lore raw

PATCH v3 3/21 efi: add some missing __packed

In reply to Rob Clark

All of the device-path related structures should be packed. UEFI defines the device-path as a byte-aligned data structure.

---
 include/efi_api.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
include/efi_api.h +4 -4
index ec1b321e8e..175341348e 100644
@@ -284,11 +284,11 @@ struct efi_device_path {
 	u8 type;
 	u8 sub_type;
 	u16 length;
-};
+} __packed;
 
 struct efi_mac_addr {
 	u8 addr[32];
-};
+} __packed;
 
 #define DEVICE_PATH_TYPE_MESSAGING_DEVICE	0x03
 #  define DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR	0x0b
@@ -297,7 +297,7 @@ struct efi_device_path_mac_addr {
 	struct efi_device_path dp;
 	struct efi_mac_addr mac;
 	u8 if_type;
-};
+} __packed;
 
 #define DEVICE_PATH_TYPE_MEDIA_DEVICE		0x04
 #  define DEVICE_PATH_SUB_TYPE_FILE_PATH	0x04
@@ -305,7 +305,7 @@ struct efi_device_path_mac_addr {
 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.5
#15 lore raw

PATCH v3 3/21 efi: add some missing __packed

Quoted 13 lines · Rob Clark

All of the device-path related structures should be packed. UEFI defines the device-path as a byte-aligned data structure.

---
 include/efi_api.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
include/efi_api.h +0 -0
index ec1b321e8e..175341348e 100644

include/efi_api.h struct efi_device_path { · line 290

Quoted 8 lines
include/efi_api.h +1 -1
@@ -284,11 +284,11 @@ struct efi_device_path {
 	u8 type;
 	u8 sub_type;
 	u16 length;
-};
+} __packed;
 
 struct efi_mac_addr {
 	u8 addr[32];

Hello Alex, hello Rob,

32 bytes is more than the 6 bytes than we need.
Why was the structure defined this way?

Quoted 15 lines
include/efi_api.h +2 -2
-};
+} __packed;
 
 #define DEVICE_PATH_TYPE_MESSAGING_DEVICE	0x03
 #  define DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR	0x0b
@@ -297,7 +297,7 @@ struct efi_device_path_mac_addr {
 	struct efi_device_path dp;
 	struct efi_mac_addr mac;
 	u8 if_type;
-};
+} __packed;
 
 #define DEVICE_PATH_TYPE_MEDIA_DEVICE		0x04
 #  define DEVICE_PATH_SUB_TYPE_FILE_PATH	0x04

include/efi_api.h struct efi_device_path_mac_addr { · line 307

Quoted 3 lines
include/efi_api.h +0 -0
@@ -305,7 +305,7 @@ struct efi_device_path_mac_addr {
 struct efi_device_path_file_path {
 	struct efi_device_path dp;
 	u16 str[32];

Where do we get this 32 from?
Couldn't file/directory names be shorter (1 character) or longer (255 characters)?

Regards

Heinrich

Quoted 6 lines
include/efi_api.h +1 -1
-};
+} __packed;
 
 #define BLOCK_IO_GUID \
 	EFI_GUID(0x964e5b21, 0x6459, 0x11d2, \
author
#16 lore raw

PATCH v3 3/21 efi: add some missing __packed

Quoted 20 lines · Heinrich Schuchardt
Quoted 19 lines · Rob Clark

All of the device-path related structures should be packed. UEFI defines the device-path as a byte-aligned data structure.

---
 include/efi_api.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
include/efi_api.h +1 -1
index ec1b321e8e..175341348e 100644
@@ -284,11 +284,11 @@ struct efi_device_path {
      u8 type;
      u8 sub_type;
      u16 length;
-};
+} __packed;
 
Quoted 8 lines

include/efi_api.h struct efi_device_path { · line 290

Quoted 2 lines
include/efi_api.h +0 -0
 struct efi_mac_addr {
      u8 addr[32];

Hello Alex, hello Rob,

32 bytes is more than the 6 bytes than we need.
Why was the structure defined this way?

Off the top of my head, I'm not sure where that came from, maybe this dp node is meant to be variable length? But I guess that is also a separate patch.

Quoted 12 lines
Quoted 12 lines
include/efi_api.h +2 -2
-};
+} __packed;
 
 #define DEVICE_PATH_TYPE_MESSAGING_DEVICE    0x03
 #  define DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR  0x0b
@@ -297,7 +297,7 @@ struct efi_device_path_mac_addr {
      struct efi_device_path dp;
      struct efi_mac_addr mac;
      u8 if_type;
-};
+} __packed;
 
Quoted 11 lines

include/efi_api.h struct efi_device_path_mac_addr { · line 307

Quoted 6 lines
include/efi_api.h +0 -0
 #define DEVICE_PATH_TYPE_MEDIA_DEVICE                0x04
 #  define DEVICE_PATH_SUB_TYPE_FILE_PATH     0x04
@@ -305,7 +305,7 @@ struct efi_device_path_mac_addr {
 struct efi_device_path_file_path {
      struct efi_device_path dp;
      u16 str[32];

Where do we get this 32 from?
Couldn't file/directory names be shorter (1 character) or longer (255 characters)?

yup, file-path nodes should be variable length (and are after my patches.. I have a later one that changes it to 'u16 str[]')

I believe the hard-coded 32 is due to current staticly initialized device-path structs which go away with "efi_loader: refactor boot device and loaded_image handling"

BR,
-R

#17 lore raw

PATCH v3 3/21 efi: add some missing __packed

In reply to Heinrich Schuchardt
Quoted 20 lines · Heinrich Schuchardt
Quoted 19 lines · Rob Clark

All of the device-path related structures should be packed. UEFI defines the device-path as a byte-aligned data structure.

---
 include/efi_api.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
include/efi_api.h +1 -1
index ec1b321e8e..175341348e 100644
@@ -284,11 +284,11 @@ struct efi_device_path {
 	u8 type;
 	u8 sub_type;
 	u16 length;
-};
+} __packed;
 
Quoted 7 lines

include/efi_api.h struct efi_device_path { · line 290

Quoted 2 lines
include/efi_api.h +0 -0
 struct efi_mac_addr {
 	u8 addr[32];

Hello Alex, hello Rob,

32 bytes is more than the 6 bytes than we need.
Why was the structure defined this way?

It probably *should* have been variable length in the spec, but it is defined as 32-bytes. But 6 is also not correct - it's right for ethernet, but not for everything. I suspect they picked the length of an IPoIB (Infiniband) MAC here.

Quoted 13 lines
Quoted 13 lines
include/efi_api.h +2 -2
-};
+} __packed;
 
 #define DEVICE_PATH_TYPE_MESSAGING_DEVICE	0x03
 #  define DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR	0x0b
@@ -297,7 +297,7 @@ struct efi_device_path_mac_addr {
 	struct efi_device_path dp;
 	struct efi_mac_addr mac;
 	u8 if_type;
-};
+} __packed;
 
Quoted 10 lines

include/efi_api.h struct efi_device_path_mac_addr { · line 307

Quoted 6 lines
include/efi_api.h +0 -0
 #define DEVICE_PATH_TYPE_MEDIA_DEVICE		0x04
 #  define DEVICE_PATH_SUB_TYPE_FILE_PATH	0x04
@@ -305,7 +305,7 @@ struct efi_device_path_mac_addr {
 struct efi_device_path_file_path {
 	struct efi_device_path dp;
 	u16 str[32];

Where do we get this 32 from?
Couldn't file/directory names be shorter (1 character) or longer (255 characters)?

Note there's already another patch in this series fixing this.

Signature · 1 line
  Peter
author
#19 lore raw

PATCH v3 4/21 efi: add some more device path structures

In reply to Rob Clark

From: Peter Jones <[email protected]>

---
 include/efi_api.h | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)
include/efi_api.h +54 -0
index 175341348e..b761cf4822 100644
@@ -290,8 +290,38 @@ struct efi_mac_addr {
 	u8 addr[32];
 } __packed;
 
+#define DEVICE_PATH_TYPE_HARDWARE_DEVICE	0x01
+#  define DEVICE_PATH_SUB_TYPE_VENDOR		0x04
+
+struct efi_device_path_vendor {
+	struct efi_device_path dp;
+	efi_guid_t guid;
+	u8 vendor_data[];
+} __packed;
+
+#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;
@@ -299,9 +329,33 @@ struct efi_device_path_mac_addr {
 	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];
Signature · 1 line
2.13.5
author
#21 lore raw

PATCH v3 5/21 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_api.h                |  10 +
 include/efi_loader.h             |  26 ++
 lib/efi_loader/Makefile          |   2 +-
 lib/efi_loader/efi_boottime.c    |  13 +-
 lib/efi_loader/efi_device_path.c | 563 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 611 insertions(+), 3 deletions(-)
 create mode 100644 lib/efi_loader/efi_device_path.c
include/efi_api.h +10 -0
index b761cf4822..4e27c82129 100644
@@ -314,6 +314,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
 
@@ -329,6 +330,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;
include/efi_loader.h +26 -0
index 1179234f68..d052b03ab7 100644
@@ -197,6 +197,32 @@ extern void *efi_bounce_buffer;
 #define EFI_LOADER_BOUNCE_BUFFER_SIZE (64 * 1024 * 1024)
 #endif
 
+
+struct efi_device_path *efi_dp_next(const 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,
+				   struct efi_device_path **rem);
+unsigned efi_dp_size(const struct efi_device_path *dp);
+struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp);
+struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
+				      const struct efi_device_path *dp2);
+struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
+					   const struct efi_device_path *node);
+
+
+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)
 {
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_boottime.c +11 -2
index 43f32385fa..b962b62a97 100644
@@ -665,8 +665,17 @@ static efi_status_t EFIAPI efi_locate_device_path(efi_guid_t *protocol,
 			struct efi_device_path **device_path,
 			efi_handle_t *device)
 {
-	EFI_ENTRY("%p, %p, %p", protocol, device_path, device);
-	return EFI_EXIT(EFI_NOT_FOUND);
+	struct efi_object *efiobj;
+
+	EFI_ENTRY("%pUl, %p, %p", protocol, device_path, device);
+
+	efiobj = efi_dp_find_obj(*device_path, device_path);
+	if (!efiobj)
+		return EFI_EXIT(EFI_NOT_FOUND);
+
+	*device = efiobj->handle;
+
+	return EFI_EXIT(EFI_SUCCESS);
 }
 
 /* Collapses configuration table entries, removing index i */
lib/efi_loader/efi_device_path.c new file +563 -0

563 lines not on this page · raw message

Signature · 1 line
2.13.5
#22 lore raw

PATCH v3 5/21 efi_loader: add device-path utils

Quoted 8 lines · 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.

This patch gives me checkpatch warnings left and right (unsigned vs unsigned int, double blank lines, double assignments, unsafe define, ...). I'll pull it in for now since it seems to be functionally correct, but please fix up the warnings in a follow-up patch.

Alex

WARNING: Adding new packed members is to be done with care #56: FILE: include/efi_api.h:340:
+} __packed;

CHECK: Please don't use multiple blank lines
#69: FILE: include/efi_loader.h:200:

+

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #74: FILE: include/efi_loader.h:205:
+unsigned efi_dp_size(const struct efi_device_path *dp);

CHECK: Please don't use multiple blank lines
#81: FILE: include/efi_loader.h:212:
+
+

CHECK: Macro argument reuse '_dp' - possible side-effects? #91: FILE: include/efi_loader.h:222:
+#define EFI_DP_TYPE(_dp, _type, _subtype) \
+ (((_dp)->type == DEVICE_PATH_TYPE_##_type) && \
+ ((_dp)->sub_type == DEVICE_PATH_SUB_TYPE_##_subtype))

WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? #136:
new file mode 100644

CHECK: Comparison to NULL could be written "!dp"
#195: FILE: lib/efi_loader/efi_device_path.c:55:
+ if (dp == NULL)

CHECK: Please don't use multiple blank lines
#232: FILE: lib/efi_loader/efi_device_path.c:92:
+
+

CHECK: Please don't use multiple blank lines
#301: FILE: lib/efi_loader/efi_device_path.c:161:
+
+

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #320: FILE: lib/efi_loader/efi_device_path.c:180:
+unsigned efi_dp_size(const struct efi_device_path *dp)

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #322: FILE: lib/efi_loader/efi_device_path.c:182:
+ unsigned sz = 0;

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #335: FILE: lib/efi_loader/efi_device_path.c:195:
+ unsigned sz = efi_dp_size(dp) + sizeof(END);

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #357: FILE: lib/efi_loader/efi_device_path.c:217:
+ unsigned sz1 = efi_dp_size(dp1);

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #358: FILE: lib/efi_loader/efi_device_path.c:218:
+ unsigned sz2 = efi_dp_size(dp2);

WARNING: Missing a blank line after declarations
#360: FILE: lib/efi_loader/efi_device_path.c:220:
+ void *p = dp_alloc(sz1 + sz2 + sizeof(END));
+ memcpy(p, dp1, sz1);

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #379: FILE: lib/efi_loader/efi_device_path.c:239:
+ unsigned sz = node->length;

WARNING: Missing a blank line after declarations
#381: FILE: lib/efi_loader/efi_device_path.c:241:
+ void *p = dp_alloc(sz + sizeof(END));
+ memcpy(p, node, sz);

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #386: FILE: lib/efi_loader/efi_device_path.c:246:
+ unsigned sz = efi_dp_size(dp);

WARNING: Missing a blank line after declarations
#388: FILE: lib/efi_loader/efi_device_path.c:248:
+ void *p = dp_alloc(sz + node->length + sizeof(END));
+ memcpy(p, dp, sz);

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #401: FILE: lib/efi_loader/efi_device_path.c:261:
+static unsigned dp_size(struct udevice *dev)

CHECK: multiple assignments should be avoided
#484: FILE: lib/efi_loader/efi_device_path.c:344:
+ start = buf = dp_alloc(dp_size(dev) + sizeof(END));

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #492: FILE: lib/efi_loader/efi_device_path.c:352:
+static unsigned dp_part_size(struct blk_desc *desc, int part)

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #494: FILE: lib/efi_loader/efi_device_path.c:354:
+ unsigned dpsize;

CHECK: Please don't use multiple blank lines
#581: FILE: lib/efi_loader/efi_device_path.c:441:
+
+

CHECK: multiple assignments should be avoided
#587: FILE: lib/efi_loader/efi_device_path.c:447:
+ start = buf = dp_alloc(dp_part_size(desc, part) + sizeof(END));

WARNING: Missing a blank line after declarations
#601: FILE: lib/efi_loader/efi_device_path.c:461:
+ char c = *(path++);
+ if (c == '/')

CHECK: Alignment should match open parenthesis
#613: FILE: lib/efi_loader/efi_device_path.c:473:
+struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
+ const char *path)

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #617: FILE: lib/efi_loader/efi_device_path.c:477:
+ unsigned dpsize = 0, fpsize;

CHECK: multiple assignments should be avoided
#625: FILE: lib/efi_loader/efi_device_path.c:485:
+ start = buf = dp_alloc(dpsize + sizeof(END));

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #648: FILE: lib/efi_loader/efi_device_path.c:508:
+ unsigned dpsize = 0;

CHECK: multiple assignments should be avoided
#659: FILE: lib/efi_loader/efi_device_path.c:519:
+ start = buf = dp_alloc(dpsize + sizeof(END));

total: 0 errors, 19 warnings, 12 checks, 644 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or
--fix-inplace.

Your patch has style problems, please review.

NOTE: Ignored message types: COMPLEX_MACRO CONSIDER_KSTRTO MINMAX MULTISTATEMENT_MACRO_USE_DO_WHILE NETWORKING_BLOCK_COMMENT_STYLE PREFER_ETHER_ADDR_COPY USLEEP_RANGE

NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.

author
#23 lore raw

PATCH v3 5/21 efi_loader: add device-path utils

Quoted 20 lines · Alexander Graf
Quoted 9 lines · 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.

This patch gives me checkpatch warnings left and right (unsigned vs unsigned int, double blank lines, double assignments, unsafe define, ...). I'll pull it in for now since it seems to be functionally correct, but please fix up the warnings in a follow-up patch.

Quoted 5 lines

Alex

WARNING: Adding new packed members is to be done with care #56: FILE: include/efi_api.h:340:
+} __packed;

fwiw, the __packed warnings are bogus and should be ignored. Maybe someone can fix checkpatch to whitelist efi_api.h (or just drop that warning again).

BR,
-R

Quoted 148 lines

CHECK: Please don't use multiple blank lines
#69: FILE: include/efi_loader.h:200:

+

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #74: FILE: include/efi_loader.h:205:
+unsigned efi_dp_size(const struct efi_device_path *dp);

CHECK: Please don't use multiple blank lines
#81: FILE: include/efi_loader.h:212:

(quoted diff) +2 -0
+
+

CHECK: Macro argument reuse '_dp' - possible side-effects? #91: FILE: include/efi_loader.h:222:

include/efi_loader.h +3 -0
+#define EFI_DP_TYPE(_dp, _type, _subtype) \
+       (((_dp)->type == DEVICE_PATH_TYPE_##_type) && \
+        ((_dp)->sub_type == DEVICE_PATH_SUB_TYPE_##_subtype))

WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? #136:
new file mode 100644

CHECK: Comparison to NULL could be written "!dp"
#195: FILE: lib/efi_loader/efi_device_path.c:55:
+ if (dp == NULL)

CHECK: Please don't use multiple blank lines
#232: FILE: lib/efi_loader/efi_device_path.c:92:

(quoted diff) +2 -0
+
+

CHECK: Please don't use multiple blank lines
#301: FILE: lib/efi_loader/efi_device_path.c:161:

(quoted diff) +2 -0
+
+

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #320: FILE: lib/efi_loader/efi_device_path.c:180:
+unsigned efi_dp_size(const struct efi_device_path *dp)

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #322: FILE: lib/efi_loader/efi_device_path.c:182:
+ unsigned sz = 0;

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #335: FILE: lib/efi_loader/efi_device_path.c:195:
+ unsigned sz = efi_dp_size(dp) + sizeof(END);

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #357: FILE: lib/efi_loader/efi_device_path.c:217:
+ unsigned sz1 = efi_dp_size(dp1);

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #358: FILE: lib/efi_loader/efi_device_path.c:218:
+ unsigned sz2 = efi_dp_size(dp2);

WARNING: Missing a blank line after declarations
#360: FILE: lib/efi_loader/efi_device_path.c:220:

lib/efi_loader/efi_device_path.c new file +2 -0
+               void *p = dp_alloc(sz1 + sz2 + sizeof(END));
+               memcpy(p, dp1, sz1);

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #379: FILE: lib/efi_loader/efi_device_path.c:239:
+ unsigned sz = node->length;

WARNING: Missing a blank line after declarations
#381: FILE: lib/efi_loader/efi_device_path.c:241:

lib/efi_loader/efi_device_path.c new file +2 -0
+               void *p = dp_alloc(sz + sizeof(END));
+               memcpy(p, node, sz);

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #386: FILE: lib/efi_loader/efi_device_path.c:246:
+ unsigned sz = efi_dp_size(dp);

WARNING: Missing a blank line after declarations
#388: FILE: lib/efi_loader/efi_device_path.c:248:

lib/efi_loader/efi_device_path.c new file +2 -0
+               void *p = dp_alloc(sz + node->length + sizeof(END));
+               memcpy(p, dp, sz);

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #401: FILE: lib/efi_loader/efi_device_path.c:261:
+static unsigned dp_size(struct udevice *dev)

CHECK: multiple assignments should be avoided
#484: FILE: lib/efi_loader/efi_device_path.c:344:
+ start = buf = dp_alloc(dp_size(dev) + sizeof(END));

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #492: FILE: lib/efi_loader/efi_device_path.c:352:
+static unsigned dp_part_size(struct blk_desc *desc, int part)

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #494: FILE: lib/efi_loader/efi_device_path.c:354:
+ unsigned dpsize;

CHECK: Please don't use multiple blank lines
#581: FILE: lib/efi_loader/efi_device_path.c:441:

(quoted diff) +2 -0
+
+

CHECK: multiple assignments should be avoided
#587: FILE: lib/efi_loader/efi_device_path.c:447:
+ start = buf = dp_alloc(dp_part_size(desc, part) + sizeof(END));

WARNING: Missing a blank line after declarations
#601: FILE: lib/efi_loader/efi_device_path.c:461:

lib/efi_loader/efi_device_path.c new file +2 -0
+               char c = *(path++);
+               if (c == '/')

CHECK: Alignment should match open parenthesis
#613: FILE: lib/efi_loader/efi_device_path.c:473:

lib/efi_loader/efi_device_path.c new file +2 -0
+struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
+               const char *path)

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #617: FILE: lib/efi_loader/efi_device_path.c:477:
+ unsigned dpsize = 0, fpsize;

CHECK: multiple assignments should be avoided
#625: FILE: lib/efi_loader/efi_device_path.c:485:
+ start = buf = dp_alloc(dpsize + sizeof(END));

WARNING: Prefer 'unsigned int' to bare use of 'unsigned' #648: FILE: lib/efi_loader/efi_device_path.c:508:
+ unsigned dpsize = 0;

CHECK: multiple assignments should be avoided
#659: FILE: lib/efi_loader/efi_device_path.c:519:
+ start = buf = dp_alloc(dpsize + sizeof(END));

total: 0 errors, 19 warnings, 12 checks, 644 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or
--fix-inplace.

Your patch has style problems, please review.

NOTE: Ignored message types: COMPLEX_MACRO CONSIDER_KSTRTO MINMAX MULTISTATEMENT_MACRO_USE_DO_WHILE NETWORKING_BLOCK_COMMENT_STYLE PREFER_ETHER_ADDR_COPY USLEEP_RANGE

NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.

#24 lore raw

PATCH v3 5/21 efi_loader: add device-path utils

In reply to Rob Clark
Quoted 8 lines

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.

Thanks, applied to efi-next

Alex

#25 lore raw

PATCH v3 5/21 efi_loader: add device-path utils

In reply to Rob Clark
Quoted 100 lines · 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_api.h                |  10 +
 include/efi_loader.h             |  26 ++
 lib/efi_loader/Makefile          |   2 +-
 lib/efi_loader/efi_boottime.c    |  13 +-
 lib/efi_loader/efi_device_path.c | 563 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 611 insertions(+), 3 deletions(-)
 create mode 100644 lib/efi_loader/efi_device_path.c
include/efi_api.h +10 -0
index b761cf4822..4e27c82129 100644
@@ -314,6 +314,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
 
@@ -329,6 +330,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;
include/efi_loader.h +26 -0
index 1179234f68..d052b03ab7 100644
@@ -197,6 +197,32 @@ extern void *efi_bounce_buffer;
 #define EFI_LOADER_BOUNCE_BUFFER_SIZE (64 * 1024 * 1024)
 #endif
 
+
+struct efi_device_path *efi_dp_next(const 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,
+				   struct efi_device_path **rem);
+unsigned efi_dp_size(const struct efi_device_path *dp);
+struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp);
+struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
+				      const struct efi_device_path *dp2);
+struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
+					   const struct efi_device_path *node);
+
+
+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)
 {
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_boottime.c +0 -0
index 43f32385fa..b962b62a97 100644

lib/efi_loader/efi_boottime.c static efi_status_t EFIAPI efi_locate_device_path(efi_guid_t *protocol, · line 672

Quoted 10 lines
lib/efi_loader/efi_boottime.c +5 -2
@@ -665,8 +665,17 @@ static efi_status_t EFIAPI efi_locate_device_path(efi_guid_t *protocol,
 			struct efi_device_path **device_path,
 			efi_handle_t *device)
 {
-	EFI_ENTRY("%p, %p, %p", protocol, device_path, device);
-	return EFI_EXIT(EFI_NOT_FOUND);
+	struct efi_object *efiobj;
+
+	EFI_ENTRY("%pUl, %p, %p", protocol, device_path, device);
+
+	efiobj = efi_dp_find_obj(*device_path, device_path);

The patch is already merged.

efi_dp_find_obj does not implement the logic required by LocateDevicePath. Parameter protocol is completely ignored!

This is what LocateDevicePath is expected to do:
Start at the full device path and check if the related handle implements the protocol.
Iteratively remove the rightmost node and try again.
Return the handle of the longest left subpath implementing the protocol.

Given handles with device paths
'/part1' and '/part1/part2' implementing 'protocol' a query for device path '/part1/part2/part3' and 'protocol' should return the handle of '/part1/part2' and remaining path '/part3'.

Quoted 161 lines
lib/efi_loader/efi_boottime.c +6 -0
+	if (!efiobj)
+		return EFI_EXIT(EFI_NOT_FOUND);
+
+	*device = efiobj->handle;
+
+	return EFI_EXIT(EFI_SUCCESS);
 }
 
 /* Collapses configuration table entries, removing index i */
lib/efi_loader/efi_device_path.c new file +146 -0
new file mode 100644
index 0000000000..5d5c3b3464
@@ -0,0 +1,563 @@
+/*
+ * 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>
+
+/* template END node: */
+static const struct efi_device_path END = {
+	.type     = DEVICE_PATH_TYPE_END,
+	.sub_type = DEVICE_PATH_SUB_TYPE_END,
+	.length   = sizeof(END),
+};
+
+#define U_BOOT_GUID \
+	EFI_GUID(0xe61d73b9, 0xa384, 0x4acc, \
+		 0xae, 0xab, 0x82, 0xe8, 0x28, 0xf3, 0x62, 0x8b)
+
+/* template ROOT node: */
+static const struct efi_device_path_vendor ROOT = {
+	.dp = {
+		.type     = DEVICE_PATH_TYPE_HARDWARE_DEVICE,
+		.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
+		.length   = sizeof(ROOT),
+	},
+	.guid = U_BOOT_GUID,
+};
+
+static void *dp_alloc(size_t sz)
+{
+	void *buf;
+
+	if (efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, sz, &buf) != EFI_SUCCESS)
+		return NULL;
+
+	return buf;
+}
+
+/*
+ * Iterate to next block in device-path, terminating (returning NULL)
+ * at /End* node.
+ */
+struct efi_device_path *efi_dp_next(const struct efi_device_path *dp)
+{
+	if (dp == NULL)
+		return NULL;
+	if (dp->type == DEVICE_PATH_TYPE_END)
+		return NULL;
+	dp = ((void *)dp) + dp->length;
+	if (dp->type == DEVICE_PATH_TYPE_END)
+		return NULL;
+	return (struct efi_device_path *)dp;
+}
+
+/*
+ * Compare two device-paths, stopping when the shorter of the two hits
+ * 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_device_path **rem)
+{
+	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;
+
+			if (!handler->guid)
+				break;
+
+			if (guidcmp(handler->guid, &efi_guid_device_path))
+				continue;
+
+			obj_dp = handler->protocol_interface;
+
+			do {
+				if (efi_dp_match(dp, obj_dp) == 0) {
+					if (rem) {

lib/efi_loader/efi_device_path.c line 156

Quoted 10 lines
lib/efi_loader/efi_device_path.c new file +10 -0
+						*rem = ((void *)dp) +
+							efi_dp_size(obj_dp);
+					}
+					return efiobj;
+				}
+
+				obj_dp = shorten_path(efi_dp_next(obj_dp));
+			} while (short_path && obj_dp);
+		}
+	}

To which part of the UEFI spec does this logic relate? I would like to see a comment in the coding.

As this has to be refactored when moving to a linked list for protocols a unit test (e.g. in lib/efi_selftest/) would be very helpful.

Best regards

Heinrich

Quoted 408 lines
lib/efi_loader/efi_device_path.c new file +407 -0
+
+	return NULL;
+}
+
+
+/*
+ * Find an efiobj from device-path, if 'rem' is not NULL, returns the
+ * remaining part of the device path after the matched object.
+ */
+struct efi_object *efi_dp_find_obj(struct efi_device_path *dp,
+				   struct efi_device_path **rem)
+{
+	struct efi_object *efiobj;
+
+	efiobj = find_obj(dp, false, rem);
+
+	if (!efiobj)
+		efiobj = find_obj(dp, true, rem);
+
+	return efiobj;
+}
+
+/* return size not including End node: */
+unsigned efi_dp_size(const 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(const struct efi_device_path *dp)
+{
+	struct efi_device_path *ndp;
+	unsigned sz = efi_dp_size(dp) + sizeof(END);
+
+	if (!dp)
+		return NULL;
+
+	ndp = dp_alloc(sz);
+	memcpy(ndp, dp, sz);
+
+	return ndp;
+}
+
+struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1,
+				      const struct efi_device_path *dp2)
+{
+	struct efi_device_path *ret;
+
+	if (!dp1) {
+		ret = efi_dp_dup(dp2);
+	} else if (!dp2) {
+		ret = efi_dp_dup(dp1);
+	} else {
+		/* both dp1 and dp2 are non-null */
+		unsigned sz1 = efi_dp_size(dp1);
+		unsigned sz2 = efi_dp_size(dp2);
+		void *p = dp_alloc(sz1 + sz2 + sizeof(END));
+		memcpy(p, dp1, sz1);
+		memcpy(p + sz1, dp2, sz2);
+		memcpy(p + sz1 + sz2, &END, sizeof(END));
+		ret = p;
+	}
+
+	return ret;
+}
+
+struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
+					   const struct efi_device_path *node)
+{
+	struct efi_device_path *ret;
+
+	if (!node && !dp) {
+		ret = efi_dp_dup(&END);
+	} else if (!node) {
+		ret = efi_dp_dup(dp);
+	} else if (!dp) {
+		unsigned sz = node->length;
+		void *p = dp_alloc(sz + sizeof(END));
+		memcpy(p, node, sz);
+		memcpy(p + sz, &END, sizeof(END));
+		ret = p;
+	} else {
+		/* both dp and node are non-null */
+		unsigned sz = efi_dp_size(dp);
+		void *p = dp_alloc(sz + node->length + sizeof(END));
+		memcpy(p, dp, sz);
+		memcpy(p + sz, node, node->length);
+		memcpy(p + sz + node->length, &END, sizeof(END));
+		ret = p;
+	}
+
+	return ret;
+}
+
+#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_vendor *vdp = buf;
+		*vdp = ROOT;
+		return &vdp[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 = dev->seq;
+
+		return &sddp[1];
+	}
+#endif
+	case UCLASS_MASS_STORAGE:
+	case UCLASS_USB_HUB: {
+		struct efi_device_path_usb_class *udp =
+			dp_fill(buf, dev->parent);
+		struct usb_device *udev = dev_get_parent_priv(dev);
+		struct usb_device_descriptor *desc = &udev->descriptor;
+
+		udp->dp.type     = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+		udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS;
+		udp->dp.length   = sizeof(*udp);
+		udp->vendor_id   = desc->idVendor;
+		udp->product_id  = desc->idProduct;
+		udp->device_class    = desc->bDeviceClass;
+		udp->device_subclass = desc->bDeviceSubClass;
+		udp->device_protocol = desc->bDeviceProtocol;
+
+		return &udp[1];
+	}
+	default:
+		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 = dp_alloc(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_usb);
+#endif
+
+	if (part == 0) /* the actual disk, not a partition */
+		return dpsize;
+
+	if (desc->part_type == PART_TYPE_ISO)
+		dpsize += sizeof(struct efi_device_path_cdrom_path);
+	else
+		dpsize += sizeof(struct efi_device_path_hard_drive_path);
+
+	return dpsize;
+}
+
+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
+	/*
+	 * We *could* make a more accurate path, by looking at if_type
+	 * and handling all the different cases like we do for non-
+	 * legacy (ie CONFIG_BLK=y) case.  But most important thing
+	 * is just to have a unique device-path for if_type+devnum.
+	 * So map things to a fictional USB device:
+	 */
+	struct efi_device_path_usb *udp;
+
+	memcpy(buf, &ROOT, sizeof(ROOT));
+	buf += sizeof(ROOT);
+
+	udp = buf;
+	udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+	udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB;
+	udp->dp.length = sizeof(*udp);
+	udp->parent_port_number = desc->if_type;
+	udp->usb_interface = desc->devnum;
+	buf = &udp[1];
+#endif
+
+	if (part == 0) /* the actual disk, not a partition */
+		return buf;
+
+	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 = dp_alloc(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);
+
+	fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
+	dpsize += fpsize;
+
+	start = buf = dp_alloc(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 = dp_alloc(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;
+}
author
#26 lore raw

PATCH v3 6/21 efi_loader: drop redundant efi_device_path_protocol

In reply to Rob Clark

This is really the same thing as the efi_device_path struct.

---
 include/efi_api.h                        | 12 ++----------
 lib/efi_loader/efi_device_path_to_text.c | 13 ++++++++-----
 2 files changed, 10 insertions(+), 15 deletions(-)
include/efi_api.h +2 -10
index 4e27c82129..ac58fd58de 100644
@@ -487,22 +487,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);
 };
lib/efi_loader/efi_device_path_to_text.c +8 -5
index 4b2f43f0c8..f9d071ac50 100644
@@ -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)
 {
Signature · 1 line
2.13.5
#27 lore raw

PATCH v3 6/21 efi_loader: drop redundant efi_device_path_protocol

Quoted 3 lines

This is really the same thing as the efi_device_path struct.

Thanks, applied to efi-next

Alex

author
#28 lore raw

PATCH v3 7/21 efi_loader: flesh out device-path to text

In reply to Rob Clark

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.

---
 include/efi_api.h                        |   1 +
 include/efi_loader.h                     |   2 +
 lib/efi_loader/efi_device_path_to_text.c | 241 +++++++++++++++++++++++--------
 3 files changed, 181 insertions(+), 63 deletions(-)
include/efi_api.h +1 -0
index ac58fd58de..0c36122107 100644
@@ -304,6 +304,7 @@ struct efi_device_path_vendor {
 
 #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;
include/efi_loader.h +2 -0
index d052b03ab7..f39c2ee6da 100644
@@ -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;
lib/efi_loader/efi_device_path_to_text.c +178 -63

260 lines not on this page · raw message

Signature · 1 line
2.13.5
#29 lore raw

PATCH v3 7/21 efi_loader: flesh out device-path to text

Quoted 7 lines

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.

Thanks, applied to efi-next

Alex

author
#30 lore raw

PATCH v3 8/21 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 | 54 +++++++++++++++++++++++++++--------------------
 1 file changed, 31 insertions(+), 23 deletions(-)
lib/efi_loader/efi_disk.c +31 -23
index ed06485e33..14f3e020c8 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++;
 
 		/*
@@ -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++;
 
 			/*
Signature · 1 line
2.13.5
#31 lore raw

PATCH v3 8/21 efi_loader: use proper device-paths for partitions

Quoted 28 lines

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.

Quoted 5 lines

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.

Thanks, applied to efi-next

Alex

author
#32 lore raw

PATCH v3 9/21 efi_loader: use proper device-paths for net

In reply to Rob Clark
---
 lib/efi_loader/efi_net.c | 19 ++-----------------
 1 file changed, 2 insertions(+), 17 deletions(-)
lib/efi_loader/efi_net.c +2 -17
index 0b949d86e8..aa0618fd3a 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;
@@ -213,16 +210,6 @@ void efi_net_set_dhcp_ack(void *pkt, int len)
 int efi_net_register(void **handle)
 {
 	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;
 
Signature · 1 line
2.13.5
author
#34 lore raw

PATCH v3 10/21 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                 | 201 +++++++++++++-----------------------------
 include/efi_loader.h          |   5 +-
 lib/efi_loader/efi_boottime.c |  36 ++++++++
 lib/efi_loader/efi_net.c      |   5 +-
 4 files changed, 100 insertions(+), 147 deletions(-)
cmd/bootefi.c +59 -142
index 3196d86040..0980088668 100644
@@ -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);
 }
include/efi_loader.h +4 -1
index f39c2ee6da..ec8803f588 100644
@@ -136,7 +136,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);
 
@@ -193,6 +193,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);
+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 +36 -0
index b962b62a97..837e61d8fe 100644
@@ -726,6 +726,42 @@ 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_device_path_to_text;
+
+	info->file_path = file_path;
+	info->device_handle = efi_dp_find_obj(device_path, NULL);
+
+	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,
lib/efi_loader/efi_net.c +1 -4
index aa0618fd3a..91f1e4a69e 100644
@@ -207,7 +207,7 @@ 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;
 
@@ -253,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.5
#35 lore raw

PATCH v3 10/21 efi_loader: refactor boot device and loaded_image handling

Quoted 11 lines

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.

Thanks, applied to efi-next

Alex

author
#36 lore raw

PATCH v3 11/21 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.

---
 include/efi.h                     |   2 +
 include/efi_api.h                 |  65 +++++
 include/efi_loader.h              |  13 +
 lib/efi_loader/Makefile           |   1 +
 lib/efi_loader/efi_disk.c         |  32 +++
 lib/efi_loader/efi_file.c         | 560 ++++++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_image_loader.c |   3 +
 7 files changed, 676 insertions(+)
 create mode 100644 lib/efi_loader/efi_file.c
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 ec8803f588..b0c1e8fb78 100644
@@ -65,6 +65,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;
@@ -140,6 +142,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);
 
@@ -168,6 +173,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 */
lib/efi_loader/Makefile +1 -0
index f35e5ce8a8..cce92cfeb5 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_file.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 +32 -0
index 14f3e020c8..9805585eb1 100644
@@ -31,6 +31,8 @@ struct efi_disk_obj {
 	struct efi_device_path *dp;
 	/* partition # */
 	unsigned part;
+	/* handle to filesys proto (for partition objects) */
+	struct efi_simple_file_system_protocol *volume;
 	/* Offset into disk for simple partitions */
 	lbaint_t offset;
 	/* Internal block device */
@@ -172,6 +174,28 @@ static const struct efi_block_io block_io_disk_template = {
 	.flush_blocks = &efi_disk_flush_blocks,
 };
 
+/*
+ * Find filesystem from a device-path.  The passed in path 'p' probably
+ * contains one or more /File(name) nodes, so the comparison stops at
+ * the first /File() node, and returns the pointer to that via 'rp'.
+ * This is mostly intended to be a helper to map a device-path to an
+ * efi_file_handle object.
+ */
+struct efi_simple_file_system_protocol *
+efi_fs_from_path(struct efi_device_path *fp)
+{
+	struct efi_object *efiobj;
+	struct efi_disk_obj *diskobj;
+
+	efiobj = efi_dp_find_obj(fp, NULL);
+	if (!efiobj)
+		return NULL;
+
+	diskobj = container_of(efiobj, struct efi_disk_obj, parent);
+
+	return diskobj->volume;
+}
+
 static void efi_disk_add_dev(const char *name,
 			     const char *if_typename,
 			     struct blk_desc *desc,
@@ -194,6 +218,14 @@ static void efi_disk_add_dev(const char *name,
 	diskobj->parent.protocols[0].protocol_interface = &diskobj->ops;
 	diskobj->parent.protocols[1].guid = &efi_guid_device_path;
 	diskobj->parent.protocols[1].protocol_interface = diskobj->dp;
+	if (part >= 1) {
+		diskobj->volume = efi_simple_file_system(desc, part,
+							 diskobj->dp);
+		diskobj->parent.protocols[2].guid =
+			&efi_simple_file_system_protocol_guid;
+		diskobj->parent.protocols[2].protocol_interface =
+			diskobj->volume;
+	}
 	diskobj->parent.handle = diskobj;
 	diskobj->ops = block_io_disk_template;
 	diskobj->ifname = if_typename;
lib/efi_loader/efi_file.c new file +560 -0

560 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.5
#37 lore raw

PATCH v3 11/21 efi_loader: add file/filesys support

Quoted 4 lines

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

Thanks, applied to efi-next

Alex

author
#38 lore raw

PATCH v3 12/21 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 | 85 +++++++++++++++++++++++++++++++++++--------
 1 file changed, 70 insertions(+), 15 deletions(-)
lib/efi_loader/efi_boottime.c +70 -15
index 837e61d8fe..ec40f41bcb 100644
@@ -762,6 +762,47 @@ 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 = 0;
+	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,
@@ -769,25 +810,40 @@ 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;
+		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:
+		 */
+		efi_dp_split_file_path(file_path, &dp, &fp);
+
+		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);
@@ -796,7 +852,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.5
#39 lore raw

PATCH v3 12/21 efi_loader: support load_image() from a file-path

Quoted 4 lines

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

Thanks, applied to efi-next

Alex

author
#40 lore raw

PATCH v3 13/21 efi_loader: make pool allocations cacheline aligned

In reply to Rob Clark

This avoids printf() spam about file reads (such as loading an image) into unaligned buffers (and the associated memcpy()). And generally seems like a good idea.

---
 lib/efi_loader/efi_memory.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
lib/efi_loader/efi_memory.c +3 -2
index 9e079f1fa3..1f0b9d0449 100644
@@ -43,7 +43,7 @@ void *efi_bounce_buffer;
  */
 struct efi_pool_allocation {
 	u64 num_pages;
-	char data[];
+	char data[] __attribute__((aligned(ARCH_DMA_MINALIGN)));
 };
 
 /*
@@ -356,7 +356,8 @@ efi_status_t efi_allocate_pool(int pool_type, unsigned long size,
 {
 	efi_status_t r;
 	efi_physical_addr_t t;
-	u64 num_pages = (size + sizeof(u64) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
+	u64 num_pages = (size + sizeof(struct efi_pool_allocation) +
+			 EFI_PAGE_MASK) >> EFI_PAGE_SHIFT;
 
 	if (size == 0) {
 		*buffer = NULL;
Signature · 1 line
2.13.5
#41 lore raw

PATCH v3 13/21 efi_loader: make pool allocations cacheline aligned

Quoted 5 lines

This avoids printf() spam about file reads (such as loading an image) into unaligned buffers (and the associated memcpy()). And generally seems like a good idea.

Thanks, applied to efi-next

Alex

author
#42 lore raw

PATCH v3 14/21 efi_loader: efi variable support

In reply to Rob Clark

Add EFI variable support, mapping to u-boot environment variables. Variables are pretty important for setting up boot order, among other things. If the board supports saveenv, then it will be called in ExitBootServices() to persist variables set by the efi payload. (For example, fallback.efi configuring BootOrder and BootXXXX load-option variables.)

Variables are *not* currently exposed at runtime, post ExitBootServices. On boards without a dedicated device for storage, which the loaded OS is not trying to also use, this is rather tricky. One idea, at least for boards that can persist RAM across reboot, is to keep a "journal" of modified variables in RAM, and then turn halt into a reboot into u-boot, plus store variables, plus halt. Whatever the solution, it likely involves some per-board support.

Mapping between EFI variables and u-boot variables:

efi_$guid_$varname = {attributes}(type)value

For example:

efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported=

     "{ro,boot,run}(blob)0000000000000000"
  efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder=
     "(blob)00010000"

The attributes are a comma separated list of these possible attributes:

+ ro - read-only
+ boot - boot-services access
+ run - runtime access

NOTE: with current implementation, no variables are available after ExitBootServices, and all are persisted (if possible).

If not specified, the attributes default to "{boot}".

The required type is one of:

+ utf8 - raw utf8 string
+ blob - arbitrary length hex string

---
 cmd/bootefi.c                 |   4 +
 include/efi.h                 |  19 +++
 include/efi_loader.h          |  10 ++
 lib/efi_loader/Makefile       |   2 +-
 lib/efi_loader/efi_boottime.c |   6 +
 lib/efi_loader/efi_runtime.c  |  17 ++-
 lib/efi_loader/efi_variable.c | 335 ++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 388 insertions(+), 5 deletions(-)
 create mode 100644 lib/efi_loader/efi_variable.c
cmd/bootefi.c +4 -0
index 0980088668..d3ae33e25b 100644
@@ -181,6 +181,10 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt,
 		goto exit;
 	}
 
+	/* we don't support much: */
+	env_set("efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported",
+		"{ro,boot}(blob)0000000000000000");
+
 	/* Call our payload! */
 	debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry);
 
include/efi.h +19 -0
index ddd2b96417..04e83220b4 100644
@@ -324,6 +324,25 @@ extern char image_base[];
 /* Start and end of U-Boot image (for payload) */
 extern char _binary_u_boot_bin_start[], _binary_u_boot_bin_end[];
 
+/*
+ * Variable Attributes
+ */
+#define EFI_VARIABLE_NON_VOLATILE       0x0000000000000001
+#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
+#define EFI_VARIABLE_RUNTIME_ACCESS     0x0000000000000004
+#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x0000000000000008
+#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x0000000000000010
+#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x0000000000000020
+#define EFI_VARIABLE_APPEND_WRITE	0x0000000000000040
+
+#define EFI_VARIABLE_MASK	(EFI_VARIABLE_NON_VOLATILE | \
+				EFI_VARIABLE_BOOTSERVICE_ACCESS | \
+				EFI_VARIABLE_RUNTIME_ACCESS | \
+				EFI_VARIABLE_HARDWARE_ERROR_RECORD | \
+				EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \
+				EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | \
+				EFI_VARIABLE_APPEND_WRITE)
+
 /**
  * efi_get_sys_table() - Get access to the main EFI system table
  *
include/efi_loader.h +10 -0
index b0c1e8fb78..9eee62dc9c 100644
@@ -277,6 +277,16 @@ efi_status_t __efi_runtime EFIAPI efi_get_time(
 			struct efi_time_cap *capabilities);
 void efi_get_time_init(void);
 
+efi_status_t EFIAPI efi_get_variable(s16 *variable_name,
+		efi_guid_t *vendor, u32 *attributes,
+		unsigned long *data_size, void *data);
+efi_status_t EFIAPI efi_get_next_variable(
+		unsigned long *variable_name_size,
+		s16 *variable_name, efi_guid_t *vendor);
+efi_status_t EFIAPI efi_set_variable(s16 *variable_name,
+		efi_guid_t *vendor, u32 attributes,
+		unsigned long data_size, void *data);
+
 #else /* defined(EFI_LOADER) && !defined(CONFIG_SPL_BUILD) */
 
 /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
lib/efi_loader/Makefile +1 -1
index cce92cfeb5..f58cb13337 100644
@@ -16,7 +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_file.o
+obj-y += efi_file.o 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_boottime.c +6 -0
index ec40f41bcb..c406ff82ff 100644
@@ -8,6 +8,7 @@
 
 #include <common.h>
 #include <efi_loader.h>
+#include <environment.h>
 #include <malloc.h>
 #include <asm/global_data.h>
 #include <libfdt_env.h>
@@ -942,6 +943,11 @@ static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
 {
 	EFI_ENTRY("%p, %ld", image_handle, map_key);
 
+#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE)
+	/* save any EFI variables that have been written: */
+	env_save();
+#endif
+
 	board_quiesce_devices();
 
 	/* Fix up caches for EFI payloads if necessary */
lib/efi_loader/efi_runtime.c +13 -4
index ad7f3754bd..2f95c766ac 100644
@@ -184,7 +184,16 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
 		/* Clean up system table */
 		.ptr = &systab.boottime,
 		.patchto = NULL,
-	},
+	}, {
+		.ptr = &efi_runtime_services.get_variable,
+		.patchto = &efi_device_error,
+	}, {
+		.ptr = &efi_runtime_services.get_next_variable,
+		.patchto = &efi_device_error,
+	}, {
+		.ptr = &efi_runtime_services.set_variable,
+		.patchto = &efi_device_error,
+	}
 };
 
 static bool efi_runtime_tobedetached(void *p)
@@ -382,9 +391,9 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
 	.set_wakeup_time = (void *)&efi_unimplemented,
 	.set_virtual_address_map = &efi_set_virtual_address_map,
 	.convert_pointer = (void *)&efi_invalid_parameter,
-	.get_variable = (void *)&efi_device_error,
-	.get_next_variable = (void *)&efi_device_error,
-	.set_variable = (void *)&efi_device_error,
+	.get_variable = efi_get_variable,
+	.get_next_variable = efi_get_next_variable,
+	.set_variable = efi_set_variable,
 	.get_next_high_mono_count = (void *)&efi_device_error,
 	.reset_system = &efi_reset_system_boottime,
 };
lib/efi_loader/efi_variable.c new file +335 -0

335 lines not on this page · raw message

Signature · 1 line
2.13.5
#43 lore raw

PATCH v3 14/21 efi_loader: efi variable support

Quoted 36 lines

Add EFI variable support, mapping to u-boot environment variables. Variables are pretty important for setting up boot order, among other things. If the board supports saveenv, then it will be called in ExitBootServices() to persist variables set by the efi payload. (For example, fallback.efi configuring BootOrder and BootXXXX load-option variables.)

Variables are *not* currently exposed at runtime, post ExitBootServices. On boards without a dedicated device for storage, which the loaded OS is not trying to also use, this is rather tricky. One idea, at least for boards that can persist RAM across reboot, is to keep a "journal" of modified variables in RAM, and then turn halt into a reboot into u-boot, plus store variables, plus halt. Whatever the solution, it likely involves some per-board support.

Mapping between EFI variables and u-boot variables:

efi_$guid_$varname = {attributes}(type)value

For example:

efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported=

     "{ro,boot,run}(blob)0000000000000000"
  efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder=
     "(blob)00010000"

The attributes are a comma separated list of these possible attributes:

+ ro - read-only
+ boot - boot-services access
+ run - runtime access

NOTE: with current implementation, no variables are available after ExitBootServices, and all are persisted (if possible).

Quoted 8 lines

If not specified, the attributes default to "{boot}".

The required type is one of:

+ utf8 - raw utf8 string
+ blob - arbitrary length hex string

Thanks, applied to efi-next

Alex

#44 lore raw

[U-Boot] Unexpected saving of all environment variables during EFI boot

In reply to Rob Clark

This was merged as
ad644e7c18238026fecc647f62584d87d2c5b0a3
efi_loader: efi variable support

Quoted 13 lines
lib/efi_loader/efi_boottime.c +1 -0
index ec40f41bcb..c406ff82ff 100644
@@ -8,6 +8,7 @@
 
 #include <common.h>
 #include <efi_loader.h>
+#include <environment.h>
 #include <malloc.h>
 #include <asm/global_data.h>
 #include <libfdt_env.h>

lib/efi_loader/efi_boottime.c static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle, · line 948

Quoted 6 lines
lib/efi_loader/efi_boottime.c +3 -0
@@ -942,6 +943,11 @@ static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
 {
 	EFI_ENTRY("%p, %ld", image_handle, map_key);
 
+#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE)
+	/* save any EFI variables that have been written: */
+	env_save();

You save all changed variables and not specifically those bound to EFI. This is completely unexpected behavior for the user and not covered by the commit message.

Furthermore you call env_save even if no EFI variable has been touched at all.

This leads to writing to flash on every boot via EFI. Depending on technology this might ruin the flash after a few hundred reboots.

Please, revert this part of the change.

Best regards

Heinrich Schuchardt

#45 lore raw

[U-Boot] Unexpected saving of all environment variables during EFI boot

Quoted 28 lines · Heinrich Schuchardt

This was merged as
ad644e7c18238026fecc647f62584d87d2c5b0a3
efi_loader: efi variable support

Quoted 13 lines
lib/efi_loader/efi_boottime.c +1 -0
index ec40f41bcb..c406ff82ff 100644
@@ -8,6 +8,7 @@
 
 #include <common.h>
 #include <efi_loader.h>
+#include <environment.h>
 #include <malloc.h>
 #include <asm/global_data.h>
 #include <libfdt_env.h>

lib/efi_loader/efi_boottime.c static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle, · line 948

Quoted 6 lines
lib/efi_loader/efi_boottime.c +3 -0
@@ -942,6 +943,11 @@ static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
 {
 	EFI_ENTRY("%p, %ld", image_handle, map_key);
 
+#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE)
+	/* save any EFI variables that have been written: */
+	env_save();

You save all changed variables and not specifically those bound to EFI. This is completely unexpected behavior for the user and not covered by the commit message.

Quoted 5 lines

Furthermore you call env_save even if no EFI variable has been touched at all.

This leads to writing to flash on every boot via EFI. Depending on technology this might ruin the flash after a few hundred reboots.

I agree that overwriting flash on every boot isn't a terribly smart idea.

Also saving random environment variables that are set while we are in a distro loop to persistent storage isn't great either.

I agree that we should probably improve this code to only save efi variables.

Rob, how bad would it be for Fedora if we remove the persistent variable store call (just the env_save() line) for 2017.11 to not kill people's storage devices? Then for the next version tackle it for real and only save efi variable changes here and only if anything really did change.

Alex

author
#46 lore raw

[U-Boot] Unexpected saving of all environment variables during EFI boot

Quoted 42 lines · Alexander Graf
Quoted 28 lines · Heinrich Schuchardt

This was merged as
ad644e7c18238026fecc647f62584d87d2c5b0a3
efi_loader: efi variable support

Quoted 13 lines
lib/efi_loader/efi_boottime.c +1 -0
index ec40f41bcb..c406ff82ff 100644
@@ -8,6 +8,7 @@
 
 #include <common.h>
 #include <efi_loader.h>
+#include <environment.h>
 #include <malloc.h>
 #include <asm/global_data.h>
 #include <libfdt_env.h>

lib/efi_loader/efi_boottime.c static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle, · line 948

Quoted 6 lines
lib/efi_loader/efi_boottime.c +3 -0
@@ -942,6 +943,11 @@ static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
 {
     EFI_ENTRY("%p, %ld", image_handle, map_key);
 
+#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE)
+    /* save any EFI variables that have been written: */
+    env_save();

You save all changed variables and not specifically those bound to EFI. This is completely unexpected behavior for the user and not covered by the commit message.

Quoted 5 lines

Furthermore you call env_save even if no EFI variable has been touched at all.

This leads to writing to flash on every boot via EFI. Depending on technology this might ruin the flash after a few hundred reboots.

I agree that overwriting flash on every boot isn't a terribly smart idea.

Also saving random environment variables that are set while we are in a distro loop to persistent storage isn't great either.

Quoted 8 lines

I agree that we should probably improve this code to only save efi variables.

Rob, how bad would it be for Fedora if we remove the persistent variable store call (just the env_save() line) for 2017.11 to not kill people's storage devices? Then for the next version tackle it for real and only save efi variable changes here and only if anything really did change.

As I mentioned on #u-boot, I think we can just comment the env_save().. or perhaps make it a config option. It will be mildly annoying, since it means every boot is "first boot", ie. falling back to fallback.efi to populate BootOrder/BootNNNN variables, and never using bootmgr. But since we can't set EFI variables from userspace yet, it isn't really a regression (yet).

Longer term, maybe we need to split out EFI variables from u-boot env vars.. I thought it would be nice to store them together, since it gives a convenient way to set EFI variables from script or cmdline. But at least a subset of the EFI vars should be persisted to reach EBBR, and if that breaks expectations about u-boot env vars, then I guess we need to split them.

Side note, devices that can't repeatedly save env (even if we split out the EFI variables to be stored separately) might be problematic with EFI_LOADER. Maybe we need a build option they can set to have a more crippled version of EFI_LOADER, so as to not penalize more capable devices.

BR,
-R

#47 lore raw

[U-Boot] Unexpected saving of all environment variables during EFI boot

Quoted 52 lines · Rob Clark
Quoted 42 lines · Alexander Graf
Quoted 28 lines · Heinrich Schuchardt

This was merged as
ad644e7c18238026fecc647f62584d87d2c5b0a3
efi_loader: efi variable support

Quoted 13 lines
lib/efi_loader/efi_boottime.c +1 -0
index ec40f41bcb..c406ff82ff 100644
@@ -8,6 +8,7 @@
 
 #include <common.h>
 #include <efi_loader.h>
+#include <environment.h>
 #include <malloc.h>
 #include <asm/global_data.h>
 #include <libfdt_env.h>

lib/efi_loader/efi_boottime.c static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle, · line 948

Quoted 6 lines
lib/efi_loader/efi_boottime.c +3 -0
@@ -942,6 +943,11 @@ static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
 {
     EFI_ENTRY("%p, %ld", image_handle, map_key);
 
+#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE)
+    /* save any EFI variables that have been written: */
+    env_save();

You save all changed variables and not specifically those bound to EFI. This is completely unexpected behavior for the user and not covered by the commit message.

Quoted 5 lines

Furthermore you call env_save even if no EFI variable has been touched at all.

This leads to writing to flash on every boot via EFI. Depending on technology this might ruin the flash after a few hundred reboots.

I agree that overwriting flash on every boot isn't a terribly smart idea.

Also saving random environment variables that are set while we are in a distro loop to persistent storage isn't great either.

Quoted 8 lines

I agree that we should probably improve this code to only save efi variables.

Rob, how bad would it be for Fedora if we remove the persistent variable store call (just the env_save() line) for 2017.11 to not kill people's storage devices? Then for the next version tackle it for real and only save efi variable changes here and only if anything really did change.

Quoted 6 lines

As I mentioned on #u-boot, I think we can just comment the env_save().. or perhaps make it a config option. It will be mildly annoying, since it means every boot is "first boot", ie. falling back to fallback.efi to populate BootOrder/BootNNNN variables, and never using bootmgr. But since we can't set EFI variables from userspace yet, it isn't really a regression (yet).

Ok, I sent a patch to remove the env_save() line.

Quoted 6 lines

Longer term, maybe we need to split out EFI variables from u-boot env vars.. I thought it would be nice to store them together, since it gives a convenient way to set EFI variables from script or cmdline. But at least a subset of the EFI vars should be persisted to reach EBBR, and if that breaks expectations about u-boot env vars, then I guess we need to split them.

I really like the sharing of the env space. We just need to have a different write mechanism for EFI variables: Instead of writing the current env, we could for example reread the original env, copy all current efi variables into that and write it back?

Quoted 6 lines

Side note, devices that can't repeatedly save env (even if we split out the EFI variables to be stored separately) might be problematic with EFI_LOADER. Maybe we need a build option they can set to have a more crippled version of EFI_LOADER, so as to not penalize more capable devices.

Every flash device self-destroys :).

Really, it boils down to the amount of saves. Writing to flash on every boot is a pretty bad idea. If we simply only write when a variable write actually occurred (which in most cases won't), then we should be ok on all devices I think.

Alex

#48 lore raw

[U-Boot] Unexpected saving of all environment variables during EFI boot

Quoted 55 lines · Alexander Graf
Quoted 52 lines · Rob Clark
Quoted 50 lines · Alexander Graf
Quoted 28 lines · Heinrich Schuchardt

This was merged as
ad644e7c18238026fecc647f62584d87d2c5b0a3
efi_loader: efi variable support

Quoted 13 lines
lib/efi_loader/efi_boottime.c +1 -0
index ec40f41bcb..c406ff82ff 100644
@@ -8,6 +8,7 @@
 
 #include <common.h>
 #include <efi_loader.h>
+#include <environment.h>
 #include <malloc.h>
 #include <asm/global_data.h>
 #include <libfdt_env.h>

lib/efi_loader/efi_boottime.c static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle, · line 948

Quoted 6 lines
lib/efi_loader/efi_boottime.c +3 -0
@@ -942,6 +943,11 @@ static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
 {
     EFI_ENTRY("%p, %ld", image_handle, map_key);
 
+#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE)
+    /* save any EFI variables that have been written: */
+    env_save();

You save all changed variables and not specifically those bound to EFI. This is completely unexpected behavior for the user and not covered by the commit message.

Quoted 5 lines

Furthermore you call env_save even if no EFI variable has been touched at all.

This leads to writing to flash on every boot via EFI. Depending on technology this might ruin the flash after a few hundred reboots.

I agree that overwriting flash on every boot isn't a terribly smart idea.

Also saving random environment variables that are set while we are in a distro loop to persistent storage isn't great either.

I agree that we should probably improve this code to only save efi variables.

Rob, how bad would it be for Fedora if we remove the persistent variable store call (just the env_save() line) for 2017.11 to not kill people's storage devices? Then for the next version tackle it for real and only save efi variable changes here and only if anything really did change.

Quoted 8 lines
Quoted 6 lines

As I mentioned on #u-boot, I think we can just comment the env_save().. or perhaps make it a config option. It will be mildly annoying, since it means every boot is "first boot", ie. falling back to fallback.efi to populate BootOrder/BootNNNN variables, and never using bootmgr. But since we can't set EFI variables from userspace yet, it isn't really a regression (yet).

Ok, I sent a patch to remove the env_save() line.

Seems we worked in parallel :)

Quoted 8 lines
Quoted 7 lines

Longer term, maybe we need to split out EFI variables from u-boot env vars.. I thought it would be nice to store them together, since it gives a convenient way to set EFI variables from script or cmdline. But at least a subset of the EFI vars should be persisted to reach EBBR, and if that breaks expectations about u-boot env vars, then I guess we need to split them.

Quoted 4 lines

I really like the sharing of the env space. We just need to have a different write mechanism for EFI variables: Instead of writing the current env, we could for example reread the original env, copy all current efi variables into that and write it back?

That is why I said we have to add read and write methods in struct env_driver.

Quoted 9 lines
Quoted 7 lines

Side note, devices that can't repeatedly save env (even if we split out the EFI variables to be stored separately) might be problematic with EFI_LOADER. Maybe we need a build option they can set to have a more crippled version of EFI_LOADER, so as to not penalize more capable devices.

Every flash device self-destroys :).

There are also env implementations in U-Boot writing to disk:
- env/ext4.c
- env/fat.c

Quoted 5 lines

Really, it boils down to the amount of saves. Writing to flash on every boot is a pretty bad idea. If we simply only write when a variable write actually occurred (which in most cases won't), then we should be ok on all devices I think.

According to the UEFI standard we should save the monotonic counter on every boot.

Regards

Heinrich

#49 lore raw

PATCH 1/1 efi_loader: disable saving of EFI variables

In reply to Rob Clark

The current implementation of saving of EFI variables has unwanted side effects:

- Writing to flash on every boot may harm the memory.
- All variables are saved. Not only the EFI ones.
- Variables are saved even if there is not change.

So let us disable saving for now until we have a complete solution.

This will also mean every boot is "first boot", ie. falling back to fallback.efi to populate BootOrder/BootNNNN variables, and never using bootmgr.

Fixes ad644e7c1823 efi_loader: efi variable support
---
 lib/efi_loader/efi_boottime.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
lib/efi_loader/efi_boottime.c +11 -2
index f627340de4..a734d84d38 100644
@@ -1440,8 +1440,17 @@ static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
 	efi_tpl = TPL_HIGH_LEVEL;
 
 #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE)
-	/* save any EFI variables that have been written: */
-	env_save();
+	/*
+	 * save any EFI variables that have been written:
+	 *
+	 * This feature is disabled due to the following deficiencies:
+	 * Writing to flash on every boot may ruin the memory.
+	 * We should not save non-EFI variables here.
+	 * We should only save if an EFI variable has actually been
+	 * changed.
+	 *
+	 * env_save();
+	 */
 #endif
 
 	board_quiesce_devices();
Signature · 1 line
2.14.2
#50 lore raw

PATCH 1/1 efi_loader: disable saving of EFI variables

Quoted 14 lines · Heinrich Schuchardt

The current implementation of saving of EFI variables has unwanted side effects:

- Writing to flash on every boot may harm the memory.
- All variables are saved. Not only the EFI ones.
- Variables are saved even if there is not change.

So let us disable saving for now until we have a complete solution.

This will also mean every boot is "first boot", ie. falling back to fallback.efi to populate BootOrder/BootNNNN variables, and never using bootmgr.

Quoted 5 lines
Fixes ad644e7c1823 efi_loader: efi variable support
---
 lib/efi_loader/efi_boottime.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
author
#51 lore raw

PATCH v3 15/21 efi_loader: add bootmgr

In reply to Rob Clark

Similar to a "real" UEFI implementation, the bootmgr looks at the BootOrder and BootXXXX variables to try to find an EFI payload to load and boot. This is added as a sub-command of bootefi.

The idea is that the distro bootcmd would first try loading a payload via the bootmgr, and then if that fails (ie. first boot or corrupted EFI variables) it would fallback to loading bootaa64.efi. (Which would then load fallback.efi which would look for \EFI\*\boot.csv and populate BootOrder and BootXXXX based on what it found.)

---
 cmd/bootefi.c                     |  48 +++++++++-
 include/config_distro_bootcmd.h   |   5 ++
 include/efi_api.h                 |   4 +
 include/efi_loader.h              |   6 ++
 lib/efi_loader/Makefile           |   2 +-
 lib/efi_loader/efi_bootmgr.c      | 180 ++++++++++++++++++++++++++++++++++++++
 lib/efi_loader/efi_boottime.c     |   6 +-
 lib/efi_loader/efi_image_loader.c |   1 +
 8 files changed, 246 insertions(+), 6 deletions(-)
 create mode 100644 lib/efi_loader/efi_bootmgr.c
cmd/bootefi.c +46 -2
index d3ae33e25b..fcd8dbafc1 100644
@@ -219,6 +219,36 @@ exit:
 	return ret;
 }
 
+static int do_bootefi_bootmgr_exec(unsigned long fdt_addr)
+{
+	struct efi_device_path *device_path, *file_path;
+	void *addr;
+	efi_status_t r;
+
+	/* Initialize and populate EFI object list */
+	if (!efi_obj_list_initalized)
+		efi_init_obj_list();
+
+	/*
+	 * 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
+	 */
+	efi_save_gd();
+
+	addr = efi_bootmgr_load(&device_path, &file_path);
+	if (!addr)
+		return 1;
+
+	printf("## Starting EFI application at %p ...\n", addr);
+	r = do_bootefi_exec(addr, (void *)fdt_addr, device_path, file_path);
+	printf("## Application terminated, r = %lu\n",
+	       r & ~EFI_ERROR_MASK);
+
+	if (r != EFI_SUCCESS)
+		return 1;
+
+	return 0;
+}
 
 /* Interpreter command to boot an arbitrary EFI image from memory */
 static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
@@ -237,7 +267,14 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 		memcpy((char *)addr, __efi_hello_world_begin, size);
 	} else
 #endif
-	{
+	if (!strcmp(argv[1], "bootmgr")) {
+		unsigned long fdt_addr = 0;
+
+		if (argc > 2)
+			fdt_addr = simple_strtoul(argv[2], NULL, 16);
+
+		return do_bootefi_bootmgr_exec(fdt_addr);
+	} else {
 		saddr = argv[1];
 
 		addr = simple_strtoul(saddr, NULL, 16);
@@ -270,7 +307,11 @@ static char bootefi_help_text[] =
 	"hello\n"
 	"  - boot a sample Hello World application stored within U-Boot"
 #endif
-	;
+	"bootmgr [fdt addr]\n"
+	"  - load and boot EFI payload based on BootOrder/BootXXXX variables.\n"
+	"\n"
+	"    If specified, the device tree located at <fdt address> gets\n"
+	"    exposed as EFI configuration table.\n";
 #endif
 
 U_BOOT_CMD(
@@ -308,6 +349,9 @@ void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
 #endif
 	}
 
+	if (!path)
+		return;
+
 	if (strcmp(dev, "Net")) {
 		/* Add leading / to fs paths, because they're absolute */
 		snprintf(filename, sizeof(filename), "/%s", path);
include/config_distro_bootcmd.h +5 -0
index 9ed6b9892c..e0d0034ed3 100644
@@ -112,6 +112,11 @@
 
 #define BOOTENV_SHARED_EFI                                                \
 	"boot_efi_binary="                                                \
+		"if fdt addr ${fdt_addr_r}; then "                        \
+			"bootefi bootmgr ${fdt_addr_r};"                  \
+		"else "                                                   \
+			"bootefi bootmgr ${fdtcontroladdr};"              \
+		"fi;"                                                     \
 		"load ${devtype} ${devnum}:${distro_bootpart} "           \
 			"${kernel_addr_r} efi/boot/"BOOTEFI_NAME"; "      \
 		"if fdt addr ${fdt_addr_r}; then "                        \
include/efi_api.h +4 -0
index 1aae96355f..d0aefa8221 100644
@@ -211,6 +211,10 @@ struct efi_runtime_services {
 	EFI_GUID(0x00000000, 0x0000, 0x0000, 0x00, 0x00, \
 		 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
 
+#define EFI_GLOBAL_VARIABLE_GUID \
+	EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, \
+		 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
+
 #define LOADED_IMAGE_PROTOCOL_GUID \
 	EFI_GUID(0x5b1b31a1, 0x9562, 0x11d2, 0x8e, 0x3f, \
 		 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
include/efi_loader.h +6 -0
index 9eee62dc9c..adc0bcf609 100644
@@ -61,6 +61,7 @@ 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_global_variable_guid;
 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;
@@ -209,6 +210,8 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table
 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);
+efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
+				      void **buffer);
 
 #ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
 extern void *efi_bounce_buffer;
@@ -287,6 +290,9 @@ efi_status_t EFIAPI efi_set_variable(s16 *variable_name,
 		efi_guid_t *vendor, u32 attributes,
 		unsigned long data_size, void *data);
 
+void *efi_bootmgr_load(struct efi_device_path **device_path,
+		       struct efi_device_path **file_path);
+
 #else /* defined(EFI_LOADER) && !defined(CONFIG_SPL_BUILD) */
 
 /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
lib/efi_loader/Makefile +1 -1
index f58cb13337..930c0e218e 100644
@@ -16,7 +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_file.o efi_variable.o
+obj-y += efi_file.o efi_variable.o efi_bootmgr.o
 obj-$(CONFIG_LCD) += efi_gop.o
 obj-$(CONFIG_DM_VIDEO) += efi_gop.o
 obj-$(CONFIG_PARTITIONS) += efi_disk.o
lib/efi_loader/efi_bootmgr.c new file +180 -0

180 lines not on this page · raw message

lib/efi_loader/efi_boottime.c +3 -3
index c406ff82ff..cea242cd49 100644
@@ -763,8 +763,8 @@ 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)
+efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
+				      void **buffer)
 {
 	struct efi_file_info *info = NULL;
 	struct efi_file_handle *f;
@@ -824,7 +824,7 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy,
 		struct efi_device_path *dp, *fp;
 		efi_status_t ret;
 
-		ret = load_image_from_path(file_path, &source_buffer);
+		ret = efi_load_image_from_path(file_path, &source_buffer);
 		if (ret != EFI_SUCCESS) {
 			free(info);
 			free(obj);
lib/efi_loader/efi_image_loader.c +1 -0
index 469acae082..242e6a504b 100644
@@ -15,6 +15,7 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
+const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID;
 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 =
Signature · 1 line
2.13.5
#52 lore raw

PATCH v3 15/21 efi_loader: add bootmgr

Quoted 11 lines · Rob Clark

Similar to a "real" UEFI implementation, the bootmgr looks at the BootOrder and BootXXXX variables to try to find an EFI payload to load and boot. This is added as a sub-command of bootefi.

The idea is that the distro bootcmd would first try loading a payload via the bootmgr, and then if that fails (ie. first boot or corrupted EFI variables) it would fallback to loading bootaa64.efi. (Which would then load fallback.efi which would look for \EFI\*\boot.csv and populate BootOrder and BootXXXX based on what it found.)

Would it make sense to convert the bootmgr into a genuine EFI application now that we have Heinrich's test framework available?

Alex

author
#53 lore raw

PATCH v3 15/21 efi_loader: add bootmgr

Quoted 14 lines · Alexander Graf
Quoted 11 lines · Rob Clark

Similar to a "real" UEFI implementation, the bootmgr looks at the BootOrder and BootXXXX variables to try to find an EFI payload to load and boot. This is added as a sub-command of bootefi.

The idea is that the distro bootcmd would first try loading a payload via the bootmgr, and then if that fails (ie. first boot or corrupted EFI variables) it would fallback to loading bootaa64.efi. (Which would then load fallback.efi which would look for \EFI\*\boot.csv and populate BootOrder and BootXXXX based on what it found.)

Quoted 6 lines
Quoted 1 line

Would it make sense to convert the bootmgr into a genuine EFI application now that we have Heinrich's test framework available?

I had considered that, but then decided it was nice to be able to use printf()/malloc()/etc.. plus easier to gdb/debug..

Maybe at some point it would be worth trying to fixup edk2 build so some things like this and HII/unicode protocols and maybe a few other things could be built as standalone .efi drivers and loaded by u-boot. (Might make sense by the time someone wants a full blown HII "bios setup menu" ;-))

BR,
-R

#54 lore raw

PATCH v3 15/21 efi_loader: add bootmgr

Hi,

Quoted 22 lines · Rob Clark
Quoted 14 lines · Alexander Graf
Quoted 11 lines · Rob Clark

Similar to a "real" UEFI implementation, the bootmgr looks at the BootOrder and BootXXXX variables to try to find an EFI payload to load and boot. This is added as a sub-command of bootefi.

The idea is that the distro bootcmd would first try loading a payload via the bootmgr, and then if that fails (ie. first boot or corrupted EFI variables) it would fallback to loading bootaa64.efi. (Which would then load fallback.efi which would look for \EFI\*\boot.csv and populate BootOrder and BootXXXX based on what it found.)

Quoted 6 lines
Quoted 1 line

Would it make sense to convert the bootmgr into a genuine EFI application now that we have Heinrich's test framework available?

Quoted 8 lines

I had considered that, but then decided it was nice to be able to use printf()/malloc()/etc.. plus easier to gdb/debug..

Maybe at some point it would be worth trying to fixup edk2 build so some things like this and HII/unicode protocols and maybe a few other things could be built as standalone .efi drivers and loaded by u-boot. (Might make sense by the time someone wants a full blown HII "bios setup menu" ;-))

Another advantage of the current approach used by this series is that we can test it with sandbox. With a separate EFI application we would lose that ability.

Regards,
Simon

#55 lore raw

PATCH v3 15/21 efi_loader: add bootmgr

Quoted 38 lines

Am 21.09.2017 um 06:58 schrieb Simon Glass <[email protected]>:

Hi,

Quoted 23 lines · Rob Clark
Quoted 15 lines · Alexander Graf
Quoted 14 lines · Rob Clark

Similar to a "real" UEFI implementation, the bootmgr looks at the BootOrder and BootXXXX variables to try to find an EFI payload to load and boot. This is added as a sub-command of bootefi.

The idea is that the distro bootcmd would first try loading a payload via the bootmgr, and then if that fails (ie. first boot or corrupted EFI variables) it would fallback to loading bootaa64.efi. (Which would then load fallback.efi which would look for \EFI\*\boot.csv and populate BootOrder and BootXXXX based on what it found.)

Quoted 6 lines
Quoted 1 line

Would it make sense to convert the bootmgr into a genuine EFI application now that we have Heinrich's test framework available?

Quoted 8 lines

I had considered that, but then decided it was nice to be able to use printf()/malloc()/etc.. plus easier to gdb/debug..

Maybe at some point it would be worth trying to fixup edk2 build so some things like this and HII/unicode protocols and maybe a few other things could be built as standalone .efi drivers and loaded by u-boot. (Might make sense by the time someone wants a full blown HII "bios setup menu" ;-))

Quoted 3 lines

Another advantage of the current approach used by this series is that we can test it with sandbox. With a separate EFI application we would lose that ability.

By separate I meant something similar to Heinrich's self-test that is compiled in, but runs in uefi context rather than u-boot context.

I agree that it's not terribly important though. I was just wondering if there was any benefit to it, as it mostly seems to consist of EFI_CALL() calls ;).

Alex

author
#56 lore raw

PATCH v3 15/21 efi_loader: add bootmgr

In reply to Simon Glass
Quoted 34 lines · Simon Glass

Hi,

Quoted 22 lines · Rob Clark
Quoted 14 lines · Alexander Graf
Quoted 11 lines · Rob Clark

Similar to a "real" UEFI implementation, the bootmgr looks at the BootOrder and BootXXXX variables to try to find an EFI payload to load and boot. This is added as a sub-command of bootefi.

The idea is that the distro bootcmd would first try loading a payload via the bootmgr, and then if that fails (ie. first boot or corrupted EFI variables) it would fallback to loading bootaa64.efi. (Which would then load fallback.efi which would look for \EFI\*\boot.csv and populate BootOrder and BootXXXX based on what it found.)

Quoted 6 lines
Quoted 1 line

Would it make sense to convert the bootmgr into a genuine EFI application now that we have Heinrich's test framework available?

Quoted 8 lines

I had considered that, but then decided it was nice to be able to use printf()/malloc()/etc.. plus easier to gdb/debug..

Maybe at some point it would be worth trying to fixup edk2 build so some things like this and HII/unicode protocols and maybe a few other things could be built as standalone .efi drivers and loaded by u-boot. (Might make sense by the time someone wants a full blown HII "bios setup menu" ;-))

Quoted 4 lines

Another advantage of the current approach used by this series is that we can test it with sandbox. With a separate EFI application we would lose that ability.

jfwiw, I do actually have Shell.efi very nearly loading in sandbox.. it crashes part-way through startup (shortly after reading "ShellOpt" variable) and I haven't had a chance to debug further.. but I suspect in some form or another having VA != "PA" in sandbox is biting us.

Anyways, getting a bit off topic, but separate .efi in sandbox doesn't seem like it should be completely out of the question for sandbox.. and it would make sandbox *way* more useful for EFI testing, since this is really half of the point of efi..

BR,
-R

#57 lore raw

PATCH v3 15/21 efi_loader: add bootmgr

Hi Rob,

Quoted 45 lines · Rob Clark
Quoted 34 lines · Simon Glass

Hi,

Quoted 22 lines · Rob Clark
Quoted 14 lines · Alexander Graf
Quoted 11 lines · Rob Clark

Similar to a "real" UEFI implementation, the bootmgr looks at the BootOrder and BootXXXX variables to try to find an EFI payload to load and boot. This is added as a sub-command of bootefi.

The idea is that the distro bootcmd would first try loading a payload via the bootmgr, and then if that fails (ie. first boot or corrupted EFI variables) it would fallback to loading bootaa64.efi. (Which would then load fallback.efi which would look for \EFI\*\boot.csv and populate BootOrder and BootXXXX based on what it found.)

Quoted 6 lines
Quoted 1 line

Would it make sense to convert the bootmgr into a genuine EFI application now that we have Heinrich's test framework available?

Quoted 8 lines

I had considered that, but then decided it was nice to be able to use printf()/malloc()/etc.. plus easier to gdb/debug..

Maybe at some point it would be worth trying to fixup edk2 build so some things like this and HII/unicode protocols and maybe a few other things could be built as standalone .efi drivers and loaded by u-boot. (Might make sense by the time someone wants a full blown HII "bios setup menu" ;-))

Quoted 4 lines

Another advantage of the current approach used by this series is that we can test it with sandbox. With a separate EFI application we would lose that ability.

jfwiw, I do actually have Shell.efi very nearly loading in sandbox.. it crashes part-way through startup (shortly after reading "ShellOpt" variable) and I haven't had a chance to debug further.. but I suspect in some form or another having VA != "PA" in sandbox is biting us.

Quoted 4 lines

Anyways, getting a bit off topic, but separate .efi in sandbox doesn't seem like it should be completely out of the question for sandbox.. and it would make sandbox *way* more useful for EFI testing, since this is really half of the point of efi..

Sounds very promising! The goal for sandbox is to test all U-Boot code where it is practical to do so.

Regards,
Simon

#58 lore raw

PATCH v3 15/21 efi_loader: add bootmgr

In reply to Rob Clark
Quoted 11 lines

Similar to a "real" UEFI implementation, the bootmgr looks at the BootOrder and BootXXXX variables to try to find an EFI payload to load and boot. This is added as a sub-command of bootefi.

The idea is that the distro bootcmd would first try loading a payload via the bootmgr, and then if that fails (ie. first boot or corrupted EFI variables) it would fallback to loading bootaa64.efi. (Which would then load fallback.efi which would look for \EFI\*\boot.csv and populate BootOrder and BootXXXX based on what it found.)

Thanks, applied to efi-next

Alex

author
#59 lore raw

PATCH v3 16/21 efi_loader: file_path should be variable length

In reply to Rob Clark
---
 include/efi_api.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
include/efi_api.h +1 -1
index d0aefa8221..604c5b7ec4 100644
@@ -373,7 +373,7 @@ struct efi_device_path_cdrom_path {
 
 struct efi_device_path_file_path {
 	struct efi_device_path dp;
-	u16 str[32];
+	u16 str[];
 } __packed;
 
 #define BLOCK_IO_GUID \
Signature · 1 line
2.13.5
author
#61 lore raw

PATCH v3 17/21 efi_loader: set loaded image code/data type properly

In reply to Rob Clark

These should be set according to the image type. Shell.efi and SCT.efi use these fields to determine what sort of image they are loading.

---
 include/pe.h                      |  6 ++++++
 lib/efi_loader/efi_image_loader.c | 22 ++++++++++++++++++++++
 2 files changed, 28 insertions(+)
include/pe.h +6 -0
index deb35a0ea4..4ef3e92efa 100644
@@ -62,6 +62,12 @@ typedef struct _IMAGE_DATA_DIRECTORY {
 
 #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
 
+/* PE32+ Subsystem type for EFI images */
+#define IMAGE_SUBSYSTEM_EFI_APPLICATION         10
+#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11
+#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER      12
+#define IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER      13
+
 typedef struct _IMAGE_OPTIONAL_HEADER64 {
 	uint16_t Magic; /* 0x20b */
 	uint8_t  MajorLinkerVersion;
lib/efi_loader/efi_image_loader.c +22 -0
index 242e6a504b..af29cc4f04 100644
@@ -94,6 +94,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
 	unsigned long virt_size = 0;
 	bool can_run_nt64 = true;
 	bool can_run_nt32 = true;
+	uint16_t image_type;
 
 #if defined(CONFIG_ARM64)
 	can_run_nt32 = false;
@@ -139,6 +140,7 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
 		entry = efi_reloc + opt->AddressOfEntryPoint;
 		rel_size = opt->DataDirectory[rel_idx].Size;
 		rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
+		image_type = opt->Subsystem;
 	} else if (can_run_nt32 &&
 		   (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) {
 		IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
@@ -152,12 +154,32 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info)
 		entry = efi_reloc + opt->AddressOfEntryPoint;
 		rel_size = opt->DataDirectory[rel_idx].Size;
 		rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
+		image_type = opt->Subsystem;
 	} else {
 		printf("%s: Invalid optional header magic %x\n", __func__,
 		       nt->OptionalHeader.Magic);
 		return NULL;
 	}
 
+	switch (image_type) {
+	case IMAGE_SUBSYSTEM_EFI_APPLICATION:
+		loaded_image_info->image_code_type = EFI_LOADER_CODE;
+		loaded_image_info->image_data_type = EFI_LOADER_DATA;
+		break;
+	case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
+		loaded_image_info->image_code_type = EFI_BOOT_SERVICES_CODE;
+		loaded_image_info->image_data_type = EFI_BOOT_SERVICES_DATA;
+		break;
+	case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
+	case IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER:
+		loaded_image_info->image_code_type = EFI_RUNTIME_SERVICES_CODE;
+		loaded_image_info->image_data_type = EFI_RUNTIME_SERVICES_DATA;
+		break;
+	default:
+		printf("%s: invalid image type: %u\n", __func__, image_type);
+		break;
+	}
+
 	/* Load sections into RAM */
 	for (i = num_sections - 1; i >= 0; i--) {
 		IMAGE_SECTION_HEADER *sec = &sections[i];
Signature · 1 line
2.13.5
#62 lore raw

PATCH v3 17/21 efi_loader: set loaded image code/data type properly

Quoted 4 lines

These should be set according to the image type. Shell.efi and SCT.efi use these fields to determine what sort of image they are loading.

Thanks, applied to efi-next

Alex

author
#63 lore raw

PATCH v3 18/21 efi_loader: print GUIDs

In reply to Rob Clark

Utilize printf GUID support to print GUIDs.

---
 lib/efi_loader/efi_boottime.c | 22 +++++++++++-----------
 lib/efi_loader/efi_variable.c |  6 +++---
 2 files changed, 14 insertions(+), 14 deletions(-)
lib/efi_loader/efi_boottime.c +11 -11
index cea242cd49..b6f32c98d9 100644
@@ -514,7 +514,7 @@ static efi_status_t EFIAPI efi_install_protocol_interface_ext(void **handle,
 			efi_guid_t *protocol, int protocol_interface_type,
 			void *protocol_interface)
 {
-	EFI_ENTRY("%p, %p, %d, %p", handle, protocol, protocol_interface_type,
+	EFI_ENTRY("%p, %pUl, %d, %p", handle, protocol, protocol_interface_type,
 		  protocol_interface);
 
 	return EFI_EXIT(efi_install_protocol_interface(handle, protocol,
@@ -526,7 +526,7 @@ static efi_status_t EFIAPI efi_reinstall_protocol_interface(void *handle,
 			efi_guid_t *protocol, void *old_interface,
 			void *new_interface)
 {
-	EFI_ENTRY("%p, %p, %p, %p", handle, protocol, old_interface,
+	EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, old_interface,
 		  new_interface);
 	return EFI_EXIT(EFI_ACCESS_DENIED);
 }
@@ -575,7 +575,7 @@ out:
 static efi_status_t EFIAPI efi_uninstall_protocol_interface_ext(void *handle,
 			efi_guid_t *protocol, void *protocol_interface)
 {
-	EFI_ENTRY("%p, %p, %p", handle, protocol, protocol_interface);
+	EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface);
 
 	return EFI_EXIT(efi_uninstall_protocol_interface(handle, protocol,
 							 protocol_interface));
@@ -585,7 +585,7 @@ static efi_status_t EFIAPI efi_register_protocol_notify(efi_guid_t *protocol,
 							struct efi_event *event,
 							void **registration)
 {
-	EFI_ENTRY("%p, %p, %p", protocol, event, registration);
+	EFI_ENTRY("%pUl, %p, %p", protocol, event, registration);
 	return EFI_EXIT(EFI_OUT_OF_RESOURCES);
 }
 
@@ -655,7 +655,7 @@ static efi_status_t EFIAPI efi_locate_handle_ext(
 			efi_guid_t *protocol, void *search_key,
 			unsigned long *buffer_size, efi_handle_t *buffer)
 {
-	EFI_ENTRY("%d, %p, %p, %p, %p", search_type, protocol, search_key,
+	EFI_ENTRY("%d, %pUl, %p, %p, %p", search_type, protocol, search_key,
 		  buffer_size, buffer);
 
 	return EFI_EXIT(efi_locate_handle(search_type, protocol, search_key,
@@ -723,7 +723,7 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table
 static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid,
 							       void *table)
 {
-	EFI_ENTRY("%p, %p", guid, table);
+	EFI_ENTRY("%pUl, %p", guid, table);
 	return EFI_EXIT(efi_install_configuration_table(guid, table));
 }
 
@@ -1012,7 +1012,7 @@ static efi_status_t EFIAPI efi_close_protocol(void *handle,
 					      void *agent_handle,
 					      void *controller_handle)
 {
-	EFI_ENTRY("%p, %p, %p, %p", handle, protocol, agent_handle,
+	EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, agent_handle,
 		  controller_handle);
 	return EFI_EXIT(EFI_NOT_FOUND);
 }
@@ -1022,7 +1022,7 @@ static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle,
 			struct efi_open_protocol_info_entry **entry_buffer,
 			unsigned long *entry_count)
 {
-	EFI_ENTRY("%p, %p, %p, %p", handle, protocol, entry_buffer,
+	EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, entry_buffer,
 		  entry_count);
 	return EFI_EXIT(EFI_NOT_FOUND);
 }
@@ -1088,7 +1088,7 @@ static efi_status_t EFIAPI efi_locate_handle_buffer(
 	efi_status_t r;
 	unsigned long buffer_size = 0;
 
-	EFI_ENTRY("%d, %p, %p, %p, %p", search_type, protocol, search_key,
+	EFI_ENTRY("%d, %pUl, %p, %p, %p", search_type, protocol, search_key,
 		  no_handles, buffer);
 
 	if (!no_handles || !buffer) {
@@ -1120,7 +1120,7 @@ static efi_status_t EFIAPI efi_locate_protocol(efi_guid_t *protocol,
 	struct list_head *lhandle;
 	int i;
 
-	EFI_ENTRY("%p, %p, %p", protocol, registration, protocol_interface);
+	EFI_ENTRY("%pUl, %p, %p", protocol, registration, protocol_interface);
 
 	if (!protocol || !protocol_interface)
 		return EFI_EXIT(EFI_INVALID_PARAMETER);
@@ -1228,7 +1228,7 @@ static efi_status_t EFIAPI efi_open_protocol(
 	int i;
 	efi_status_t r = EFI_INVALID_PARAMETER;
 
-	EFI_ENTRY("%p, %p, %p, %p, %p, 0x%x", handle, protocol,
+	EFI_ENTRY("%p, %pUl, %p, %p, %p, 0x%x", handle, protocol,
 		  protocol_interface, agent_handle, controller_handle,
 		  attributes);
 
lib/efi_loader/efi_variable.c +3 -3
index 5569b3d3f0..6c177da3a6 100644
@@ -187,7 +187,7 @@ efi_status_t EFIAPI efi_get_variable(s16 *variable_name,
 	const char *val, *s;
 	u32 attr;
 
-	EFI_ENTRY("%p %p %p %p %p", variable_name, vendor, attributes,
+	EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
 		  data_size, data);
 
 	if (!variable_name || !vendor || !data_size)
@@ -255,7 +255,7 @@ efi_status_t EFIAPI efi_get_next_variable(
 		unsigned long *variable_name_size,
 		s16 *variable_name, efi_guid_t *vendor)
 {
-	EFI_ENTRY("%p %p %p", variable_name_size, variable_name, vendor);
+	EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor);
 
 	return EFI_EXIT(EFI_DEVICE_ERROR);
 }
@@ -270,7 +270,7 @@ efi_status_t EFIAPI efi_set_variable(s16 *variable_name,
 	char *val, *s;
 	u32 attr;
 
-	EFI_ENTRY("%p %p %x %lu %p", variable_name, vendor, attributes,
+	EFI_ENTRY("\"%ls\" %pUl %x %lu %p", variable_name, vendor, attributes,
 		  data_size, data);
 
 	if (!variable_name || !vendor)
Signature · 1 line
2.13.5
author
#65 lore raw

PATCH v3 19/21 efi_loader: split out escape sequence based size query

In reply to Rob Clark

We need to do something different for vidconsole, since it cannot respond to the query on stdin. Prep work for next patch.

---
 lib/efi_loader/efi_console.c | 53 +++++++++++++++++++++++++-------------------
 1 file changed, 30 insertions(+), 23 deletions(-)
lib/efi_loader/efi_console.c +30 -23
index 3fc82b8726..e8c02870eb 100644
@@ -186,6 +186,34 @@ static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols)
 	return (mode->rows == rows) && (mode->columns == cols);
 }
 
+static int query_console_serial(int *rows, int *cols)
+{
+	/* Ask the terminal about its size */
+	int n[3];
+	u64 timeout;
+
+	/* Empty input buffer */
+	while (tstc())
+		getc();
+
+	printf(ESC"[18t");
+
+	/* Check if we have a terminal that understands */
+	timeout = timer_get_us() + 1000000;
+	while (!tstc())
+		if (timer_get_us() > timeout)
+			return -1;
+
+	/* Read {depth,rows,cols} */
+	if (term_read_reply(n, 3, 't'))
+		return -1;
+
+	*cols = n[2];
+	*rows = n[1];
+
+	return 0;
+}
+
 static efi_status_t EFIAPI efi_cout_query_mode(
 			struct efi_simple_text_output_protocol *this,
 			unsigned long mode_number, unsigned long *columns,
@@ -194,33 +222,12 @@ static efi_status_t EFIAPI efi_cout_query_mode(
 	EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
 
 	if (!console_size_queried) {
-		/* Ask the terminal about its size */
-		int n[3];
-		int cols;
-		int rows;
-		u64 timeout;
+		int rows, cols;
 
 		console_size_queried = true;
 
-		/* Empty input buffer */
-		while (tstc())
-			getc();
-
-		printf(ESC"[18t");
-
-		/* Check if we have a terminal that understands */
-		timeout = timer_get_us() + 1000000;
-		while (!tstc())
-			if (timer_get_us() > timeout)
-				goto out;
-
-		/* Read {depth,rows,cols} */
-		if (term_read_reply(n, 3, 't')) {
+		if (query_console_serial(&rows, &cols))
 			goto out;
-		}
-
-		cols = n[2];
-		rows = n[1];
 
 		/* Test if we can have Mode 1 */
 		if (cols >= 80 && rows >= 50) {
Signature · 1 line
2.13.5
#66 lore raw

PATCH v3 19/21 efi_loader: split out escape sequence based size query

Quoted 4 lines

We need to do something different for vidconsole, since it cannot respond to the query on stdin. Prep work for next patch.

Thanks, applied to efi-next

Alex

author
#67 lore raw

PATCH v3 20/21 efi_loader: Correctly figure out size for vidconsole

In reply to Rob Clark

If stdout is vidconsole, we cannot rely on ANSI escape sequences to query the size, as vidconsole cannot reply on stdin. Instead special- case this if stdout is vidconsole.

---
 lib/efi_loader/efi_console.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)
lib/efi_loader/efi_console.c +15 -1
index e8c02870eb..9048a8d32c 100644
@@ -8,7 +8,10 @@
 
 #include <common.h>
 #include <charset.h>
+#include <dm/device.h>
 #include <efi_loader.h>
+#include <stdio_dev.h>
+#include <video_console.h>
 
 static bool console_size_queried;
 
@@ -222,12 +225,23 @@ static efi_status_t EFIAPI efi_cout_query_mode(
 	EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
 
 	if (!console_size_queried) {
+		const char *stdout_name = env_get("stdout");
 		int rows, cols;
 
 		console_size_queried = true;
 
-		if (query_console_serial(&rows, &cols))
+		if (stdout_name && !strcmp(stdout_name, "vidconsole") &&
+		    IS_ENABLED(CONFIG_DM_VIDEO)) {
+			struct stdio_dev *stdout_dev =
+				stdio_get_by_name("vidconsole");
+			struct udevice *dev = stdout_dev->priv;
+			struct vidconsole_priv *priv =
+				dev_get_uclass_priv(dev);
+			rows = priv->rows;
+			cols = priv->cols;
+		} else if (query_console_serial(&rows, &cols)) {
 			goto out;
+		}
 
 		/* Test if we can have Mode 1 */
 		if (cols >= 80 && rows >= 50) {
Signature · 1 line
2.13.5
#68 lore raw

PATCH v3 20/21 efi_loader: Correctly figure out size for vidconsole

Quoted 5 lines

If stdout is vidconsole, we cannot rely on ANSI escape sequences to query the size, as vidconsole cannot reply on stdin. Instead special- case this if stdout is vidconsole.

Thanks, applied to efi-next

Alex

author
#69 lore raw

PATCH v3 21/21 efi_loader: Some console improvements for vidconsole

In reply to Rob Clark

1) use fputs() to reduce cache flushes from once-per-char to once-per-string
2) handle \r, \t, and \b in addition to just \n for tracking cursor position
3) cursor row/col are zero based, not one based

---
 include/efi_api.h            |  6 +++--
 lib/efi_loader/efi_console.c | 58 ++++++++++++++++++++++++++------------------
 2 files changed, 39 insertions(+), 25 deletions(-)
include/efi_api.h +4 -2
index 604c5b7ec4..c3b9032a48 100644
@@ -29,6 +29,8 @@ enum efi_timer_delay {
 };
 
 #define UINTN size_t
+typedef long INTN;
+typedef uint16_t *efi_string_t;
 
 #define EVT_TIMER				0x80000000
 #define EVT_RUNTIME				0x40000000
@@ -427,10 +429,10 @@ struct efi_simple_text_output_protocol {
 	void *reset;
 	efi_status_t (EFIAPI *output_string)(
 			struct efi_simple_text_output_protocol *this,
-			const unsigned short *str);
+			const efi_string_t str);
 	efi_status_t (EFIAPI *test_string)(
 			struct efi_simple_text_output_protocol *this,
-			const unsigned short *str);
+			const efi_string_t str);
 	efi_status_t(EFIAPI *query_mode)(
 			struct efi_simple_text_output_protocol *this,
 			unsigned long mode_number, unsigned long *columns,
lib/efi_loader/efi_console.c +35 -23

76 lines not on this page · raw message

Signature · 1 line
2.13.5
#70 lore raw

PATCH v3 21/21 efi_loader: Some console improvements for vidconsole

Quoted 7 lines

1) use fputs() to reduce cache flushes from once-per-char to once-per-string
2) handle \r, \t, and \b in addition to just \n for tracking cursor position
3) cursor row/col are zero based, not one based

Thanks, applied to efi-next

Alex

#71 lore raw

v3 cover efi_loader: enough UEFI for standard distro boot

In reply to Rob Clark
Quoted 28 lines · Rob Clark

This patchset fleshes out EFI_LOADER enough to support booting an upstream \EFI\BOOT\bootaa64.efi (which then loads fallback.efi and then eventually the per-distro shim.efi which loads the per-distro grubaa64.efi) without resorting to hacks to hard-code u-boot to load a particular distro's grub, or other hacks like setting up the distro installation as live-media.

Background: with a normal UEFI implementation, the boot process is:

a) firmware (u-boot) looks at BootOrder and the BootXXXX variables to try to determine what to boot.
b) the firmware will look at the BootXXXX variables (which contain an EFI_LOAD_OPTION "struct" in order specified by BootOrder, and will boot the first bootable option.
c) The EFI_LOAD_OPTION specifies a device-path which identifies the device and file path of the .efi payload to exectute.

This is implemented with the 'bootefi bootmgr' command.

If there are no bootable options the firmware falls back to loading \EFI\BOOT\bootaa64.efi (exact name varies depending on arch), which for distro's using fallback/shim (which should include most modern linux distros) then loads fallback.efi which uses the EFI_SIMPLE_FILE_SYSTEM_PROTCOL and EFI_FILE_PROTOCOL to search for any \EFI\*\boot.csv, and will then set BootOrder and BootXXXX EFI variables accordingly so that on next boot fallback.efi is not necessary.

Quoted 3 lines

The last 5 patches are a bit unrelated, just pulling forward some of the patches I have from the next patchset, to get Shell.efi and SCT working.

I've pulled the patches into efi-next, but want to run them through Travis first and then do some smoke tests myself before sending them as pull request.

Please follow up with fixes to the "unsigned" type warnings. Just use "unsigned int" everywhere.

Alex