[U-Boot] [PATCH v0 00/20] 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.
The first seven patches add dependencies that will be needed later in the series. Patches 8-15 make u-boot work with upstream grub, without relying on distro patches. Patches 16-19 add missing bits of the UEFI implementation needed to support shim/fallback. And finally patch 20 adds bootmanager support to avoid shim/fallback after first boot.
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.
If there are no bootable options the firmware falls back to loading \EFI\BOOT\bootaa64.efi (exact name varies depending on arch), which then loads fallback.efi which uses the EFI_SIMPLE_FILE_SYSTEM_PROTCOL and EFI_FILE_PROTOCOL to search for \EFI*\boot.csv, and will then set BootOrder and BootXXXX EFI variables accordingly so that on next boot fallback.efi is not necessary.
(I'm ignoring secure boot, it is out of scope here.)
For example, if you had both fedora and opensuse installed on the same disk in different partitions, you would have both:
+ \EFI\fedora\boot.csv + \EFI\opensuse\boot.csv
The former would contain the filename of \EFI\fedora\shim.efi and the latter to \EFI\opensuse\shim.efi (each of which would know to load \EFI\fedora\grubaa64.efi or \EFI\opensuse\grubaa64.efi). Based on this, fallback.efi would setup EFI_LOAD_OPTION's Boot0000 and Boot0001 (and BootOrder would control the order the load-options are considered).
With a real UEFI fw there would also be some sort of boot-order menu (ie. hold down f9 at boot, and get a menu to pick which of the Boot* load-options to try first). That is not part of this patchset but would be a useful next step to allow installing multiple operating systems on the same disk.
This patchset provides EFI variable support during bootservices, so viewing or modifying EFI variables after linux ExitBootServices()'s is not possible. If the board supports saveenv() this will be called in efi_exit_boot_services() to persist variables that where set during the boot process. Making variables available after EBS is tricky on hardware that does not have dedicated storage, as after EBS u-boot no longer controls the devices. An approach that Alexander Graf suggested, is that since reboot/halt is controlled via UEFI, is that on boards that can ensure memory is persisted across reboot, to store modified EFI variables in a special memory location and turn halt into reboot uboot -> appropriate setenv() calls -> saveenv() -> halt in order to persist modified variables. Which is also not part of this patchset, and will likely require some board-specific help.
There will be some updates to this patchset depending on whether we move to c11 as Heinrich suggested (ie s/L"string"/u"string" and some changeups in the vsprintf patch). But rather than calling this an RFC (which I figured was more likely to get ignored for review) I am calling this a v0.
Thanks to Peter Jones for a couple of the patches, and a bunch of help understanding what the things above the UEFI fw expect (and fixing a few shim and grub bugs that we found along the way).
Peter Jones (2): part: extract MBR signature from partitions efi: add some more device path structures
Rob Clark (18): fs: add fs_readdir() fs/fat: implement readdir short-wchar common: add some utf16 handling helpers vsprintf.c: add wide string (%ls) support efi_loader: add back optional efi_handler::open() efi_loader: add device-path utils efi_loader: drop redundant efi_device_path_protocol efi_loader: add guidstr helper 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
cmd/bootefi.c | 249 +++++++--------- common/Makefile | 1 + common/charset.c | 81 ++++++ config.mk | 2 +- disk/part_dos.c | 12 +- disk/part_efi.c | 20 ++ fs/fat/fat.c | 59 +++- fs/fs.c | 46 +++ include/blk.h | 15 + include/charset.h | 18 ++ include/config_distro_bootcmd.h | 5 + include/efi.h | 25 ++ include/efi_api.h | 141 ++++++++- include/efi_loader.h | 83 +++++- include/fat.h | 4 +- include/fs.h | 23 ++ include/part.h | 3 +- include/part_efi.h | 4 - lib/efi_loader/Makefile | 3 +- lib/efi_loader/efi_bootmgr.c | 169 +++++++++++ lib/efi_loader/efi_boottime.c | 153 ++++++++-- lib/efi_loader/efi_console.c | 17 +- lib/efi_loader/efi_device_path.c | 485 +++++++++++++++++++++++++++++++ lib/efi_loader/efi_device_path_to_text.c | 224 ++++++++++---- lib/efi_loader/efi_disk.c | 105 +++++-- lib/efi_loader/efi_file.c | 468 +++++++++++++++++++++++++++++ lib/efi_loader/efi_image_loader.c | 4 + 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 | 342 ++++++++++++++++++++++ lib/vsprintf.c | 30 +- 32 files changed, 2508 insertions(+), 329 deletions(-) create mode 100644 common/charset.c create mode 100644 include/charset.h 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

Needed to support efi file protocol. The fallback.efi loader wants to be able to read the contents of the /EFI directory to find an OS to boot.
For reference, the expected EFI semantics are described in (v2.7 of UEFI spec) in section 13.5 (page 609). Or for convenience, see:
http://wiki.phoenix.com/wiki/index.php/EFI_FILE_PROTOCOL#Read.28.29
The EFI level semantics are implemented in a later patch, so they are not too important to the understanding of this patch.
Signed-off-by: Rob Clark robdclark@gmail.com --- fs/fs.c | 25 +++++++++++++++++++++++++ include/fs.h | 21 +++++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/fs/fs.c b/fs/fs.c index 595ff1fe69..5ac4226ece 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -69,6 +69,12 @@ static inline int fs_uuid_unsupported(char *uuid_str) return -1; }
+static inline int fs_readdir_unsupported(const char *filename, loff_t offset, + struct fs_dirent *dent) +{ + return -ENXIO; +} + struct fstype_info { int fstype; char *name; @@ -92,6 +98,7 @@ struct fstype_info { loff_t len, loff_t *actwrite); void (*close)(void); int (*uuid)(char *uuid_str); + int (*readdir)(const char *filename, loff_t offset, struct fs_dirent *dent); };
static struct fstype_info fstypes[] = { @@ -112,6 +119,7 @@ static struct fstype_info fstypes[] = { .write = fs_write_unsupported, #endif .uuid = fs_uuid_unsupported, + .readdir = fs_readdir_unsupported, }, #endif #ifdef CONFIG_FS_EXT4 @@ -131,6 +139,7 @@ static struct fstype_info fstypes[] = { .write = fs_write_unsupported, #endif .uuid = ext4fs_uuid, + .readdir = fs_readdir_unsupported, }, #endif #ifdef CONFIG_SANDBOX @@ -146,6 +155,7 @@ static struct fstype_info fstypes[] = { .read = fs_read_sandbox, .write = fs_write_sandbox, .uuid = fs_uuid_unsupported, + .readdir = fs_readdir_unsupported, }, #endif #ifdef CONFIG_CMD_UBIFS @@ -161,6 +171,7 @@ static struct fstype_info fstypes[] = { .read = ubifs_read, .write = fs_write_unsupported, .uuid = fs_uuid_unsupported, + .readdir = fs_readdir_unsupported, }, #endif { @@ -175,6 +186,7 @@ static struct fstype_info fstypes[] = { .read = fs_read_unsupported, .write = fs_write_unsupported, .uuid = fs_uuid_unsupported, + .readdir = fs_readdir_unsupported, }, };
@@ -334,6 +346,19 @@ int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len, return ret; }
+int fs_readdir(const char *filename, loff_t offset, struct fs_dirent *dent) +{ + struct fstype_info *info = fs_get_info(fs_type); + int ret; + + memset(dent, 0, sizeof(*dent)); + + ret = info->readdir(filename, offset, dent); + fs_close(); + + return ret; +} + int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], int fstype) { diff --git a/include/fs.h b/include/fs.h index 2f2aca8378..71051d7c66 100644 --- a/include/fs.h +++ b/include/fs.h @@ -79,6 +79,27 @@ int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len, loff_t *actwrite);
/* + * A directory entry. + */ +struct fs_dirent { + loff_t size; + char name[256]; +}; + +/* + * fs_readdir - Read a directory. + * + * @filename: Name of file to read from + * @offset: The offset into the directory to read, ie. offset of N returns + * the N'th directory entry + * @dent: on success, filled in with directory entry + * @return 0 on success, -ENOTDIR if specified file is not a directory, + * or -ENOENT if offset is beyond last directory entry, or -ENXIO if + * operation is not supported. + */ +int fs_readdir(const char *filename, loff_t offset, struct fs_dirent *dent); + +/* * Common implementation for various filesystem commands, optionally limited * to a specific filesystem type via the fstype parameter. */

On Freitag, 4. August 2017 21:31:43 CEST Rob Clark wrote:
Needed to support efi file protocol. The fallback.efi loader wants to be able to read the contents of the /EFI directory to find an OS to boot.
For reference, the expected EFI semantics are described in (v2.7 of UEFI spec) in section 13.5 (page 609). Or for convenience, see:
http://wiki.phoenix.com/wiki/index.php/EFI_FILE_PROTOCOL#Read.28.29
The EFI level semantics are implemented in a later patch, so they are not too important to the understanding of this patch.
Signed-off-by: Rob Clark robdclark@gmail.com
fs/fs.c | 25 +++++++++++++++++++++++++ include/fs.h | 21 +++++++++++++++++++++ 2 files changed, 46 insertions(+)
Still, the commit message is in no way helpful when trying to understand what your changes are actually doing.
You introduce an arbitrary new API in the filesystem level (you neither expose an existing API, nor are you implementing the API requested by EFI, nor anything roughly resembling it).
The API you expose adds an index-based directory lookup, while EFI wants an POSIX-like directory stream. After reading through both the EFI spec and U- Boots file system code, its clear you want to have some matching layer between the mostly stateless U-Boot filesystem layer and the stateful EFI API.
Please provide a thorough description why you create this new API in the fs layer, state that it is a hack to achieve what you want. If sometime later someone else wants to clean this up (both the FAT implementation, and the API), she/he should not have to go through all the code.
Regards,
Stefan

On Mon, Aug 7, 2017 at 2:19 PM, Brüns, Stefan Stefan.Bruens@rwth-aachen.de wrote:
On Freitag, 4. August 2017 21:31:43 CEST Rob Clark wrote:
Needed to support efi file protocol. The fallback.efi loader wants to be able to read the contents of the /EFI directory to find an OS to boot.
For reference, the expected EFI semantics are described in (v2.7 of UEFI spec) in section 13.5 (page 609). Or for convenience, see:
http://wiki.phoenix.com/wiki/index.php/EFI_FILE_PROTOCOL#Read.28.29
The EFI level semantics are implemented in a later patch, so they are not too important to the understanding of this patch.
Signed-off-by: Rob Clark robdclark@gmail.com
fs/fs.c | 25 +++++++++++++++++++++++++ include/fs.h | 21 +++++++++++++++++++++ 2 files changed, 46 insertions(+)
Still, the commit message is in no way helpful when trying to understand what your changes are actually doing.
You were the one that wanted a reference to the relevant EFI protocol, even though it is not terribly useful for understanding this patch ;-)
You introduce an arbitrary new API in the filesystem level (you neither expose an existing API, nor are you implementing the API requested by EFI, nor anything roughly resembling it).
I am exposing the API needed to implement the EFI API. I am not sure why you describe it as arbitrary. I would describe it as posix readdir() ported to fs's stateless design. Ie. not quite the same, neither are any of fs's other APIs.
The API you expose adds an index-based directory lookup, while EFI wants an POSIX-like directory stream. After reading through both the EFI spec and U- Boots file system code, its clear you want to have some matching layer between the mostly stateless U-Boot filesystem layer and the stateful EFI API.
What EFI wants and the way the u-boot filesystem API works are two completely different things. The u-boot fs APIs are stateless. EFI is not, not just for directories but also for file read/write. Please see patch 16/20.
Please provide a thorough description why you create this new API in the fs layer, state that it is a hack to achieve what you want. If sometime later someone else wants to clean this up (both the FAT implementation, and the API), she/he should not have to go through all the code.
The fat implementation is a hack, but the API is not. Ie. it does exactly what the comment in fs.h describes. (I might do it differently if u-boot had a concept of file handles that were stateful. But that is a bit of a departure from how u-boot's fs works.)
BR, -R

Yes, this is super-hacky. The FAT code is quite ugly, and this doesn't improve things. But it doesn't make it significantly worse either. The better option would be a massive FAT re-write to get rid of the hacky way that fat_file_ls() works. Volunteers welcome.
Signed-off-by: Rob Clark robdclark@gmail.com --- Yeah, not my proudest patch, but there is little that can be done in fat.c to be proud of short of re-writing it.. which I am not signing up for. I don't think it makes things that much worse than it already is, but if you disagree I still suggest we merge the previous patch, to enable merging the efi_loader patches that directly or indirectly depend on it. At least then we minimize what is not upstream in order to have a useful UEFI implementation in u-boot.
fs/fat/fat.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++------------- fs/fs.c | 2 +- include/fat.h | 4 +++- 3 files changed, 50 insertions(+), 15 deletions(-)
diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 9ad18f96ff..04d8616598 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -14,6 +14,7 @@ #include <config.h> #include <exports.h> #include <fat.h> +#include <fs.h> #include <asm/byteorder.h> #include <part.h> #include <malloc.h> @@ -575,17 +576,25 @@ static __u8 mkcksum(const char name[8], const char ext[3]) /* * Get the directory entry associated with 'filename' from the directory * starting at 'startsect' + * + * Last two args are only used for dols==LS_READDIR */ __u8 get_dentfromdir_block[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN);
-static dir_entry *get_dentfromdir(fsdata *mydata, int startsect, - char *filename, dir_entry *retdent, - int dols) +static dir_entry *get_dentfromdir(fsdata *mydata, char *filename, + dir_entry *retdent, int dols, + loff_t pos, struct fs_dirent *d) { __u16 prevcksum = 0xffff; __u32 curclust = START(retdent); int files = 0, dirs = 0; + int readdir = 0; + + if (dols == LS_READDIR) { + readdir = 1; + dols = 0; + }
debug("get_dentfromdir: %s\n", filename);
@@ -618,7 +627,7 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect, get_vfatname(mydata, curclust, get_dentfromdir_block, dentptr, l_name); - if (dols) { + if (dols || readdir) { int isdir; char dirc; int doit = 0; @@ -637,7 +646,14 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect, } } if (doit) { - if (dirc == ' ') { + if (readdir) { + if ((dirs + files - 1) == pos) { + strcpy(d->name, l_name); + if (!isdir) + d->size = FAT2CPU32(dentptr->size); + return NULL; + } + } else if (dirc == ' ') { printf(" %8u %s%c\n", FAT2CPU32(dentptr->size), l_name, @@ -676,7 +692,7 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect, }
get_name(dentptr, s_name); - if (dols) { + if (dols || readdir) { int isdir = (dentptr->attr & ATTR_DIR); char dirc; int doit = 0; @@ -694,7 +710,14 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect, }
if (doit) { - if (dirc == ' ') { + if (readdir) { + if ((dirs + files - 1) == pos) { + strcpy(d->name, s_name); + if (!isdir) + d->size = FAT2CPU32(dentptr->size); + return NULL; + } + } else if (dirc == ' ') { printf(" %8u %s%c\n", FAT2CPU32(dentptr->size), s_name, dirc); @@ -825,7 +848,7 @@ int do_fat_read_at(const char *filename, loff_t pos, void *buffer, __u32 cursect; int idx, isdir = 0; int files = 0, dirs = 0; - int ret = -1; + int ret = (dols == LS_READDIR) ? -ENOTDIR : -1; int firsttime; __u32 root_cluster = 0; __u32 read_blk; @@ -906,7 +929,8 @@ root_reparse: if (!dols) goto exit;
- dols = LS_ROOT; + if (dols == LS_YES) + dols = LS_ROOT; } else if ((idx = dirdelim(fnamecopy)) >= 0) { isdir = 1; fnamecopy[idx] = '\0'; @@ -1151,8 +1175,6 @@ rootdir_done: firsttime = 1;
while (isdir) { - int startsect = mydata->data_begin - + START(dentptr) * mydata->clust_size; dir_entry dent; char *nextname = NULL;
@@ -1177,10 +1199,14 @@ rootdir_done: } }
- if (get_dentfromdir(mydata, startsect, subname, dentptr, - isdir ? 0 : dols) == NULL) { + if (get_dentfromdir(mydata, subname, dentptr, + isdir ? 0 : dols, pos, buffer) == NULL) { if (dols && !isdir) *size = 0; + if (dols == LS_READDIR) { + struct fs_dirent *dent = buffer; + ret = dent->name[0] ? 0 : -ENOENT; + } goto exit; }
@@ -1353,6 +1379,13 @@ int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len, return ret; }
+int fat_readdir(const char *filename, loff_t offset, struct fs_dirent *dent) +{ + loff_t actread; + return do_fat_read_at(filename, offset, dent, sizeof(*dent), + LS_READDIR, 0, &actread); +} + void fat_close(void) { } diff --git a/fs/fs.c b/fs/fs.c index 5ac4226ece..42a028a6ce 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -119,7 +119,7 @@ static struct fstype_info fstypes[] = { .write = fs_write_unsupported, #endif .uuid = fs_uuid_unsupported, - .readdir = fs_readdir_unsupported, + .readdir = fat_readdir, }, #endif #ifdef CONFIG_FS_EXT4 diff --git a/include/fat.h b/include/fat.h index 71879f01ca..0ef3f5be16 100644 --- a/include/fat.h +++ b/include/fat.h @@ -61,8 +61,8 @@ /* Flags telling whether we should read a file or list a directory */ #define LS_NO 0 #define LS_YES 1 -#define LS_DIR 1 #define LS_ROOT 2 +#define LS_READDIR 3 /* read directory entry at specified offset */
#define ISDIRDELIM(c) ((c) == '/' || (c) == '\')
@@ -210,5 +210,7 @@ int file_fat_write(const char *filename, void *buf, loff_t offset, loff_t len, loff_t *actwrite); int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len, loff_t *actread); +struct fs_dirent; +int fat_readdir(const char *filename, loff_t offset, struct fs_dirent *dir); void fat_close(void); #endif /* _FAT_H_ */

For now, pending conclusion on proposal about using c11 and u"string" instead of L"string" plus -fshort-wchar.
Background: UEFI uses utf16 strings universally. An UEFI implementation that does not use -fshort-wchar has to do a lot of cumbersome charset conversion back/forth. Mixing object files that use -fshort-wchar and others that do not is a bad idea (and gcc will warn about it). There are 3 reasonable solutions that I can think of:
1) Use -fshort-wchar across the board. We don't dynamically link against other shared libraries at runtime, so there isn't much downside to this approach. Heinrich brought up ext4, which uses utf32, but I guess this mostly matters for fs_ls(), and it does not seem so bad for it to do utf32->utf8 conversion.
2) Use -fshort-wchar only if CONFIG_EFI_LOADER=y.. UEFI only requires fat/vfat so we don't need ext4 and efi loader at the same time.
3) Go with Heinrich's suggestion of requiring c11. Possibly this requirement could be loosened to only require c11 for efi loader. This seems like the best approach, and at least no one has so far brought up any objection to his proposal.
Not-signed-off-by: Rob Clark robdclark@gmail.com --- config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config.mk b/config.mk index b77d58903c..5ad9e7198d 100644 --- a/config.mk +++ b/config.mk @@ -74,7 +74,7 @@ endif RELFLAGS := $(PLATFORM_RELFLAGS)
PLATFORM_CPPFLAGS += $(RELFLAGS) -PLATFORM_CPPFLAGS += -pipe +PLATFORM_CPPFLAGS += -pipe -fshort-wchar
LDFLAGS += $(PLATFORM_LDFLAGS) LDFLAGS_FINAL += -Bstatic

On 08/04/2017 09:31 PM, Rob Clark wrote:
For now, pending conclusion on proposal about using c11 and u"string" instead of L"string" plus -fshort-wchar.
Background: UEFI uses utf16 strings universally. An UEFI implementation that does not use -fshort-wchar has to do a lot of cumbersome charset conversion back/forth. Mixing object files that use -fshort-wchar and others that do not is a bad idea (and gcc will warn about it). There are 3 reasonable solutions that I can think of:
Use -fshort-wchar across the board. We don't dynamically link against other shared libraries at runtime, so there isn't much downside to this approach. Heinrich brought up ext4, which uses utf32, but I guess this mostly matters for fs_ls(), and it does not seem so bad for it to do utf32->utf8 conversion.
Use -fshort-wchar only if CONFIG_EFI_LOADER=y.. UEFI only requires fat/vfat so we don't need ext4 and efi loader at the same time.
Go with Heinrich's suggestion of requiring c11. Possibly this requirement could be loosened to only require c11 for efi loader. This seems like the best approach, and at least no one has so far brought up any objection to his proposal.
Not-signed-off-by: Rob Clark robdclark@gmail.com
Do I understand you right? You do not want the current version of the patch series merged?
It might have been better to replace [PATCH] by [RFC] in the subject lines of the patch set in this case.
I am currently trying to find out if the switch to C11 is possible, cf. https://travis-ci.org/xypron2/u-boot/builds/261098363.
Unfortunately Travis uses gcc 4.8.2 for some jobs which does not yet support C11.
Furthermore there seems to be some problem in disk/part_efi.c with PARTITION_SYSTEM_GUID.
Best regards
Heinrich
config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config.mk b/config.mk index b77d58903c..5ad9e7198d 100644 --- a/config.mk +++ b/config.mk @@ -74,7 +74,7 @@ endif RELFLAGS := $(PLATFORM_RELFLAGS)
PLATFORM_CPPFLAGS += $(RELFLAGS) -PLATFORM_CPPFLAGS += -pipe +PLATFORM_CPPFLAGS += -pipe -fshort-wchar
LDFLAGS += $(PLATFORM_LDFLAGS) LDFLAGS_FINAL += -Bstatic

On Fri, Aug 4, 2017 at 4:28 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote:
For now, pending conclusion on proposal about using c11 and u"string" instead of L"string" plus -fshort-wchar.
Background: UEFI uses utf16 strings universally. An UEFI implementation that does not use -fshort-wchar has to do a lot of cumbersome charset conversion back/forth. Mixing object files that use -fshort-wchar and others that do not is a bad idea (and gcc will warn about it). There are 3 reasonable solutions that I can think of:
Use -fshort-wchar across the board. We don't dynamically link against other shared libraries at runtime, so there isn't much downside to this approach. Heinrich brought up ext4, which uses utf32, but I guess this mostly matters for fs_ls(), and it does not seem so bad for it to do utf32->utf8 conversion.
Use -fshort-wchar only if CONFIG_EFI_LOADER=y.. UEFI only requires fat/vfat so we don't need ext4 and efi loader at the same time.
Go with Heinrich's suggestion of requiring c11. Possibly this requirement could be loosened to only require c11 for efi loader. This seems like the best approach, and at least no one has so far brought up any objection to his proposal.
Not-signed-off-by: Rob Clark robdclark@gmail.com
Do I understand you right? You do not want the current version of the patch series merged?
nope, I fully expect review comments and updates, hence calling it "v0".. I just don't want it to be ignored, as is the tendency for large RFC's on other lists that I am on. Hence the "Not-signed-off-by" ;-)
It might have been better to replace [PATCH] by [RFC] in the subject lines of the patch set in this case.
I am currently trying to find out if the switch to C11 is possible, cf. https://travis-ci.org/xypron2/u-boot/builds/261098363.
Unfortunately Travis uses gcc 4.8.2 for some jobs which does not yet support C11.
Furthermore there seems to be some problem in disk/part_efi.c with PARTITION_SYSTEM_GUID.
hmm, link/pastebin error, and I can have a look? On my wip branch that included this patchset, plus other bits needed to make it work on db410c, I didn't see any such issues, but I might have screwed something up in splitting up the db410c and related fixes from the efi_loader patchset.
I do have some regressions on a couple of the vexpress boards, which are totally unrelated to the patch that introduces the regression and I cannot reproduce locally (although locally I have newer gcc and qemu). See [1] which is introduced by patch 15. I'm pretty sure this is a problem with the test environment (either gcc or qemu), but not quite sure what to do about it.
[1] https://travis-ci.org/robclark/u-boot/jobs/260997492
BR, -R
Best regards
Heinrich
config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config.mk b/config.mk index b77d58903c..5ad9e7198d 100644 --- a/config.mk +++ b/config.mk @@ -74,7 +74,7 @@ endif RELFLAGS := $(PLATFORM_RELFLAGS)
PLATFORM_CPPFLAGS += $(RELFLAGS) -PLATFORM_CPPFLAGS += -pipe +PLATFORM_CPPFLAGS += -pipe -fshort-wchar
LDFLAGS += $(PLATFORM_LDFLAGS) LDFLAGS_FINAL += -Bstatic

From: Peter Jones pjones@redhat.com
EFI client programs need the signature information from the partition table to determine the disk a partition is on, so we need to fill that in here.
Signed-off-by: Peter Jones pjones@redhat.com [separated from efi_loader part, and fixed build-errors for non- CONFIG_EFI_PARTITION case] Signed-off-by: Rob Clark robdclark@gmail.com --- disk/part_dos.c | 12 +++++++++--- disk/part_efi.c | 20 ++++++++++++++++++++ include/blk.h | 15 +++++++++++++++ include/efi.h | 4 ++++ include/part.h | 3 ++- include/part_efi.h | 4 ---- 6 files changed, 50 insertions(+), 8 deletions(-)
diff --git a/disk/part_dos.c b/disk/part_dos.c index 7ede15ec26..850a538e83 100644 --- a/disk/part_dos.c +++ b/disk/part_dos.c @@ -89,14 +89,20 @@ static int test_block_type(unsigned char *buffer)
static int part_test_dos(struct blk_desc *dev_desc) { - ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz); + ALLOC_CACHE_ALIGN_BUFFER(legacy_mbr, mbr, dev_desc->blksz);
- if (blk_dread(dev_desc, 0, 1, (ulong *)buffer) != 1) + if (blk_dread(dev_desc, 0, 1, (ulong *)mbr) != 1) return -1;
- if (test_block_type(buffer) != DOS_MBR) + if (test_block_type((unsigned char *)mbr) != DOS_MBR) return -1;
+ if (dev_desc->sig_type == SIG_TYPE_NONE && + mbr->unique_mbr_signature != 0) { + dev_desc->sig_type = SIG_TYPE_MBR; + dev_desc->mbr_sig = mbr->unique_mbr_signature; + } + return 0; }
diff --git a/disk/part_efi.c b/disk/part_efi.c index 1b7ba27947..71e4188455 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -871,11 +871,19 @@ static int is_pmbr_valid(legacy_mbr * mbr) static int is_gpt_valid(struct blk_desc *dev_desc, u64 lba, gpt_header *pgpt_head, gpt_entry **pgpt_pte) { + ALLOC_CACHE_ALIGN_BUFFER(legacy_mbr, mbr, dev_desc->blksz); + if (!dev_desc || !pgpt_head) { printf("%s: Invalid Argument(s)\n", __func__); return 0; }
+ /* Read MBR Header from device */ + if (blk_dread(dev_desc, 0, 1, (ulong *)mbr) != 1) { + printf("*** ERROR: Can't read MBR header ***\n"); + return 0; + } + /* Read GPT Header from device */ if (blk_dread(dev_desc, (lbaint_t)lba, 1, pgpt_head) != 1) { printf("*** ERROR: Can't read GPT header ***\n"); @@ -885,6 +893,18 @@ static int is_gpt_valid(struct blk_desc *dev_desc, u64 lba, if (validate_gpt_header(pgpt_head, (lbaint_t)lba, dev_desc->lba)) return 0;
+ if (dev_desc->sig_type == SIG_TYPE_NONE) { + efi_guid_t empty = {}; + if (memcmp(&pgpt_head->disk_guid, &empty, sizeof(empty))) { + dev_desc->sig_type = SIG_TYPE_GUID; + memcpy(&dev_desc->guid_sig, &pgpt_head->disk_guid, + sizeof(empty)); + } else if (mbr->unique_mbr_signature != 0) { + dev_desc->sig_type = SIG_TYPE_MBR; + dev_desc->mbr_sig = mbr->unique_mbr_signature; + } + } + /* Read and allocate Partition Table Entries */ *pgpt_pte = alloc_read_gpt_entries(dev_desc, pgpt_head); if (*pgpt_pte == NULL) { diff --git a/include/blk.h b/include/blk.h index ef29a07ee2..3a5e04c00d 100644 --- a/include/blk.h +++ b/include/blk.h @@ -8,6 +8,8 @@ #ifndef BLK_H #define BLK_H
+#include <efi.h> + #ifdef CONFIG_SYS_64BIT_LBA typedef uint64_t lbaint_t; #define LBAFlength "ll" @@ -35,6 +37,14 @@ enum if_type { IF_TYPE_COUNT, /* Number of interface types */ };
+enum sig_type { + SIG_TYPE_NONE, + SIG_TYPE_MBR, + SIG_TYPE_GUID, + + SIG_TYPE_COUNT /* Number of signature types */ +}; + /* * With driver model (CONFIG_BLK) this is uclass platform data, accessible * with dev_get_uclass_platdata(dev) @@ -62,6 +72,11 @@ struct blk_desc { char vendor[40+1]; /* IDE model, SCSI Vendor */ char product[20+1]; /* IDE Serial no, SCSI product */ char revision[8+1]; /* firmware revision */ + enum sig_type sig_type; /* Partition table signature type */ + union { + uint32_t mbr_sig; /* MBR integer signature */ + efi_guid_t guid_sig; /* GPT GUID Signature */ + }; #ifdef CONFIG_BLK /* * For now we have a few functions which take struct blk_desc as a diff --git a/include/efi.h b/include/efi.h index 02b78b31b1..87b0b43f20 100644 --- a/include/efi.h +++ b/include/efi.h @@ -28,6 +28,10 @@
struct efi_device_path;
+typedef struct { + u8 b[16]; +} efi_guid_t; + #define EFI_BITS_PER_LONG BITS_PER_LONG
/* diff --git a/include/part.h b/include/part.h index 83bce05a43..ac5ee895e9 100644 --- a/include/part.h +++ b/include/part.h @@ -259,8 +259,9 @@ struct part_driver { #define U_BOOT_PART_TYPE(__name) \ ll_entry_declare(struct part_driver, __name, part_driver)
-#if CONFIG_IS_ENABLED(EFI_PARTITION) #include <part_efi.h> + +#if CONFIG_IS_ENABLED(EFI_PARTITION) /* disk/part_efi.c */ /** * write_gpt_table() - Write the GUID Partition Table to disk diff --git a/include/part_efi.h b/include/part_efi.h index 317c044795..31e6bc6e14 100644 --- a/include/part_efi.h +++ b/include/part_efi.h @@ -58,10 +58,6 @@ /* linux/include/efi.h */ typedef u16 efi_char16_t;
-typedef struct { - u8 b[16]; -} efi_guid_t; - /* based on linux/include/genhd.h */ struct partition { u8 boot_ind; /* 0x80 - active */

From: Peter Jones pjones@redhat.com
Signed-off-by: Peter Jones pjones@redhat.com Signed-off-by: Rob Clark robdclark@gmail.com --- include/efi_api.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-)
diff --git a/include/efi_api.h b/include/efi_api.h index ec1b321e8e..85afbeb72b 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -290,22 +290,67 @@ struct efi_mac_addr { u8 addr[32]; };
+#define DEVICE_PATH_TYPE_ACPI_DEVICE 0x02 +#define DEVICE_PATH_SUB_TYPE_ACPI_DEVICE 0x01 + +#define EFI_PNP_ID(ID) (u32)(((ID) << 16) | 0x41D0) +#define EISA_PNP_ID(ID) EFI_PNP_ID(ID) + +struct efi_device_path_acpi_path { + struct efi_device_path dp; + u32 hid; + u32 uid; +} __packed; + #define DEVICE_PATH_TYPE_MESSAGING_DEVICE 0x03 +# define DEVICE_PATH_SUB_TYPE_MSG_USB 0x05 # define DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR 0x0b +# define DEVICE_PATH_SUB_TYPE_MSG_SD 0x1a +# define DEVICE_PATH_SUB_TYPE_MSG_MMC 0x1d + +struct efi_device_path_usb { + struct efi_device_path dp; + u8 parent_port_number; + u8 usb_interface; +} __packed;
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type; -}; +} __packed; + +struct efi_device_path_sd_mmc_path { + struct efi_device_path dp; + u8 slot_number; +} __packed;
#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04 +# define DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH 0x01 +# define DEVICE_PATH_SUB_TYPE_CDROM_PATH 0x02 # define DEVICE_PATH_SUB_TYPE_FILE_PATH 0x04
+struct efi_device_path_hard_drive_path { + struct efi_device_path dp; + u32 partition_number; + u64 partition_start; + u64 partition_end; + u8 partition_signature[16]; + u8 partmap_type; + u8 signature_type; +} __packed; + +struct efi_device_path_cdrom_path { + struct efi_device_path dp; + u32 boot_entry; + u64 partition_start; + u64 partition_end; +} __packed; + struct efi_device_path_file_path { struct efi_device_path dp; u16 str[32]; -}; +} __packed;
#define BLOCK_IO_GUID \ EFI_GUID(0x964e5b21, 0x6459, 0x11d2, \

We'll eventually want these in a few places in efi_loader, and also vsprintf.
Signed-off-by: Rob Clark robdclark@gmail.com --- common/Makefile | 1 + common/charset.c | 81 ++++++++++++++++++++++++++++++++++++++++++++ include/charset.h | 18 ++++++++++ lib/efi_loader/efi_console.c | 17 ++-------- 4 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 common/charset.c create mode 100644 include/charset.h
diff --git a/common/Makefile b/common/Makefile index 60681c845c..44c8e1ba52 100644 --- a/common/Makefile +++ b/common/Makefile @@ -175,5 +175,6 @@ obj-$(CONFIG_CMD_DFU) += dfu.o obj-y += command.o obj-y += s_record.o obj-y += xyzModem.o +obj-y += charset.o
CFLAGS_env_embedded.o := -Wa,--no-warn -DENV_CRC=$(shell tools/envcrc 2>/dev/null) diff --git a/common/charset.c b/common/charset.c new file mode 100644 index 0000000000..eaff2e542e --- /dev/null +++ b/common/charset.c @@ -0,0 +1,81 @@ +/* + * charset conversion utils + * + * Copyright (c) 2017 Rob Clark + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <charset.h> + +/* + * utf8/utf16 conversion mostly lifted from grub + */ + +size_t utf16_strlen(uint16_t *in) +{ + size_t i; + for (i = 0; in[i]; i++); + return i; +} + +size_t utf16_strnlen(const uint16_t *in, size_t count) +{ + size_t i; + for (i = 0; count-- && in[i]; i++); + return i; +} + +/* Convert UTF-16 to UTF-8. */ +uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size) +{ + uint32_t code_high = 0; + + while (size--) { + uint32_t code = *src++; + + if (code_high) { + if (code >= 0xDC00 && code <= 0xDFFF) { + /* Surrogate pair. */ + code = ((code_high - 0xD800) << 10) + (code - 0xDC00) + 0x10000; + + *dest++ = (code >> 18) | 0xF0; + *dest++ = ((code >> 12) & 0x3F) | 0x80; + *dest++ = ((code >> 6) & 0x3F) | 0x80; + *dest++ = (code & 0x3F) | 0x80; + } else { + /* Error... */ + *dest++ = '?'; + /* *src may be valid. Don't eat it. */ + src--; + } + + code_high = 0; + } else { + if (code <= 0x007F) { + *dest++ = code; + } else if (code <= 0x07FF) { + *dest++ = (code >> 6) | 0xC0; + *dest++ = (code & 0x3F) | 0x80; + } else if (code >= 0xD800 && code <= 0xDBFF) { + code_high = code; + continue; + } else if (code >= 0xDC00 && code <= 0xDFFF) { + /* Error... */ + *dest++ = '?'; + } else if (code < 0x10000) { + *dest++ = (code >> 12) | 0xE0; + *dest++ = ((code >> 6) & 0x3F) | 0x80; + *dest++ = (code & 0x3F) | 0x80; + } else { + *dest++ = (code >> 18) | 0xF0; + *dest++ = ((code >> 12) & 0x3F) | 0x80; + *dest++ = ((code >> 6) & 0x3F) | 0x80; + *dest++ = (code & 0x3F) | 0x80; + } + } + } + + return dest; +} diff --git a/include/charset.h b/include/charset.h new file mode 100644 index 0000000000..2ee1172182 --- /dev/null +++ b/include/charset.h @@ -0,0 +1,18 @@ +/* + * charset conversion utils + * + * Copyright (c) 2017 Rob Clark + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __CHARSET_H_ +#define __CHARSET_H_ + +#define MAX_UTF8_PER_UTF16 4 + +size_t utf16_strlen(uint16_t *in); +size_t utf16_strnlen(const uint16_t *in, size_t count); +uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size); + +#endif /* __CHARSET_H_ */ diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 5ebce4b544..3fc82b8726 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -7,6 +7,7 @@ */
#include <common.h> +#include <charset.h> #include <efi_loader.h>
static bool console_size_queried; @@ -138,20 +139,8 @@ static efi_status_t EFIAPI efi_cout_reset(
static void print_unicode_in_utf8(u16 c) { - char utf8[4] = { 0 }; - char *b = utf8; - - if (c < 0x80) { - *(b++) = c; - } else if (c < 0x800) { - *(b++) = 192 + c / 64; - *(b++) = 128 + c % 64; - } else { - *(b++) = 224 + c / 4096; - *(b++) = 128 + c / 64 % 64; - *(b++) = 128 + c % 64; - } - + char utf8[MAX_UTF8_PER_UTF16] = { 0 }; + utf16_to_utf8((u8 *)utf8, &c, 1); puts(utf8); }

On 4 August 2017 at 13:31, Rob Clark robdclark@gmail.com wrote:
We'll eventually want these in a few places in efi_loader, and also vsprintf.
Signed-off-by: Rob Clark robdclark@gmail.com
common/Makefile | 1 + common/charset.c | 81 ++++++++++++++++++++++++++++++++++++++++++++ include/charset.h | 18 ++++++++++ lib/efi_loader/efi_console.c | 17 ++-------- 4 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 common/charset.c create mode 100644 include/charset.h
diff --git a/common/Makefile b/common/Makefile index 60681c845c..44c8e1ba52 100644 --- a/common/Makefile +++ b/common/Makefile @@ -175,5 +175,6 @@ obj-$(CONFIG_CMD_DFU) += dfu.o obj-y += command.o obj-y += s_record.o obj-y += xyzModem.o +obj-y += charset.o
CFLAGS_env_embedded.o := -Wa,--no-warn -DENV_CRC=$(shell tools/envcrc 2>/dev/null) diff --git a/common/charset.c b/common/charset.c new file mode 100644 index 0000000000..eaff2e542e --- /dev/null +++ b/common/charset.c @@ -0,0 +1,81 @@ +/*
- charset conversion utils
- Copyright (c) 2017 Rob Clark
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <charset.h>
+/*
- utf8/utf16 conversion mostly lifted from grub
- */
+size_t utf16_strlen(uint16_t *in) +{
size_t i;
for (i = 0; in[i]; i++);
return i;
+}
+size_t utf16_strnlen(const uint16_t *in, size_t count) +{
size_t i;
for (i = 0; count-- && in[i]; i++);
return i;
+}
+/* Convert UTF-16 to UTF-8. */ +uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size) +{
uint32_t code_high = 0;
while (size--) {
uint32_t code = *src++;
if (code_high) {
if (code >= 0xDC00 && code <= 0xDFFF) {
/* Surrogate pair. */
code = ((code_high - 0xD800) << 10) + (code - 0xDC00) + 0x10000;
*dest++ = (code >> 18) | 0xF0;
*dest++ = ((code >> 12) & 0x3F) | 0x80;
*dest++ = ((code >> 6) & 0x3F) | 0x80;
*dest++ = (code & 0x3F) | 0x80;
} else {
/* Error... */
*dest++ = '?';
/* *src may be valid. Don't eat it. */
src--;
}
code_high = 0;
} else {
if (code <= 0x007F) {
*dest++ = code;
} else if (code <= 0x07FF) {
*dest++ = (code >> 6) | 0xC0;
*dest++ = (code & 0x3F) | 0x80;
} else if (code >= 0xD800 && code <= 0xDBFF) {
code_high = code;
continue;
} else if (code >= 0xDC00 && code <= 0xDFFF) {
/* Error... */
*dest++ = '?';
} else if (code < 0x10000) {
*dest++ = (code >> 12) | 0xE0;
*dest++ = ((code >> 6) & 0x3F) | 0x80;
*dest++ = (code & 0x3F) | 0x80;
} else {
*dest++ = (code >> 18) | 0xF0;
*dest++ = ((code >> 12) & 0x3F) | 0x80;
*dest++ = ((code >> 6) & 0x3F) | 0x80;
*dest++ = (code & 0x3F) | 0x80;
}
}
}
return dest;
+} diff --git a/include/charset.h b/include/charset.h new file mode 100644 index 0000000000..2ee1172182 --- /dev/null +++ b/include/charset.h @@ -0,0 +1,18 @@ +/*
- charset conversion utils
- Copyright (c) 2017 Rob Clark
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __CHARSET_H_ +#define __CHARSET_H_
+#define MAX_UTF8_PER_UTF16 4
+size_t utf16_strlen(uint16_t *in); +size_t utf16_strnlen(const uint16_t *in, size_t count); +uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size);
Please don't add new exported functions without comments.
+#endif /* __CHARSET_H_ */ diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 5ebce4b544..3fc82b8726 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -7,6 +7,7 @@ */
#include <common.h> +#include <charset.h> #include <efi_loader.h>
static bool console_size_queried; @@ -138,20 +139,8 @@ static efi_status_t EFIAPI efi_cout_reset(
static void print_unicode_in_utf8(u16 c) {
char utf8[4] = { 0 };
char *b = utf8;
if (c < 0x80) {
*(b++) = c;
} else if (c < 0x800) {
*(b++) = 192 + c / 64;
*(b++) = 128 + c % 64;
} else {
*(b++) = 224 + c / 4096;
*(b++) = 128 + c / 64 % 64;
*(b++) = 128 + c % 64;
}
char utf8[MAX_UTF8_PER_UTF16] = { 0 };
utf16_to_utf8((u8 *)utf8, &c, 1); puts(utf8);
}
-- 2.13.0

On 08/04/2017 09:31 PM, Rob Clark wrote:
We'll eventually want these in a few places in efi_loader, and also vsprintf.
Signed-off-by: Rob Clark robdclark@gmail.com
common/Makefile | 1 + common/charset.c | 81 ++++++++++++++++++++++++++++++++++++++++++++ include/charset.h | 18 ++++++++++ lib/efi_loader/efi_console.c | 17 ++-------- 4 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 common/charset.c create mode 100644 include/charset.h
diff --git a/common/Makefile b/common/Makefile index 60681c845c..44c8e1ba52 100644 --- a/common/Makefile +++ b/common/Makefile @@ -175,5 +175,6 @@ obj-$(CONFIG_CMD_DFU) += dfu.o obj-y += command.o obj-y += s_record.o obj-y += xyzModem.o +obj-y += charset.o
CFLAGS_env_embedded.o := -Wa,--no-warn -DENV_CRC=$(shell tools/envcrc 2>/dev/null) diff --git a/common/charset.c b/common/charset.c new file mode 100644 index 0000000000..eaff2e542e --- /dev/null +++ b/common/charset.c @@ -0,0 +1,81 @@ +/*
- charset conversion utils
- Copyright (c) 2017 Rob Clark
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <charset.h>
+/*
- utf8/utf16 conversion mostly lifted from grub
- */
+size_t utf16_strlen(uint16_t *in) +{
- size_t i;
- for (i = 0; in[i]; i++);
- return i;
+}
+size_t utf16_strnlen(const uint16_t *in, size_t count) +{
- size_t i;
- for (i = 0; count-- && in[i]; i++);
- return i;
+}
+/* Convert UTF-16 to UTF-8. */ +uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size) +{
- uint32_t code_high = 0;
- while (size--) {
We should not read past the trailing null world. Check *src == 0 somewhere.
uint32_t code = *src++;
if (code_high) {
if (code >= 0xDC00 && code <= 0xDFFF) {
/* Surrogate pair. */
code = ((code_high - 0xD800) << 10) + (code - 0xDC00) + 0x10000;
*dest++ = (code >> 18) | 0xF0;
*dest++ = ((code >> 12) & 0x3F) | 0x80;
*dest++ = ((code >> 6) & 0x3F) | 0x80;
*dest++ = (code & 0x3F) | 0x80;
} else {
/* Error... */
*dest++ = '?';
/* *src may be valid. Don't eat it. */
src--;
}
code_high = 0;
} else {
if (code <= 0x007F) {
*dest++ = code;
} else if (code <= 0x07FF) {
*dest++ = (code >> 6) | 0xC0;
*dest++ = (code & 0x3F) | 0x80;
} else if (code >= 0xD800 && code <= 0xDBFF) {
code_high = code;
continue;
} else if (code >= 0xDC00 && code <= 0xDFFF) {
/* Error... */
*dest++ = '?';
The error handling is somewhat inconsistent:
No output if code 0xD800-0xDBFF is the last word. Output '?' for 0xDC00-0xDFFF where not expected. Output extraneous '?' for 0xD800-0xDBFF not followed by 0xDC00-0xDBFF.
Best regards
Heinrich
} else if (code < 0x10000) {
*dest++ = (code >> 12) | 0xE0;
*dest++ = ((code >> 6) & 0x3F) | 0x80;
*dest++ = (code & 0x3F) | 0x80;
} else {
*dest++ = (code >> 18) | 0xF0;
*dest++ = ((code >> 12) & 0x3F) | 0x80;
*dest++ = ((code >> 6) & 0x3F) | 0x80;
*dest++ = (code & 0x3F) | 0x80;
}
}
- }
- return dest;
+} diff --git a/include/charset.h b/include/charset.h new file mode 100644 index 0000000000..2ee1172182 --- /dev/null +++ b/include/charset.h @@ -0,0 +1,18 @@ +/*
- charset conversion utils
- Copyright (c) 2017 Rob Clark
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __CHARSET_H_ +#define __CHARSET_H_
+#define MAX_UTF8_PER_UTF16 4
+size_t utf16_strlen(uint16_t *in); +size_t utf16_strnlen(const uint16_t *in, size_t count); +uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size);
+#endif /* __CHARSET_H_ */ diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 5ebce4b544..3fc82b8726 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -7,6 +7,7 @@ */
#include <common.h> +#include <charset.h> #include <efi_loader.h>
static bool console_size_queried; @@ -138,20 +139,8 @@ static efi_status_t EFIAPI efi_cout_reset(
static void print_unicode_in_utf8(u16 c) {
- char utf8[4] = { 0 };
- char *b = utf8;
- if (c < 0x80) {
*(b++) = c;
- } else if (c < 0x800) {
*(b++) = 192 + c / 64;
*(b++) = 128 + c % 64;
- } else {
*(b++) = 224 + c / 4096;
*(b++) = 128 + c / 64 % 64;
*(b++) = 128 + c % 64;
- }
- char utf8[MAX_UTF8_PER_UTF16] = { 0 };
- utf16_to_utf8((u8 *)utf8, &c, 1); puts(utf8);
}

On Tue, Aug 8, 2017 at 6:50 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote:
We'll eventually want these in a few places in efi_loader, and also vsprintf.
Signed-off-by: Rob Clark robdclark@gmail.com
common/Makefile | 1 + common/charset.c | 81 ++++++++++++++++++++++++++++++++++++++++++++ include/charset.h | 18 ++++++++++ lib/efi_loader/efi_console.c | 17 ++-------- 4 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 common/charset.c create mode 100644 include/charset.h
diff --git a/common/Makefile b/common/Makefile index 60681c845c..44c8e1ba52 100644 --- a/common/Makefile +++ b/common/Makefile @@ -175,5 +175,6 @@ obj-$(CONFIG_CMD_DFU) += dfu.o obj-y += command.o obj-y += s_record.o obj-y += xyzModem.o +obj-y += charset.o
CFLAGS_env_embedded.o := -Wa,--no-warn -DENV_CRC=$(shell tools/envcrc 2>/dev/null) diff --git a/common/charset.c b/common/charset.c new file mode 100644 index 0000000000..eaff2e542e --- /dev/null +++ b/common/charset.c @@ -0,0 +1,81 @@ +/*
- charset conversion utils
- Copyright (c) 2017 Rob Clark
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <charset.h>
+/*
- utf8/utf16 conversion mostly lifted from grub
- */
+size_t utf16_strlen(uint16_t *in) +{
size_t i;
for (i = 0; in[i]; i++);
return i;
+}
+size_t utf16_strnlen(const uint16_t *in, size_t count) +{
size_t i;
for (i = 0; count-- && in[i]; i++);
return i;
+}
+/* Convert UTF-16 to UTF-8. */ +uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size) +{
uint32_t code_high = 0;
while (size--) {
We should not read past the trailing null world. Check *src == 0 somewhere.
so, all the places this is used in u-boot, and all the places I've seen it used in grub (which is where this code comes from.. I won't claim to the a utfN expert, this is the first time I've looked at this sort of thing), you already know the string length.. either via "protocol" (ie. you know the size of the file-path efi_device_path element) or you have to do one of the utf16_strlen() variants before calling this to know the size of the output string.
so utf16_to_utf8() shouldn't rely on null terminators, that seems like it is just a sign the caller is doing something wrong.
Not sure if there is an equiv to WARN_ON() in u-boot.. maybe just assert()? But checking for null should be more of an assert()/WARN_ON() sort of thing, imho.
I'll add an assert(size == utf16_strnlen(src, size)) (unless someone has something better to suggest)
uint32_t code = *src++;
if (code_high) {
if (code >= 0xDC00 && code <= 0xDFFF) {
/* Surrogate pair. */
code = ((code_high - 0xD800) << 10) + (code - 0xDC00) + 0x10000;
*dest++ = (code >> 18) | 0xF0;
*dest++ = ((code >> 12) & 0x3F) | 0x80;
*dest++ = ((code >> 6) & 0x3F) | 0x80;
*dest++ = (code & 0x3F) | 0x80;
} else {
/* Error... */
*dest++ = '?';
/* *src may be valid. Don't eat it. */
src--;
}
code_high = 0;
} else {
if (code <= 0x007F) {
*dest++ = code;
} else if (code <= 0x07FF) {
*dest++ = (code >> 6) | 0xC0;
*dest++ = (code & 0x3F) | 0x80;
} else if (code >= 0xD800 && code <= 0xDBFF) {
code_high = code;
continue;
} else if (code >= 0xDC00 && code <= 0xDFFF) {
/* Error... */
*dest++ = '?';
The error handling is somewhat inconsistent:
No output if code 0xD800-0xDBFF is the last word. Output '?' for 0xDC00-0xDFFF where not expected. Output extraneous '?' for 0xD800-0xDBFF not followed by 0xDC00-0xDBFF.
if you have something better to suggest I look at let me know. Seems like (and I'm assuming the grub code can't be too bad since it boots a whole lot of linux systems every day) this should only happen with a malformed utf16 string?
(for the record, my only contribution to this code is utf16_strnlen() and comments (and correcting grub2's painful indentation style :-P))
BR, -R
Best regards
Heinrich
} else if (code < 0x10000) {
*dest++ = (code >> 12) | 0xE0;
*dest++ = ((code >> 6) & 0x3F) | 0x80;
*dest++ = (code & 0x3F) | 0x80;
} else {
*dest++ = (code >> 18) | 0xF0;
*dest++ = ((code >> 12) & 0x3F) | 0x80;
*dest++ = ((code >> 6) & 0x3F) | 0x80;
*dest++ = (code & 0x3F) | 0x80;
}
}
}
return dest;
+} diff --git a/include/charset.h b/include/charset.h new file mode 100644 index 0000000000..2ee1172182 --- /dev/null +++ b/include/charset.h @@ -0,0 +1,18 @@ +/*
- charset conversion utils
- Copyright (c) 2017 Rob Clark
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __CHARSET_H_ +#define __CHARSET_H_
+#define MAX_UTF8_PER_UTF16 4
+size_t utf16_strlen(uint16_t *in); +size_t utf16_strnlen(const uint16_t *in, size_t count); +uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size);
+#endif /* __CHARSET_H_ */ diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 5ebce4b544..3fc82b8726 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -7,6 +7,7 @@ */
#include <common.h> +#include <charset.h> #include <efi_loader.h>
static bool console_size_queried; @@ -138,20 +139,8 @@ static efi_status_t EFIAPI efi_cout_reset(
static void print_unicode_in_utf8(u16 c) {
char utf8[4] = { 0 };
char *b = utf8;
if (c < 0x80) {
*(b++) = c;
} else if (c < 0x800) {
*(b++) = 192 + c / 64;
*(b++) = 128 + c % 64;
} else {
*(b++) = 224 + c / 4096;
*(b++) = 128 + c / 64 % 64;
*(b++) = 128 + c % 64;
}
char utf8[MAX_UTF8_PER_UTF16] = { 0 };
utf16_to_utf8((u8 *)utf8, &c, 1); puts(utf8);
}

This is convenient for efi_loader which deals a lot with utf16.
Signed-off-by: Rob Clark robdclark@gmail.com --- lib/vsprintf.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 874a2951f7..0c40f852ce 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -17,6 +17,7 @@ #include <linux/ctype.h>
#include <common.h> +#include <charset.h>
#include <div64.h> #define noinline __attribute__((noinline)) @@ -270,6 +271,26 @@ static char *string(char *buf, char *end, char *s, int field_width, return buf; }
+static char *string16(char *buf, char *end, u16 *s, int field_width, + int precision, int flags) +{ + u16 *str = s ? s : L"<NULL>"; + int len = utf16_strnlen(str, precision); + u8 utf8[len * MAX_UTF8_PER_UTF16]; + int i; + + *utf16_to_utf8(utf8, str, len) = '\0'; + + if (!(flags & LEFT)) + while (len < field_width--) + ADDCH(buf, ' '); + for (i = 0; i < len; ++i) + ADDCH(buf, utf8[i]); + while (len < field_width--) + ADDCH(buf, ' '); + return buf; +} + #ifdef CONFIG_CMD_NET static const char hex_asc[] = "0123456789abcdef"; #define hex_asc_lo(x) hex_asc[((x) & 0x0f)] @@ -528,8 +549,13 @@ repeat: continue;
case 's': - str = string(str, end, va_arg(args, char *), - field_width, precision, flags); + if (qualifier == 'l') { + str = string16(str, end, va_arg(args, u16 *), + field_width, precision, flags); + } else { + str = string(str, end, va_arg(args, char *), + field_width, precision, flags); + } continue;
case 'p':

On 08/04/2017 09:31 PM, Rob Clark wrote:
This is convenient for efi_loader which deals a lot with utf16.
Signed-off-by: Rob Clark robdclark@gmail.com
Please, put this patch together with [PATCH] vsprintf.c: add GUID printing https://patchwork.ozlabs.org/patch/798362/ and [PATCH v0 06/20] common: add some utf16 handling helpers https://patchwork.ozlabs.org/patch/797968/ into a separate patch series.
These three patches can be reviewed independently of the efi_loader patches and probably will not be integrated via the efi-next tree.
lib/vsprintf.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 874a2951f7..0c40f852ce 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -17,6 +17,7 @@ #include <linux/ctype.h>
#include <common.h> +#include <charset.h>
#include <div64.h> #define noinline __attribute__((noinline)) @@ -270,6 +271,26 @@ static char *string(char *buf, char *end, char *s, int field_width, return buf; }
+static char *string16(char *buf, char *end, u16 *s, int field_width,
int precision, int flags)
+{
- u16 *str = s ? s : L"<NULL>";
Please, do not use the L-notation here as it requires -fshort-wchar. As we currently cannot switch the complete project to C11 you cannot use the u-notation either.
- int len = utf16_strnlen(str, precision);
- u8 utf8[len * MAX_UTF8_PER_UTF16];
Didn't you forget 1 byte for \0 here?
This is what strlnlen does:
The strnlen() function returns the number of characters in the string pointed to by s, **excluding** the terminating null byte ('\0'), but at most maxlen.
I would expect the exclusion of the terminating null word by an utf16_strnlen function.
- int i;
- *utf16_to_utf8(utf8, str, len) = '\0';
- if (!(flags & LEFT))
while (len < field_width--)
ADDCH(buf, ' ');
- for (i = 0; i < len; ++i)
ADDCH(buf, utf8[i]);
- while (len < field_width--)
ADDCH(buf, ' ');
- return buf;
+}
#ifdef CONFIG_CMD_NET static const char hex_asc[] = "0123456789abcdef"; #define hex_asc_lo(x) hex_asc[((x) & 0x0f)] @@ -528,8 +549,13 @@ repeat: continue;
case 's':
str = string(str, end, va_arg(args, char *),
field_width, precision, flags);
if (qualifier == 'l') {
%ls refers to wchar with implementation dependent width in the C standard. There is no qualifier for 16-bit wchar. Couldn't we use %us here in reference to the u-notation ( u'MyString' ). This would leave the path open for a standard compliant '%ls'.
Best regards
Heinrich
str = string16(str, end, va_arg(args, u16 *),
field_width, precision, flags);
} else {
str = string(str, end, va_arg(args, char *),
field_width, precision, flags);
} continue;
case 'p':

On Tue, Aug 8, 2017 at 6:03 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote:
This is convenient for efi_loader which deals a lot with utf16.
Signed-off-by: Rob Clark robdclark@gmail.com
Please, put this patch together with [PATCH] vsprintf.c: add GUID printing https://patchwork.ozlabs.org/patch/798362/ and [PATCH v0 06/20] common: add some utf16 handling helpers https://patchwork.ozlabs.org/patch/797968/ into a separate patch series.
These three patches can be reviewed independently of the efi_loader patches and probably will not be integrated via the efi-next tree.
I'll resend these as a separate patchset, and just not in next revision of efi_loader patchset that it is a dependency
lib/vsprintf.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 874a2951f7..0c40f852ce 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -17,6 +17,7 @@ #include <linux/ctype.h>
#include <common.h> +#include <charset.h>
#include <div64.h> #define noinline __attribute__((noinline)) @@ -270,6 +271,26 @@ static char *string(char *buf, char *end, char *s, int field_width, return buf; }
+static char *string16(char *buf, char *end, u16 *s, int field_width,
int precision, int flags)
+{
u16 *str = s ? s : L"<NULL>";
Please, do not use the L-notation here as it requires -fshort-wchar. As we currently cannot switch the complete project to C11 you cannot use the u-notation either.
current plan was to either switch whole project to -fshort-wchar or c11 and rework these patches (as well as a few patches in the efi_loader patchset). (In the c11 case, I'm not sure what we'll use as the fmt string, since afaict that isn't specified. We could use %S although that seems to be a deprecated way to do %ls, or something different like %A, I guess)..
how far are we from c11? If there is stuff I can do to help let me know. If feasible, I'd rather do that first rather than have a bunch of stuff in vsprintf and elsewhere that needs to be cleaned up later after the switch.
int len = utf16_strnlen(str, precision);
u8 utf8[len * MAX_UTF8_PER_UTF16];
Didn't you forget 1 byte for \0 here?
This is what strlnlen does:
The strnlen() function returns the number of characters in the string pointed to by s, **excluding** the terminating null byte ('\0'), but at most maxlen.
I would expect the exclusion of the terminating null word by an utf16_strnlen function.
you are right, but fixing the wrong problem.. the code is definitely wrong since length of a utf16 string != length of a utf8 string, and we don't need to append a null terminator.. so my logic below using 'len' is wrong. I'll fix that in the next version.
int i;
*utf16_to_utf8(utf8, str, len) = '\0';
if (!(flags & LEFT))
while (len < field_width--)
ADDCH(buf, ' ');
for (i = 0; i < len; ++i)
ADDCH(buf, utf8[i]);
while (len < field_width--)
ADDCH(buf, ' ');
return buf;
+}
#ifdef CONFIG_CMD_NET static const char hex_asc[] = "0123456789abcdef"; #define hex_asc_lo(x) hex_asc[((x) & 0x0f)] @@ -528,8 +549,13 @@ repeat: continue;
case 's':
str = string(str, end, va_arg(args, char *),
field_width, precision, flags);
if (qualifier == 'l') {
%ls refers to wchar with implementation dependent width in the C standard. There is no qualifier for 16-bit wchar. Couldn't we use %us here in reference to the u-notation ( u'MyString' ). This would leave the path open for a standard compliant '%ls'.
hmm, yeah, that is a clever idea, and I like it better than %A or %S.. so if we go the c11 route I'll do that. The c11 committee should have thought of that ;-)
BR, -R
Best regards
Heinrich
str = string16(str, end, va_arg(args, u16 *),
field_width, precision, flags);
} else {
str = string(str, end, va_arg(args, char *),
field_width, precision, flags);
} continue; case 'p':

On 08/09/2017 12:44 AM, Rob Clark wrote:
On Tue, Aug 8, 2017 at 6:03 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote:
This is convenient for efi_loader which deals a lot with utf16.
Signed-off-by: Rob Clark robdclark@gmail.com
Please, put this patch together with [PATCH] vsprintf.c: add GUID printing https://patchwork.ozlabs.org/patch/798362/ and [PATCH v0 06/20] common: add some utf16 handling helpers https://patchwork.ozlabs.org/patch/797968/ into a separate patch series.
These three patches can be reviewed independently of the efi_loader patches and probably will not be integrated via the efi-next tree.
I'll resend these as a separate patchset, and just not in next revision of efi_loader patchset that it is a dependency
lib/vsprintf.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 874a2951f7..0c40f852ce 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -17,6 +17,7 @@ #include <linux/ctype.h>
#include <common.h> +#include <charset.h>
#include <div64.h> #define noinline __attribute__((noinline)) @@ -270,6 +271,26 @@ static char *string(char *buf, char *end, char *s, int field_width, return buf; }
+static char *string16(char *buf, char *end, u16 *s, int field_width,
int precision, int flags)
+{
u16 *str = s ? s : L"<NULL>";
Please, do not use the L-notation here as it requires -fshort-wchar. As we currently cannot switch the complete project to C11 you cannot use the u-notation either.
current plan was to either switch whole project to -fshort-wchar or c11 and rework these patches (as well as a few patches in the efi_loader patchset). (In the c11 case, I'm not sure what we'll use as the fmt string, since afaict that isn't specified. We could use %S although that seems to be a deprecated way to do %ls, or something different like %A, I guess)..
how far are we from c11? If there is stuff I can do to help let me know. If feasible, I'd rather do that first rather than have a bunch of stuff in vsprintf and elsewhere that needs to be cleaned up later after the switch.
buildman downloads very old compilers (gcc < 4.8) from kernel.org which do not support C11. Travis CI uses Ubuntu 14.04 with gcc 4.8.4 which incorrectly throws an error for disk/part.c in C11 mode.
To get things right we would have to * build our own cross tool chains based on a current gcc version * use our own tool chain in Travis for x86-64 or use a docker container with a current gcc version.
In the long run heading for C11 would be the right thing to do. Until then use an initializer { '<', 'N', 'U', 'L', 'L', '>' }. It looks ugly but does not consume more bytes once compiled.
Regards
Heinrich
int len = utf16_strnlen(str, precision);
u8 utf8[len * MAX_UTF8_PER_UTF16];
Didn't you forget 1 byte for \0 here?
This is what strlnlen does:
The strnlen() function returns the number of characters in the string pointed to by s, **excluding** the terminating null byte ('\0'), but at most maxlen.
I would expect the exclusion of the terminating null word by an utf16_strnlen function.
you are right, but fixing the wrong problem.. the code is definitely wrong since length of a utf16 string != length of a utf8 string, and we don't need to append a null terminator.. so my logic below using 'len' is wrong. I'll fix that in the next version.
int i;
*utf16_to_utf8(utf8, str, len) = '\0';
if (!(flags & LEFT))
while (len < field_width--)
ADDCH(buf, ' ');
for (i = 0; i < len; ++i)
ADDCH(buf, utf8[i]);
while (len < field_width--)
ADDCH(buf, ' ');
return buf;
+}
#ifdef CONFIG_CMD_NET static const char hex_asc[] = "0123456789abcdef"; #define hex_asc_lo(x) hex_asc[((x) & 0x0f)] @@ -528,8 +549,13 @@ repeat: continue;
case 's':
str = string(str, end, va_arg(args, char *),
field_width, precision, flags);
if (qualifier == 'l') {
%ls refers to wchar with implementation dependent width in the C standard. There is no qualifier for 16-bit wchar. Couldn't we use %us here in reference to the u-notation ( u'MyString' ). This would leave the path open for a standard compliant '%ls'.
hmm, yeah, that is a clever idea, and I like it better than %A or %S.. so if we go the c11 route I'll do that. The c11 committee should have thought of that ;-)
BR, -R
Best regards
Heinrich
str = string16(str, end, va_arg(args, u16 *),
field_width, precision, flags);
} else {
str = string(str, end, va_arg(args, char *),
field_width, precision, flags);
} continue; case 'p':

Am 09.08.2017 um 00:08 schrieb Heinrich Schuchardt xypron.glpk@gmx.de:
On 08/09/2017 12:44 AM, Rob Clark wrote:
On Tue, Aug 8, 2017 at 6:03 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote: This is convenient for efi_loader which deals a lot with utf16.
Signed-off-by: Rob Clark robdclark@gmail.com
Please, put this patch together with [PATCH] vsprintf.c: add GUID printing https://patchwork.ozlabs.org/patch/798362/ and [PATCH v0 06/20] common: add some utf16 handling helpers https://patchwork.ozlabs.org/patch/797968/ into a separate patch series.
These three patches can be reviewed independently of the efi_loader patches and probably will not be integrated via the efi-next tree.
I'll resend these as a separate patchset, and just not in next revision of efi_loader patchset that it is a dependency
lib/vsprintf.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 874a2951f7..0c40f852ce 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -17,6 +17,7 @@ #include <linux/ctype.h>
#include <common.h> +#include <charset.h>
#include <div64.h> #define noinline __attribute__((noinline)) @@ -270,6 +271,26 @@ static char *string(char *buf, char *end, char *s, int field_width, return buf; }
+static char *string16(char *buf, char *end, u16 *s, int field_width,
int precision, int flags)
+{
u16 *str = s ? s : L"<NULL>";
Please, do not use the L-notation here as it requires -fshort-wchar. As we currently cannot switch the complete project to C11 you cannot use the u-notation either.
current plan was to either switch whole project to -fshort-wchar or c11 and rework these patches (as well as a few patches in the efi_loader patchset). (In the c11 case, I'm not sure what we'll use as the fmt string, since afaict that isn't specified. We could use %S although that seems to be a deprecated way to do %ls, or something different like %A, I guess)..
how far are we from c11? If there is stuff I can do to help let me know. If feasible, I'd rather do that first rather than have a bunch of stuff in vsprintf and elsewhere that needs to be cleaned up later after the switch.
buildman downloads very old compilers (gcc < 4.8) from kernel.org which do not support C11. Travis CI uses Ubuntu 14.04 with gcc 4.8.4 which incorrectly throws an error for disk/part.c in C11 mode.
To get things right we would have to
- build our own cross tool chains based on a current gcc version
- use our own tool chain in Travis for x86-64 or use a docker
container with a current gcc version.
In the long run heading for C11 would be the right thing to do. Until then use an initializer { '<', 'N', 'U', 'L', 'L', '>' }.
Don't forget the , 0 :)
Alex
It looks ugly but does not consume more bytes once compiled.
Regards
Heinrich
int len = utf16_strnlen(str, precision);
u8 utf8[len * MAX_UTF8_PER_UTF16];
Didn't you forget 1 byte for \0 here?
This is what strlnlen does:
The strnlen() function returns the number of characters in the string pointed to by s, **excluding** the terminating null byte ('\0'), but at most maxlen.
I would expect the exclusion of the terminating null word by an utf16_strnlen function.
you are right, but fixing the wrong problem.. the code is definitely wrong since length of a utf16 string != length of a utf8 string, and we don't need to append a null terminator.. so my logic below using 'len' is wrong. I'll fix that in the next version.
int i;
*utf16_to_utf8(utf8, str, len) = '\0';
if (!(flags & LEFT))
while (len < field_width--)
ADDCH(buf, ' ');
for (i = 0; i < len; ++i)
ADDCH(buf, utf8[i]);
while (len < field_width--)
ADDCH(buf, ' ');
return buf;
+}
#ifdef CONFIG_CMD_NET static const char hex_asc[] = "0123456789abcdef"; #define hex_asc_lo(x) hex_asc[((x) & 0x0f)] @@ -528,8 +549,13 @@ repeat: continue;
case 's':
str = string(str, end, va_arg(args, char *),
field_width, precision, flags);
if (qualifier == 'l') {
%ls refers to wchar with implementation dependent width in the C standard. There is no qualifier for 16-bit wchar. Couldn't we use %us here in reference to the u-notation ( u'MyString' ). This would leave the path open for a standard compliant '%ls'.
hmm, yeah, that is a clever idea, and I like it better than %A or %S.. so if we go the c11 route I'll do that. The c11 committee should have thought of that ;-)
BR, -R
Best regards
Heinrich
str = string16(str, end, va_arg(args, u16 *),
field_width, precision, flags);
} else {
str = string(str, end, va_arg(args, char *),
field_width, precision, flags);
} continue; case 'p':

On Tue, Aug 8, 2017 at 7:08 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/09/2017 12:44 AM, Rob Clark wrote:
On Tue, Aug 8, 2017 at 6:03 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote:
This is convenient for efi_loader which deals a lot with utf16.
Signed-off-by: Rob Clark robdclark@gmail.com
Please, put this patch together with [PATCH] vsprintf.c: add GUID printing https://patchwork.ozlabs.org/patch/798362/ and [PATCH v0 06/20] common: add some utf16 handling helpers https://patchwork.ozlabs.org/patch/797968/ into a separate patch series.
These three patches can be reviewed independently of the efi_loader patches and probably will not be integrated via the efi-next tree.
I'll resend these as a separate patchset, and just not in next revision of efi_loader patchset that it is a dependency
lib/vsprintf.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 874a2951f7..0c40f852ce 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -17,6 +17,7 @@ #include <linux/ctype.h>
#include <common.h> +#include <charset.h>
#include <div64.h> #define noinline __attribute__((noinline)) @@ -270,6 +271,26 @@ static char *string(char *buf, char *end, char *s, int field_width, return buf; }
+static char *string16(char *buf, char *end, u16 *s, int field_width,
int precision, int flags)
+{
u16 *str = s ? s : L"<NULL>";
Please, do not use the L-notation here as it requires -fshort-wchar. As we currently cannot switch the complete project to C11 you cannot use the u-notation either.
current plan was to either switch whole project to -fshort-wchar or c11 and rework these patches (as well as a few patches in the efi_loader patchset). (In the c11 case, I'm not sure what we'll use as the fmt string, since afaict that isn't specified. We could use %S although that seems to be a deprecated way to do %ls, or something different like %A, I guess)..
how far are we from c11? If there is stuff I can do to help let me know. If feasible, I'd rather do that first rather than have a bunch of stuff in vsprintf and elsewhere that needs to be cleaned up later after the switch.
buildman downloads very old compilers (gcc < 4.8) from kernel.org which do not support C11. Travis CI uses Ubuntu 14.04 with gcc 4.8.4 which incorrectly throws an error for disk/part.c in C11 mode.
ugg, 4.8 is pretty old.. Not sure how much older than 4.8 buildman uses. It seems like *some* c11 was supported w/ >=4.6 so if we approach the conversion piecemeal (for example skipping code that triggers gcc bugs on old compilers) we might be able to keep 4.8.4 working until travis provides something newer.
(btw, even going back say 8 fedora releases or more, I've used distro packaged arm and aarch64 toolchains exclusively.. are there that many distro's where we really can't assume availability of an cross-toolchain? If there isn't something newer from kernel.org can we just drop relying on ancient prebuilt toolchains? I'm anyways not hugely a fan of downloading binary executables from even kernel.org, instead of using something from a distro build system which I at least know is very locked down.)
To get things right we would have to
- build our own cross tool chains based on a current gcc version
- use our own tool chain in Travis for x86-64 or use a docker container with a current gcc version.
In the long run heading for C11 would be the right thing to do. Until then use an initializer { '<', 'N', 'U', 'L', 'L', '>' }. It looks ugly but does not consume more bytes once compiled.
Sure, that I'm less worried about, as much as adding stuff that is very soon going to be legacy. Even in vfprintf.c it isn't such a big deal, as efi_loader where it would be more cumbersome.
Maybe we can write out u"<NULL>" longhand in vsprintf.c as you suggest, but restrict efi_loader to gcc >= 4.9? That seems like it shouldn't be a problem for any arm/arm64 device and it shouldn't be a problem for any device that is likely to have an efi payload to load in the first place..
BR, -R

On 09.08.17 00:39, Rob Clark wrote:
On Tue, Aug 8, 2017 at 7:08 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/09/2017 12:44 AM, Rob Clark wrote:
On Tue, Aug 8, 2017 at 6:03 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote:
This is convenient for efi_loader which deals a lot with utf16.
Signed-off-by: Rob Clark robdclark@gmail.com
Please, put this patch together with [PATCH] vsprintf.c: add GUID printing https://patchwork.ozlabs.org/patch/798362/ and [PATCH v0 06/20] common: add some utf16 handling helpers https://patchwork.ozlabs.org/patch/797968/ into a separate patch series.
These three patches can be reviewed independently of the efi_loader patches and probably will not be integrated via the efi-next tree.
I'll resend these as a separate patchset, and just not in next revision of efi_loader patchset that it is a dependency
lib/vsprintf.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 874a2951f7..0c40f852ce 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -17,6 +17,7 @@ #include <linux/ctype.h>
#include <common.h> +#include <charset.h>
#include <div64.h> #define noinline __attribute__((noinline)) @@ -270,6 +271,26 @@ static char *string(char *buf, char *end, char *s, int field_width, return buf; }
+static char *string16(char *buf, char *end, u16 *s, int field_width,
int precision, int flags)
+{
u16 *str = s ? s : L"<NULL>";
Please, do not use the L-notation here as it requires -fshort-wchar. As we currently cannot switch the complete project to C11 you cannot use the u-notation either.
current plan was to either switch whole project to -fshort-wchar or c11 and rework these patches (as well as a few patches in the efi_loader patchset). (In the c11 case, I'm not sure what we'll use as the fmt string, since afaict that isn't specified. We could use %S although that seems to be a deprecated way to do %ls, or something different like %A, I guess)..
how far are we from c11? If there is stuff I can do to help let me know. If feasible, I'd rather do that first rather than have a bunch of stuff in vsprintf and elsewhere that needs to be cleaned up later after the switch.
buildman downloads very old compilers (gcc < 4.8) from kernel.org which do not support C11. Travis CI uses Ubuntu 14.04 with gcc 4.8.4 which incorrectly throws an error for disk/part.c in C11 mode.
ugg, 4.8 is pretty old.. Not sure how much older than 4.8 buildman uses. It seems like *some* c11 was supported w/ >=4.6 so if we approach the conversion piecemeal (for example skipping code that triggers gcc bugs on old compilers) we might be able to keep 4.8.4 working until travis provides something newer.
(btw, even going back say 8 fedora releases or more, I've used distro packaged arm and aarch64 toolchains exclusively.. are there that many distro's where we really can't assume availability of an cross-toolchain? If there isn't something newer from kernel.org can we just drop relying on ancient prebuilt toolchains? I'm anyways not hugely a fan of downloading binary executables from even kernel.org, instead of using something from a distro build system which I at least know is very locked down.)
To get things right we would have to
- build our own cross tool chains based on a current gcc version
- use our own tool chain in Travis for x86-64 or use a docker container with a current gcc version.
In the long run heading for C11 would be the right thing to do. Until then use an initializer { '<', 'N', 'U', 'L', 'L', '>' }. It looks ugly but does not consume more bytes once compiled.
Sure, that I'm less worried about, as much as adding stuff that is very soon going to be legacy. Even in vfprintf.c it isn't such a big deal, as efi_loader where it would be more cumbersome.
Maybe we can write out u"<NULL>" longhand in vsprintf.c as you suggest, but restrict efi_loader to gcc >= 4.9? That seems like it shouldn't be a problem for any arm/arm64 device and it shouldn't be a problem for any device that is likely to have an efi payload to load in the first place..
I don't understand? We enable EFI_LOADER on all arm/arm64 systems for a good reason, so they all get checked by travis. If we break travis, that won't do anyone any good.
I do remember however that Tom wanted to set certain compiler versions as minimum required versions. Tom, do you remember which one that was?
Alex

On Tue, Aug 8, 2017 at 7:55 PM, Alexander Graf agraf@suse.de wrote:
On 09.08.17 00:39, Rob Clark wrote:
On Tue, Aug 8, 2017 at 7:08 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/09/2017 12:44 AM, Rob Clark wrote:
On Tue, Aug 8, 2017 at 6:03 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote:
This is convenient for efi_loader which deals a lot with utf16.
Signed-off-by: Rob Clark robdclark@gmail.com
Please, put this patch together with [PATCH] vsprintf.c: add GUID printing https://patchwork.ozlabs.org/patch/798362/ and [PATCH v0 06/20] common: add some utf16 handling helpers https://patchwork.ozlabs.org/patch/797968/ into a separate patch series.
These three patches can be reviewed independently of the efi_loader patches and probably will not be integrated via the efi-next tree.
I'll resend these as a separate patchset, and just not in next revision of efi_loader patchset that it is a dependency
lib/vsprintf.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 874a2951f7..0c40f852ce 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -17,6 +17,7 @@ #include <linux/ctype.h>
#include <common.h> +#include <charset.h>
#include <div64.h> #define noinline __attribute__((noinline)) @@ -270,6 +271,26 @@ static char *string(char *buf, char *end, char *s, int field_width, return buf; }
+static char *string16(char *buf, char *end, u16 *s, int field_width,
int precision, int flags)
+{
u16 *str = s ? s : L"<NULL>";
Please, do not use the L-notation here as it requires -fshort-wchar. As we currently cannot switch the complete project to C11 you cannot use the u-notation either.
current plan was to either switch whole project to -fshort-wchar or c11 and rework these patches (as well as a few patches in the efi_loader patchset). (In the c11 case, I'm not sure what we'll use as the fmt string, since afaict that isn't specified. We could use %S although that seems to be a deprecated way to do %ls, or something different like %A, I guess)..
how far are we from c11? If there is stuff I can do to help let me know. If feasible, I'd rather do that first rather than have a bunch of stuff in vsprintf and elsewhere that needs to be cleaned up later after the switch.
buildman downloads very old compilers (gcc < 4.8) from kernel.org which do not support C11. Travis CI uses Ubuntu 14.04 with gcc 4.8.4 which incorrectly throws an error for disk/part.c in C11 mode.
ugg, 4.8 is pretty old.. Not sure how much older than 4.8 buildman uses. It seems like *some* c11 was supported w/ >=4.6 so if we approach the conversion piecemeal (for example skipping code that triggers gcc bugs on old compilers) we might be able to keep 4.8.4 working until travis provides something newer.
(btw, even going back say 8 fedora releases or more, I've used distro packaged arm and aarch64 toolchains exclusively.. are there that many distro's where we really can't assume availability of an cross-toolchain? If there isn't something newer from kernel.org can we just drop relying on ancient prebuilt toolchains? I'm anyways not hugely a fan of downloading binary executables from even kernel.org, instead of using something from a distro build system which I at least know is very locked down.)
To get things right we would have to
- build our own cross tool chains based on a current gcc version
- use our own tool chain in Travis for x86-64 or use a docker container with a current gcc version.
In the long run heading for C11 would be the right thing to do. Until then use an initializer { '<', 'N', 'U', 'L', 'L', '>' }. It looks ugly but does not consume more bytes once compiled.
Sure, that I'm less worried about, as much as adding stuff that is very soon going to be legacy. Even in vfprintf.c it isn't such a big deal, as efi_loader where it would be more cumbersome.
Maybe we can write out u"<NULL>" longhand in vsprintf.c as you suggest, but restrict efi_loader to gcc >= 4.9? That seems like it shouldn't be a problem for any arm/arm64 device and it shouldn't be a problem for any device that is likely to have an efi payload to load in the first place..
I don't understand? We enable EFI_LOADER on all arm/arm64 systems for a good reason, so they all get checked by travis. If we break travis, that won't do anyone any good.
I was more thinking if there was some oddball non-arm arch that u-boot supported which didn't haven good mainline gcc support and required something ancient ;-)
For arm/arm64, it seems like we could somehow come up w/ a solution using a new enough toolchain, given that arm support in gcc has been good for a long time.. not like the old days where we had to use some codesourcery build (or figure out how to compile the cs src code drop ourselves). A toolchain >= 4.9 for arm/arm64 shouldn't be hard to come by.
BR, -R
I do remember however that Tom wanted to set certain compiler versions as minimum required versions. Tom, do you remember which one that was?
Alex

On Tue, Aug 8, 2017 at 8:14 PM, Rob Clark robdclark@gmail.com wrote:
On Tue, Aug 8, 2017 at 7:55 PM, Alexander Graf agraf@suse.de wrote:
On 09.08.17 00:39, Rob Clark wrote:
On Tue, Aug 8, 2017 at 7:08 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/09/2017 12:44 AM, Rob Clark wrote:
On Tue, Aug 8, 2017 at 6:03 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote: > > This is convenient for efi_loader which deals a lot with utf16. > > Signed-off-by: Rob Clark robdclark@gmail.com
Please, put this patch together with [PATCH] vsprintf.c: add GUID printing https://patchwork.ozlabs.org/patch/798362/ and [PATCH v0 06/20] common: add some utf16 handling helpers https://patchwork.ozlabs.org/patch/797968/ into a separate patch series.
These three patches can be reviewed independently of the efi_loader patches and probably will not be integrated via the efi-next tree.
I'll resend these as a separate patchset, and just not in next revision of efi_loader patchset that it is a dependency
> --- > lib/vsprintf.c | 30 ++++++++++++++++++++++++++++-- > 1 file changed, 28 insertions(+), 2 deletions(-) > > diff --git a/lib/vsprintf.c b/lib/vsprintf.c > index 874a2951f7..0c40f852ce 100644 > --- a/lib/vsprintf.c > +++ b/lib/vsprintf.c > @@ -17,6 +17,7 @@ > #include <linux/ctype.h> > > #include <common.h> > +#include <charset.h> > > #include <div64.h> > #define noinline __attribute__((noinline)) > @@ -270,6 +271,26 @@ static char *string(char *buf, char *end, char > *s, int field_width, > return buf; > } > > +static char *string16(char *buf, char *end, u16 *s, int field_width, > + int precision, int flags) > +{ > + u16 *str = s ? s : L"<NULL>";
Please, do not use the L-notation here as it requires -fshort-wchar. As we currently cannot switch the complete project to C11 you cannot use the u-notation either.
current plan was to either switch whole project to -fshort-wchar or c11 and rework these patches (as well as a few patches in the efi_loader patchset). (In the c11 case, I'm not sure what we'll use as the fmt string, since afaict that isn't specified. We could use %S although that seems to be a deprecated way to do %ls, or something different like %A, I guess)..
how far are we from c11? If there is stuff I can do to help let me know. If feasible, I'd rather do that first rather than have a bunch of stuff in vsprintf and elsewhere that needs to be cleaned up later after the switch.
buildman downloads very old compilers (gcc < 4.8) from kernel.org which do not support C11. Travis CI uses Ubuntu 14.04 with gcc 4.8.4 which incorrectly throws an error for disk/part.c in C11 mode.
ugg, 4.8 is pretty old.. Not sure how much older than 4.8 buildman uses. It seems like *some* c11 was supported w/ >=4.6 so if we approach the conversion piecemeal (for example skipping code that triggers gcc bugs on old compilers) we might be able to keep 4.8.4 working until travis provides something newer.
(btw, even going back say 8 fedora releases or more, I've used distro packaged arm and aarch64 toolchains exclusively.. are there that many distro's where we really can't assume availability of an cross-toolchain? If there isn't something newer from kernel.org can we just drop relying on ancient prebuilt toolchains? I'm anyways not hugely a fan of downloading binary executables from even kernel.org, instead of using something from a distro build system which I at least know is very locked down.)
To get things right we would have to
- build our own cross tool chains based on a current gcc version
- use our own tool chain in Travis for x86-64 or use a docker container with a current gcc version.
In the long run heading for C11 would be the right thing to do. Until then use an initializer { '<', 'N', 'U', 'L', 'L', '>' }. It looks ugly but does not consume more bytes once compiled.
Sure, that I'm less worried about, as much as adding stuff that is very soon going to be legacy. Even in vfprintf.c it isn't such a big deal, as efi_loader where it would be more cumbersome.
Maybe we can write out u"<NULL>" longhand in vsprintf.c as you suggest, but restrict efi_loader to gcc >= 4.9? That seems like it shouldn't be a problem for any arm/arm64 device and it shouldn't be a problem for any device that is likely to have an efi payload to load in the first place..
I don't understand? We enable EFI_LOADER on all arm/arm64 systems for a good reason, so they all get checked by travis. If we break travis, that won't do anyone any good.
I was more thinking if there was some oddball non-arm arch that u-boot supported which didn't haven good mainline gcc support and required something ancient ;-)
For arm/arm64, it seems like we could somehow come up w/ a solution using a new enough toolchain, given that arm support in gcc has been good for a long time.. not like the old days where we had to use some codesourcery build (or figure out how to compile the cs src code drop ourselves). A toolchain >= 4.9 for arm/arm64 shouldn't be hard to come by.
btw, I haven't confirmed it yet (I don't have such an old compiler handy) but I *think* according to [1] that gcc 4.7 should be new enough for u"string" literals.. which is kind of the main thing we want at this point.
that sets the compiler version dependency bar *pretty* low..
[1] https://gcc.gnu.org/gcc-4.7/changes.html
BR, -R

On Tue, Aug 08, 2017 at 08:14:41PM -0400, Rob Clark wrote:
On Tue, Aug 8, 2017 at 7:55 PM, Alexander Graf agraf@suse.de wrote:
On 09.08.17 00:39, Rob Clark wrote:
On Tue, Aug 8, 2017 at 7:08 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/09/2017 12:44 AM, Rob Clark wrote:
On Tue, Aug 8, 2017 at 6:03 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote: > > This is convenient for efi_loader which deals a lot with utf16. > > Signed-off-by: Rob Clark robdclark@gmail.com
Please, put this patch together with [PATCH] vsprintf.c: add GUID printing https://patchwork.ozlabs.org/patch/798362/ and [PATCH v0 06/20] common: add some utf16 handling helpers https://patchwork.ozlabs.org/patch/797968/ into a separate patch series.
These three patches can be reviewed independently of the efi_loader patches and probably will not be integrated via the efi-next tree.
I'll resend these as a separate patchset, and just not in next revision of efi_loader patchset that it is a dependency
> --- > lib/vsprintf.c | 30 ++++++++++++++++++++++++++++-- > 1 file changed, 28 insertions(+), 2 deletions(-) > > diff --git a/lib/vsprintf.c b/lib/vsprintf.c > index 874a2951f7..0c40f852ce 100644 > --- a/lib/vsprintf.c > +++ b/lib/vsprintf.c > @@ -17,6 +17,7 @@ > #include <linux/ctype.h> > > #include <common.h> > +#include <charset.h> > > #include <div64.h> > #define noinline __attribute__((noinline)) > @@ -270,6 +271,26 @@ static char *string(char *buf, char *end, char > *s, int field_width, > return buf; > } > > +static char *string16(char *buf, char *end, u16 *s, int field_width, > + int precision, int flags) > +{ > + u16 *str = s ? s : L"<NULL>";
Please, do not use the L-notation here as it requires -fshort-wchar. As we currently cannot switch the complete project to C11 you cannot use the u-notation either.
current plan was to either switch whole project to -fshort-wchar or c11 and rework these patches (as well as a few patches in the efi_loader patchset). (In the c11 case, I'm not sure what we'll use as the fmt string, since afaict that isn't specified. We could use %S although that seems to be a deprecated way to do %ls, or something different like %A, I guess)..
how far are we from c11? If there is stuff I can do to help let me know. If feasible, I'd rather do that first rather than have a bunch of stuff in vsprintf and elsewhere that needs to be cleaned up later after the switch.
buildman downloads very old compilers (gcc < 4.8) from kernel.org which do not support C11. Travis CI uses Ubuntu 14.04 with gcc 4.8.4 which incorrectly throws an error for disk/part.c in C11 mode.
ugg, 4.8 is pretty old.. Not sure how much older than 4.8 buildman uses. It seems like *some* c11 was supported w/ >=4.6 so if we approach the conversion piecemeal (for example skipping code that triggers gcc bugs on old compilers) we might be able to keep 4.8.4 working until travis provides something newer.
(btw, even going back say 8 fedora releases or more, I've used distro packaged arm and aarch64 toolchains exclusively.. are there that many distro's where we really can't assume availability of an cross-toolchain? If there isn't something newer from kernel.org can we just drop relying on ancient prebuilt toolchains? I'm anyways not hugely a fan of downloading binary executables from even kernel.org, instead of using something from a distro build system which I at least know is very locked down.)
To get things right we would have to
- build our own cross tool chains based on a current gcc version
- use our own tool chain in Travis for x86-64 or use a docker container with a current gcc version.
In the long run heading for C11 would be the right thing to do. Until then use an initializer { '<', 'N', 'U', 'L', 'L', '>' }. It looks ugly but does not consume more bytes once compiled.
Sure, that I'm less worried about, as much as adding stuff that is very soon going to be legacy. Even in vfprintf.c it isn't such a big deal, as efi_loader where it would be more cumbersome.
Maybe we can write out u"<NULL>" longhand in vsprintf.c as you suggest, but restrict efi_loader to gcc >= 4.9? That seems like it shouldn't be a problem for any arm/arm64 device and it shouldn't be a problem for any device that is likely to have an efi payload to load in the first place..
I don't understand? We enable EFI_LOADER on all arm/arm64 systems for a good reason, so they all get checked by travis. If we break travis, that won't do anyone any good.
I was more thinking if there was some oddball non-arm arch that u-boot supported which didn't haven good mainline gcc support and required something ancient ;-)
So, with v2018.01 I want to say that for ARM*, gcc-6.x is the minimum.
Looking around, nds32 requires gcc-4.4.4. NDS32 folks, is there a newer toolchain available?
Other than that, everything else has a gcc-6.x available in some form or another. Thanks!

Hi Tom
Yes, Nds32 has a newer toolchain gcc 4.9 available now.
Rick
-----Original Message----- From: Tom Rini [mailto:trini@konsulko.com] Sent: Wednesday, August 09, 2017 7:27 PM To: Rob Clark; Rick Jian-Zhi Chen(陳建志) Cc: Alexander Graf; Heinrich Schuchardt; U-Boot Mailing List; Peter Jones; Simon Glass; Sekhar Nori; Bin Meng Subject: Re: [U-Boot,v0,07/20] vsprintf.c: add wide string (%ls) support
On Tue, Aug 08, 2017 at 08:14:41PM -0400, Rob Clark wrote:
On Tue, Aug 8, 2017 at 7:55 PM, Alexander Graf agraf@suse.de wrote:
On 09.08.17 00:39, Rob Clark wrote:
On Tue, Aug 8, 2017 at 7:08 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/09/2017 12:44 AM, Rob Clark wrote:
On Tue, Aug 8, 2017 at 6:03 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote: > > This is convenient for efi_loader which deals a lot with utf16. > > Signed-off-by: Rob Clark robdclark@gmail.com
Please, put this patch together with [PATCH] vsprintf.c: add GUID printing https://patchwork.ozlabs.org/patch/798362/ and [PATCH v0 06/20] common: add some utf16 handling helpers https://patchwork.ozlabs.org/patch/797968/ into a separate patch series.
These three patches can be reviewed independently of the efi_loader patches and probably will not be integrated via the efi-next tree.
I'll resend these as a separate patchset, and just not in next revision of efi_loader patchset that it is a dependency
> --- > lib/vsprintf.c | 30 ++++++++++++++++++++++++++++-- > 1 file changed, 28 insertions(+), 2 deletions(-) > > diff --git a/lib/vsprintf.c b/lib/vsprintf.c index > 874a2951f7..0c40f852ce 100644 > --- a/lib/vsprintf.c > +++ b/lib/vsprintf.c > @@ -17,6 +17,7 @@ > #include <linux/ctype.h> > > #include <common.h> > +#include <charset.h> > > #include <div64.h> > #define noinline __attribute__((noinline)) @@ -270,6 +271,26 > @@ static char *string(char *buf, char *end, char *s, int > field_width, > return buf; > } > > +static char *string16(char *buf, char *end, u16 *s, int field_width, > + int precision, int flags) { > + u16 *str = s ? s : L"<NULL>";
Please, do not use the L-notation here as it requires -fshort-wchar. As we currently cannot switch the complete project to C11 you cannot use the u-notation either.
current plan was to either switch whole project to -fshort-wchar or c11 and rework these patches (as well as a few patches in the efi_loader patchset). (In the c11 case, I'm not sure what we'll use as the fmt string, since afaict that isn't specified. We could use %S although that seems to be a deprecated way to do %ls, or something different like %A, I guess)..
how far are we from c11? If there is stuff I can do to help let me know. If feasible, I'd rather do that first rather than have a bunch of stuff in vsprintf and elsewhere that needs to be cleaned up later after the switch.
buildman downloads very old compilers (gcc < 4.8) from kernel.org which do not support C11. Travis CI uses Ubuntu 14.04 with gcc 4.8.4 which incorrectly throws an error for disk/part.c in C11 mode.
ugg, 4.8 is pretty old.. Not sure how much older than 4.8 buildman uses. It seems like *some* c11 was supported w/ >=4.6 so if we approach the conversion piecemeal (for example skipping code that triggers gcc bugs on old compilers) we might be able to keep 4.8.4 working until travis provides something newer.
(btw, even going back say 8 fedora releases or more, I've used distro packaged arm and aarch64 toolchains exclusively.. are there that many distro's where we really can't assume availability of an cross-toolchain? If there isn't something newer from kernel.org can we just drop relying on ancient prebuilt toolchains? I'm anyways not hugely a fan of downloading binary executables from even kernel.org, instead of using something from a distro build system which I at least know is very locked down.)
To get things right we would have to
- build our own cross tool chains based on a current gcc version
- use our own tool chain in Travis for x86-64 or use a docker container with a current gcc version.
In the long run heading for C11 would be the right thing to do. Until then use an initializer { '<', 'N', 'U', 'L', 'L', '>' }. It looks ugly but does not consume more bytes once compiled.
Sure, that I'm less worried about, as much as adding stuff that is very soon going to be legacy. Even in vfprintf.c it isn't such a big deal, as efi_loader where it would be more cumbersome.
Maybe we can write out u"<NULL>" longhand in vsprintf.c as you suggest, but restrict efi_loader to gcc >= 4.9? That seems like it shouldn't be a problem for any arm/arm64 device and it shouldn't be a problem for any device that is likely to have an efi payload to load in the first place..
I don't understand? We enable EFI_LOADER on all arm/arm64 systems for a good reason, so they all get checked by travis. If we break travis, that won't do anyone any good.
I was more thinking if there was some oddball non-arm arch that u-boot supported which didn't haven good mainline gcc support and required something ancient ;-)
So, with v2018.01 I want to say that for ARM*, gcc-6.x is the minimum.
Looking around, nds32 requires gcc-4.4.4. NDS32 folks, is there a newer toolchain available?
Other than that, everything else has a gcc-6.x available in some form or another. Thanks!
-- Tom CONFIDENTIALITY NOTICE:
This e-mail (and its attachments) may contain confidential and legally privileged information or information protected from disclosure. If you are not the intended recipient, you are hereby notified that any disclosure, copying, distribution, or use of the information contained herein is strictly prohibited. In this case, please immediately notify the sender by return e-mail, delete the message (and any accompanying documents) and destroy all printed hard copies. Thank you for your cooperation.
Copyright ANDES TECHNOLOGY CORPORATION - All Rights Reserved.

buildman downloads very old compilers (gcc < 4.8) from kernel.org which do not support C11. Travis CI uses Ubuntu 14.04 with gcc 4.8.4 which incorrectly throws an error for disk/part.c in C11 mode.
ugg, 4.8 is pretty old.. Not sure how much older than 4.8 buildman uses. It seems like *some* c11 was supported w/ >=4.6 so if we approach the conversion piecemeal (for example skipping code that triggers gcc bugs on old compilers) we might be able to keep 4.8.4 working until travis provides something newer.
For reference el7 (RHEL/CentOS etc) has gcc 4.8.5
(btw, even going back say 8 fedora releases or more, I've used distro packaged arm and aarch64 toolchains exclusively.. are there that many distro's where we really can't assume availability of an cross-toolchain? If there isn't something newer from kernel.org can we just drop relying on ancient prebuilt toolchains? I'm anyways not hugely a fan of downloading binary executables from even kernel.org, instead of using something from a distro build system which I at least know is very locked down.)
To get things right we would have to
- build our own cross tool chains based on a current gcc version
- use our own tool chain in Travis for x86-64 or use a docker container with a current gcc version.
In the long run heading for C11 would be the right thing to do. Until then use an initializer { '<', 'N', 'U', 'L', 'L', '>' }. It looks ugly but does not consume more bytes once compiled.
Sure, that I'm less worried about, as much as adding stuff that is very soon going to be legacy. Even in vfprintf.c it isn't such a big deal, as efi_loader where it would be more cumbersome.
Maybe we can write out u"<NULL>" longhand in vsprintf.c as you suggest, but restrict efi_loader to gcc >= 4.9? That seems like it shouldn't be a problem for any arm/arm64 device and it shouldn't be a problem for any device that is likely to have an efi payload to load in the first place..
BR, -R _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

On Tue, Aug 8, 2017 at 6:03 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote:
This is convenient for efi_loader which deals a lot with utf16.
Signed-off-by: Rob Clark robdclark@gmail.com
Please, put this patch together with [PATCH] vsprintf.c: add GUID printing https://patchwork.ozlabs.org/patch/798362/ and [PATCH v0 06/20] common: add some utf16 handling helpers https://patchwork.ozlabs.org/patch/797968/ into a separate patch series.
These three patches can be reviewed independently of the efi_loader patches and probably will not be integrated via the efi-next tree.
lib/vsprintf.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 874a2951f7..0c40f852ce 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -17,6 +17,7 @@ #include <linux/ctype.h>
#include <common.h> +#include <charset.h>
#include <div64.h> #define noinline __attribute__((noinline)) @@ -270,6 +271,26 @@ static char *string(char *buf, char *end, char *s, int field_width, return buf; }
+static char *string16(char *buf, char *end, u16 *s, int field_width,
int precision, int flags)
+{
u16 *str = s ? s : L"<NULL>";
Please, do not use the L-notation here as it requires -fshort-wchar. As we currently cannot switch the complete project to C11 you cannot use the u-notation either.
actually, one thought.. unlike -fshort-wchar, we could probably switch u-boot over to c11 piecemeal, if there are a lot of places where things need to be fixed for c11. For example start by switching efi_loader and vsprintf.c ;-)
(I'm not completely sure what the issues are, so this may or may not make sense.. but if c11 is causing a lot of compile errors all over the place, this might be a reasonable approach.)
BR, -R

On Tue, Aug 8, 2017 at 6:03 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote:
@@ -528,8 +549,13 @@ repeat: continue;
case 's':
str = string(str, end, va_arg(args, char *),
field_width, precision, flags);
if (qualifier == 'l') {
%ls refers to wchar with implementation dependent width in the C standard. There is no qualifier for 16-bit wchar. Couldn't we use %us here in reference to the u-notation ( u'MyString' ). This would leave the path open for a standard compliant '%ls'.
So two drawbacks I'm running into when converting to c11 u"string" style, compared to the -fshort-wchar:
1) with -fshort-wchar plus %ls, gcc knows how to typecheck the printf/sprintf/etc args 2) introducing a non-standard conversion character (since there doesn't seem to be a standard one) means we need to drop -Wformat
So far, afaict, the only argument against -fshort-wchar seems to be that someday ext4 might support utf32 filenames? (And really -fshort-wchar doesn't preclude that. So I'm not sure this is a valid argument.)
So independent of c11 (which might be a good idea for other reasons), I'm back to thinking we should use -fshort-wchar. Possibly as a kconfig option that EFI_LOADER selects.. or possibly just unconditionally.
Thoughts?
BR, -R

Am 09.08.2017 um 14:38 schrieb Rob Clark robdclark@gmail.com:
On Tue, Aug 8, 2017 at 6:03 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote:
@@ -528,8 +549,13 @@ repeat: continue;
case 's':
str = string(str, end, va_arg(args, char *),
field_width, precision, flags);
if (qualifier == 'l') {
%ls refers to wchar with implementation dependent width in the C standard. There is no qualifier for 16-bit wchar. Couldn't we use %us here in reference to the u-notation ( u'MyString' ). This would leave the path open for a standard compliant '%ls'.
So two drawbacks I'm running into when converting to c11 u"string" style, compared to the -fshort-wchar:
- with -fshort-wchar plus %ls, gcc knows how to typecheck the
printf/sprintf/etc args 2) introducing a non-standard conversion character (since there doesn't seem to be a standard one) means we need to drop -Wformat
So far, afaict, the only argument against -fshort-wchar seems to be that someday ext4 might support utf32 filenames? (And really -fshort-wchar doesn't preclude that. So I'm not sure this is a valid argument.)
So independent of c11 (which might be a good idea for other reasons), I'm back to thinking we should use -fshort-wchar. Possibly as a kconfig option that EFI_LOADER selects.. or possibly just unconditionally.
Thoughts?
If we select it, I'd rather have it be unconditional, to not oprn potential for undetected breakage.
Alex
BR, -R

On Wed, Aug 9, 2017 at 9:48 AM, Alexander Graf agraf@suse.de wrote:
Am 09.08.2017 um 14:38 schrieb Rob Clark robdclark@gmail.com:
On Tue, Aug 8, 2017 at 6:03 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote:
@@ -528,8 +549,13 @@ repeat: continue;
case 's':
str = string(str, end, va_arg(args, char *),
field_width, precision, flags);
if (qualifier == 'l') {
%ls refers to wchar with implementation dependent width in the C standard. There is no qualifier for 16-bit wchar. Couldn't we use %us here in reference to the u-notation ( u'MyString' ). This would leave the path open for a standard compliant '%ls'.
So two drawbacks I'm running into when converting to c11 u"string" style, compared to the -fshort-wchar:
- with -fshort-wchar plus %ls, gcc knows how to typecheck the
printf/sprintf/etc args 2) introducing a non-standard conversion character (since there doesn't seem to be a standard one) means we need to drop -Wformat
So far, afaict, the only argument against -fshort-wchar seems to be that someday ext4 might support utf32 filenames? (And really -fshort-wchar doesn't preclude that. So I'm not sure this is a valid argument.)
So independent of c11 (which might be a good idea for other reasons), I'm back to thinking we should use -fshort-wchar. Possibly as a kconfig option that EFI_LOADER selects.. or possibly just unconditionally.
Thoughts?
If we select it, I'd rather have it be unconditional, to not oprn potential for undetected breakage.
I could go either way on kconfig option vs unconditional -fshort-wchar. Although as far as breakage, that seems pretty solvable by adding a fallback.efi -> grub.efi test, and maybe something that exercises device-path-to-text, in travis.
I suppose it might be useful, for example, for TINY_PRINTF to depend on !CC_SHORT_WCHAR? Not sure.
I'll include a patch w/ kconfig option in my patchset for now, but happy to drop it if folks want to do -fshort-wchar unconditionally.
BR, -R

In some cases it is useful to defer creation of the protocol interface object. So add back an optional ->open() hook that is used if protcol_interface is NULL.
I've slightly simplified the fxn ptr signature to remove unneeded args, and so compiler will complain if patches that used the "old way" are, and which do not need this extra complexity, are rebased.
Signed-off-by: Rob Clark robdclark@gmail.com --- include/efi_loader.h | 12 +++++++++++- lib/efi_loader/efi_boottime.c | 30 ++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 7 deletions(-)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 037cc7c543..03c4ed5e1c 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -70,10 +70,17 @@ extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop; /* * When the UEFI payload wants to open a protocol on an object to get its * interface (usually a struct with callback functions), this struct maps the - * protocol GUID to the respective protocol interface */ + * protocol GUID to the respective protocol interface. + * + * The optional ->open() fxn can be used for cases where the protocol + * interface is constructed on-demand, and is called if protocol_interface + * is NULL. + */ struct efi_handler { const efi_guid_t *guid; void *protocol_interface; + efi_status_t (EFIAPI *open)(void *handle, const efi_guid_t *protocol, + void **protocol_interface); };
/* @@ -191,6 +198,9 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, int efi_memory_init(void); /* Adds new or overrides configuration table entry to the system table */ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table); +efi_status_t efi_get_protocol(struct efi_object *efiobj, + struct efi_handler *handler, + void **protocol_interface);
#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER extern void *efi_bounce_buffer; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 59479eddb9..4b78f6d556 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1004,6 +1004,23 @@ out: return EFI_EXIT(r); }
+efi_status_t efi_get_protocol(struct efi_object *efiobj, + struct efi_handler *handler, + void **protocol_interface) +{ + efi_status_t ret = EFI_SUCCESS; + + if (!handler->protocol_interface) { + ret = handler->open(efiobj->handle, + handler->guid, + &handler->protocol_interface); + } + *protocol_interface = + handler->protocol_interface; + + return ret; +} + static efi_status_t EFIAPI efi_locate_protocol(efi_guid_t *protocol, void *registration, void **protocol_interface) @@ -1026,9 +1043,10 @@ static efi_status_t EFIAPI efi_locate_protocol(efi_guid_t *protocol, if (!handler->guid) continue; if (!guidcmp(handler->guid, protocol)) { - *protocol_interface = - handler->protocol_interface; - return EFI_EXIT(EFI_SUCCESS); + efi_status_t ret; + ret = efi_get_protocol(efiobj, handler, + protocol_interface); + return EFI_EXIT(ret); } } } @@ -1162,12 +1180,12 @@ static efi_status_t EFIAPI efi_open_protocol( if (!hprotocol) continue; if (!guidcmp(hprotocol, protocol)) { + r = EFI_SUCCESS; if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL) { - *protocol_interface = - handler->protocol_interface; + r = efi_get_protocol(efiobj, handler, + protocol_interface); } - r = EFI_SUCCESS; goto out; } }

Helpers to construct device-paths from devices, partitions, files, and for parsing and manipulating device-paths.
For non-legacy devices, this will use u-boot's device-model to construct device-paths which include bus hierarchy to construct device-paths. For legacy devices we still fake it, but slightly more convincingly.
Signed-off-by: Rob Clark robdclark@gmail.com --- include/efi_api.h | 10 + include/efi_loader.h | 20 ++ lib/efi_loader/Makefile | 2 +- lib/efi_loader/efi_device_path.c | 485 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 516 insertions(+), 1 deletion(-) create mode 100644 lib/efi_loader/efi_device_path.c
diff --git a/include/efi_api.h b/include/efi_api.h index 85afbeb72b..0ebe8d0283 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -305,6 +305,7 @@ struct efi_device_path_acpi_path { #define DEVICE_PATH_TYPE_MESSAGING_DEVICE 0x03 # define DEVICE_PATH_SUB_TYPE_MSG_USB 0x05 # define DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR 0x0b +# define DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS 0x0f # define DEVICE_PATH_SUB_TYPE_MSG_SD 0x1a # define DEVICE_PATH_SUB_TYPE_MSG_MMC 0x1d
@@ -320,6 +321,15 @@ struct efi_device_path_mac_addr { u8 if_type; } __packed;
+struct efi_device_path_usb_class { + struct efi_device_path dp; + u16 vendor_id; + u16 product_id; + u8 device_class; + u8 device_subclass; + u8 device_protocol; +} __packed; + struct efi_device_path_sd_mmc_path { struct efi_device_path dp; u8 slot_number; diff --git a/include/efi_loader.h b/include/efi_loader.h index 03c4ed5e1c..1028bfb75d 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -207,6 +207,26 @@ extern void *efi_bounce_buffer; #define EFI_LOADER_BOUNCE_BUFFER_SIZE (64 * 1024 * 1024) #endif
+ +struct efi_device_path *efi_dp_next(struct efi_device_path *dp); +int efi_dp_match(struct efi_device_path *a, struct efi_device_path *b); +struct efi_object *efi_dp_find_obj(struct efi_device_path *dp); +unsigned efi_dp_size(struct efi_device_path *dp); +struct efi_device_path *efi_dp_dup(struct efi_device_path *dp); + +struct efi_device_path *efi_dp_from_dev(struct udevice *dev); +struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part); +struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part, + const char *path); +struct efi_device_path *efi_dp_from_eth(void); +void efi_dp_split_file_path(struct efi_device_path *full_path, + struct efi_device_path **device_path, + struct efi_device_path **file_path); + +#define EFI_DP_TYPE(_dp, _type, _subtype) \ + (((_dp)->type == DEVICE_PATH_TYPE_##_type) && \ + ((_dp)->sub_type == DEVICE_PATH_SUB_TYPE_##_subtype)) + /* Convert strings from normal C strings to uEFI strings */ static inline void ascii2unicode(u16 *unicode, const char *ascii) { diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 30bf343a36..f35e5ce8a8 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -15,7 +15,7 @@ always := $(efiprogs-y)
obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o -obj-y += efi_memory.o efi_device_path_to_text.o +obj-y += efi_memory.o efi_device_path_to_text.o efi_device_path.o obj-$(CONFIG_LCD) += efi_gop.o obj-$(CONFIG_DM_VIDEO) += efi_gop.o obj-$(CONFIG_PARTITIONS) += efi_disk.o diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c new file mode 100644 index 0000000000..b5acf73f98 --- /dev/null +++ b/lib/efi_loader/efi_device_path.c @@ -0,0 +1,485 @@ +/* + * EFI device path from u-boot device-model mapping + * + * (C) Copyright 2017 Rob Clark + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <blk.h> +#include <dm.h> +#include <usb.h> +#include <mmc.h> +#include <efi_loader.h> +#include <inttypes.h> +#include <part.h> +#include <malloc.h> + +/* template END node: */ +const static struct efi_device_path END = { + .type = DEVICE_PATH_TYPE_END, + .sub_type = DEVICE_PATH_SUB_TYPE_END, + .length = sizeof(END), +}; + +/* template ROOT node, a fictional ACPI PNP device: */ +const static struct efi_device_path_acpi_path ROOT = { + .dp = { + .type = DEVICE_PATH_TYPE_ACPI_DEVICE, + .sub_type = DEVICE_PATH_SUB_TYPE_ACPI_DEVICE, + .length = sizeof(ROOT), + }, + .hid = EISA_PNP_ID(0x1337), + .uid = 0, +}; + + +/* + * Iterate to next block in device-path, terminating (returning NULL) + * at /End* node. + */ +struct efi_device_path *efi_dp_next(struct efi_device_path *dp) +{ + if (dp == NULL) + return NULL; + dp = ((void *)dp) + dp->length; + if (dp->type == DEVICE_PATH_TYPE_END) + return NULL; + return dp; +} + +/* + * Compare two device-paths, stopping when the shorter of the two hits + * an End* node. This is useful to, for example, compare a device-path + * representing a device with one representing a file on the device, or + * a device with a parent device. + */ +int efi_dp_match(struct efi_device_path *a, struct efi_device_path *b) +{ + while (1) { + int ret; + + ret = memcmp(&a->length, &b->length, sizeof(a->length)); + if (ret) + return ret; + + ret = memcmp(a, b, a->length); + if (ret) + return ret; + + a = efi_dp_next(a); + b = efi_dp_next(b); + + if (!a || !b) + return 0; + } +} + + +/* + * See UEFI spec (section 3.1.2, about short-form device-paths.. + * tl;dr: we can have a device-path that starts with a USB WWID + * or USB Class node, and a few other cases which don't encode + * the full device path with bus hierarchy: + * + * - MESSAGING:USB_WWID + * - MESSAGING:USB_CLASS + * - MEDIA:FILE_PATH + * - MEDIA:HARD_DRIVE + * - MESSAGING:URI + */ +static struct efi_device_path *shorten_path(struct efi_device_path *dp) +{ + while (dp) { + /* + * TODO: Add MESSAGING:USB_WWID and MESSAGING:URI.. + * in practice fallback.efi just uses MEDIA:HARD_DRIVE + * so not sure when we would see these other cases. + */ + if (EFI_DP_TYPE(dp, MESSAGING_DEVICE, MSG_USB_CLASS) || + EFI_DP_TYPE(dp, MEDIA_DEVICE, HARD_DRIVE_PATH) || + EFI_DP_TYPE(dp, MEDIA_DEVICE, FILE_PATH)) + return dp; + + dp = efi_dp_next(dp); + } + + return dp; +} + +static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path) +{ + struct efi_object *efiobj; + + list_for_each_entry(efiobj, &efi_obj_list, link) { + int i; + + for (i = 0; i < ARRAY_SIZE(efiobj->protocols); i++) { + struct efi_handler *handler = &efiobj->protocols[i]; + struct efi_device_path *obj_dp; + efi_status_t ret; + + if (!handler->guid) + break; + + if (guidcmp(handler->guid, &efi_guid_device_path)) + continue; + + ret = efi_get_protocol(efiobj, handler, (void **)&obj_dp); + if (ret != EFI_SUCCESS) + continue; + + do { + if (efi_dp_match(dp, obj_dp) == 0) + return efiobj; + + obj_dp = shorten_path(efi_dp_next(obj_dp)); + } while (short_path && obj_dp); + } + } + + return NULL; +} + + +/* Find an efiobj from device-path */ +struct efi_object *efi_dp_find_obj(struct efi_device_path *dp) +{ + struct efi_object *efiobj; + + efiobj = find_obj(dp, false); + + if (!efiobj) + efiobj = find_obj(dp, true); + + return efiobj; +} + +/* return size not including End node: */ +unsigned efi_dp_size(struct efi_device_path *dp) +{ + unsigned sz = 0; + + while (dp) { + sz += dp->length; + dp = efi_dp_next(dp); + } + + return sz; +} + +struct efi_device_path *efi_dp_dup(struct efi_device_path *dp) +{ + struct efi_device_path *ndp; + unsigned sz = efi_dp_size(dp) + sizeof(struct efi_device_path); + + ndp = malloc(sz); + memcpy(ndp, dp, sz); + + return ndp; +} + +#ifdef CONFIG_DM +/* size of device-path not including END node for device and all parents + * up to the root device. + */ +static unsigned dp_size(struct udevice *dev) +{ + if (!dev || !dev->driver) + return sizeof(ROOT); + + switch (dev->driver->id) { + case UCLASS_ROOT: + case UCLASS_SIMPLE_BUS: + /* stop traversing parents at this point: */ + return sizeof(ROOT); + case UCLASS_MMC: + return dp_size(dev->parent) + sizeof(struct efi_device_path_sd_mmc_path); + case UCLASS_MASS_STORAGE: + case UCLASS_USB_HUB: + return dp_size(dev->parent) + sizeof(struct efi_device_path_usb_class); + default: + /* just skip over unknown classes: */ + return dp_size(dev->parent); + } +} + +static void *dp_fill(void *buf, struct udevice *dev) +{ + if (!dev || !dev->driver) + return buf; + + switch (dev->driver->id) { + case UCLASS_ROOT: + case UCLASS_SIMPLE_BUS: { + /* stop traversing parents at this point: */ + struct efi_device_path_acpi_path *adp = buf; + *adp = ROOT; + return &adp[1]; + } +#if defined(CONFIG_DM_MMC) && defined (CONFIG_MMC) + case UCLASS_MMC: { + struct efi_device_path_sd_mmc_path *sddp = + dp_fill(buf, dev->parent); + struct mmc *mmc = mmc_get_mmc_dev(dev); + struct blk_desc *desc = mmc_get_blk_desc(mmc); + + sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + sddp->dp.sub_type = (desc->if_type == IF_TYPE_MMC) ? + DEVICE_PATH_SUB_TYPE_MSG_MMC : + DEVICE_PATH_SUB_TYPE_MSG_SD; + sddp->dp.length = sizeof(*sddp); + sddp->slot_number = 0; // XXX ??? + + return &sddp[1]; + } +#endif + case UCLASS_MASS_STORAGE: + case UCLASS_USB_HUB: { + struct efi_device_path_usb_class *udp = + dp_fill(buf, dev->parent); + struct usb_device *udev = dev_get_parent_priv(dev); + struct usb_device_descriptor *desc = &udev->descriptor; + + udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS; + udp->dp.length = sizeof(*udp); + udp->vendor_id = desc->idVendor; + udp->product_id = desc->idProduct; + udp->device_class = desc->bDeviceClass; + udp->device_subclass = desc->bDeviceSubClass; + udp->device_protocol = desc->bDeviceProtocol; + + return &udp[1]; + } + default: + debug("unhandled device class: %s (%u)\n", + dev->name, dev->driver->id); + return dp_fill(buf, dev->parent); + } +} + +/* Construct a device-path from a device: */ +struct efi_device_path *efi_dp_from_dev(struct udevice *dev) +{ + void *buf, *start; + + start = buf = calloc(1, dp_size(dev) + sizeof(END)); + buf = dp_fill(buf, dev); + *((struct efi_device_path *)buf) = END; + + return start; +} +#endif + +static unsigned dp_part_size(struct blk_desc *desc, int part) +{ + unsigned dpsize; + +#ifdef CONFIG_BLK + dpsize = dp_size(desc->bdev->parent); +#else + dpsize = sizeof(ROOT) + sizeof(struct efi_device_path_file_path); +#endif + + if (part == 0) /* the actual disk, not a partition */ + return dpsize; + + if (desc->part_type == PART_TYPE_ISO) { + dpsize += sizeof(struct efi_device_path_cdrom_path); + } else { + dpsize += sizeof(struct efi_device_path_hard_drive_path); + } + + return dpsize; +} + +static void *dp_part_fill(void *buf, struct blk_desc *desc, int part) +{ + disk_partition_t info; + +#ifdef CONFIG_BLK + buf = dp_fill(buf, desc->bdev->parent); +#else + struct efi_device_path_file_path *fp; + char devname[32] = { 0 }; /* fp->str is u16[32] long */ + + snprintf(devname, sizeof(devname), "%d.%d.%d", desc->if_type, + desc->devnum, part); + + memcpy(buf, &ROOT, sizeof(ROOT)); + buf += sizeof(ROOT); + + fp = buf; + fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; + fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH; + fp->dp.length = sizeof(*fp); + ascii2unicode(fp->str, devname); + buf = &fp[1]; +#endif + + if (part == 0) /* the actual disk, not a partition */ + return buf; + + part_get_info(desc, part, &info); + + if (desc->part_type == PART_TYPE_ISO) { + struct efi_device_path_cdrom_path *cddp = buf; + + cddp->boot_entry = part - 1; + cddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; + cddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CDROM_PATH; + cddp->dp.length = sizeof (*cddp); + cddp->partition_start = info.start; + cddp->partition_end = info.size; + + buf = &cddp[1]; + } else { + struct efi_device_path_hard_drive_path *hddp = buf; + + hddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; + hddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH; + hddp->dp.length = sizeof (*hddp); + hddp->partition_number = part - 1; + hddp->partition_start = info.start; + hddp->partition_end = info.size; + if (desc->part_type == PART_TYPE_EFI) + hddp->partmap_type = 2; + else + hddp->partmap_type = 1; + hddp->signature_type = desc->sig_type; + if (hddp->signature_type != 0) + memcpy(hddp->partition_signature, &desc->guid_sig, + sizeof(hddp->partition_signature)); + + buf = &hddp[1]; + } + + return buf; +} + + +/* Construct a device-path from a partition on a blk device: */ +struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part) +{ + void *buf, *start; + + start = buf = calloc(1, dp_part_size(desc, part) + sizeof(END)); + + buf = dp_part_fill(buf, desc, part); + + *((struct efi_device_path *)buf) = END; + + return start; +} + +/* convert path to an UEFI style path (ie. DOS style backslashes and utf16) */ +static void path_to_uefi(u16 *uefi, const char *path) +{ + while (*path) { + char c = *(path++); + if (c == '/') + c = '\'; + *(uefi++) = c; + } + *uefi = '\0'; +} + +/* + * If desc is NULL, this creates a path with only the file component, + * otherwise it creates a full path with both device and file components + */ +struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part, + const char *path) +{ + struct efi_device_path_file_path *fp; + void *buf, *start; + unsigned dpsize = 0, fpsize; + + if (desc) + dpsize = dp_part_size(desc, part); + + // TODO efi_device_path_file_path should be variable length: + fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1); + dpsize += fpsize; + + start = buf = calloc(1, dpsize + sizeof(END)); + + if (desc) + buf = dp_part_fill(buf, desc, part); + + /* add file-path: */ + fp = buf; + fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; + fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH; + fp->dp.length = fpsize; + path_to_uefi(fp->str, path); + buf += fpsize; + + *((struct efi_device_path *)buf) = END; + + return start; +} + +#ifdef CONFIG_NET +struct efi_device_path *efi_dp_from_eth(void) +{ + struct efi_device_path_mac_addr *ndp; + void *buf, *start; + unsigned dpsize = 0; + + assert(eth_get_dev()); + +#ifdef CONFIG_DM_ETH + dpsize += dp_size(eth_get_dev()); +#else + dpsize += sizeof(ROOT); +#endif + dpsize += sizeof(*ndp); + + start = buf = calloc(1, dpsize + sizeof(END)); + +#ifdef CONFIG_DM_ETH + buf = dp_fill(buf, eth_get_dev()); +#else + memcpy(buf, &ROOT, sizeof(ROOT)); + buf += sizeof(ROOT); +#endif + + ndp = buf; + ndp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + ndp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR; + ndp->dp.length = sizeof(*ndp); + memcpy(ndp->mac.addr, eth_get_ethaddr(), ARP_HLEN); + buf = &ndp[1]; + + *((struct efi_device_path *)buf) = END; + + return start; +} +#endif + +/* + * Helper to split a full device path (containing both device and file + * parts) into it's constituent parts. + */ +void efi_dp_split_file_path(struct efi_device_path *full_path, + struct efi_device_path **device_path, + struct efi_device_path **file_path) +{ + struct efi_device_path *p, *dp, *fp; + + dp = efi_dp_dup(full_path); + p = dp; + while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) + p = efi_dp_next(p); + fp = efi_dp_dup(p); + + p->type = DEVICE_PATH_TYPE_END; + p->sub_type = DEVICE_PATH_SUB_TYPE_END; + p->length = sizeof(*p); + + *device_path = dp; + *file_path = fp; +}

This is really the same thing as the efi_device_path struct.
Signed-off-by: Rob Clark robdclark@gmail.com --- include/efi_api.h | 12 ++---------- lib/efi_loader/efi_device_path_to_text.c | 13 ++++++++----- 2 files changed, 10 insertions(+), 15 deletions(-)
diff --git a/include/efi_api.h b/include/efi_api.h index 0ebe8d0283..7691a054a5 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -478,22 +478,14 @@ struct efi_console_control_protocol EFI_GUID(0x8b843e20, 0x8132, 0x4852, \ 0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c)
-struct efi_device_path_protocol -{ - uint8_t type; - uint8_t sub_type; - uint16_t length; - uint8_t data[]; -}; - struct efi_device_path_to_text_protocol { uint16_t *(EFIAPI *convert_device_node_to_text)( - struct efi_device_path_protocol *device_node, + struct efi_device_path *device_node, bool display_only, bool allow_shortcuts); uint16_t *(EFIAPI *convert_device_path_to_text)( - struct efi_device_path_protocol *device_path, + struct efi_device_path *device_path, bool display_only, bool allow_shortcuts); }; diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c index 4b2f43f0c8..f9d071ac50 100644 --- a/lib/efi_loader/efi_device_path_to_text.c +++ b/lib/efi_loader/efi_device_path_to_text.c @@ -16,7 +16,7 @@ const efi_guid_t efi_guid_device_path_to_text_protocol = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
static uint16_t *efi_convert_device_node_to_text( - struct efi_device_path_protocol *device_node, + struct efi_device_path *device_node, bool display_only, bool allow_shortcuts) { @@ -55,15 +55,18 @@ static uint16_t *efi_convert_device_node_to_text( break; case DEVICE_PATH_TYPE_MEDIA_DEVICE: switch (device_node->sub_type) { - case DEVICE_PATH_SUB_TYPE_FILE_PATH: + case DEVICE_PATH_SUB_TYPE_FILE_PATH: { + struct efi_device_path_file_path *fp = + (struct efi_device_path_file_path *)device_node; buffer_size = device_node->length - 4; r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, buffer_size, (void **) &buffer); if (r != EFI_SUCCESS) return NULL; - memcpy(buffer, device_node->data, buffer_size); + memcpy(buffer, fp->str, buffer_size); break; } + } break; }
@@ -89,7 +92,7 @@ static uint16_t *efi_convert_device_node_to_text( }
static uint16_t EFIAPI *efi_convert_device_node_to_text_ext( - struct efi_device_path_protocol *device_node, + struct efi_device_path *device_node, bool display_only, bool allow_shortcuts) { @@ -105,7 +108,7 @@ static uint16_t EFIAPI *efi_convert_device_node_to_text_ext( }
static uint16_t EFIAPI *efi_convert_device_path_to_text( - struct efi_device_path_protocol *device_path, + struct efi_device_path *device_path, bool display_only, bool allow_shortcuts) {

There are a couple places where we'll need GUID -> string. So add a helper.
Signed-off-by: Rob Clark robdclark@gmail.com --- include/efi_loader.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 1028bfb75d..e6c46f713e 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -239,6 +239,21 @@ static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2) return memcmp(g1, g2, sizeof(efi_guid_t)); }
+static inline int guidstr(char *s, const efi_guid_t *g) +{ + /* unpacked-guid, otherwise we have to have to consider endianess */ + struct { + uint32_t data1; + uint16_t data2; + uint16_t data3; + uint8_t data4[8]; + } *ug = (void *)g; + return sprintf(s, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + ug->data1, ug->data2, ug->data3, ug->data4[0], + ug->data4[1], ug->data4[2], ug->data4[3], ug->data4[4], + ug->data4[5], ug->data4[6], ug->data4[7]); +} + /* * Use these to indicate that your code / data should go into the EFI runtime * section and thus still be available when the OS is running

On 08/04/2017 09:31 PM, Rob Clark wrote:
There are a couple places where we'll need GUID -> string. So add a helper.
Signed-off-by: Rob Clark robdclark@gmail.com
include/efi_loader.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 1028bfb75d..e6c46f713e 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -239,6 +239,21 @@ static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2) return memcmp(g1, g2, sizeof(efi_guid_t)); }
+static inline int guidstr(char *s, const efi_guid_t *g) +{
- /* unpacked-guid, otherwise we have to have to consider endianess */
- struct {
uint32_t data1;
uint16_t data2;
uint16_t data3;
uint8_t data4[8];
- } *ug = (void *)g;
- return sprintf(s, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
ug->data1, ug->data2, ug->data3, ug->data4[0],
ug->data4[1], ug->data4[2], ug->data4[3], ug->data4[4],
ug->data4[5], ug->data4[6], ug->data4[7]);
+}
/*
- Use these to indicate that your code / data should go into the EFI runtime
- section and thus still be available when the OS is running
You may want to have a look at these:
include/uuid.h:40 int uuid_guid_get_bin(const char *guid_str, unsigned char *guid_bin);
int uuid_guid_get_str(unsigned char *guid_bin, char *guid_str);
With a conversion to uchar* you should be able to use uuid_guid_get_str for your purposes. So this patch could be eliminated from the series.
Regards
Heinrich

On Sat, Aug 5, 2017 at 3:33 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote:
There are a couple places where we'll need GUID -> string. So add a helper.
Signed-off-by: Rob Clark robdclark@gmail.com
include/efi_loader.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 1028bfb75d..e6c46f713e 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -239,6 +239,21 @@ static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2) return memcmp(g1, g2, sizeof(efi_guid_t)); }
+static inline int guidstr(char *s, const efi_guid_t *g) +{
/* unpacked-guid, otherwise we have to have to consider endianess */
struct {
uint32_t data1;
uint16_t data2;
uint16_t data3;
uint8_t data4[8];
} *ug = (void *)g;
return sprintf(s, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
ug->data1, ug->data2, ug->data3, ug->data4[0],
ug->data4[1], ug->data4[2], ug->data4[3], ug->data4[4],
ug->data4[5], ug->data4[6], ug->data4[7]);
+}
/*
- Use these to indicate that your code / data should go into the EFI runtime
- section and thus still be available when the OS is running
You may want to have a look at these:
include/uuid.h:40 int uuid_guid_get_bin(const char *guid_str, unsigned char *guid_bin);
int uuid_guid_get_str(unsigned char *guid_bin, char *guid_str);
With a conversion to uchar* you should be able to use uuid_guid_get_str for your purposes. So this patch could be eliminated from the series.
hmm, wow, those seem a bit over-engineered. But I think we should add this to vsprintf.c and drop both this patch plus your patch that adds a macro to print GUIDs. (And vsprintf.c should re-use uuid_guid_get_str().. and I could re-use uuid_guid_get_bin() when I get around to implementing efi_get_next_variable(), so thanks for pointing that out.)
BR, -R

On 08/05/2017 09:56 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 3:33 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:31 PM, Rob Clark wrote:
There are a couple places where we'll need GUID -> string. So add a helper.
Signed-off-by: Rob Clark robdclark@gmail.com
include/efi_loader.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/include/efi_loader.h b/include/efi_loader.h index 1028bfb75d..e6c46f713e 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -239,6 +239,21 @@ static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2) return memcmp(g1, g2, sizeof(efi_guid_t)); }
+static inline int guidstr(char *s, const efi_guid_t *g) +{
/* unpacked-guid, otherwise we have to have to consider endianess */
struct {
uint32_t data1;
uint16_t data2;
uint16_t data3;
uint8_t data4[8];
} *ug = (void *)g;
return sprintf(s, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
ug->data1, ug->data2, ug->data3, ug->data4[0],
ug->data4[1], ug->data4[2], ug->data4[3], ug->data4[4],
ug->data4[5], ug->data4[6], ug->data4[7]);
+}
/*
- Use these to indicate that your code / data should go into the EFI runtime
- section and thus still be available when the OS is running
You may want to have a look at these:
include/uuid.h:40 int uuid_guid_get_bin(const char *guid_str, unsigned char *guid_bin);
int uuid_guid_get_str(unsigned char *guid_bin, char *guid_str);
With a conversion to uchar* you should be able to use uuid_guid_get_str for your purposes. So this patch could be eliminated from the series.
hmm, wow, those seem a bit over-engineered. But I think we should add this to vsprintf.c and drop both this patch plus your patch that adds a macro to print GUIDs. (And vsprintf.c should re-use uuid_guid_get_str().. and I could re-use uuid_guid_get_bin() when I get around to implementing efi_get_next_variable(), so thanks for pointing that out.)
BR, -R
In the case of guidstr we are switching two functions where the existing one has one constant extra parameter.
In EFI_PRINT_GUID we would replace a single debug(format, guid) by code that would have to check _DEBUG that only is used inside the definition of debug, a variable declaration, a call to uuid_guid_get_str and a printf.
I think we should not evaluate _DEBUG outside include/common.h.
Regards
Heinrich

It needs to handle more device-path node types, and also multiple levels of path hierarchy. To simplify this, initially construct utf8 string to a temporary buffer, and then allocate the real utf16 buffer that is returned. This should be mostly for debugging or at least not critical- path so an extra copy won't hurt, and is saner than the alternative.
Signed-off-by: Rob Clark robdclark@gmail.com --- include/efi_api.h | 1 + include/efi_loader.h | 2 + lib/efi_loader/efi_device_path_to_text.c | 223 ++++++++++++++++++++++--------- 3 files changed, 163 insertions(+), 63 deletions(-)
diff --git a/include/efi_api.h b/include/efi_api.h index 7691a054a5..dd79cace32 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -295,6 +295,7 @@ struct efi_mac_addr {
#define EFI_PNP_ID(ID) (u32)(((ID) << 16) | 0x41D0) #define EISA_PNP_ID(ID) EFI_PNP_ID(ID) +#define EISA_PNP_NUM(ID) ((ID) >> 16)
struct efi_device_path_acpi_path { struct efi_device_path dp; diff --git a/include/efi_loader.h b/include/efi_loader.h index e6c46f713e..1ab4af0f88 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -59,6 +59,8 @@ extern struct efi_simple_input_interface efi_con_in; extern const struct efi_console_control_protocol efi_console_control; extern const struct efi_device_path_to_text_protocol efi_device_path_to_text;
+uint16_t *efi_dp_str(struct efi_device_path *dp); + extern const efi_guid_t efi_guid_console_control; extern const efi_guid_t efi_guid_device_path; extern const efi_guid_t efi_guid_loaded_image; diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c index f9d071ac50..d10a1319af 100644 --- a/lib/efi_loader/efi_device_path_to_text.c +++ b/lib/efi_loader/efi_device_path_to_text.c @@ -15,82 +15,179 @@ const efi_guid_t efi_guid_device_path_to_text_protocol = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
-static uint16_t *efi_convert_device_node_to_text( - struct efi_device_path *device_node, - bool display_only, - bool allow_shortcuts) +static char *dp_unknown(char *s, struct efi_device_path *dp) { - unsigned long buffer_size; - efi_status_t r; - uint16_t *buffer = NULL; - int i; + s += sprintf(s, "/UNKNOWN(%04x,%04x)", dp->type, dp->sub_type); + return s; +}
- switch (device_node->type) { - case DEVICE_PATH_TYPE_END: - return NULL; - case DEVICE_PATH_TYPE_MESSAGING_DEVICE: - switch (device_node->sub_type) { - case DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR: { - struct efi_device_path_mac_addr *dp = - (struct efi_device_path_mac_addr *)device_node; - - if (dp->if_type != 0 && dp->if_type != 1) - break; - r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, - 2 * MAC_OUTPUT_LEN, - (void **)&buffer); - if (r != EFI_SUCCESS) - return NULL; - sprintf((char *)buffer, - "MAC(%02x%02x%02x%02x%02x%02x,0x%1x)", - dp->mac.addr[0], dp->mac.addr[1], - dp->mac.addr[2], dp->mac.addr[3], - dp->mac.addr[4], dp->mac.addr[5], - dp->if_type); - for (i = MAC_OUTPUT_LEN - 1; i >= 0; --i) - buffer[i] = ((uint8_t *)buffer)[i]; +static char *dp_acpi(char *s, struct efi_device_path *dp) +{ + switch (dp->sub_type) { + case DEVICE_PATH_SUB_TYPE_ACPI_DEVICE: { + struct efi_device_path_acpi_path *adp = + (struct efi_device_path_acpi_path *)dp; + s += sprintf(s, "/Acpi(PNP%04x", EISA_PNP_NUM(adp->hid)); + if (adp->uid) + s += sprintf(s, ",%d", adp->uid); + s += sprintf(s, ")"); + break; + } + default: + s = dp_unknown(s, dp); + break; + } + return s; +} + +static char *dp_msging(char *s, struct efi_device_path *dp) +{ + switch (dp->sub_type) { + case DEVICE_PATH_SUB_TYPE_MSG_USB: { + struct efi_device_path_usb *udp = + (struct efi_device_path_usb *)dp; + s += sprintf(s, "/Usb(0x%x,0x%x)", udp->parent_port_number, + udp->usb_interface); + break; + } + case DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR: { + struct efi_device_path_mac_addr *mdp = + (struct efi_device_path_mac_addr *)dp; + + if (mdp->if_type != 0 && mdp->if_type != 1) break; - } - } + + s += sprintf(s, "/MAC(%02x%02x%02x%02x%02x%02x,0x%1x)", + mdp->mac.addr[0], mdp->mac.addr[1], + mdp->mac.addr[2], mdp->mac.addr[3], + mdp->mac.addr[4], mdp->mac.addr[5], + mdp->if_type); + + break; + } + case DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS: { + struct efi_device_path_usb_class *ucdp = + (struct efi_device_path_usb_class *)dp; + + s += sprintf(s, "/USBClass(%x,%x,%x,%x,%x)", + ucdp->vendor_id, ucdp->product_id, + ucdp->device_class, ucdp->device_subclass, + ucdp->device_protocol); + + break; + } + case DEVICE_PATH_SUB_TYPE_MSG_SD: + case DEVICE_PATH_SUB_TYPE_MSG_MMC: { + const char *typename = + (dp->sub_type == DEVICE_PATH_SUB_TYPE_MSG_SD) ? + "SDCard" : "MMC"; + struct efi_device_path_sd_mmc_path *sddp = + (struct efi_device_path_sd_mmc_path *)dp; + s += sprintf(s, "/%s(Slot%u)", typename, sddp->slot_number); + break; + } + default: + s = dp_unknown(s, dp); break; - case DEVICE_PATH_TYPE_MEDIA_DEVICE: - switch (device_node->sub_type) { - case DEVICE_PATH_SUB_TYPE_FILE_PATH: { - struct efi_device_path_file_path *fp = - (struct efi_device_path_file_path *)device_node; - buffer_size = device_node->length - 4; - r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, - buffer_size, (void **) &buffer); - if (r != EFI_SUCCESS) - return NULL; - memcpy(buffer, fp->str, buffer_size); + } + return s; +} + +static char *dp_media(char *s, struct efi_device_path *dp) +{ + switch (dp->sub_type) { + case DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH: { + struct efi_device_path_hard_drive_path *hddp = + (struct efi_device_path_hard_drive_path *)dp; + void *sig = hddp->partition_signature; + + switch (hddp->signature_type) { + case SIG_TYPE_MBR: + s += sprintf(s, "/HD(Part%d,Sig%08x)", + hddp->partition_number, + *(uint32_t *)sig); break; + case SIG_TYPE_GUID: + s += sprintf(s, "HD(Part%d,Sig", hddp->partition_number); + s += guidstr(s, (efi_guid_t *)sig); + s += sprintf(s, ")"); + default: + s += sprintf(s, "/HD(Part%d,MBRType=%02x,SigType=%02x)", + hddp->partition_number, hddp->partmap_type, + hddp->signature_type); } - } + + break; + } + case DEVICE_PATH_SUB_TYPE_CDROM_PATH: { + struct efi_device_path_cdrom_path *cddp = + (struct efi_device_path_cdrom_path *)dp; + s += sprintf(s, "/CDROM(0x%x)", cddp->boot_entry); + break; + } + case DEVICE_PATH_SUB_TYPE_FILE_PATH: { + struct efi_device_path_file_path *fp = + (struct efi_device_path_file_path *)dp; + int slen = (dp->length - sizeof(*dp)) / 2; + s += sprintf(s, "/%-*ls", slen, fp->str); + break; + } + default: + s = dp_unknown(s, dp); break; } + return s; +}
- /* - * For all node types that we do not yet support return - * 'UNKNOWN(type,subtype)'. - */ - if (!buffer) { - r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, - 2 * UNKNOWN_OUTPUT_LEN, - (void **)&buffer); - if (r != EFI_SUCCESS) - return NULL; - sprintf((char *)buffer, - "UNKNOWN(%04x,%04x)", - device_node->type, - device_node->sub_type); - for (i = UNKNOWN_OUTPUT_LEN - 1; i >= 0; --i) - buffer[i] = ((uint8_t *)buffer)[i]; +static uint16_t *efi_convert_device_node_to_text( + struct efi_device_path *dp, + bool display_only, + bool allow_shortcuts) +{ + unsigned long len; + efi_status_t r; + char buf[512]; /* this ought be be big enough for worst case */ + char *str = buf; + uint16_t *out; + + while (dp) { + switch (dp->type) { + case DEVICE_PATH_TYPE_ACPI_DEVICE: + str = dp_acpi(str, dp); + break; + case DEVICE_PATH_TYPE_MESSAGING_DEVICE: + str = dp_msging(str, dp); + break; + case DEVICE_PATH_TYPE_MEDIA_DEVICE: + str = dp_media(str, dp); + break; + default: + str = dp_unknown(str, dp); + } + + dp = efi_dp_next(dp); }
- return buffer; + *str++ = '\0'; + + len = str - buf; + r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, 2 * len, (void **)&out); + if (r != EFI_SUCCESS) + return NULL; + + ascii2unicode(out, buf); + out[len - 1] = 0; + + return out; }
+/* helper for debug prints.. efi_free_pool() the result. */ +uint16_t *efi_dp_str(struct efi_device_path *dp) +{ + return efi_convert_device_node_to_text(dp, true, true); +} + + static uint16_t EFIAPI *efi_convert_device_node_to_text_ext( struct efi_device_path *device_node, bool display_only,

Also, create disk objects for the disk itself, in addition to the partitions. (UEFI terminology is a bit confusing, a "disk" object is really a partition.) This helps grub properly identify the boot device since it is trying to match up partition "disk" object with it's parent device.
Now instead of seeing devices like:
/File(sdhci@07864000.blk)/EndEntire /File(usb_mass_storage.lun0)/EndEntire
You see:
/ACPI(133741d0,0)/UnknownMessaging(1d)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(0,800,64000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(1,64800,200000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(2,264800,19a000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(0,800,60000,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(1,61000,155000,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(2,20fa800,1bbf8800,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(3,1b6800,1f44000,38ca680200000000,1,1)/EndEntire
This is on a board with single USB disk and single sd-card. The UnknownMessaging(1d) node in the device-path is the MMC device, but grub_efi_print_device_path() hasn't been updated yet for some of the newer device-path sub-types.
This patch is inspired by a patch originally from Peter Jones, but re-worked to use efi_device_path, so it doesn't much resemble the original.
Signed-off-by: Rob Clark robdclark@gmail.com --- lib/efi_loader/efi_disk.c | 54 +++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 23 deletions(-)
diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index ed06485e33..eea65a402a 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -28,11 +28,13 @@ struct efi_disk_obj { /* EFI Interface Media descriptor struct, referenced by ops */ struct efi_block_io_media media; /* EFI device path to this block device */ - struct efi_device_path_file_path *dp; + struct efi_device_path *dp; + /* partition # */ + unsigned part; /* Offset into disk for simple partitions */ lbaint_t offset; /* Internal block device */ - const struct blk_desc *desc; + struct blk_desc *desc; };
static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io *this, @@ -172,26 +174,26 @@ static const struct efi_block_io block_io_disk_template = {
static void efi_disk_add_dev(const char *name, const char *if_typename, - const struct blk_desc *desc, + struct blk_desc *desc, int dev_index, - lbaint_t offset) + lbaint_t offset, + unsigned part) { struct efi_disk_obj *diskobj; - struct efi_device_path_file_path *dp; - int objlen = sizeof(*diskobj) + (sizeof(*dp) * 2);
/* Don't add empty devices */ if (!desc->lba) return;
- diskobj = calloc(1, objlen); + diskobj = calloc(1, sizeof(*diskobj));
/* Fill in object data */ - dp = (void *)&diskobj[1]; + diskobj->dp = efi_dp_from_part(desc, part); + diskobj->part = part; diskobj->parent.protocols[0].guid = &efi_block_io_guid; diskobj->parent.protocols[0].protocol_interface = &diskobj->ops; diskobj->parent.protocols[1].guid = &efi_guid_device_path; - diskobj->parent.protocols[1].protocol_interface = dp; + diskobj->parent.protocols[1].protocol_interface = diskobj->dp; diskobj->parent.handle = diskobj; diskobj->ops = block_io_disk_template; diskobj->ifname = if_typename; @@ -207,17 +209,6 @@ static void efi_disk_add_dev(const char *name, diskobj->media.last_block = desc->lba - offset; diskobj->ops.media = &diskobj->media;
- /* Fill in device path */ - diskobj->dp = dp; - dp[0].dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; - dp[0].dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH; - dp[0].dp.length = sizeof(*dp); - ascii2unicode(dp[0].str, name); - - dp[1].dp.type = DEVICE_PATH_TYPE_END; - dp[1].dp.sub_type = DEVICE_PATH_SUB_TYPE_END; - dp[1].dp.length = sizeof(*dp); - /* Hook up to the device list */ list_add_tail(&diskobj->parent.link, &efi_obj_list); } @@ -236,14 +227,18 @@ static int efi_disk_create_eltorito(struct blk_desc *desc, if (desc->part_type != PART_TYPE_ISO) return 0;
+ /* and devices for each partition: */ while (!part_get_info(desc, part, &info)) { snprintf(devname, sizeof(devname), "%s:%d", pdevname, part); efi_disk_add_dev(devname, if_typename, desc, diskid, - info.start); + info.start, part); part++; disks++; } + + /* ... and add block device: */ + efi_disk_add_dev(devname, if_typename, desc, diskid, 0, 0); #endif
return disks; @@ -271,9 +266,22 @@ int efi_disk_register(void) uclass_next_device_check(&dev)) { struct blk_desc *desc = dev_get_uclass_platdata(dev); const char *if_typename = dev->driver->name; + disk_partition_t info; + int part = 1;
printf("Scanning disk %s...\n", dev->name); - efi_disk_add_dev(dev->name, if_typename, desc, desc->devnum, 0); + + /* add devices for each partition: */ + while (!part_get_info(desc, part, &info)) { + efi_disk_add_dev(dev->name, if_typename, desc, + desc->devnum, 0, part); + part++; + } + + /* ... and add block device: */ + efi_disk_add_dev(dev->name, if_typename, desc, + desc->devnum, 0, 0); + disks++;
/* @@ -309,7 +317,7 @@ int efi_disk_register(void)
snprintf(devname, sizeof(devname), "%s%d", if_typename, i); - efi_disk_add_dev(devname, if_typename, desc, i, 0); + efi_disk_add_dev(devname, if_typename, desc, i, 0, 0); disks++;
/*

From: Rob Clark robdclark@gmail.com Date: Fri, 4 Aug 2017 15:31:55 -0400
Hi Rob,
OpenBSD has been an early adopter of efi_loader and pretty much completely relies on it for booting OpenBSD/armv7 and OpenBSD/arm64. We use our own bootloader which is fairly lightweight. Obviously we'd like to keep it working if this patchset gets adopted. We don't make use of EFI variables and don't really plan to make use of them on our ARM platforms. But obviously we have to deal with device paths...
Also, create disk objects for the disk itself, in addition to the partitions. (UEFI terminology is a bit confusing, a "disk" object is really a partition.) This helps grub properly identify the boot device since it is trying to match up partition "disk" object with it's parent device.
Now instead of seeing devices like:
/File(sdhci@07864000.blk)/EndEntire /File(usb_mass_storage.lun0)/EndEntire
You see:
/ACPI(133741d0,0)/UnknownMessaging(1d)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(0,800,64000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(1,64800,200000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(2,264800,19a000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(0,800,60000,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(1,61000,155000,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(2,20fa800,1bbf8800,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(3,1b6800,1f44000,38ca680200000000,1,1)/EndEntire
This is on a board with single USB disk and single sd-card. The UnknownMessaging(1d) node in the device-path is the MMC device, but grub_efi_print_device_path() hasn't been updated yet for some of the newer device-path sub-types.
..and what you're sketching out here should work with recent enough versions of our bootloader.
However, to me having an ACPI Device Path component in there doesn't make much sense on a board without ACPI. It certainly doesn't help mapping a boot path back to an actual hardware device. Wouldn't it be more logical to a Hardware Device Path there instead? In particular a Memory Mapped Device Path would make a lot of sense as the start address would make it fairly easy to do the mapping.
Cheers,
Mark

On Fri, Aug 4, 2017 at 4:41 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Fri, 4 Aug 2017 15:31:55 -0400
Hi Rob,
OpenBSD has been an early adopter of efi_loader and pretty much completely relies on it for booting OpenBSD/armv7 and OpenBSD/arm64. We use our own bootloader which is fairly lightweight. Obviously we'd like to keep it working if this patchset gets adopted. We don't make use of EFI variables and don't really plan to make use of them on our ARM platforms. But obviously we have to deal with device paths...
Also, create disk objects for the disk itself, in addition to the partitions. (UEFI terminology is a bit confusing, a "disk" object is really a partition.) This helps grub properly identify the boot device since it is trying to match up partition "disk" object with it's parent device.
Now instead of seeing devices like:
/File(sdhci@07864000.blk)/EndEntire /File(usb_mass_storage.lun0)/EndEntire
You see:
/ACPI(133741d0,0)/UnknownMessaging(1d)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(0,800,64000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(1,64800,200000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(2,264800,19a000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(0,800,60000,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(1,61000,155000,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(2,20fa800,1bbf8800,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(3,1b6800,1f44000,38ca680200000000,1,1)/EndEntire
This is on a board with single USB disk and single sd-card. The UnknownMessaging(1d) node in the device-path is the MMC device, but grub_efi_print_device_path() hasn't been updated yet for some of the newer device-path sub-types.
..and what you're sketching out here should work with recent enough versions of our bootloader.
However, to me having an ACPI Device Path component in there doesn't make much sense on a board without ACPI. It certainly doesn't help mapping a boot path back to an actual hardware device. Wouldn't it be more logical to a Hardware Device Path there instead? In particular a Memory Mapped Device Path would make a lot of sense as the start address would make it fairly easy to do the mapping.
It was pretty much an arbitrary choice, and it wouldn't be hard to change. From my reading of the grub code, all it really cares about is that there is *some* parent of the "partition" HD device. I'm not really sure what maps best in a UEFI world to the "soc" node in a device tree world. I'm certainly open to changing this.
It would be cool if you have a chance to give this a try w/ OpenBSD and let me know if you run into issues. I want this to be something that works across-distro so I'll try to help as much as I can. (Not sure if there is an OpenBSD port for db410c, but I guess there is always qemu..). Fwiw, if git pull/cherry-pick is easier than grabbing patches from list, then [1].
[1] https://github.com/robclark/u-boot/commits/enough-uefi-for-shim
BR, -R

Authentication-Results: xs4all.nl; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com; dmarc=pass header.from=gmail.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=qkk49+XF6+Jn7RUjFU/ZFP7/ho5kdUEUNpSrCpqy9yw=; b=knLDTd1vrl7R3BnRReKrxUD1UxYSIahqh5VTND3PEt1cLHtskGRiDc280ADRTm6ffV izImBjr6hq94Qu/Jnbd6kn7+jSDuhDva9FU1/ZndFaNkTUhsatlgtvrK5RzJ38FpwvLa 0X7Y8NuVm99K+ijWdKI34YruaZfz47t863L40UYJhjdaj3/TLdIWrC/NzEBiQ/fXemHU 8mN1HM9JBD7STB64mMlDSttSTC2vJOPyX81CCbDnXLI/puqcyXewaJ+kW8Km+VDra2xs PIWVzyA0PoV7nIihMbkrv95K7GQgpSOUUL7nlEmAreQoEbCCb8Iw2inSe6LqhN6IbJ4u Js9A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=qkk49+XF6+Jn7RUjFU/ZFP7/ho5kdUEUNpSrCpqy9yw=; b=kiBq9lLF93IqOIhjRAbghuqi5rcOJ7mf7OvAvgo8T4qg72k9iEBX84R7yIY7KVyDpX DxV+JJDPePbOtIN3qxpmIZF6vqT/HJ0w+z8RnsUiUGJxGCxTA9R97e+tcB1Ovh7zRIJe JPSP/6/TqRmFgrlIsZdoPZAsypVgTx/AHfdMA/z3l4EOOdSOcd3AOd+sDdGnhCAm2G0+ QyAXxkxleKeB3t2AvXKiXpJknXzSb9FxM+0ug0gAPlTTT7d2JFEOZ43gzgLCTTsCVDdL lWiJepjIeynmIuGKwcZJpFdV6C8nx2RMztWLpbhPMrbbR+eLkQcaJnJJrgv859lzbUrM 9XhQ== X-Gm-Message-State: AHYfb5hmb8q9IgSaZxMt3OsQOUjxd0l+fyjrLiox4JEB/7NaaHlf1K9h HiWJc8WMqU156stR2hj9qS4oZRxkCuHp0xg= X-Received: by 10.25.59.29 with SMTP id i29mr1058615lfa.224.1501880271850; Fri, 04 Aug 2017 13:57:51 -0700 (PDT) From: Rob Clark robdclark@gmail.com Date: Fri, 4 Aug 2017 16:57:50 -0400 Cc: U-Boot Mailing List u-boot@lists.denx.de, Heinrich Schuchardt xypron.glpk@gmx.de, Peter Jones pjones@redhat.com X-CNFS-Analysis: v=2.2 cv=eoad9chX c=1 sm=0 tr=0 a=bdpqe3ZLSLbgpdprsY8WZA==:117 a=IkcTkHD0fZMA:10 a=x7bEGLp0ZPQA:10 a=D2htVsi0J-IA:10 a=KeKAF7QvOSUA:10 a=pGLkceISAAAA:8 a=NEAV23lmAAAA:8 a=YyEjxAJ5dRpCWmjka9UA:9 a=QEXdDO2ut3YA:10 a=6kGIvZw6iX1k4Y-7sg4_:22 X-Virus-Scanned: by XS4ALL Virus Scanner X-XS4ALL-Spam-Score: -0.1 () DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, SPF_PASS X-XS4ALL-Spam: NO Envelope-To: mark.kettenis@xs4all.nl
On Fri, Aug 4, 2017 at 4:41 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Fri, 4 Aug 2017 15:31:55 -0400
Hi Rob,
OpenBSD has been an early adopter of efi_loader and pretty much completely relies on it for booting OpenBSD/armv7 and OpenBSD/arm64. We use our own bootloader which is fairly lightweight. Obviously we'd like to keep it working if this patchset gets adopted. We don't make use of EFI variables and don't really plan to make use of them on our ARM platforms. But obviously we have to deal with device paths...
Also, create disk objects for the disk itself, in addition to the partitions. (UEFI terminology is a bit confusing, a "disk" object is really a partition.) This helps grub properly identify the boot device since it is trying to match up partition "disk" object with it's parent device.
Now instead of seeing devices like:
/File(sdhci@07864000.blk)/EndEntire /File(usb_mass_storage.lun0)/EndEntire
You see:
/ACPI(133741d0,0)/UnknownMessaging(1d)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(0,800,64000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(1,64800,200000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(2,264800,19a000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(0,800,60000,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(1,61000,155000,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(2,20fa800,1bbf8800,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(3,1b6800,1f44000,38ca680200000000,1,1)/EndEntire
This is on a board with single USB disk and single sd-card. The UnknownMessaging(1d) node in the device-path is the MMC device, but grub_efi_print_device_path() hasn't been updated yet for some of the newer device-path sub-types.
..and what you're sketching out here should work with recent enough versions of our bootloader.
However, to me having an ACPI Device Path component in there doesn't make much sense on a board without ACPI. It certainly doesn't help mapping a boot path back to an actual hardware device. Wouldn't it be more logical to a Hardware Device Path there instead? In particular a Memory Mapped Device Path would make a lot of sense as the start address would make it fairly easy to do the mapping.
It was pretty much an arbitrary choice, and it wouldn't be hard to change. From my reading of the grub code, all it really cares about is that there is *some* parent of the "partition" HD device. I'm not really sure what maps best in a UEFI world to the "soc" node in a device tree world. I'm certainly open to changing this.
It would be cool if you have a chance to give this a try w/ OpenBSD and let me know if you run into issues. I want this to be something that works across-distro so I'll try to help as much as I can. (Not sure if there is an OpenBSD port for db410c, but I guess there is always qemu..). Fwiw, if git pull/cherry-pick is easier than grabbing patches from list, then [1].
OpenBSD doesn't run on the db410c. However, our EFI bootloader should just run. You can download it from:
http://ftp.openbsd.org/pub/OpenBSD/snapshots/arm64/BOOTAA64.EFI
for the 64-bit (AArch64) and
http://ftp.openbsd.org/pub/OpenBSD/snapshots/armv7/BOOTARM.EFI
for the 32-bit version (AArch32).
Unfortunately something in this patch series breaks things for me on a Banana Pi:
U-Boot SPL 2017.09-rc1-00020-g2ad6933c64-dirty (Aug 05 2017 - 15:26:15) DRAM: 1024 MiB CPU: 912000000Hz, AXI/AHB/APB: 3/2/2 Trying to boot from MMC1
U-Boot 2017.09-rc1-00020-g2ad6933c64-dirty (Aug 05 2017 - 15:26:15 +0200) Allwinner Technology
CPU: Allwinner A20 (SUN7I) Model: LeMaker Banana Pi I2C: ready DRAM: 1 GiB MMC: SUNXI SD/MMC: 0 *** Warning - bad CRC, using default environment
Setting up a 720x576i composite-pal console (overscan 32x20) In: serial Out: vga Err: vga SCSI: Target spinup took 0 ms. AHCI 0001.0100 32 slots 1 ports 3 Gbps 0x1 impl SATA mode flags: ncq stag pm led clo only pmp pio slum part ccc apst Net: eth0: ethernet@01c50000 starting USB... USB0: USB EHCI 1.00 USB1: USB OHCI 1.0 USB2: USB EHCI 1.00 USB3: USB OHCI 1.0 scanning bus 0 for devices... 1 USB Device(s) found scanning bus 2 for devices... 1 USB Device(s) found scanning usb for storage devices... 0 Storage Device(s) found Hit any key to stop autoboot: 0 switch to partitions #0, OK mmc0 is current device Scanning mmc 0:1... Found EFI removable media binary efi/boot/bootarm.efi Scanning disks on scsi... Scanning disks on usb... Scanning disks on mmc... MMC Device 1 not found MMC Device 2 not found MMC Device 3 not found Found 6 disks data abort pc : [<7ef8d878>] lr : [<7ef8d874>] reloc pc : [<4a039878>] lr : [<4a039874>] sp : 7af29660 ip : 00000000 fp : 7af29774 r10: 7efec230 r9 : 7af33ee0 r8 : 00000000 r7 : 00000009 r6 : 7ef9e8b8 r5 : 7af296a0 r4 : 7efa4495 r3 : 7af296a0 r2 : 0000008c r1 : 7af29658 r0 : 00000004 Flags: nzCV IRQs off FIQs off Mode SVC_32 Resetting CPU ...
resetting ...
Normally it would print something like:
OpenBSD/armv7 BOOTARM 0.8
boot>
at that stage.

On Sat, Aug 5, 2017 at 10:01 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
On Fri, Aug 4, 2017 at 4:41 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Fri, 4 Aug 2017 15:31:55 -0400
Hi Rob,
OpenBSD has been an early adopter of efi_loader and pretty much completely relies on it for booting OpenBSD/armv7 and OpenBSD/arm64. We use our own bootloader which is fairly lightweight. Obviously we'd like to keep it working if this patchset gets adopted. We don't make use of EFI variables and don't really plan to make use of them on our ARM platforms. But obviously we have to deal with device paths...
Also, create disk objects for the disk itself, in addition to the partitions. (UEFI terminology is a bit confusing, a "disk" object is really a partition.) This helps grub properly identify the boot device since it is trying to match up partition "disk" object with it's parent device.
Now instead of seeing devices like:
/File(sdhci@07864000.blk)/EndEntire /File(usb_mass_storage.lun0)/EndEntire
You see:
/ACPI(133741d0,0)/UnknownMessaging(1d)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(0,800,64000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(1,64800,200000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/UnknownMessaging(1d)/HD(2,264800,19a000,dd904a8c00000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(0,800,60000,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(1,61000,155000,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(2,20fa800,1bbf8800,38ca680200000000,1,1)/EndEntire /ACPI(133741d0,0)/USB(0,0)/USB(0,0)/USB(0,0)/HD(3,1b6800,1f44000,38ca680200000000,1,1)/EndEntire
This is on a board with single USB disk and single sd-card. The UnknownMessaging(1d) node in the device-path is the MMC device, but grub_efi_print_device_path() hasn't been updated yet for some of the newer device-path sub-types.
..and what you're sketching out here should work with recent enough versions of our bootloader.
However, to me having an ACPI Device Path component in there doesn't make much sense on a board without ACPI. It certainly doesn't help mapping a boot path back to an actual hardware device. Wouldn't it be more logical to a Hardware Device Path there instead? In particular a Memory Mapped Device Path would make a lot of sense as the start address would make it fairly easy to do the mapping.
It was pretty much an arbitrary choice, and it wouldn't be hard to change. From my reading of the grub code, all it really cares about is that there is *some* parent of the "partition" HD device. I'm not really sure what maps best in a UEFI world to the "soc" node in a device tree world. I'm certainly open to changing this.
It would be cool if you have a chance to give this a try w/ OpenBSD and let me know if you run into issues. I want this to be something that works across-distro so I'll try to help as much as I can. (Not sure if there is an OpenBSD port for db410c, but I guess there is always qemu..). Fwiw, if git pull/cherry-pick is easier than grabbing patches from list, then [1].
OpenBSD doesn't run on the db410c. However, our EFI bootloader should just run. You can download it from:
http://ftp.openbsd.org/pub/OpenBSD/snapshots/arm64/BOOTAA64.EFI
for the 64-bit (AArch64) and
http://ftp.openbsd.org/pub/OpenBSD/snapshots/armv7/BOOTARM.EFI
for the 32-bit version (AArch32).
oh, good point.. I can try that
Unfortunately something in this patch series breaks things for me on a Banana Pi:
U-Boot SPL 2017.09-rc1-00020-g2ad6933c64-dirty (Aug 05 2017 - 15:26:15) DRAM: 1024 MiB CPU: 912000000Hz, AXI/AHB/APB: 3/2/2 Trying to boot from MMC1
U-Boot 2017.09-rc1-00020-g2ad6933c64-dirty (Aug 05 2017 - 15:26:15 +0200) Allwinner Technology
CPU: Allwinner A20 (SUN7I) Model: LeMaker Banana Pi I2C: ready DRAM: 1 GiB MMC: SUNXI SD/MMC: 0 *** Warning - bad CRC, using default environment
Setting up a 720x576i composite-pal console (overscan 32x20) In: serial Out: vga Err: vga SCSI: Target spinup took 0 ms. AHCI 0001.0100 32 slots 1 ports 3 Gbps 0x1 impl SATA mode flags: ncq stag pm led clo only pmp pio slum part ccc apst Net: eth0: ethernet@01c50000 starting USB... USB0: USB EHCI 1.00 USB1: USB OHCI 1.0 USB2: USB EHCI 1.00 USB3: USB OHCI 1.0 scanning bus 0 for devices... 1 USB Device(s) found scanning bus 2 for devices... 1 USB Device(s) found scanning usb for storage devices... 0 Storage Device(s) found Hit any key to stop autoboot: 0 switch to partitions #0, OK mmc0 is current device Scanning mmc 0:1... Found EFI removable media binary efi/boot/bootarm.efi Scanning disks on scsi... Scanning disks on usb... Scanning disks on mmc... MMC Device 1 not found MMC Device 2 not found MMC Device 3 not found Found 6 disks data abort pc : [<7ef8d878>] lr : [<7ef8d874>] reloc pc : [<4a039878>] lr : [<4a039874>] sp : 7af29660 ip : 00000000 fp : 7af29774 r10: 7efec230 r9 : 7af33ee0 r8 : 00000000 r7 : 00000009 r6 : 7ef9e8b8 r5 : 7af296a0 r4 : 7efa4495 r3 : 7af296a0 r2 : 0000008c r1 : 7af29658 r0 : 00000004 Flags: nzCV IRQs off FIQs off Mode SVC_32 Resetting CPU ...
resetting ...
Normally it would print something like:
OpenBSD/armv7 BOOTARM 0.8
boot>
at that stage.
hmm, well I'll give a quick try w/ your bootaa64.efi, but I guess this is probably something board specific.
Could you:
$(CROSS_COMPILE)-addr2line -e u-boot 7ef8d878
while you still have the build handy?
BR, -R

Date: Sat, 5 Aug 2017 16:01:51 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Unfortunately something in this patch series breaks things for me on a Banana Pi:
And according to git bisect:
4e3e748a50fc3f43e20c7ff407184596d7c9a589 is the first bad commit commit 4e3e748a50fc3f43e20c7ff407184596d7c9a589 Author: Peter Jones pjones@redhat.com Date: Wed Jun 21 16:39:02 2017 -0400
efi: add some more device path structures
Signed-off-by: Peter Jones pjones@redhat.com Signed-off-by: Rob Clark robdclark@gmail.com

On Sat, Aug 5, 2017 at 10:28 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
Date: Sat, 5 Aug 2017 16:01:51 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Unfortunately something in this patch series breaks things for me on a Banana Pi:
And according to git bisect:
4e3e748a50fc3f43e20c7ff407184596d7c9a589 is the first bad commit commit 4e3e748a50fc3f43e20c7ff407184596d7c9a589 Author: Peter Jones pjones@redhat.com Date: Wed Jun 21 16:39:02 2017 -0400
efi: add some more device path structures Signed-off-by: Peter Jones <pjones@redhat.com> Signed-off-by: Rob Clark <robdclark@gmail.com>
hmm, odd.. it is only adding some #define's and structs that are not used until a later commit..
although it does also make 'struct efi_device_path_mac_addr' __packed, which it should have been before. Is this an armv7? I wonder if we have some troubles with unaligned accesses on armv7 that we don't have on aarch64 (or maybe compiler isn't turning access to device-path nodes into byte accesses if it can't do unaligned accesses. (The node in the device-path structure are byte-packed.)
addr2line the faulting address I guess should confirm that. If this is the issue, it's going to be a bit sad since we'll have to do a lot of copying back/forth of efi_device_path ptrs to aligned addresses :-/
BR, -R

From: Rob Clark robdclark@gmail.com Date: Sat, 5 Aug 2017 10:35:08 -0400
On Sat, Aug 5, 2017 at 10:28 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
Date: Sat, 5 Aug 2017 16:01:51 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Unfortunately something in this patch series breaks things for me on a Banana Pi:
And according to git bisect:
4e3e748a50fc3f43e20c7ff407184596d7c9a589 is the first bad commit commit 4e3e748a50fc3f43e20c7ff407184596d7c9a589 Author: Peter Jones pjones@redhat.com Date: Wed Jun 21 16:39:02 2017 -0400
efi: add some more device path structures Signed-off-by: Peter Jones <pjones@redhat.com> Signed-off-by: Rob Clark <robdclark@gmail.com>
hmm, odd.. it is only adding some #define's and structs that are not used until a later commit..
although it does also make 'struct efi_device_path_mac_addr' __packed, which it should have been before. Is this an armv7? I wonder if we have some troubles with unaligned accesses on armv7 that we don't have on aarch64 (or maybe compiler isn't turning access to device-path nodes into byte accesses if it can't do unaligned accesses. (The node in the device-path structure are byte-packed.)
This is indeed armv7.
addr2line the faulting address I guess should confirm that. If this is the issue, it's going to be a bit sad since we'll have to do a lot of copying back/forth of efi_device_path ptrs to aligned addresses :-/
Sadly that's not going to help you:
$ arm-none-eabi-addr2line -e u-boot 7ef8d878 ??:0
I suppose it is faulting somewhere in BOOTARM.EFI,
Anyway, removing __packed from struct efi_device_path_file_path makes me boot again with a tree checked out out with that commit.
Our bootloader code doesn't explicitly enable alignment faults. But of course the UEFI standard says that for AArc32 platforms:
* Unaligned access should be enabled if supported; Alignment faults are enabled otherwise.
So the efi_loader code has to align things properly I fear.

On Sat, Aug 5, 2017 at 11:08 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Sat, 5 Aug 2017 10:35:08 -0400
On Sat, Aug 5, 2017 at 10:28 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
Date: Sat, 5 Aug 2017 16:01:51 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Unfortunately something in this patch series breaks things for me on a Banana Pi:
And according to git bisect:
4e3e748a50fc3f43e20c7ff407184596d7c9a589 is the first bad commit commit 4e3e748a50fc3f43e20c7ff407184596d7c9a589 Author: Peter Jones pjones@redhat.com Date: Wed Jun 21 16:39:02 2017 -0400
efi: add some more device path structures Signed-off-by: Peter Jones <pjones@redhat.com> Signed-off-by: Rob Clark <robdclark@gmail.com>
hmm, odd.. it is only adding some #define's and structs that are not used until a later commit..
although it does also make 'struct efi_device_path_mac_addr' __packed, which it should have been before. Is this an armv7? I wonder if we have some troubles with unaligned accesses on armv7 that we don't have on aarch64 (or maybe compiler isn't turning access to device-path nodes into byte accesses if it can't do unaligned accesses. (The node in the device-path structure are byte-packed.)
This is indeed armv7.
addr2line the faulting address I guess should confirm that. If this is the issue, it's going to be a bit sad since we'll have to do a lot of copying back/forth of efi_device_path ptrs to aligned addresses :-/
Sadly that's not going to help you:
$ arm-none-eabi-addr2line -e u-boot 7ef8d878 ??:0
I suppose it is faulting somewhere in BOOTARM.EFI,
Anyway, removing __packed from struct efi_device_path_file_path makes me boot again with a tree checked out out with that commit.
Our bootloader code doesn't explicitly enable alignment faults. But of course the UEFI standard says that for AArc32 platforms:
- Unaligned access should be enabled if supported; Alignment faults are enabled otherwise.
So the efi_loader code has to align things properly I fear.
Ok, so I have an idea for a reasonably (imho) sane way forward:
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type; #ifdef BROKEN_UNALIGNED u8 pad[3]; #endif } __packed;
We'll just define BROKEN_UNALIGNED for armv7 and any other arch's that can't handle unaligned accesses. Technically it is a bit outside the way things are *supposed* to work according to my understanding of the UEFI spec. But all the code I've seen that parses the device-paths honors the length field in the efi_device_path header to find the start of the next node in the path. I can't guarantee that you'll be able to boot windows from u-boot (but I guess that isn't what most people care about ;-)), but it at least won't be more broken than it was before on these archs.
It will take a bit of extra special handling for efi_device_path_file_path (which is variable length) but with my patchset that only gets constructed in one place, so it isn't so bad.
BR, -R

On 08/05/2017 05:22 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 11:08 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Sat, 5 Aug 2017 10:35:08 -0400
On Sat, Aug 5, 2017 at 10:28 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
Date: Sat, 5 Aug 2017 16:01:51 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Unfortunately something in this patch series breaks things for me on a Banana Pi:
And according to git bisect:
4e3e748a50fc3f43e20c7ff407184596d7c9a589 is the first bad commit commit 4e3e748a50fc3f43e20c7ff407184596d7c9a589 Author: Peter Jones pjones@redhat.com Date: Wed Jun 21 16:39:02 2017 -0400
efi: add some more device path structures Signed-off-by: Peter Jones <pjones@redhat.com> Signed-off-by: Rob Clark <robdclark@gmail.com>
hmm, odd.. it is only adding some #define's and structs that are not used until a later commit..
although it does also make 'struct efi_device_path_mac_addr' __packed, which it should have been before. Is this an armv7? I wonder if we have some troubles with unaligned accesses on armv7 that we don't have on aarch64 (or maybe compiler isn't turning access to device-path nodes into byte accesses if it can't do unaligned accesses. (The node in the device-path structure are byte-packed.)
This is indeed armv7.
addr2line the faulting address I guess should confirm that. If this is the issue, it's going to be a bit sad since we'll have to do a lot of copying back/forth of efi_device_path ptrs to aligned addresses :-/
Sadly that's not going to help you:
$ arm-none-eabi-addr2line -e u-boot 7ef8d878 ??:0
I suppose it is faulting somewhere in BOOTARM.EFI,
Anyway, removing __packed from struct efi_device_path_file_path makes me boot again with a tree checked out out with that commit.
Our bootloader code doesn't explicitly enable alignment faults. But of course the UEFI standard says that for AArc32 platforms:
- Unaligned access should be enabled if supported; Alignment faults are enabled otherwise.
So the efi_loader code has to align things properly I fear.
Ok, so I have an idea for a reasonably (imho) sane way forward:
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type; #ifdef BROKEN_UNALIGNED u8 pad[3]; #endif } __packed;
Why do you need _packed here?
These are the current definitions (before your patches):
struct efi_device_path { u8 type; u8 sub_type; u16 length; };
struct efi_mac_addr { u8 addr[32]; };
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type; };
Everything is perfectly aligned to natural bounderies. The only thing that does not conform to the UEFI standard is the length of efi_mac_addr, which should be 6 for if_type in {0, 1}.
If you want to copy the data to or from unaligned addresses use memcpy.
Best regards
Heinrich
We'll just define BROKEN_UNALIGNED for armv7 and any other arch's that can't handle unaligned accesses. Technically it is a bit outside the way things are *supposed* to work according to my understanding of the UEFI spec. But all the code I've seen that parses the device-paths honors the length field in the efi_device_path header to find the start of the next node in the path. I can't guarantee that you'll be able to boot windows from u-boot (but I guess that isn't what most people care about ;-)), but it at least won't be more broken than it was before on these archs.
It will take a bit of extra special handling for efi_device_path_file_path (which is variable length) but with my patchset that only gets constructed in one place, so it isn't so bad.
BR, -R

On Sat, Aug 5, 2017 at 12:02 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 05:22 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 11:08 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Sat, 5 Aug 2017 10:35:08 -0400
On Sat, Aug 5, 2017 at 10:28 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
Date: Sat, 5 Aug 2017 16:01:51 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Unfortunately something in this patch series breaks things for me on a Banana Pi:
And according to git bisect:
4e3e748a50fc3f43e20c7ff407184596d7c9a589 is the first bad commit commit 4e3e748a50fc3f43e20c7ff407184596d7c9a589 Author: Peter Jones pjones@redhat.com Date: Wed Jun 21 16:39:02 2017 -0400
efi: add some more device path structures Signed-off-by: Peter Jones <pjones@redhat.com> Signed-off-by: Rob Clark <robdclark@gmail.com>
hmm, odd.. it is only adding some #define's and structs that are not used until a later commit..
although it does also make 'struct efi_device_path_mac_addr' __packed, which it should have been before. Is this an armv7? I wonder if we have some troubles with unaligned accesses on armv7 that we don't have on aarch64 (or maybe compiler isn't turning access to device-path nodes into byte accesses if it can't do unaligned accesses. (The node in the device-path structure are byte-packed.)
This is indeed armv7.
addr2line the faulting address I guess should confirm that. If this is the issue, it's going to be a bit sad since we'll have to do a lot of copying back/forth of efi_device_path ptrs to aligned addresses :-/
Sadly that's not going to help you:
$ arm-none-eabi-addr2line -e u-boot 7ef8d878 ??:0
I suppose it is faulting somewhere in BOOTARM.EFI,
Anyway, removing __packed from struct efi_device_path_file_path makes me boot again with a tree checked out out with that commit.
Our bootloader code doesn't explicitly enable alignment faults. But of course the UEFI standard says that for AArc32 platforms:
- Unaligned access should be enabled if supported; Alignment faults are enabled otherwise.
So the efi_loader code has to align things properly I fear.
Ok, so I have an idea for a reasonably (imho) sane way forward:
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type; #ifdef BROKEN_UNALIGNED u8 pad[3]; #endif } __packed;
Why do you need _packed here?
We probably crossed threads, but see the other email I sent that quoted the relevant part from the UEFI spec.
These are the current definitions (before your patches):
struct efi_device_path { u8 type; u8 sub_type; u16 length; };
struct efi_mac_addr { u8 addr[32]; };
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type; };
Everything is perfectly aligned to natural bounderies. The only thing that does not conform to the UEFI standard is the length of efi_mac_addr, which should be 6 for if_type in {0, 1}.
Actually, the mac is fixed size and zero padded, see 10.3.5.11. The only thing incorrect here was the missing __packed.
If you want to copy the data to or from unaligned addresses use memcpy.
The problem isn't *just* u-boot. We could do that, but it would be annoying and make the code much more convoluted. But that doesn't solve the problem for grub/shim/fallback/etc.
BR, -R

On 08/05/2017 04:35 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 10:28 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
Date: Sat, 5 Aug 2017 16:01:51 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Unfortunately something in this patch series breaks things for me on a Banana Pi:
And according to git bisect:
4e3e748a50fc3f43e20c7ff407184596d7c9a589 is the first bad commit commit 4e3e748a50fc3f43e20c7ff407184596d7c9a589 Author: Peter Jones pjones@redhat.com Date: Wed Jun 21 16:39:02 2017 -0400
efi: add some more device path structures Signed-off-by: Peter Jones <pjones@redhat.com> Signed-off-by: Rob Clark <robdclark@gmail.com>
hmm, odd.. it is only adding some #define's and structs that are not used until a later commit..
although it does also make 'struct efi_device_path_mac_addr' __packed, which it should have been before. Is this an armv7? I wonder if we have some troubles with unaligned accesses on armv7 that we don't have on aarch64 (or maybe compiler isn't turning access to device-path nodes into byte accesses if it can't do unaligned accesses. (The node in the device-path structure are byte-packed.)
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka15414.htm...
<cite>On older processors, such as ARM9 family based processors, an unaligned load had to be synthesised in software. Typically by doing a series of small accesses, and combining the results. ... Unaligned access support must be enabled by setting the SCTLR.A bit in the system control coprocessor</cite>
Generally packing structures is not a good idea performance-wise. The sequence of fields should be carefully chosen to fill up to powers of two (2, 4 , 8).
Regards
Heinrich
addr2line the faulting address I guess should confirm that. If this is the issue, it's going to be a bit sad since we'll have to do a lot of copying back/forth of efi_device_path ptrs to aligned addresses :-/
BR, -R

On Sat, Aug 5, 2017 at 11:10 AM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 04:35 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 10:28 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
Date: Sat, 5 Aug 2017 16:01:51 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Unfortunately something in this patch series breaks things for me on a Banana Pi:
And according to git bisect:
4e3e748a50fc3f43e20c7ff407184596d7c9a589 is the first bad commit commit 4e3e748a50fc3f43e20c7ff407184596d7c9a589 Author: Peter Jones pjones@redhat.com Date: Wed Jun 21 16:39:02 2017 -0400
efi: add some more device path structures Signed-off-by: Peter Jones <pjones@redhat.com> Signed-off-by: Rob Clark <robdclark@gmail.com>
hmm, odd.. it is only adding some #define's and structs that are not used until a later commit..
although it does also make 'struct efi_device_path_mac_addr' __packed, which it should have been before. Is this an armv7? I wonder if we have some troubles with unaligned accesses on armv7 that we don't have on aarch64 (or maybe compiler isn't turning access to device-path nodes into byte accesses if it can't do unaligned accesses. (The node in the device-path structure are byte-packed.)
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka15414.htm...
<cite>On older processors, such as ARM9 family based processors, an unaligned load had to be synthesised in software. Typically by doing a series of small accesses, and combining the results. ... Unaligned access support must be enabled by setting the SCTLR.A bit in the system control coprocessor</cite>
Generally packing structures is not a good idea performance-wise. The sequence of fields should be carefully chosen to fill up to powers of two (2, 4 , 8).
Yeah, it was clearly a dumb idea for UEFI to not make device-path nodes word aligned. But when implementing a standard, we don't have a choice but to implement it properly, warts and all :-/
BR, -R
Regards
Heinrich
addr2line the faulting address I guess should confirm that. If this is the issue, it's going to be a bit sad since we'll have to do a lot of copying back/forth of efi_device_path ptrs to aligned addresses :-/
BR, -R

On Sat, Aug 5, 2017 at 11:24 AM, Rob Clark robdclark@gmail.com wrote:
On Sat, Aug 5, 2017 at 11:10 AM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 04:35 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 10:28 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
Date: Sat, 5 Aug 2017 16:01:51 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Unfortunately something in this patch series breaks things for me on a Banana Pi:
And according to git bisect:
4e3e748a50fc3f43e20c7ff407184596d7c9a589 is the first bad commit commit 4e3e748a50fc3f43e20c7ff407184596d7c9a589 Author: Peter Jones pjones@redhat.com Date: Wed Jun 21 16:39:02 2017 -0400
efi: add some more device path structures Signed-off-by: Peter Jones <pjones@redhat.com> Signed-off-by: Rob Clark <robdclark@gmail.com>
hmm, odd.. it is only adding some #define's and structs that are not used until a later commit..
although it does also make 'struct efi_device_path_mac_addr' __packed, which it should have been before. Is this an armv7? I wonder if we have some troubles with unaligned accesses on armv7 that we don't have on aarch64 (or maybe compiler isn't turning access to device-path nodes into byte accesses if it can't do unaligned accesses. (The node in the device-path structure are byte-packed.)
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka15414.htm...
<cite>On older processors, such as ARM9 family based processors, an unaligned load had to be synthesised in software. Typically by doing a series of small accesses, and combining the results. ... Unaligned access support must be enabled by setting the SCTLR.A bit in the system control coprocessor</cite>
Generally packing structures is not a good idea performance-wise. The sequence of fields should be carefully chosen to fill up to powers of two (2, 4 , 8).
Yeah, it was clearly a dumb idea for UEFI to not make device-path nodes word aligned. But when implementing a standard, we don't have a choice but to implement it properly, warts and all :-/
btw, just for reference (if anyone is curious), see sect 10.3.1 in UEFI spec v2.7. If you don't want to bother looking it up, here is the exact wording:
A Device Path is a series of generic Device Path nodes. The first Device Path node starts at byte offset zero of the Device Path. The next Device Path node starts at the end of the previous Device Path node. Therefore all nodes are byte-packed data structures that may appear on any byte boundary. All code references to device path notes must assume all fields are unaligned. Since every Device Path node contains a length field in a known place, it is possible to traverse Device Path nodes that are of an unknown type. There is no limit to the number, type, or sequence of nodes in a Device Path.
So clearly what we were doing before was incorrect.. but cheating w/ extra padding bytes on arch's that cannot handle unaligned accesses will avoid having to go fix grub, bootaa64/shim/fallback (and anything else that uses gnu-efi), and apparently openbsd's bootaa64.efi. It is kinda weird to be using efi on these arch's in the first place, so I guess I don't feel as badly about the padding bytes hack on those arch's. (But we should not use the hack on aarch64.)
BR, -R

From: Rob Clark robdclark@gmail.com Date: Sat, 5 Aug 2017 11:36:25 -0400
On Sat, Aug 5, 2017 at 11:24 AM, Rob Clark robdclark@gmail.com wrote:
On Sat, Aug 5, 2017 at 11:10 AM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 04:35 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 10:28 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
Date: Sat, 5 Aug 2017 16:01:51 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Unfortunately something in this patch series breaks things for me on a Banana Pi:
And according to git bisect:
4e3e748a50fc3f43e20c7ff407184596d7c9a589 is the first bad commit commit 4e3e748a50fc3f43e20c7ff407184596d7c9a589 Author: Peter Jones pjones@redhat.com Date: Wed Jun 21 16:39:02 2017 -0400
efi: add some more device path structures Signed-off-by: Peter Jones <pjones@redhat.com> Signed-off-by: Rob Clark <robdclark@gmail.com>
hmm, odd.. it is only adding some #define's and structs that are not used until a later commit..
although it does also make 'struct efi_device_path_mac_addr' __packed, which it should have been before. Is this an armv7? I wonder if we have some troubles with unaligned accesses on armv7 that we don't have on aarch64 (or maybe compiler isn't turning access to device-path nodes into byte accesses if it can't do unaligned accesses. (The node in the device-path structure are byte-packed.)
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka15414.htm...
<cite>On older processors, such as ARM9 family based processors, an unaligned load had to be synthesised in software. Typically by doing a series of small accesses, and combining the results. ... Unaligned access support must be enabled by setting the SCTLR.A bit in the system control coprocessor</cite>
Generally packing structures is not a good idea performance-wise. The sequence of fields should be carefully chosen to fill up to powers of two (2, 4 , 8).
Yeah, it was clearly a dumb idea for UEFI to not make device-path nodes word aligned. But when implementing a standard, we don't have a choice but to implement it properly, warts and all :-/
btw, just for reference (if anyone is curious), see sect 10.3.1 in UEFI spec v2.7. If you don't want to bother looking it up, here is the exact wording:
A Device Path is a series of generic Device Path nodes. The first Device Path node starts at byte offset zero of the Device Path. The next Device Path node starts at the end of the previous Device Path node. Therefore all nodes are byte-packed data structures that may appear on any byte boundary. All code references to device path notes must assume all fields are unaligned. Since every Device Path node contains a length field in a known place, it is possible to traverse Device Path nodes that are of an unknown type. There is no limit to the number, type, or sequence of nodes in a Device Path.
So clearly what we were doing before was incorrect.. but cheating w/ extra padding bytes on arch's that cannot handle unaligned accesses will avoid having to go fix grub, bootaa64/shim/fallback (and anything else that uses gnu-efi), and apparently openbsd's bootaa64.efi. It is kinda weird to be using efi on these arch's in the first place, so I guess I don't feel as badly about the padding bytes hack on those arch's. (But we should not use the hack on aarch64.)
Looking a bit more closely at the OpenBSD efiboot code, I'm pretty sure we handle the parsing of device path nodes correctly. We use an incarnation of the Intel EFI header files which have:
typedef struct _EFI_DEVICE_PATH { UINT8 Type; UINT8 SubType; UINT8 Length[2]; } EFI_DEVICE_PATH;
#define DevicePathNodeLength(a) ( ((a)->Length[0]) | ((a)->Length[1] << 8) )
so this is all done using byte access.
Going back to the original crash report:
data abort pc : [<7ef8d878>] lr : [<7ef8d874>] reloc pc : [<4a039878>] lr : [<4a039874>] sp : 7af29660 ip : 00000000 fp : 7af29774 r10: 7efec230 r9 : 7af33ee0 r8 : 00000000 r7 : 00000009 r6 : 7ef9e8b8 r5 : 7af296a0 r4 : 7efa4495 r3 : 7af296a0 r2 : 0000008c r1 : 7af29658 r0 : 00000004 Flags: nzCV IRQs off FIQs off Mode SVC_32
I think it is actually "reloc pc" instead of "pc" that we need to look at:
$ addr2line -e u-boot 0x4a039874 /home/kettenis/tmp/rclark/u-boot/include/efi_loader.h:272
which points at the guidstr() function. That code certainly looks suspicious. It will defenitely trigger alignment faults if the guid isn't 32-bit aligned.
The relevant instruction is a 16-bit load:
4a039878: e1d430b4 ldrh r3, [r4, #4]
and with r4 = 7efa4495 that will defenitely trap.
Looking at the defenition efi_guid_t in u-boot:
typedef struct { u8 b[16]; } efi_guid_t;
there is no guarantee that GUIDs are properly aligned, so you'll need to fix the guidstr function introduced in commit b6d913c6101ba891eb2bcb08a4ee9fc8fb57367.
Things are already broken before that commit though, so there is another problem. I'll see if I can figure out what it is...

On Sun, Aug 6, 2017 at 9:16 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Sat, 5 Aug 2017 11:36:25 -0400
On Sat, Aug 5, 2017 at 11:24 AM, Rob Clark robdclark@gmail.com wrote:
On Sat, Aug 5, 2017 at 11:10 AM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 04:35 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 10:28 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
> Date: Sat, 5 Aug 2017 16:01:51 +0200 (CEST) > From: Mark Kettenis mark.kettenis@xs4all.nl > > Unfortunately something in this patch series breaks things for me on a > Banana Pi:
And according to git bisect:
4e3e748a50fc3f43e20c7ff407184596d7c9a589 is the first bad commit commit 4e3e748a50fc3f43e20c7ff407184596d7c9a589 Author: Peter Jones pjones@redhat.com Date: Wed Jun 21 16:39:02 2017 -0400
efi: add some more device path structures Signed-off-by: Peter Jones <pjones@redhat.com> Signed-off-by: Rob Clark <robdclark@gmail.com>
hmm, odd.. it is only adding some #define's and structs that are not used until a later commit..
although it does also make 'struct efi_device_path_mac_addr' __packed, which it should have been before. Is this an armv7? I wonder if we have some troubles with unaligned accesses on armv7 that we don't have on aarch64 (or maybe compiler isn't turning access to device-path nodes into byte accesses if it can't do unaligned accesses. (The node in the device-path structure are byte-packed.)
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka15414.htm...
<cite>On older processors, such as ARM9 family based processors, an unaligned load had to be synthesised in software. Typically by doing a series of small accesses, and combining the results. ... Unaligned access support must be enabled by setting the SCTLR.A bit in the system control coprocessor</cite>
Generally packing structures is not a good idea performance-wise. The sequence of fields should be carefully chosen to fill up to powers of two (2, 4 , 8).
Yeah, it was clearly a dumb idea for UEFI to not make device-path nodes word aligned. But when implementing a standard, we don't have a choice but to implement it properly, warts and all :-/
btw, just for reference (if anyone is curious), see sect 10.3.1 in UEFI spec v2.7. If you don't want to bother looking it up, here is the exact wording:
A Device Path is a series of generic Device Path nodes. The first Device Path node starts at byte offset zero of the Device Path. The next Device Path node starts at the end of the previous Device Path node. Therefore all nodes are byte-packed data structures that may appear on any byte boundary. All code references to device path notes must assume all fields are unaligned. Since every Device Path node contains a length field in a known place, it is possible to traverse Device Path nodes that are of an unknown type. There is no limit to the number, type, or sequence of nodes in a Device Path.
So clearly what we were doing before was incorrect.. but cheating w/ extra padding bytes on arch's that cannot handle unaligned accesses will avoid having to go fix grub, bootaa64/shim/fallback (and anything else that uses gnu-efi), and apparently openbsd's bootaa64.efi. It is kinda weird to be using efi on these arch's in the first place, so I guess I don't feel as badly about the padding bytes hack on those arch's. (But we should not use the hack on aarch64.)
Looking a bit more closely at the OpenBSD efiboot code, I'm pretty sure we handle the parsing of device path nodes correctly. We use an incarnation of the Intel EFI header files which have:
typedef struct _EFI_DEVICE_PATH { UINT8 Type; UINT8 SubType; UINT8 Length[2]; } EFI_DEVICE_PATH;
#define DevicePathNodeLength(a) ( ((a)->Length[0]) | ((a)->Length[1] << 8) )
so this is all done using byte access.
Hmm, I assume the OpenBSD efiboot code does look at the payload of device-path nodes, like HARDDRIVE_DEVICE_PATH.. which does use u32 and u64 fields (which would be unaligned). Although that might not be the problem here.
Going back to the original crash report:
data abort pc : [<7ef8d878>] lr : [<7ef8d874>] reloc pc : [<4a039878>] lr : [<4a039874>] sp : 7af29660 ip : 00000000 fp : 7af29774 r10: 7efec230 r9 : 7af33ee0 r8 : 00000000 r7 : 00000009 r6 : 7ef9e8b8 r5 : 7af296a0 r4 : 7efa4495 r3 : 7af296a0 r2 : 0000008c r1 : 7af29658 r0 : 00000004 Flags: nzCV IRQs off FIQs off Mode SVC_32
I think it is actually "reloc pc" instead of "pc" that we need to look at:
$ addr2line -e u-boot 0x4a039874 /home/kettenis/tmp/rclark/u-boot/include/efi_loader.h:272
which points at the guidstr() function. That code certainly looks suspicious. It will defenitely trigger alignment faults if the guid isn't 32-bit aligned.
hmm, interesting. At least gnu-efi's EFI_GUID uses the same u32+u16+u16+u8[8] layout. And iirc so did linux kernel and grub, so it seemed like u-boot was the odd one out for using u8[16]. Although maybe we are printing one of our own guid's or openbsd efiboot is also using u8[16].
Either way I've dropped this patch and instead added %pG support to vsprintf, using uuid_bin_to_str() which only does byte accesses.
The latest can be found here:
https://github.com/robclark/u-boot/commits/enough-uefi-for-shim
https://github.com/robclark/u-boot.git enough-uefi-for-shim
The relevant instruction is a 16-bit load:
4a039878: e1d430b4 ldrh r3, [r4, #4]
and with r4 = 7efa4495 that will defenitely trap.
Looking at the defenition efi_guid_t in u-boot:
typedef struct { u8 b[16]; } efi_guid_t;
there is no guarantee that GUIDs are properly aligned, so you'll need to fix the guidstr function introduced in commit b6d913c6101ba891eb2bcb08a4ee9fc8fb57367.
Things are already broken before that commit though, so there is another problem. I'll see if I can figure out what it is...
Thanks
BR, -R

On Sun, Aug 6, 2017 at 10:17 AM, Rob Clark robdclark@gmail.com wrote:
On Sun, Aug 6, 2017 at 9:16 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Sat, 5 Aug 2017 11:36:25 -0400
On Sat, Aug 5, 2017 at 11:24 AM, Rob Clark robdclark@gmail.com wrote:
On Sat, Aug 5, 2017 at 11:10 AM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 04:35 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 10:28 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote: >> Date: Sat, 5 Aug 2017 16:01:51 +0200 (CEST) >> From: Mark Kettenis mark.kettenis@xs4all.nl >> >> Unfortunately something in this patch series breaks things for me on a >> Banana Pi: > > And according to git bisect: > > 4e3e748a50fc3f43e20c7ff407184596d7c9a589 is the first bad commit > commit 4e3e748a50fc3f43e20c7ff407184596d7c9a589 > Author: Peter Jones pjones@redhat.com > Date: Wed Jun 21 16:39:02 2017 -0400 > > efi: add some more device path structures > > Signed-off-by: Peter Jones pjones@redhat.com > Signed-off-by: Rob Clark robdclark@gmail.com
hmm, odd.. it is only adding some #define's and structs that are not used until a later commit..
although it does also make 'struct efi_device_path_mac_addr' __packed, which it should have been before. Is this an armv7? I wonder if we have some troubles with unaligned accesses on armv7 that we don't have on aarch64 (or maybe compiler isn't turning access to device-path nodes into byte accesses if it can't do unaligned accesses. (The node in the device-path structure are byte-packed.)
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka15414.htm...
<cite>On older processors, such as ARM9 family based processors, an unaligned load had to be synthesised in software. Typically by doing a series of small accesses, and combining the results. ... Unaligned access support must be enabled by setting the SCTLR.A bit in the system control coprocessor</cite>
Generally packing structures is not a good idea performance-wise. The sequence of fields should be carefully chosen to fill up to powers of two (2, 4 , 8).
Yeah, it was clearly a dumb idea for UEFI to not make device-path nodes word aligned. But when implementing a standard, we don't have a choice but to implement it properly, warts and all :-/
btw, just for reference (if anyone is curious), see sect 10.3.1 in UEFI spec v2.7. If you don't want to bother looking it up, here is the exact wording:
A Device Path is a series of generic Device Path nodes. The first Device Path node starts at byte offset zero of the Device Path. The next Device Path node starts at the end of the previous Device Path node. Therefore all nodes are byte-packed data structures that may appear on any byte boundary. All code references to device path notes must assume all fields are unaligned. Since every Device Path node contains a length field in a known place, it is possible to traverse Device Path nodes that are of an unknown type. There is no limit to the number, type, or sequence of nodes in a Device Path.
So clearly what we were doing before was incorrect.. but cheating w/ extra padding bytes on arch's that cannot handle unaligned accesses will avoid having to go fix grub, bootaa64/shim/fallback (and anything else that uses gnu-efi), and apparently openbsd's bootaa64.efi. It is kinda weird to be using efi on these arch's in the first place, so I guess I don't feel as badly about the padding bytes hack on those arch's. (But we should not use the hack on aarch64.)
Looking a bit more closely at the OpenBSD efiboot code, I'm pretty sure we handle the parsing of device path nodes correctly. We use an incarnation of the Intel EFI header files which have:
typedef struct _EFI_DEVICE_PATH { UINT8 Type; UINT8 SubType; UINT8 Length[2]; } EFI_DEVICE_PATH;
#define DevicePathNodeLength(a) ( ((a)->Length[0]) | ((a)->Length[1] << 8) )
so this is all done using byte access.
Hmm, I assume the OpenBSD efiboot code does look at the payload of device-path nodes, like HARDDRIVE_DEVICE_PATH.. which does use u32 and u64 fields (which would be unaligned). Although that might not be the problem here.
Going back to the original crash report:
data abort pc : [<7ef8d878>] lr : [<7ef8d874>] reloc pc : [<4a039878>] lr : [<4a039874>] sp : 7af29660 ip : 00000000 fp : 7af29774 r10: 7efec230 r9 : 7af33ee0 r8 : 00000000 r7 : 00000009 r6 : 7ef9e8b8 r5 : 7af296a0 r4 : 7efa4495 r3 : 7af296a0 r2 : 0000008c r1 : 7af29658 r0 : 00000004 Flags: nzCV IRQs off FIQs off Mode SVC_32
I think it is actually "reloc pc" instead of "pc" that we need to look at:
$ addr2line -e u-boot 0x4a039874 /home/kettenis/tmp/rclark/u-boot/include/efi_loader.h:272
which points at the guidstr() function. That code certainly looks suspicious. It will defenitely trigger alignment faults if the guid isn't 32-bit aligned.
hmm, interesting. At least gnu-efi's EFI_GUID uses the same u32+u16+u16+u8[8] layout. And iirc so did linux kernel and grub, so it seemed like u-boot was the odd one out for using u8[16]. Although maybe we are printing one of our own guid's or openbsd efiboot is also using u8[16].
Either way I've dropped this patch and instead added %pG support to vsprintf, using uuid_bin_to_str() which only does byte accesses.
The latest can be found here:
https://github.com/robclark/u-boot/commits/enough-uefi-for-shim
https://github.com/robclark/u-boot.git enough-uefi-for-shim
The relevant instruction is a 16-bit load:
4a039878: e1d430b4 ldrh r3, [r4, #4]
and with r4 = 7efa4495 that will defenitely trap.
Looking at the defenition efi_guid_t in u-boot:
typedef struct { u8 b[16]; } efi_guid_t;
there is no guarantee that GUIDs are properly aligned, so you'll need to fix the guidstr function introduced in commit b6d913c6101ba891eb2bcb08a4ee9fc8fb57367.
Things are already broken before that commit though, so there is another problem. I'll see if I can figure out what it is...
Thanks
btw, we do have some travis tests that run grub.efi (in qemu) on armv7 and others.. maybe adding OpenBSD's efiboot to the test suit would be a good idea? (And eventually shim+fallback.efi after this patchset is merged..)
BR, -R

Date: Sun, 6 Aug 2017 15:16:09 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Things are already broken before that commit though, so there is another problem. I'll see if I can figure out what it is...
data abort pc : [<7ef59160>] lr : [<7ef59118>] reloc pc : [<4a003160>] lr : [<4a003118>] sp : 7af2b820 ip : 7af69635 fp : 7ef5aee4 r10: 00000005 r9 : 7af35ee0 r8 : 7efb4490 r7 : 7af695e8 r6 : 7af69620 r5 : 0000005c r4 : 7af2b828 r3 : 7efae477 r2 : 0000005c r1 : 0000002f r0 : 00000000 Flags: nzCv IRQs off FIQs off Mode SVC_32
addr2line -e u-boot.bin 0x4a003160 /home/kettenis/tmp/rclark/u-boot/include/efi_loader.h:204
which is the ascii2unicode() function which is used in efi_disk_add_dev() and indeed does 16-bit stores to potentiaslly unaligned memory. And yes, adding __packed to struct efi_device_file_path will trigger the unaligned access in this case.

On Sun, Aug 6, 2017 at 10:28 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
Date: Sun, 6 Aug 2017 15:16:09 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Things are already broken before that commit though, so there is another problem. I'll see if I can figure out what it is...
data abort pc : [<7ef59160>] lr : [<7ef59118>] reloc pc : [<4a003160>] lr : [<4a003118>] sp : 7af2b820 ip : 7af69635 fp : 7ef5aee4 r10: 00000005 r9 : 7af35ee0 r8 : 7efb4490 r7 : 7af695e8 r6 : 7af69620 r5 : 0000005c r4 : 7af2b828 r3 : 7efae477 r2 : 0000005c r1 : 0000002f r0 : 00000000 Flags: nzCv IRQs off FIQs off Mode SVC_32
addr2line -e u-boot.bin 0x4a003160 /home/kettenis/tmp/rclark/u-boot/include/efi_loader.h:204
which is the ascii2unicode() function which is used in efi_disk_add_dev() and indeed does 16-bit stores to potentiaslly unaligned memory. And yes, adding __packed to struct efi_device_file_path will trigger the unaligned access in this case.
Hmm, I could see that. Have you had a chance to try with "efi_loader: hack for archs that cannot do unaligned accesses"? (That patch should probably be squashed back in to various earlier patches, but I figured keeping it separate for now would be easier to review.) ascii2unicode() is probably only the first place that would hit unaligned accesses..
But that all said, [1] seems to imply armv7 *can* do unaligned accesses. So maybe this is a banana-pi specific issue. Maybe some cp15 bit not set correctly? (Otherwise I think I should have it this issue in travis with tests that load grub.efi on various qemu platforms.)
[1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka15414.htm...
I've started trying to hack up test_efi_loader.py to add a test that loads OpenBSD's bootloader.. kinda muddling through it at this point, since not a py expert or too familiar w/ u-boot's test framework. But I'll see if I can get to the point where I can run the same thing on various arm7 and aarch64 devices in qemu.
BR, -R

On Sun, Aug 6, 2017 at 10:45 AM, Rob Clark robdclark@gmail.com wrote:
I've started trying to hack up test_efi_loader.py to add a test that loads OpenBSD's bootloader.. kinda muddling through it at this point, since not a py expert or too familiar w/ u-boot's test framework. But I'll see if I can get to the point where I can run the same thing on various arm7 and aarch64 devices in qemu.
Making a bit of progress on this (running it on a vexpress_ca15_tc2 board in qemu).. any hint where I can find BOOTARM.EFI src code?
=> tftpboot 80400000 obsdboot.efi smc911x: MAC 52:54:00:12:34:56 smc911x: detected LAN9118 controller smc911x: phy initialized smc911x: MAC 52:54:00:12:34:56 Using smc911x-0 device TFTP from server 10.0.2.2; our IP address is 10.0.2.15 Filename 'obsdboot.efi'. Load address: 0x80400000 Loading: *%08##### 12.4 MiB/s done Bytes transferred = 64908 (fd8c hex) smc911x: MAC 52:54:00:12:34:56 => crc32 80400000 $filesize CRC32 for 80400000 ... 8040fd8b ==> a9ac4fcf => bootefi 80400000 ## Starting EFI application at 80400000 ... WARNING: Invalid device tree, expect boot to fail BS->LocateHandle() returns 0 undefined instruction pc : [<9eec65c4>] lr : [<9eeca390>] sp : 9fed7a18 ip : 0000003f fp : 9fed7a2c r10: 00000000 r9 : 9eed4658 r8 : 00000000 r7 : 9eed1ce4 r6 : 9eed3538 r5 : 9fed7a6c r4 : 9eed4658 r3 : 00000000 r2 : 9eed2f8e r1 : 9eed1ee0 r0 : 00000000 Flags: nZCv IRQs off FIQs off Mode SVC_32 Resetting CPU ...
resetting ...
U-Boot 2017.09-rc1-00025-g534695d189 (Aug 06 2017 - 06:58:16 -0400)
DRAM: 1 GiB WARNING: Caches not enabled Flash: 128 MiB MMC: MMC: 0 *** Warning - bad CRC, using default environment
In: serial Out: serial Err: serial Net: smc911x-0 Hit any key to stop autoboot: 2

On 08/06/2017 05:34 PM, Rob Clark wrote:
On Sun, Aug 6, 2017 at 10:45 AM, Rob Clark robdclark@gmail.com wrote:
I've started trying to hack up test_efi_loader.py to add a test that loads OpenBSD's bootloader.. kinda muddling through it at this point, since not a py expert or too familiar w/ u-boot's test framework. But I'll see if I can get to the point where I can run the same thing on various arm7 and aarch64 devices in qemu.
Making a bit of progress on this (running it on a vexpress_ca15_tc2 board in qemu).. any hint where I can find BOOTARM.EFI src code?
On Debian arm64 the following commands create bootaa64.efi.
sudo apt-get install grub-efi-arm64 sudo update-grub sudo grub-install --target=arm64-efi --boot-directory=/boot --efi-directory=/EFI {/EFI is my mounted FAT partition}
I guess you can do the same on armhf to create bootarm.efi
Regards
Heinrich
=> tftpboot 80400000 obsdboot.efi smc911x: MAC 52:54:00:12:34:56 smc911x: detected LAN9118 controller smc911x: phy initialized smc911x: MAC 52:54:00:12:34:56 Using smc911x-0 device TFTP from server 10.0.2.2; our IP address is 10.0.2.15 Filename 'obsdboot.efi'. Load address: 0x80400000 Loading: *%08##### 12.4 MiB/s done Bytes transferred = 64908 (fd8c hex) smc911x: MAC 52:54:00:12:34:56 => crc32 80400000 $filesize CRC32 for 80400000 ... 8040fd8b ==> a9ac4fcf => bootefi 80400000 ## Starting EFI application at 80400000 ... WARNING: Invalid device tree, expect boot to fail BS->LocateHandle() returns 0 undefined instruction pc : [<9eec65c4>] lr : [<9eeca390>] sp : 9fed7a18 ip : 0000003f fp : 9fed7a2c r10: 00000000 r9 : 9eed4658 r8 : 00000000 r7 : 9eed1ce4 r6 : 9eed3538 r5 : 9fed7a6c r4 : 9eed4658 r3 : 00000000 r2 : 9eed2f8e r1 : 9eed1ee0 r0 : 00000000 Flags: nZCv IRQs off FIQs off Mode SVC_32 Resetting CPU ...
resetting ...
U-Boot 2017.09-rc1-00025-g534695d189 (Aug 06 2017 - 06:58:16 -0400)
DRAM: 1 GiB WARNING: Caches not enabled Flash: 128 MiB MMC: MMC: 0 *** Warning - bad CRC, using default environment
In: serial Out: serial Err: serial Net: smc911x-0 Hit any key to stop autoboot: 2

On Sun, Aug 06, 2017 at 11:34:15AM -0400, Rob Clark wrote:
On Sun, Aug 6, 2017 at 10:45 AM, Rob Clark robdclark@gmail.com wrote:
I've started trying to hack up test_efi_loader.py to add a test that loads OpenBSD's bootloader.. kinda muddling through it at this point, since not a py expert or too familiar w/ u-boot's test framework. But I'll see if I can get to the point where I can run the same thing on various arm7 and aarch64 devices in qemu.
Making a bit of progress on this (running it on a vexpress_ca15_tc2 board in qemu).. any hint where I can find BOOTARM.EFI src code?
https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/arch/armv7/stand/efiboot/ https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/stand/efi/include/

From: Rob Clark robdclark@gmail.com Date: Sun, 6 Aug 2017 11:34:15 -0400
On Sun, Aug 6, 2017 at 10:45 AM, Rob Clark robdclark@gmail.com wrote:
I've started trying to hack up test_efi_loader.py to add a test that loads OpenBSD's bootloader.. kinda muddling through it at this point, since not a py expert or too familiar w/ u-boot's test framework. But I'll see if I can get to the point where I can run the same thing on various arm7 and aarch64 devices in qemu.
Making a bit of progress on this (running it on a vexpress_ca15_tc2 board in qemu).. any hint where I can find BOOTARM.EFI src code?
https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/arch/armv7/stand/efiboot/e...
Your failure below looks a bit different from what I'm getting on the Banana Pi now. There I get stuck because the 2nd BS->HandleProtocol() call in efi_main() fails. Somehow the device path of the registered disk devices isn't matched correctly to the boot device path...
BTW, the OpenBSD code runs fine without the alignment hack. Our code is pretty minimal and doesn't actualy look into the device path components. It just matches the components based on type and by soing memcmp.
=> tftpboot 80400000 obsdboot.efi smc911x: MAC 52:54:00:12:34:56 smc911x: detected LAN9118 controller smc911x: phy initialized smc911x: MAC 52:54:00:12:34:56 Using smc911x-0 device TFTP from server 10.0.2.2; our IP address is 10.0.2.15 Filename 'obsdboot.efi'. Load address: 0x80400000 Loading: *%08##### 12.4 MiB/s done Bytes transferred = 64908 (fd8c hex) smc911x: MAC 52:54:00:12:34:56 => crc32 80400000 $filesize CRC32 for 80400000 ... 8040fd8b ==> a9ac4fcf => bootefi 80400000 ## Starting EFI application at 80400000 ... WARNING: Invalid device tree, expect boot to fail BS->LocateHandle() returns 0 undefined instruction pc : [<9eec65c4>] lr : [<9eeca390>] sp : 9fed7a18 ip : 0000003f fp : 9fed7a2c r10: 00000000 r9 : 9eed4658 r8 : 00000000 r7 : 9eed1ce4 r6 : 9eed3538 r5 : 9fed7a6c r4 : 9eed4658 r3 : 00000000 r2 : 9eed2f8e r1 : 9eed1ee0 r0 : 00000000 Flags: nZCv IRQs off FIQs off Mode SVC_32 Resetting CPU ...
resetting ...
U-Boot 2017.09-rc1-00025-g534695d189 (Aug 06 2017 - 06:58:16 -0400)
DRAM: 1 GiB WARNING: Caches not enabled Flash: 128 MiB MMC: MMC: 0 *** Warning - bad CRC, using default environment
In: serial Out: serial Err: serial Net: smc911x-0 Hit any key to stop autoboot: 2

On Sun, Aug 6, 2017 at 1:28 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Sun, 6 Aug 2017 11:34:15 -0400
On Sun, Aug 6, 2017 at 10:45 AM, Rob Clark robdclark@gmail.com wrote:
I've started trying to hack up test_efi_loader.py to add a test that loads OpenBSD's bootloader.. kinda muddling through it at this point, since not a py expert or too familiar w/ u-boot's test framework. But I'll see if I can get to the point where I can run the same thing on various arm7 and aarch64 devices in qemu.
Making a bit of progress on this (running it on a vexpress_ca15_tc2 board in qemu).. any hint where I can find BOOTARM.EFI src code?
https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/arch/armv7/stand/efiboot/e...
Your failure below looks a bit different from what I'm getting on the Banana Pi now. There I get stuck because the 2nd BS->HandleProtocol() call in efi_main() fails. Somehow the device path of the registered disk devices isn't matched correctly to the boot device path...
that is.. odd.. mind adding in lib/efi_loader/Makefile:
ccflags-y += -DDEBUG=1
?
(you can make the console output easier to read again w/ #undef DEBUG at top of efi_console.c)
With my complete patchset (ie. assuming this isn't in the middle of a bisect between 13/20 and 15/20) the device paths for the diskobj and EFI_LOADED_IMAGE::DeviceHandle should be constructed identically. (Ie. the patchset consolidates the two different places it was constructed before... and also fixes the thing I notice you work around in efi_diskprobe())
BTW, the OpenBSD code runs fine without the alignment hack. Our code is pretty minimal and doesn't actualy look into the device path components. It just matches the components based on type and by soing memcmp.
Hmm, well I do suspect there are still cases where u-boot could crash because of unaligned access without the hack. Although I'm less convinced that we should need the hack on armv7 and more thinking this is something specific about banana-pi (or allwinner?). The vexpress_ca15_tc2 "board" in qemu seems to be working properly..
Mind sending me or pastebin'ing your u-boot .config? There are some different device-path construction depending on legacy vs CONFIG_DM+CONFIG_BLK (the legacy case *looks* right to me, and is used by vexpress_ca15_tc2.. so I think it should work..)
If OpenBSD supports the vexpress boards, I guess with a suitable qemu -sd disk.img I should be able to try booting all the way to OS..
BR, -R

On Sun, Aug 6, 2017 at 6:49 PM, Rob Clark robdclark@gmail.com wrote:
On Sun, Aug 6, 2017 at 1:28 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Sun, 6 Aug 2017 11:34:15 -0400
On Sun, Aug 6, 2017 at 10:45 AM, Rob Clark robdclark@gmail.com wrote:
I've started trying to hack up test_efi_loader.py to add a test that loads OpenBSD's bootloader.. kinda muddling through it at this point, since not a py expert or too familiar w/ u-boot's test framework. But I'll see if I can get to the point where I can run the same thing on various arm7 and aarch64 devices in qemu.
Making a bit of progress on this (running it on a vexpress_ca15_tc2 board in qemu).. any hint where I can find BOOTARM.EFI src code?
https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/arch/armv7/stand/efiboot/e...
Your failure below looks a bit different from what I'm getting on the Banana Pi now. There I get stuck because the 2nd BS->HandleProtocol() call in efi_main() fails. Somehow the device path of the registered disk devices isn't matched correctly to the boot device path...
that is.. odd.. mind adding in lib/efi_loader/Makefile:
ccflags-y += -DDEBUG=1
?
(you can make the console output easier to read again w/ #undef DEBUG at top of efi_console.c)
With my complete patchset (ie. assuming this isn't in the middle of a bisect between 13/20 and 15/20) the device paths for the diskobj and EFI_LOADED_IMAGE::DeviceHandle should be constructed identically. (Ie. the patchset consolidates the two different places it was constructed before... and also fixes the thing I notice you work around in efi_diskprobe())
BTW, the OpenBSD code runs fine without the alignment hack. Our code is pretty minimal and doesn't actualy look into the device path components. It just matches the components based on type and by soing memcmp.
Hmm, well I do suspect there are still cases where u-boot could crash because of unaligned access without the hack. Although I'm less convinced that we should need the hack on armv7 and more thinking this is something specific about banana-pi (or allwinner?). The vexpress_ca15_tc2 "board" in qemu seems to be working properly..
All AllWinner SoCs are Cortex-A series so ARMv7, in the case of the banana pi series are AW A20s so are Cortex-A7 based so should be fine too

From: Rob Clark robdclark@gmail.com Date: Sun, 6 Aug 2017 13:49:43 -0400
On Sun, Aug 6, 2017 at 1:28 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Sun, 6 Aug 2017 11:34:15 -0400
On Sun, Aug 6, 2017 at 10:45 AM, Rob Clark robdclark@gmail.com wrote:
I've started trying to hack up test_efi_loader.py to add a test that loads OpenBSD's bootloader.. kinda muddling through it at this point, since not a py expert or too familiar w/ u-boot's test framework. But I'll see if I can get to the point where I can run the same thing on various arm7 and aarch64 devices in qemu.
Making a bit of progress on this (running it on a vexpress_ca15_tc2 board in qemu).. any hint where I can find BOOTARM.EFI src code?
https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/arch/armv7/stand/efiboot/e...
Your failure below looks a bit different from what I'm getting on the Banana Pi now. There I get stuck because the 2nd BS->HandleProtocol() call in efi_main() fails. Somehow the device path of the registered disk devices isn't matched correctly to the boot device path...
that is.. odd.. mind adding in lib/efi_loader/Makefile:
ccflags-y += -DDEBUG=1
?
(you can make the console output easier to read again w/ #undef DEBUG at top of efi_console.c)
With my complete patchset (ie. assuming this isn't in the middle of a bisect between 13/20 and 15/20) the device paths for the diskobj and EFI_LOADED_IMAGE::DeviceHandle should be constructed identically. (Ie. the patchset consolidates the two different places it was constructed before... and also fixes the thing I notice you work around in efi_diskprobe())
BTW, the OpenBSD code runs fine without the alignment hack. Our code is pretty minimal and doesn't actualy look into the device path components. It just matches the components based on type and by soing memcmp.
Hmm, well I do suspect there are still cases where u-boot could crash because of unaligned access without the hack. Although I'm less convinced that we should need the hack on armv7 and more thinking this is something specific about banana-pi (or allwinner?). The vexpress_ca15_tc2 "board" in qemu seems to be working properly..
I suspect qemu simply doesn't emulate the alignment trap. The u-boot startup code explicitly enables alignment fauls on armv7. See arch/arm/cpu/armv7/start.S:152. This helps catching bugs!
Mind sending me or pastebin'ing your u-boot .config? There are some different device-path construction depending on legacy vs CONFIG_DM+CONFIG_BLK (the legacy case *looks* right to me, and is used by vexpress_ca15_tc2.. so I think it should work..)
See below. The Banana Pi (and all other sunxi boards) indeed uses the legacy code path. And I think there is a bug in the legacy codepath where it encodes the partition in the "file" path component.
If OpenBSD supports the vexpress boards, I guess with a suitable qemu -sd disk.img I should be able to try booting all the way to OS..
I think it does. We don't have a "miniroot" image for it though. It might be possible to take
http://ftp.openbsd.org/pub/OpenBSD/snapshots/armv7/miniroot-cubie-61.fs
and dd the vexpress u-boot into the right location and copy the device tree onto the msdos filesystem in that image.
# # Automatically generated file; DO NOT EDIT. # U-Boot 2017.09-rc1 Configuration # CONFIG_CREATE_ARCH_SYMLINK=y # CONFIG_ARC is not set CONFIG_ARM=y # CONFIG_M68K is not set # CONFIG_MICROBLAZE is not set # CONFIG_MIPS is not set # CONFIG_NDS32 is not set # CONFIG_NIOS2 is not set # CONFIG_PPC is not set # CONFIG_SANDBOX is not set # CONFIG_SH is not set # CONFIG_X86 is not set # CONFIG_XTENSA is not set CONFIG_SYS_ARCH="arm" CONFIG_SYS_CPU="armv7" CONFIG_SYS_SOC="sunxi" CONFIG_SYS_BOARD="sunxi" CONFIG_SYS_CONFIG_NAME="sun7i"
# # ARM architecture # CONFIG_HAS_VBAR=y CONFIG_HAS_THUMB2=y CONFIG_ARM_ASM_UNIFIED=y CONFIG_CPU_V7=y CONFIG_SYS_ARM_ARCH=7 CONFIG_SYS_CACHE_SHIFT_6=y CONFIG_SYS_CACHELINE_SIZE=64 # CONFIG_ARM_SMCCC is not set # CONFIG_SEMIHOSTING is not set # CONFIG_SYS_THUMB_BUILD is not set CONFIG_SPL_SYS_THUMB_BUILD=y # CONFIG_SYS_L2CACHE_OFF is not set # CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK is not set # CONFIG_ARM_CORTEX_CPU_IS_UP is not set CONFIG_USE_ARCH_MEMCPY=y CONFIG_SPL_USE_ARCH_MEMCPY=y CONFIG_USE_ARCH_MEMSET=y CONFIG_SPL_USE_ARCH_MEMSET=y # CONFIG_ARM64_SUPPORT_AARCH32 is not set # CONFIG_ARCH_AT91 is not set # CONFIG_TARGET_EDB93XX is not set # CONFIG_TARGET_ASPENITE is not set # CONFIG_TARGET_GPLUGD is not set # CONFIG_ARCH_DAVINCI is not set # CONFIG_KIRKWOOD is not set # CONFIG_ARCH_MVEBU is not set # CONFIG_TARGET_DEVKIT3250 is not set # CONFIG_TARGET_WORK_92105 is not set # CONFIG_TARGET_MX25PDK is not set # CONFIG_TARGET_ZMX25 is not set # CONFIG_TARGET_APF27 is not set # CONFIG_TARGET_APX4DEVKIT is not set # CONFIG_TARGET_XFI3 is not set # CONFIG_TARGET_M28EVK is not set # CONFIG_TARGET_MX23EVK is not set # CONFIG_TARGET_MX28EVK is not set # CONFIG_TARGET_MX23_OLINUXINO is not set # CONFIG_TARGET_BG0900 is not set # CONFIG_TARGET_SANSA_FUZE_PLUS is not set # CONFIG_TARGET_SC_SPS_1 is not set # CONFIG_ORION5X is not set # CONFIG_TARGET_SPEAR300 is not set # CONFIG_TARGET_SPEAR310 is not set # CONFIG_TARGET_SPEAR320 is not set # CONFIG_TARGET_SPEAR600 is not set # CONFIG_TARGET_STV0991 is not set # CONFIG_TARGET_X600 is not set # CONFIG_TARGET_IMX31_PHYCORE is not set # CONFIG_TARGET_IMX31_PHYCORE_EET is not set # CONFIG_TARGET_MX31ADS is not set # CONFIG_TARGET_MX31PDK is not set # CONFIG_TARGET_WOODBURN is not set # CONFIG_TARGET_WOODBURN_SD is not set # CONFIG_TARGET_FLEA3 is not set # CONFIG_TARGET_MX35PDK is not set # CONFIG_ARCH_BCM283X is not set # CONFIG_TARGET_VEXPRESS_CA15_TC2 is not set # CONFIG_TARGET_VEXPRESS_CA5X2 is not set # CONFIG_TARGET_VEXPRESS_CA9X4 is not set # CONFIG_TARGET_BCM23550_W1D is not set # CONFIG_TARGET_BCM28155_AP is not set # CONFIG_TARGET_BCMCYGNUS is not set # CONFIG_TARGET_BCMNSP is not set # CONFIG_TARGET_BCMNS2 is not set # CONFIG_ARCH_EXYNOS is not set # CONFIG_ARCH_S5PC1XX is not set # CONFIG_ARCH_HIGHBANK is not set # CONFIG_ARCH_INTEGRATOR is not set # CONFIG_ARCH_KEYSTONE is not set # CONFIG_ARCH_OMAP2PLUS is not set # CONFIG_ARCH_MESON is not set # CONFIG_ARCH_MX7ULP is not set # CONFIG_ARCH_MX7 is not set # CONFIG_ARCH_MX6 is not set # CONFIG_ARCH_MX5 is not set # CONFIG_ARCH_RMOBILE is not set # CONFIG_TARGET_S32V234EVB is not set # CONFIG_ARCH_SNAPDRAGON is not set # CONFIG_ARCH_SOCFPGA is not set CONFIG_ARCH_SUNXI=y # CONFIG_TARGET_TS4600 is not set # CONFIG_ARCH_VF610 is not set # CONFIG_ARCH_ZYNQ is not set # CONFIG_ARCH_ZYNQMP is not set # CONFIG_TEGRA is not set # CONFIG_TARGET_VEXPRESS64_AEMV8A is not set # CONFIG_TARGET_VEXPRESS64_BASE_FVP is not set # CONFIG_TARGET_VEXPRESS64_BASE_FVP_DRAM is not set # CONFIG_TARGET_VEXPRESS64_JUNO is not set # CONFIG_TARGET_LS2080A_EMU is not set # CONFIG_TARGET_LS2080A_SIMU is not set # CONFIG_TARGET_LS2080AQDS is not set # CONFIG_TARGET_LS2080ARDB is not set # CONFIG_TARGET_LS2081ARDB is not set # CONFIG_TARGET_HIKEY is not set # CONFIG_TARGET_POPLAR is not set # CONFIG_TARGET_LS1012AQDS is not set # CONFIG_TARGET_LS1012ARDB is not set # CONFIG_TARGET_LS1012AFRDM is not set # CONFIG_TARGET_LS1021AQDS is not set # CONFIG_TARGET_LS1021ATWR is not set # CONFIG_TARGET_LS1021AIOT is not set # CONFIG_TARGET_LS1043AQDS is not set # CONFIG_TARGET_LS1043ARDB is not set # CONFIG_TARGET_LS1046AQDS is not set # CONFIG_TARGET_LS1046ARDB is not set # CONFIG_TARGET_H2200 is not set # CONFIG_TARGET_ZIPITZ2 is not set # CONFIG_TARGET_COLIBRI_PXA270 is not set # CONFIG_ARCH_UNIPHIER is not set # CONFIG_STM32 is not set # CONFIG_ARCH_STI is not set # CONFIG_ARCH_ROCKCHIP is not set # CONFIG_TARGET_THUNDERX_88XX is not set # CONFIG_ARCH_ASPEED is not set CONFIG_SPL_GPIO_SUPPORT=y CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y CONFIG_SYS_MALLOC_F_LEN=0x400 CONFIG_CONS_INDEX=1 CONFIG_SPL_MMC_SUPPORT=y CONFIG_SPL_SERIAL_SUPPORT=y # CONFIG_SPL_DRIVERS_MISC_SUPPORT is not set CONFIG_SPL_LIBDISK_SUPPORT=y # CONFIG_SPL_NAND_SUPPORT is not set # CONFIG_SPL_SPI_FLASH_SUPPORT is not set # CONFIG_SPL_SPI_SUPPORT is not set # CONFIG_SPL_WATCHDOG_SUPPORT is not set CONFIG_IDENT_STRING=" Allwinner Technology" # CONFIG_SUNXI_HIGH_SRAM is not set CONFIG_SUNXI_GEN_SUN4I=y # CONFIG_MACH_SUN4I is not set # CONFIG_MACH_SUN5I is not set # CONFIG_MACH_SUN6I is not set CONFIG_MACH_SUN7I=y # CONFIG_MACH_SUN8I_A23 is not set # CONFIG_MACH_SUN8I_A33 is not set # CONFIG_MACH_SUN8I_A83T is not set # CONFIG_MACH_SUN8I_H3 is not set # CONFIG_MACH_SUN8I_R40 is not set # CONFIG_MACH_SUN8I_V3S is not set # CONFIG_MACH_SUN9I is not set # CONFIG_MACH_SUN50I is not set # CONFIG_MACH_SUN50I_H5 is not set # CONFIG_RESERVE_ALLWINNER_BOOT0_HEADER is not set CONFIG_DRAM_CLK=432 CONFIG_DRAM_MBUS_CLK=300 CONFIG_DRAM_ZQ=127 # CONFIG_DRAM_ODT_EN is not set CONFIG_DRAM_EMR1=4 CONFIG_DRAM_TPR3=0 CONFIG_DRAM_DQS_GATING_DELAY=0 CONFIG_DRAM_TIMINGS_VENDOR_MAGIC=y # CONFIG_DRAM_TIMINGS_DDR3_1066F_1333H is not set # CONFIG_DRAM_TIMINGS_DDR3_800E_1066G_1333J is not set CONFIG_SYS_CLK_FREQ=912000000 # CONFIG_UART0_PORT_F is not set # CONFIG_OLD_SUNXI_KERNEL_COMPAT is not set CONFIG_MACPWR="PH23" CONFIG_MMC0_CD_PIN="" CONFIG_MMC1_CD_PIN="" CONFIG_MMC2_CD_PIN="" CONFIG_MMC3_CD_PIN="" CONFIG_MMC1_PINS="" CONFIG_MMC2_PINS="" CONFIG_MMC3_PINS="" CONFIG_MMC_SUNXI_SLOT_EXTRA=-1 CONFIG_INITIAL_USB_SCAN_DELAY=0 CONFIG_USB0_VBUS_PIN="" CONFIG_USB0_VBUS_DET="" CONFIG_USB0_ID_DET="" CONFIG_USB1_VBUS_PIN="PH6" CONFIG_USB2_VBUS_PIN="PH3" CONFIG_USB3_VBUS_PIN="" CONFIG_I2C0_ENABLE=y # CONFIG_I2C1_ENABLE is not set # CONFIG_I2C2_ENABLE is not set # CONFIG_I2C3_ENABLE is not set # CONFIG_I2C4_ENABLE is not set # CONFIG_AXP_GPIO is not set CONFIG_VIDEO=y CONFIG_VIDEO_HDMI=y # CONFIG_VIDEO_VGA is not set CONFIG_VIDEO_COMPOSITE=y CONFIG_VIDEO_LCD_MODE="" CONFIG_VIDEO_LCD_DCLK_PHASE=1 CONFIG_VIDEO_LCD_POWER="" CONFIG_VIDEO_LCD_RESET="" CONFIG_VIDEO_LCD_BL_EN="" CONFIG_VIDEO_LCD_BL_PWM="" CONFIG_VIDEO_LCD_BL_PWM_ACTIVE_LOW=y # CONFIG_VIDEO_LCD_PANEL_I2C is not set CONFIG_VIDEO_LCD_IF_PARALLEL=y # CONFIG_SUNXI_DE2 is not set CONFIG_VIDEO_LCD_PANEL_PARALLEL=y # CONFIG_VIDEO_LCD_PANEL_LVDS is not set # CONFIG_VIDEO_LCD_PANEL_MIPI_4_LANE_513_MBPS_VIA_SSD2828 is not set # CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804 is not set # CONFIG_VIDEO_LCD_PANEL_HITACHI_TX18D42VM is not set # CONFIG_VIDEO_LCD_TL059WV5C0 is not set CONFIG_SATAPWR="" CONFIG_GMAC_TX_DELAY=3 CONFIG_SPL_STACK_R_ADDR=0x4fe00000 # CONFIG_SPL_FAT_SUPPORT is not set CONFIG_CPU_V7_HAS_NONSEC=y CONFIG_CPU_V7_HAS_VIRT=y CONFIG_ARCH_SUPPORT_PSCI=y CONFIG_ARMV7_NONSEC=y # CONFIG_ARMV7_BOOT_SEC_DEFAULT is not set CONFIG_ARMV7_VIRT=y CONFIG_ARMV7_PSCI=y CONFIG_ARMV7_PSCI_NR_CPUS=4 # CONFIG_ARMV7_LPAE is not set # CONFIG_CMD_DEKBLOB is not set # CONFIG_CMD_HDMIDETECT is not set
# # ARM debug # # CONFIG_DEBUG_LL is not set CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-bananapi" CONFIG_SMBIOS_PRODUCT_NAME="sunxi" # CONFIG_DEBUG_UART is not set CONFIG_AHCI=y
# # General setup # CONFIG_LOCALVERSION="" CONFIG_LOCALVERSION_AUTO=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_DISTRO_DEFAULTS=y CONFIG_SYS_MALLOC_F=y CONFIG_SPL_SYS_MALLOC_F_LEN=0x400 CONFIG_EXPERT=y # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set # CONFIG_TOOLS_DEBUG is not set # CONFIG_PHYS_64BIT is not set
# # Boot images # # CONFIG_FIT is not set CONFIG_OF_BOARD_SETUP=y # CONFIG_OF_SYSTEM_SETUP is not set # CONFIG_OF_STDOUT_VIA_ALIAS is not set CONFIG_SYS_EXTRA_OPTIONS="" CONFIG_ARCH_FIXUP_FDT_MEMORY=y
# # API # # CONFIG_API is not set
# # Boot timing # # CONFIG_BOOTSTAGE is not set CONFIG_BOOTSTAGE_USER_COUNT=20 CONFIG_BOOTSTAGE_RECORD_COUNT=30 CONFIG_BOOTSTAGE_STASH_ADDR=0 CONFIG_BOOTSTAGE_STASH_SIZE=0x1000
# # Boot media # # CONFIG_NAND_BOOT is not set # CONFIG_ONENAND_BOOT is not set # CONFIG_QSPI_BOOT is not set # CONFIG_SATA_BOOT is not set # CONFIG_SD_BOOT is not set # CONFIG_SPI_BOOT is not set
# # Environment # # CONFIG_ENV_IS_IN_DATAFLASH is not set # CONFIG_ENV_IS_IN_EEPROM is not set # CONFIG_ENV_IS_IN_FAT is not set # CONFIG_ENV_IS_IN_FLASH is not set CONFIG_ENV_IS_IN_MMC=y # CONFIG_ENV_IS_IN_NAND is not set # CONFIG_ENV_IS_IN_NVRAM is not set # CONFIG_ENV_IS_IN_ONENAND is not set # CONFIG_ENV_IS_IN_REMOTE is not set # CONFIG_ENV_IS_IN_SPI_FLASH is not set # CONFIG_ENV_IS_IN_UBI is not set # CONFIG_ENV_IS_NOWHERE is not set CONFIG_ENV_OFFSET=0x88000 CONFIG_ENV_SIZE=0x20000 CONFIG_BOOTDELAY=2
# # Console # CONFIG_MENU=y # CONFIG_CONSOLE_RECORD is not set # CONFIG_SILENT_CONSOLE is not set CONFIG_PRE_CONSOLE_BUFFER=y CONFIG_PRE_CON_BUF_SZ=4096 CONFIG_PRE_CON_BUF_ADDR=0x4f000000 CONFIG_CONSOLE_MUX=y CONFIG_SYS_CONSOLE_IS_IN_ENV=y # CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE is not set # CONFIG_SYS_CONSOLE_ENV_OVERWRITE is not set # CONFIG_SYS_CONSOLE_INFO_QUIET is not set CONFIG_SYS_STDIO_DEREGISTER=y # CONFIG_FIT_EMBED is not set CONFIG_DEFAULT_FDT_FILE="" # CONFIG_VERSION_VARIABLE is not set CONFIG_DISPLAY_CPUINFO=y CONFIG_DISPLAY_BOARDINFO=y
# # Start-up hooks # # CONFIG_ARCH_EARLY_INIT_R is not set # CONFIG_ARCH_MISC_INIT is not set # CONFIG_BOARD_EARLY_INIT_F is not set
# # Security support # CONFIG_HASH=y
# # SPL / TPL # CONFIG_SUPPORT_SPL=y CONFIG_SPL=y # CONFIG_SPL_BOARD_INIT is not set CONFIG_SPL_RAW_IMAGE_SUPPORT=y CONFIG_SPL_LEGACY_IMAGE_SUPPORT=y CONFIG_SPL_SYS_MALLOC_SIMPLE=y CONFIG_SPL_STACK_R=y CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN=0x100000 # CONFIG_SPL_SEPARATE_BSS is not set # CONFIG_SPL_DISPLAY_PRINT is not set CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR=y CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR=0x50 # CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_PARTITION is not set # CONFIG_SPL_CPU_SUPPORT is not set # CONFIG_SPL_CRYPTO_SUPPORT is not set # CONFIG_SPL_HASH_SUPPORT is not set # CONFIG_SPL_DMA_SUPPORT is not set # CONFIG_SPL_ENV_SUPPORT is not set # CONFIG_SPL_EXT_SUPPORT is not set # CONFIG_SPL_FPGA_SUPPORT is not set CONFIG_SPL_I2C_SUPPORT=y # CONFIG_SPL_MPC8XXX_INIT_DDR_SUPPORT is not set # CONFIG_SPL_MTD_SUPPORT is not set # CONFIG_SPL_MUSB_NEW_SUPPORT is not set # CONFIG_SPL_NET_SUPPORT is not set # CONFIG_SPL_NO_CPU_SUPPORT is not set # CONFIG_SPL_NOR_SUPPORT is not set # CONFIG_SPL_XIP_SUPPORT is not set # CONFIG_SPL_ONENAND_SUPPORT is not set # CONFIG_SPL_OS_BOOT is not set # CONFIG_SPL_PCI_SUPPORT is not set # CONFIG_SPL_PCH_SUPPORT is not set # CONFIG_SPL_POST_MEM_SUPPORT is not set CONFIG_SPL_POWER_SUPPORT=y # CONFIG_SPL_RAM_SUPPORT is not set # CONFIG_SPL_RTC_SUPPORT is not set # CONFIG_SPL_SATA_SUPPORT is not set # CONFIG_SPL_TIMER_SUPPORT is not set # CONFIG_SPL_USB_HOST_SUPPORT is not set # CONFIG_SPL_USB_GADGET_SUPPORT is not set # CONFIG_SPL_YMODEM_SUPPORT is not set
# # Command line interface # CONFIG_CMDLINE=y CONFIG_HUSH_PARSER=y CONFIG_SYS_PROMPT="=> "
# # Autoboot options # CONFIG_AUTOBOOT=y # CONFIG_AUTOBOOT_KEYED is not set
# # FASTBOOT # # CONFIG_FASTBOOT is not set
# # Commands #
# # Info commands # CONFIG_CMD_BDI=y # CONFIG_CMD_CONFIG is not set CONFIG_CMD_CONSOLE=y # CONFIG_CMD_CPU is not set # CONFIG_CMD_LICENSE is not set
# # Boot commands # CONFIG_CMD_BOOTD=y CONFIG_CMD_BOOTM=y CONFIG_CMD_BOOTZ=y CONFIG_CMD_BOOTEFI=y CONFIG_CMD_BOOTEFI_HELLO_COMPILE=y # CONFIG_CMD_BOOTEFI_HELLO is not set # CONFIG_CMD_BOOTMENU is not set CONFIG_CMD_ELF=y CONFIG_CMD_FDT=y CONFIG_CMD_GO=y CONFIG_CMD_RUN=y CONFIG_CMD_IMI=y # CONFIG_CMD_IMLS is not set CONFIG_CMD_XIMG=y CONFIG_CMD_POWEROFF=y
# # Environment commands # # CONFIG_CMD_ASKENV is not set CONFIG_CMD_EXPORTENV=y CONFIG_CMD_IMPORTENV=y CONFIG_CMD_EDITENV=y # CONFIG_CMD_GREPENV is not set CONFIG_CMD_SAVEENV=y CONFIG_CMD_ENV_EXISTS=y # CONFIG_CMD_ENV_CALLBACK is not set # CONFIG_CMD_ENV_FLAGS is not set
# # Memory commands # CONFIG_CMD_MEMORY=y CONFIG_CMD_CRC32=y # CONFIG_CRC32_VERIFY is not set # CONFIG_CMD_EEPROM is not set # CONFIG_CMD_MD5SUM is not set # CONFIG_CMD_SHA1SUM is not set # CONFIG_LOOPW is not set # CONFIG_CMD_MEMTEST is not set # CONFIG_CMD_MX_CYCLIC is not set # CONFIG_CMD_MEMINFO is not set
# # Compression commands # # CONFIG_CMD_LZMADEC is not set # CONFIG_CMD_UNZIP is not set # CONFIG_CMD_ZIP is not set
# # Device access commands # # CONFIG_CMD_CLK is not set CONFIG_CMD_DM=y # CONFIG_CMD_DEMO is not set # CONFIG_CMD_IDE is not set # CONFIG_CMD_IO is not set # CONFIG_CMD_IOTRACE is not set CONFIG_CMD_LOADB=y CONFIG_CMD_LOADS=y # CONFIG_CMD_FLASH is not set # CONFIG_CMD_GPT is not set # CONFIG_CMD_ARMFLASH is not set CONFIG_CMD_MMC=y # CONFIG_CMD_NAND is not set CONFIG_CMD_PART=y # CONFIG_CMD_SF is not set # CONFIG_CMD_SPI is not set CONFIG_CMD_I2C=y CONFIG_CMD_USB=y # CONFIG_CMD_DFU is not set # CONFIG_CMD_USB_MASS_STORAGE is not set # CONFIG_CMD_FPGA is not set # CONFIG_CMD_FPGAD is not set # CONFIG_CMD_FUSE is not set CONFIG_CMD_GPIO=y # CONFIG_CMD_FDC is not set # CONFIG_CMD_SATA is not set
# # Shell scripting commands # CONFIG_CMD_ECHO=y CONFIG_CMD_ITEST=y CONFIG_CMD_SOURCE=y CONFIG_CMD_SETEXPR=y
# # Network commands # CONFIG_CMD_NET=y # CONFIG_CMD_TFTPPUT is not set # CONFIG_CMD_TFTPSRV is not set # CONFIG_CMD_RARP is not set CONFIG_CMD_DHCP=y CONFIG_CMD_PXE=y CONFIG_CMD_NFS=y CONFIG_CMD_MII=y CONFIG_CMD_PING=y # CONFIG_CMD_CDP is not set # CONFIG_CMD_SNTP is not set # CONFIG_CMD_DNS is not set # CONFIG_CMD_LINK_LOCAL is not set # CONFIG_CMD_ETHSW is not set
# # Misc commands # # CONFIG_CMD_BMP is not set # CONFIG_CMD_BSP is not set # CONFIG_CMD_BKOPS_ENABLE is not set # CONFIG_CMD_CACHE is not set # CONFIG_CMD_DISPLAY is not set # CONFIG_CMD_LED is not set # CONFIG_CMD_DATE is not set # CONFIG_CMD_TIME is not set # CONFIG_CMD_GETTIME is not set CONFIG_CMD_MISC=y # CONFIG_CMD_TIMER is not set # CONFIG_CMD_QFW is not set
# # Power commands #
# # Security commands # # CONFIG_CMD_AES is not set # CONFIG_CMD_BLOB is not set # CONFIG_CMD_HASH is not set
# # Firmware commands #
# # Filesystem commands # CONFIG_CMD_EXT2=y CONFIG_CMD_EXT4=y # CONFIG_CMD_EXT4_WRITE is not set CONFIG_CMD_FAT=y CONFIG_CMD_FS_GENERIC=y # CONFIG_CMD_FS_UUID is not set # CONFIG_CMD_JFFS2 is not set # CONFIG_CMD_MTDPARTS is not set
# # Debug commands # # CONFIG_CMD_BEDBUG is not set # CONFIG_CMD_DIAG is not set # CONFIG_CMD_KGDB is not set # CONFIG_CMD_UBI is not set
# # Partition Types # CONFIG_PARTITIONS=y # CONFIG_MAC_PARTITION is not set # CONFIG_SPL_MAC_PARTITION is not set CONFIG_DOS_PARTITION=y # CONFIG_SPL_DOS_PARTITION is not set CONFIG_ISO_PARTITION=y # CONFIG_SPL_ISO_PARTITION is not set # CONFIG_AMIGA_PARTITION is not set # CONFIG_SPL_AMIGA_PARTITION is not set CONFIG_EFI_PARTITION=y CONFIG_EFI_PARTITION_ENTRIES_OFF=0 # CONFIG_SPL_EFI_PARTITION is not set CONFIG_PARTITION_UUIDS=y # CONFIG_SPL_PARTITION_UUIDS is not set # CONFIG_PARTITION_TYPE_GUID is not set CONFIG_SUPPORT_OF_CONTROL=y
# # Device Tree Control # CONFIG_OF_CONTROL=y # CONFIG_OF_BOARD_FIXUP is not set # CONFIG_SPL_OF_CONTROL is not set # CONFIG_OF_LIVE is not set CONFIG_OF_SEPARATE=y # CONFIG_OF_EMBED is not set # CONFIG_OF_BOARD is not set CONFIG_NET=y # CONFIG_NET_RANDOM_ETHADDR is not set CONFIG_NETCONSOLE=y CONFIG_NET_TFTP_VARS=y CONFIG_BOOTP_PXE_CLIENTARCH=0x15 CONFIG_BOOTP_VCI_STRING="U-Boot.armv7"
# # Device Drivers #
# # Generic Driver Options # CONFIG_DM=y # CONFIG_SPL_DM is not set CONFIG_DM_WARN=y CONFIG_DM_DEVICE_REMOVE=y CONFIG_DM_STDIO=y CONFIG_DM_SEQ_ALIAS=y # CONFIG_SPL_DM_SEQ_ALIAS is not set # CONFIG_REGMAP is not set # CONFIG_SPL_REGMAP is not set # CONFIG_DEVRES is not set CONFIG_SIMPLE_BUS=y CONFIG_OF_TRANSLATE=y CONFIG_DM_DEV_READ_INLINE=y # CONFIG_ADC is not set # CONFIG_ADC_EXYNOS is not set # CONFIG_ADC_SANDBOX is not set # CONFIG_SATA is not set CONFIG_SCSI=y
# # SATA/SCSI device support # # CONFIG_BLK is not set # CONFIG_BLOCK_CACHE is not set # CONFIG_IDE is not set
# # Clock # # CONFIG_CLK is not set # CONFIG_CPU is not set
# # Hardware crypto devices # # CONFIG_FSL_CAAM is not set # CONFIG_SYS_FSL_SEC_BE is not set # CONFIG_SYS_FSL_SEC_LE is not set
# # Demo for driver model # # CONFIG_DM_DEMO is not set
# # DFU support #
# # DMA Support # # CONFIG_DMA is not set # CONFIG_TI_EDMA3 is not set
# # FPGA support # # CONFIG_FPGA_ALTERA is not set # CONFIG_FPGA_SOCFPGA is not set # CONFIG_FPGA_XILINX is not set
# # GPIO Support # CONFIG_DM_GPIO=y # CONFIG_ALTERA_PIO is not set # CONFIG_DWAPB_GPIO is not set # CONFIG_AT91_GPIO is not set # CONFIG_ATMEL_PIO4 is not set # CONFIG_INTEL_BROADWELL_GPIO is not set # CONFIG_IMX_RGPIO2P is not set # CONFIG_LPC32XX_GPIO is not set # CONFIG_MSM_GPIO is not set # CONFIG_ROCKCHIP_GPIO is not set # CONFIG_TEGRA_GPIO is not set # CONFIG_TEGRA186_GPIO is not set # CONFIG_VYBRID_GPIO is not set # CONFIG_DM_74X164 is not set # CONFIG_DM_PCA953X is not set # CONFIG_MPC85XX_GPIO is not set
# # I2C support # # CONFIG_DM_I2C is not set # CONFIG_DM_I2C_COMPAT is not set # CONFIG_SYS_I2C_DW is not set # CONFIG_SYS_I2C_IMX_LPI2C is not set CONFIG_DM_KEYBOARD=y # CONFIG_CROS_EC_KEYB is not set # CONFIG_I8042_KEYB is not set
# # LED Support # # CONFIG_LED is not set # CONFIG_LED_STATUS is not set
# # Mailbox Controller Support # # CONFIG_DM_MAILBOX is not set
# # Memory Controller drivers #
# # Multifunction device drivers # # CONFIG_MISC is not set # CONFIG_CROS_EC is not set # CONFIG_DS4510 is not set # CONFIG_FSL_SEC_MON is not set # CONFIG_MXC_OCOTP is not set # CONFIG_NUVOTON_NCT6102D is not set # CONFIG_PWRSEQ is not set # CONFIG_PCA9551_LED is not set # CONFIG_WINBOND_W83627 is not set
# # MMC Host controller Support # CONFIG_MMC=y # CONFIG_DM_MMC is not set # CONFIG_SPL_MMC_TINY is not set # CONFIG_MMC_DW is not set # CONFIG_MMC_MXC is not set # CONFIG_MMC_MXS is not set # CONFIG_MMC_PCI is not set # CONFIG_MMC_OMAP_HS is not set # CONFIG_MMC_SDHCI is not set CONFIG_MMC_SUNXI=y
# # MTD Support # # CONFIG_MTD is not set # CONFIG_MTD_NOR_FLASH is not set
# # NAND Device Support # # CONFIG_NAND_DENALI is not set # CONFIG_NAND_VF610_NFC is not set # CONFIG_NAND_PXA3XX is not set # CONFIG_NAND_SUNXI is not set # CONFIG_NAND_ARASAN is not set # CONFIG_NAND_ZYNQ is not set
# # Generic NAND options # # CONFIG_SYS_NAND_U_BOOT_LOCATIONS is not set # CONFIG_SPL_NAND_DENALI is not set
# # SPI Flash Support # # CONFIG_SPI_FLASH is not set # CONFIG_SPL_SPI_SUNXI is not set
# # UBI support # # CONFIG_MTD_UBI is not set # CONFIG_BITBANGMII is not set # CONFIG_MV88E6352_SWITCH is not set CONFIG_PHYLIB=y # CONFIG_MV88E61XX_SWITCH is not set # CONFIG_PHYLIB_10G is not set # CONFIG_PHY_AQUANTIA is not set # CONFIG_PHY_ATHEROS is not set # CONFIG_PHY_BROADCOM is not set # CONFIG_PHY_CORTINA is not set # CONFIG_PHY_DAVICOM is not set # CONFIG_PHY_ET1011C is not set # CONFIG_PHY_LXT is not set # CONFIG_PHY_MARVELL is not set # CONFIG_PHY_MICREL is not set # CONFIG_PHY_MSCC is not set # CONFIG_PHY_NATSEMI is not set # CONFIG_PHY_REALTEK is not set # CONFIG_PHY_SMSC is not set # CONFIG_PHY_TERANETICS is not set # CONFIG_PHY_TI is not set # CONFIG_PHY_VITESSE is not set # CONFIG_PHY_XILINX is not set # CONFIG_PHY_FIXED is not set CONFIG_DM_ETH=y CONFIG_NETDEVICES=y # CONFIG_PHY_GIGE is not set # CONFIG_ALTERA_TSE is not set # CONFIG_DWC_ETH_QOS is not set # CONFIG_E1000 is not set CONFIG_ETH_DESIGNWARE=y # CONFIG_ETHOC is not set # CONFIG_FTMAC100 is not set # CONFIG_MACB is not set CONFIG_RGMII=y # CONFIG_RTL8139 is not set # CONFIG_RTL8169 is not set CONFIG_SUN7I_GMAC=y # CONFIG_SUN4I_EMAC is not set # CONFIG_SUN8I_EMAC is not set # CONFIG_GMAC_ROCKCHIP is not set # CONFIG_PCI is not set
# # PHY Subsystem # # CONFIG_PHY is not set # CONFIG_SPL_PHY is not set # CONFIG_MVEBU_COMPHY_SUPPORT is not set
# # Pin controllers # # CONFIG_PINCTRL is not set
# # Power #
# # Power Domain Support # # CONFIG_POWER_DOMAIN is not set # CONFIG_DM_PMIC is not set # CONFIG_PMIC_AS3722 is not set # CONFIG_POWER_MC34VR500 is not set # CONFIG_DM_REGULATOR is not set # CONFIG_SUNXI_NO_PMIC is not set CONFIG_AXP209_POWER=y CONFIG_AXP_DCDC2_VOLT=1400 CONFIG_AXP_DCDC3_VOLT=1250 CONFIG_AXP_ALDO2_VOLT=3000 CONFIG_AXP_ALDO3_VOLT=0 CONFIG_AXP_ALDO4_VOLT=0 # CONFIG_DM_PWM is not set # CONFIG_PWM_SANDBOX is not set # CONFIG_RAM is not set
# # Remote Processor drivers #
# # Reset Controller Support # # CONFIG_DM_RESET is not set
# # Real Time Clock # # CONFIG_DM_RTC is not set
# # Serial drivers # CONFIG_BAUDRATE=115200 CONFIG_REQUIRE_SERIAL_CONSOLE=y CONFIG_SERIAL_PRESENT=y CONFIG_SPL_SERIAL_PRESENT=y CONFIG_DM_SERIAL=y # CONFIG_SERIAL_IRQ_BUFFER is not set CONFIG_SPL_DM_SERIAL=y # CONFIG_TPL_DM_SERIAL is not set # CONFIG_DEBUG_UART_SKIP_INIT is not set # CONFIG_ALTERA_JTAG_UART is not set # CONFIG_ALTERA_UART is not set # CONFIG_ATMEL_USART is not set # CONFIG_FSL_LPUART is not set # CONFIG_MVEBU_A3700_UART is not set CONFIG_SYS_NS16550=y # CONFIG_MSM_SERIAL is not set # CONFIG_PXA_SERIAL is not set
# # Sound support # # CONFIG_SOUND is not set
# # SPI Support # # CONFIG_DM_SPI is not set # CONFIG_SOFT_SPI is not set # CONFIG_FSL_ESPI is not set # CONFIG_FSL_QSPI is not set # CONFIG_TI_QSPI is not set
# # SPMI support # # CONFIG_SPMI is not set
# # System reset device drivers # # CONFIG_SYSRESET is not set # CONFIG_SYSRESET_SYSCON is not set # CONFIG_SYSRESET_WATCHDOG is not set # CONFIG_DM_THERMAL is not set
# # Timer Support # # CONFIG_TIMER is not set
# # TPM support # CONFIG_USB=y CONFIG_DM_USB=y
# # USB Host Controller Drivers # CONFIG_USB_HOST=y # CONFIG_USB_XHCI_HCD is not set CONFIG_USB_EHCI_HCD=y # CONFIG_USB_EHCI_MSM is not set # CONFIG_USB_EHCI_GENERIC is not set # CONFIG_USB_OHCI_HCD is not set # CONFIG_USB_UHCI_HCD is not set # CONFIG_USB_DWC2 is not set
# # MUSB Controller Driver # # CONFIG_USB_MUSB_HOST is not set # CONFIG_USB_MUSB_GADGET is not set # CONFIG_USB_MUSB_TI is not set
# # ULPI drivers #
# # USB peripherals # CONFIG_USB_STORAGE=y CONFIG_USB_KEYBOARD=y # CONFIG_USB_GADGET is not set
# # Graphics support # # CONFIG_DM_VIDEO is not set # CONFIG_SYS_WHITE_ON_BLACK is not set
# # TrueType Fonts # # CONFIG_VIDEO_VESA is not set # CONFIG_VIDEO_LCD_ANX9804 is not set # CONFIG_VIDEO_LCD_SSD2828 is not set # CONFIG_VIDEO_LCD_HITACHI_TX18D42VM is not set # CONFIG_VIDEO_MVEBU is not set # CONFIG_DISPLAY is not set # CONFIG_VIDEO_FSL_DCU_FB is not set # CONFIG_VIDEO_TEGRA20 is not set # CONFIG_VIDEO_BRIDGE is not set CONFIG_CFB_CONSOLE=y # CONFIG_CFB_CONSOLE_ANSI is not set CONFIG_VGA_AS_SINGLE_DEVICE=y CONFIG_VIDEO_SW_CURSOR=y # CONFIG_CONSOLE_EXTRA_INFO is not set CONFIG_CONSOLE_SCROLL_LINES=1 # CONFIG_VIDEO_CT69000 is not set CONFIG_SYS_CONSOLE_BG_COL=0x00 CONFIG_SYS_CONSOLE_FG_COL=0xa0 # CONFIG_LCD is not set
# # Watchdog Timer Support # # CONFIG_BCM2835_WDT is not set # CONFIG_ULP_WATCHDOG is not set # CONFIG_WDT is not set # CONFIG_PHYS_TO_BUS is not set
# # File systems # # CONFIG_FS_CBFS is not set CONFIG_FS_FAT=y CONFIG_FAT_WRITE=y CONFIG_FS_FAT_MAX_CLUSTSIZE=65536 # CONFIG_FS_JFFS2 is not set # CONFIG_FS_CRAMFS is not set
# # Library routines # # CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED is not set CONFIG_HAVE_PRIVATE_LIBGCC=y CONFIG_USE_PRIVATE_LIBGCC=y CONFIG_SYS_HZ=1000 CONFIG_USE_TINY_PRINTF=y CONFIG_REGEX=y # CONFIG_LIB_RAND is not set # CONFIG_SPL_TINY_MEMSET is not set # CONFIG_CMD_DHRYSTONE is not set
# # Security support # # CONFIG_AES is not set # CONFIG_RSA is not set # CONFIG_TPM is not set
# # Hashing Support # # CONFIG_SHA1 is not set # CONFIG_SHA256 is not set # CONFIG_SHA_HW_ACCEL is not set
# # Compression Support # # CONFIG_LZ4 is not set # CONFIG_LZMA is not set # CONFIG_LZO is not set # CONFIG_ERRNO_STR is not set CONFIG_OF_LIBFDT=y # CONFIG_OF_LIBFDT_OVERLAY is not set # CONFIG_SPL_OF_LIBFDT is not set # CONFIG_FDT_FIXUP_PARTITIONS is not set
# # System tables # CONFIG_GENERATE_SMBIOS_TABLE=y CONFIG_SMBIOS_MANUFACTURER="" CONFIG_EFI_LOADER=y # CONFIG_UNIT_TEST is not set

Date: Sun, 6 Aug 2017 20:21:45 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Mind sending me or pastebin'ing your u-boot .config? There are some different device-path construction depending on legacy vs CONFIG_DM+CONFIG_BLK (the legacy case *looks* right to me, and is used by vexpress_ca15_tc2.. so I think it should work..)
See below. The Banana Pi (and all other sunxi boards) indeed uses the legacy code path. And I think there is a bug in the legacy codepath where it encodes the partition in the "file" path component.
If I fix the code to not insert the partition number there, I can boot from SD card and SATA again.
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index b5acf73f98..8ba0db2d7a 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -305,8 +305,8 @@ static void *dp_part_fill(void *buf, struct blk_desc *desc, int part) struct efi_device_path_file_path *fp; char devname[32] = { 0 }; /* fp->str is u16[32] long */
- snprintf(devname, sizeof(devname), "%d.%d.%d", desc->if_type, - desc->devnum, part); + snprintf(devname, sizeof(devname), "%d.%d", desc->if_type, + desc->devnum);
memcpy(buf, &ROOT, sizeof(ROOT)); buf += sizeof(ROOT);

On Sun, Aug 6, 2017 at 2:37 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
Date: Sun, 6 Aug 2017 20:21:45 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Mind sending me or pastebin'ing your u-boot .config? There are some different device-path construction depending on legacy vs CONFIG_DM+CONFIG_BLK (the legacy case *looks* right to me, and is used by vexpress_ca15_tc2.. so I think it should work..)
See below. The Banana Pi (and all other sunxi boards) indeed uses the legacy code path. And I think there is a bug in the legacy codepath where it encodes the partition in the "file" path component.
If I fix the code to not insert the partition number there, I can boot from SD card and SATA again.
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index b5acf73f98..8ba0db2d7a 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -305,8 +305,8 @@ static void *dp_part_fill(void *buf, struct blk_desc *desc, int part) struct efi_device_path_file_path *fp; char devname[32] = { 0 }; /* fp->str is u16[32] long */
snprintf(devname, sizeof(devname), "%d.%d.%d", desc->if_type,
desc->devnum, part);
snprintf(devname, sizeof(devname), "%d.%d", desc->if_type,
desc->devnum); memcpy(buf, &ROOT, sizeof(ROOT)); buf += sizeof(ROOT);
Hmm, that is probably not a good idea, since now the disk object along w/ partition objects will have same devicepath. (One change from before is now we have diskobjs for the disk (part=0) and child diskobjs for each partition.. fwiw in UEFI terminology a efi_device_path_hard_drive_path is actually a partition.. for maximum confusion)
Probably that we have a file-path node w/ child hard-drive objects is confusing your previous workaround.. let me see if I can come up with something better.
BR, -R

On Sun, Aug 6, 2017 at 2:47 PM, Rob Clark robdclark@gmail.com wrote:
On Sun, Aug 6, 2017 at 2:37 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
Date: Sun, 6 Aug 2017 20:21:45 +0200 (CEST) From: Mark Kettenis mark.kettenis@xs4all.nl
Mind sending me or pastebin'ing your u-boot .config? There are some different device-path construction depending on legacy vs CONFIG_DM+CONFIG_BLK (the legacy case *looks* right to me, and is used by vexpress_ca15_tc2.. so I think it should work..)
See below. The Banana Pi (and all other sunxi boards) indeed uses the legacy code path. And I think there is a bug in the legacy codepath where it encodes the partition in the "file" path component.
If I fix the code to not insert the partition number there, I can boot from SD card and SATA again.
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index b5acf73f98..8ba0db2d7a 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -305,8 +305,8 @@ static void *dp_part_fill(void *buf, struct blk_desc *desc, int part) struct efi_device_path_file_path *fp; char devname[32] = { 0 }; /* fp->str is u16[32] long */
snprintf(devname, sizeof(devname), "%d.%d.%d", desc->if_type,
desc->devnum, part);
snprintf(devname, sizeof(devname), "%d.%d", desc->if_type,
desc->devnum); memcpy(buf, &ROOT, sizeof(ROOT)); buf += sizeof(ROOT);
Hmm, that is probably not a good idea, since now the disk object along w/ partition objects will have same devicepath. (One change from before is now we have diskobjs for the disk (part=0) and child diskobjs for each partition.. fwiw in UEFI terminology a efi_device_path_hard_drive_path is actually a partition.. for maximum confusion)
Oh, scratch that.. you are right, part shouldn't be in the string.. since it results in the children having different parent device paths.
(It is still probably a bit funny to file-paths for parent of the partition objects.. I'll try to come up with something a bit better.)
BR, -R

On Sun, Aug 6, 2017 at 2:21 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Sun, 6 Aug 2017 13:49:43 -0400
On Sun, Aug 6, 2017 at 1:28 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Sun, 6 Aug 2017 11:34:15 -0400
On Sun, Aug 6, 2017 at 10:45 AM, Rob Clark robdclark@gmail.com wrote:
I've started trying to hack up test_efi_loader.py to add a test that loads OpenBSD's bootloader.. kinda muddling through it at this point, since not a py expert or too familiar w/ u-boot's test framework. But I'll see if I can get to the point where I can run the same thing on various arm7 and aarch64 devices in qemu.
Making a bit of progress on this (running it on a vexpress_ca15_tc2 board in qemu).. any hint where I can find BOOTARM.EFI src code?
https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/arch/armv7/stand/efiboot/e...
Your failure below looks a bit different from what I'm getting on the Banana Pi now. There I get stuck because the 2nd BS->HandleProtocol() call in efi_main() fails. Somehow the device path of the registered disk devices isn't matched correctly to the boot device path...
that is.. odd.. mind adding in lib/efi_loader/Makefile:
ccflags-y += -DDEBUG=1
?
(you can make the console output easier to read again w/ #undef DEBUG at top of efi_console.c)
With my complete patchset (ie. assuming this isn't in the middle of a bisect between 13/20 and 15/20) the device paths for the diskobj and EFI_LOADED_IMAGE::DeviceHandle should be constructed identically. (Ie. the patchset consolidates the two different places it was constructed before... and also fixes the thing I notice you work around in efi_diskprobe())
BTW, the OpenBSD code runs fine without the alignment hack. Our code is pretty minimal and doesn't actualy look into the device path components. It just matches the components based on type and by soing memcmp.
Hmm, well I do suspect there are still cases where u-boot could crash because of unaligned access without the hack. Although I'm less convinced that we should need the hack on armv7 and more thinking this is something specific about banana-pi (or allwinner?). The vexpress_ca15_tc2 "board" in qemu seems to be working properly..
I suspect qemu simply doesn't emulate the alignment trap. The u-boot startup code explicitly enables alignment fauls on armv7. See arch/arm/cpu/armv7/start.S:152. This helps catching bugs!
Hmm, that is a really bad idea with EFI_LOADER.. since the efi payload is inheriting this setting.
BR, -R

On Sun, Aug 06, 2017 at 11:34:15AM -0400, Rob Clark wrote:
On Sun, Aug 6, 2017 at 10:45 AM, Rob Clark robdclark@gmail.com wrote:
I've started trying to hack up test_efi_loader.py to add a test that loads OpenBSD's bootloader.. kinda muddling through it at this point, since not a py expert or too familiar w/ u-boot's test framework. But I'll see if I can get to the point where I can run the same thing on various arm7 and aarch64 devices in qemu.
Making a bit of progress on this (running it on a vexpress_ca15_tc2 board in qemu).. any hint where I can find BOOTARM.EFI src code?
=> tftpboot 80400000 obsdboot.efi smc911x: MAC 52:54:00:12:34:56 smc911x: detected LAN9118 controller smc911x: phy initialized smc911x: MAC 52:54:00:12:34:56 Using smc911x-0 device TFTP from server 10.0.2.2; our IP address is 10.0.2.15 Filename 'obsdboot.efi'. Load address: 0x80400000 Loading: *%08##### 12.4 MiB/s done Bytes transferred = 64908 (fd8c hex) smc911x: MAC 52:54:00:12:34:56 => crc32 80400000 $filesize CRC32 for 80400000 ... 8040fd8b ==> a9ac4fcf => bootefi 80400000 ## Starting EFI application at 80400000 ... WARNING: Invalid device tree, expect boot to fail BS->LocateHandle() returns 0 undefined instruction pc : [<9eec65c4>] lr : [<9eeca390>] sp : 9fed7a18 ip : 0000003f fp : 9fed7a2c r10: 00000000 r9 : 9eed4658 r8 : 00000000 r7 : 9eed1ce4 r6 : 9eed3538 r5 : 9fed7a6c r4 : 9eed4658 r3 : 00000000 r2 : 9eed2f8e r1 : 9eed1ee0 r0 : 00000000 Flags: nZCv IRQs off FIQs off Mode SVC_32 Resetting CPU ...
resetting ...
U-Boot 2017.09-rc1-00025-g534695d189 (Aug 06 2017 - 06:58:16 -0400)
DRAM: 1 GiB WARNING: Caches not enabled Flash: 128 MiB MMC: MMC: 0 *** Warning - bad CRC, using default environment
In: serial Out: serial Err: serial Net: smc911x-0 Hit any key to stop autoboot: 2
Why does U-Boot not set fdt_addr_r or fdtfile for vexpress? Worse yet trying to load to the default kernel_addr_r fails. So it requires a script or manual commands to boot instead of the usual distro boot arrangement?
There is some kind of hard hang on OpenBSD with vexpress at the moment and there is no driver for the sd/mmc but getting to that point seemed quite a bit more painful than using U-Boot on real hardware.
After adding vexpress-v2p-ca15-tc1.dtb to the FAT16 on miniroot-panda-61.fs:
$ qemu-system-arm -M vexpress-a15 -kernel vexpress_ca15_tc2/u-boot -nographic -sd miniroot-panda-61.fs
U-Boot 2017.09-rc1 (Aug 02 2017 - 10:55:19 +1000)
DRAM: 128 MiB WARNING: Caches not enabled Flash: 128 MiB MMC: MMC: 0 *** Warning - bad CRC, using default environment
In: serial Out: serial Err: serial Net: smc911x-0 Hit any key to stop autoboot: 0 MMC Device 1 not found no mmc device at slot 1 switch to partitions #0, OK mmc0 is current device env - environment handling commands
Usage: env default [-f] -a - [forcibly] reset default environment env default [-f] var [...] - [forcibly] reset variable(s) to their default values env delete [-f] var [...] - [forcibly] delete variable(s) env export [-t | -b | -c] [-s size] addr [var ...] - export environment env import [-d] [-t [-r] | -b | -c] addr [size] - import environment env print [-a | name ...] - print environment env run var [...] - run commands in an environment variable env save - save environment env set [-f] name [arg ...]
Scanning mmc 0:1... Found EFI removable media binary efi/boot/bootarm.efi reading efi/boot/bootarm.efi 64908 bytes read in 52 ms (1.2 MiB/s) ## Starting EFI application at a0008000 ... WARNING: Invalid device tree, expect boot to fail efi_load_pe: Invalid DOS Signature ## Application terminated, r = 2147483646 EFI LOAD FAILED: continuing... smc911x: MAC 52:54:00:12:34:56 smc911x: detected LAN9118 controller smc911x: phy initialized smc911x: MAC 52:54:00:12:34:56 BOOTP broadcast 1 DHCP client bound to address 10.0.2.15 (3 ms) *** Warning: no boot file name; using '0A00020F.img' Using smc911x-0 device TFTP from server 10.0.2.2; our IP address is 10.0.2.15 Filename '0A00020F.img'. Load address: 0xa0008000 Loading: * TFTP error: 'Access violation' (2)
...
=> setenv fdt_addr_r 0x81000000 => setenv fdtfile vexpress-v2p-ca15-tc1.dtb reading vexpress-v2p-ca15-tc1.dtb 13384 bytes read in 22 ms (593.8 KiB/s) => load mmc 0:1 ${kernel_addr_r} efi/boot/bootarm.efi reading efi/boot/bootarm.efi 64908 bytes read in 51 ms (1.2 MiB/s) => bootefi ${kernel_addr_r} ${fdt_addr_r} ## Starting EFI application at a0008000 ... efi_load_pe: Invalid DOS Signature ## Application terminated, r = 2147483646 => printenv kernel_addr_r kernel_addr_r=0xa0008000
=> setenv kernel_addr_r 0x82000000 => load mmc 0:1 ${kernel_addr_r} efi/boot/bootarm.efi reading efi/boot/bootarm.efi 64908 bytes read in 49 ms (1.3 MiB/s) => bootefi ${kernel_addr_r} ${fdt_addr_r} ## Starting EFI application at 82000000 ... Scanning disks on mmc... MMC Device 1 not found MMC Device 2 not found MMC Device 3 not found Found 1 disks
OpenBSD/armv7 BOOTARM 0.8
boot> cannot open sd0a:/etc/random.seed: No such file or directory booting sd0a:/bsd: 2265112+7989364+447000 [80+314496+149702]=0xaad1c0
OpenBSD/armv7 booting ... arg0 0xc0dad1c0 arg1 0x8e0 arg2 0x86ed0000 Allocating page tables freestart = 0x80dae000, free_pages = 29266 (0x00007252) IRQ stack: p0x80ddc000 v0xc0ddc000 ABT stack: p0x80ddd000 v0xc0ddd000 UND stack: p0x80dde000 v0xc0dde000 SVC stack: p0x80ddf000 v0xc0ddf000 Creating L1 page table at 0x80db0000 Mapping kernel Constructing L2 page tables undefined page pmap board type: 2272 Copyright (c) 1982, 1986, 1989, 1991, 1993 The Regents of the University of California. All rights reserved. Copyright (c) 1995-2017 OpenBSD. All rights reserved. https://www.OpenBSD.org
OpenBSD 6.1-current (RAMDISK) #30: Sat Aug 5 22:01:16 MDT 2017 deraadt@armv7.openbsd.org:/usr/src/sys/arch/armv7/compile/RAMDISK real mem = 134217728 (128MB) avail mem = 117125120 (111MB) mainbus0 at root: V2P-CA15 cpu0 at mainbus0: ARM Cortex-A15 r2p1 (ARMv7) cpu0: DC enabled IC enabled WB disabled EABT branch prediction enabled cpu0: 32KB(64b/l,2way) I-cache, 32KB(64b/l,2way) wr-back D-cache cortex0 at mainbus0 ampintc0 at mainbus0 nirq 160, ncpu 1 agtimer0 at mainbus0: tick rate 62500 KHz simplebus0 at mainbus0: "smb" simplebus1 at simplebus0: "motherboard" simplebus2 at simplebus1: "iofpga" sysreg0 at simplebus2: ID 0x1190f500 PROCID0 0x14000237 pluart0 at simplebus2: console pluart1 at simplebus2 pluart2 at simplebus2 pluart3 at simplebus2 plrtc0 at simplebus2 simplebus3 at mainbus0: "hsb" boot device: lookup 'sd0a:/bsd' failed. root on rd0a swap on rd0b dump on rd0b e

On Mon, Aug 7, 2017 at 11:47 AM, Jonathan Gray jsg@jsg.id.au wrote:
On Sun, Aug 06, 2017 at 11:34:15AM -0400, Rob Clark wrote:
On Sun, Aug 6, 2017 at 10:45 AM, Rob Clark robdclark@gmail.com wrote:
I've started trying to hack up test_efi_loader.py to add a test that loads OpenBSD's bootloader.. kinda muddling through it at this point, since not a py expert or too familiar w/ u-boot's test framework. But I'll see if I can get to the point where I can run the same thing on various arm7 and aarch64 devices in qemu.
Making a bit of progress on this (running it on a vexpress_ca15_tc2 board in qemu).. any hint where I can find BOOTARM.EFI src code?
=> tftpboot 80400000 obsdboot.efi smc911x: MAC 52:54:00:12:34:56 smc911x: detected LAN9118 controller smc911x: phy initialized smc911x: MAC 52:54:00:12:34:56 Using smc911x-0 device TFTP from server 10.0.2.2; our IP address is 10.0.2.15 Filename 'obsdboot.efi'. Load address: 0x80400000 Loading: *%08##### 12.4 MiB/s done Bytes transferred = 64908 (fd8c hex) smc911x: MAC 52:54:00:12:34:56 => crc32 80400000 $filesize CRC32 for 80400000 ... 8040fd8b ==> a9ac4fcf => bootefi 80400000 ## Starting EFI application at 80400000 ... WARNING: Invalid device tree, expect boot to fail BS->LocateHandle() returns 0 undefined instruction pc : [<9eec65c4>] lr : [<9eeca390>] sp : 9fed7a18 ip : 0000003f fp : 9fed7a2c r10: 00000000 r9 : 9eed4658 r8 : 00000000 r7 : 9eed1ce4 r6 : 9eed3538 r5 : 9fed7a6c r4 : 9eed4658 r3 : 00000000 r2 : 9eed2f8e r1 : 9eed1ee0 r0 : 00000000 Flags: nZCv IRQs off FIQs off Mode SVC_32 Resetting CPU ...
resetting ...
U-Boot 2017.09-rc1-00025-g534695d189 (Aug 06 2017 - 06:58:16 -0400)
DRAM: 1 GiB WARNING: Caches not enabled Flash: 128 MiB MMC: MMC: 0 *** Warning - bad CRC, using default environment
In: serial Out: serial Err: serial Net: smc911x-0 Hit any key to stop autoboot: 2
Why does U-Boot not set fdt_addr_r or fdtfile for vexpress? Worse yet trying to load to the default kernel_addr_r fails. So it requires a script or manual commands to boot instead of the usual distro boot arrangement?
I suspect this is specific to the test framework (probably not enabling distro-boot-cmd so that the test framework can run the cmds it wants??)
BR, -R
There is some kind of hard hang on OpenBSD with vexpress at the moment and there is no driver for the sd/mmc but getting to that point seemed quite a bit more painful than using U-Boot on real hardware.
After adding vexpress-v2p-ca15-tc1.dtb to the FAT16 on miniroot-panda-61.fs:
$ qemu-system-arm -M vexpress-a15 -kernel vexpress_ca15_tc2/u-boot -nographic -sd miniroot-panda-61.fs
U-Boot 2017.09-rc1 (Aug 02 2017 - 10:55:19 +1000)
DRAM: 128 MiB WARNING: Caches not enabled Flash: 128 MiB MMC: MMC: 0 *** Warning - bad CRC, using default environment
In: serial Out: serial Err: serial Net: smc911x-0 Hit any key to stop autoboot: 0 MMC Device 1 not found no mmc device at slot 1 switch to partitions #0, OK mmc0 is current device env - environment handling commands
Usage: env default [-f] -a - [forcibly] reset default environment env default [-f] var [...] - [forcibly] reset variable(s) to their default values env delete [-f] var [...] - [forcibly] delete variable(s) env export [-t | -b | -c] [-s size] addr [var ...] - export environment env import [-d] [-t [-r] | -b | -c] addr [size] - import environment env print [-a | name ...] - print environment env run var [...] - run commands in an environment variable env save - save environment env set [-f] name [arg ...]
Scanning mmc 0:1... Found EFI removable media binary efi/boot/bootarm.efi reading efi/boot/bootarm.efi 64908 bytes read in 52 ms (1.2 MiB/s) ## Starting EFI application at a0008000 ... WARNING: Invalid device tree, expect boot to fail efi_load_pe: Invalid DOS Signature ## Application terminated, r = 2147483646 EFI LOAD FAILED: continuing... smc911x: MAC 52:54:00:12:34:56 smc911x: detected LAN9118 controller smc911x: phy initialized smc911x: MAC 52:54:00:12:34:56 BOOTP broadcast 1 DHCP client bound to address 10.0.2.15 (3 ms) *** Warning: no boot file name; using '0A00020F.img' Using smc911x-0 device TFTP from server 10.0.2.2; our IP address is 10.0.2.15 Filename '0A00020F.img'. Load address: 0xa0008000 Loading: * TFTP error: 'Access violation' (2)
...
=> setenv fdt_addr_r 0x81000000 => setenv fdtfile vexpress-v2p-ca15-tc1.dtb reading vexpress-v2p-ca15-tc1.dtb 13384 bytes read in 22 ms (593.8 KiB/s) => load mmc 0:1 ${kernel_addr_r} efi/boot/bootarm.efi reading efi/boot/bootarm.efi 64908 bytes read in 51 ms (1.2 MiB/s) => bootefi ${kernel_addr_r} ${fdt_addr_r} ## Starting EFI application at a0008000 ... efi_load_pe: Invalid DOS Signature ## Application terminated, r = 2147483646 => printenv kernel_addr_r kernel_addr_r=0xa0008000
=> setenv kernel_addr_r 0x82000000 => load mmc 0:1 ${kernel_addr_r} efi/boot/bootarm.efi reading efi/boot/bootarm.efi 64908 bytes read in 49 ms (1.3 MiB/s) => bootefi ${kernel_addr_r} ${fdt_addr_r} ## Starting EFI application at 82000000 ... Scanning disks on mmc... MMC Device 1 not found MMC Device 2 not found MMC Device 3 not found Found 1 disks
OpenBSD/armv7 BOOTARM 0.8
boot> cannot open sd0a:/etc/random.seed: No such file or directory booting sd0a:/bsd: 2265112+7989364+447000 [80+314496+149702]=0xaad1c0
OpenBSD/armv7 booting ... arg0 0xc0dad1c0 arg1 0x8e0 arg2 0x86ed0000 Allocating page tables freestart = 0x80dae000, free_pages = 29266 (0x00007252) IRQ stack: p0x80ddc000 v0xc0ddc000 ABT stack: p0x80ddd000 v0xc0ddd000 UND stack: p0x80dde000 v0xc0dde000 SVC stack: p0x80ddf000 v0xc0ddf000 Creating L1 page table at 0x80db0000 Mapping kernel Constructing L2 page tables undefined page pmap board type: 2272 Copyright (c) 1982, 1986, 1989, 1991, 1993 The Regents of the University of California. All rights reserved. Copyright (c) 1995-2017 OpenBSD. All rights reserved. https://www.OpenBSD.org
OpenBSD 6.1-current (RAMDISK) #30: Sat Aug 5 22:01:16 MDT 2017 deraadt@armv7.openbsd.org:/usr/src/sys/arch/armv7/compile/RAMDISK real mem = 134217728 (128MB) avail mem = 117125120 (111MB) mainbus0 at root: V2P-CA15 cpu0 at mainbus0: ARM Cortex-A15 r2p1 (ARMv7) cpu0: DC enabled IC enabled WB disabled EABT branch prediction enabled cpu0: 32KB(64b/l,2way) I-cache, 32KB(64b/l,2way) wr-back D-cache cortex0 at mainbus0 ampintc0 at mainbus0 nirq 160, ncpu 1 agtimer0 at mainbus0: tick rate 62500 KHz simplebus0 at mainbus0: "smb" simplebus1 at simplebus0: "motherboard" simplebus2 at simplebus1: "iofpga" sysreg0 at simplebus2: ID 0x1190f500 PROCID0 0x14000237 pluart0 at simplebus2: console pluart1 at simplebus2 pluart2 at simplebus2 pluart3 at simplebus2 plrtc0 at simplebus2 simplebus3 at mainbus0: "hsb" boot device: lookup 'sd0a:/bsd' failed. root on rd0a swap on rd0b dump on rd0b e

On Mon, Aug 07, 2017 at 12:16:54PM -0400, Rob Clark wrote:
On Mon, Aug 7, 2017 at 11:47 AM, Jonathan Gray jsg@jsg.id.au wrote:
On Sun, Aug 06, 2017 at 11:34:15AM -0400, Rob Clark wrote:
On Sun, Aug 6, 2017 at 10:45 AM, Rob Clark robdclark@gmail.com wrote:
I've started trying to hack up test_efi_loader.py to add a test that loads OpenBSD's bootloader.. kinda muddling through it at this point, since not a py expert or too familiar w/ u-boot's test framework. But I'll see if I can get to the point where I can run the same thing on various arm7 and aarch64 devices in qemu.
Making a bit of progress on this (running it on a vexpress_ca15_tc2 board in qemu).. any hint where I can find BOOTARM.EFI src code?
=> tftpboot 80400000 obsdboot.efi smc911x: MAC 52:54:00:12:34:56 smc911x: detected LAN9118 controller smc911x: phy initialized smc911x: MAC 52:54:00:12:34:56 Using smc911x-0 device TFTP from server 10.0.2.2; our IP address is 10.0.2.15 Filename 'obsdboot.efi'. Load address: 0x80400000 Loading: *%08##### 12.4 MiB/s done Bytes transferred = 64908 (fd8c hex) smc911x: MAC 52:54:00:12:34:56 => crc32 80400000 $filesize CRC32 for 80400000 ... 8040fd8b ==> a9ac4fcf => bootefi 80400000 ## Starting EFI application at 80400000 ... WARNING: Invalid device tree, expect boot to fail BS->LocateHandle() returns 0 undefined instruction pc : [<9eec65c4>] lr : [<9eeca390>] sp : 9fed7a18 ip : 0000003f fp : 9fed7a2c r10: 00000000 r9 : 9eed4658 r8 : 00000000 r7 : 9eed1ce4 r6 : 9eed3538 r5 : 9fed7a6c r4 : 9eed4658 r3 : 00000000 r2 : 9eed2f8e r1 : 9eed1ee0 r0 : 00000000 Flags: nZCv IRQs off FIQs off Mode SVC_32 Resetting CPU ...
resetting ...
U-Boot 2017.09-rc1-00025-g534695d189 (Aug 06 2017 - 06:58:16 -0400)
DRAM: 1 GiB WARNING: Caches not enabled Flash: 128 MiB MMC: MMC: 0 *** Warning - bad CRC, using default environment
In: serial Out: serial Err: serial Net: smc911x-0 Hit any key to stop autoboot: 2
Why does U-Boot not set fdt_addr_r or fdtfile for vexpress? Worse yet trying to load to the default kernel_addr_r fails. So it requires a script or manual commands to boot instead of the usual distro boot arrangement?
I suspect this is specific to the test framework (probably not enabling distro-boot-cmd so that the test framework can run the cmds it wants??)
I didn't attempt to use the test framework, just loaded the result of building vexpress_ca15_tc2_defconfig into qemu.
It looks like the load addresses are only good for >= 1G physmem.
U-Boot 2017.09-rc1 (Aug 02 2017 - 10:55:19 +1000)
DRAM: 128 MiB WARNING: Caches not enabled Flash: 128 MiB MMC: MMC: 0 *** Warning - bad CRC, using default environment
In: serial Out: serial Err: serial Net: smc911x-0 Hit any key to stop autoboot: 0 => printenv arch=arm baudrate=38400 board=vexpress board_name=vexpress boot_a_script=load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr} boot_efi_binary=load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_r} efi/boot/bootarm.efi; if fdt addr ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r};else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi boot_extlinux=sysboot ${devtype} ${devnum}:${distro_bootpart} any ${scriptaddr} ${prefix}extlinux/extlinux.conf boot_prefixes=/ /boot/ boot_script_dhcp=boot.scr.uimg boot_scripts=boot.scr.uimg boot.scr boot_targets=mmc1 mmc0 pxe dhcp bootcmd=run distro_bootcmd; run bootflash; bootcmd_dhcp=if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fi;setenv efi_fdtfile ${fdtfile}; if test -z "${fdtfile}" -a -n "${soc}"; then setenv efi_fdtfile ${soc}-${board}${boardver}.dtb; fi; setenv efi_old_vci ${bootp_vci};setenv efi_old_arch ${bootp_arch};setenv bootp_vci PXEClient:Arch:00010:UNDI:003000;setenv bootp_arch 0xa;if dhcp ${kernel_addr_r}; then tftpboot ${fdt_addr_r} dtb/${efi_fdtfile};if fdt addr ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r}; else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi;fi;setenv bootp_vci ${efi_old_vci};setenv bootp_arch ${efi_old_arch};setenv efi_fdtfile;setenv efi_old_arch;setenv efi_old_vci; bootcmd_mmc0=setenv devnum 0; run mmc_boot bootcmd_mmc1=setenv devnum 1; run mmc_boot bootcmd_pxe=dhcp; if pxe get; then pxe boot; fi bootdelay=2 bootflash=run flashargs; cp ${ramdisk_addr} ${ramdisk_addr_r} ${maxramdisk}; bootm ${kernel_addr} ${ramdisk_addr_r} console=ttyAMA0,38400n8 cpu=armv7 distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done dram=1024M efi_dtb_prefixes=/ /dtb/ /dtb/current/ ethact=smc911x-0 ethaddr=52:54:00:12:34:56 flashargs=setenv bootargs root=${root} console=${console} mem=${dram} mtdparts=${mtd} mmci.fmax=190000 devtmpfs.mount=0 vmalloc=256M kernel_addr=0x0c100000 kernel_addr_r=0xa0008000 load_efi_dtb=load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} ${prefix}${efi_fdtfile} loadaddr=0xa0008000 maxramdisk=0x1800000 mmc_boot=if mmc dev ${devnum}; then setenv devtype mmc; run scan_dev_for_boot_part; fi mtd=armflash:1M@0x800000(uboot),7M@0x1000000(kernel),24M@0x2000000(initrd) pxefile_addr_r=0xa8000000 ramdisk_addr=0x0c800000 ramdisk_addr_r=0x81000000 root=/dev/sda1 rw scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${distro_bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_dev_for_scripts; done;run scan_dev_for_efi; scan_dev_for_boot_part=part list ${devtype} ${devnum} -bootable devplist; env exists devplist || setenv devplist 1; for distro_bootpart in ${devplist}; do if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype; then run scan_dev_for_boot; fi; done scan_dev_for_efi=setenv efi_fdtfile ${fdtfile}; if test -z "${fdtfile}" -a -n "${soc}"; then setenv efi_fdtfile ${soc}-${board}${boardver}.dtb; fi; for prefix in ${efi_dtb_prefixes}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${efi_fdtfile}; then run load_efi_dtb; fi;done;if test -e ${devtype} ${devnum}:${distro_bootpart} efi/boot/bootarm.efi; then echo Found EFI removable media binary efi/boot/bootarm.efi; run boot_efi_binary; echo EFI LOAD FAILED: continuing...; fi; setenv efi_fdtfile scan_dev_for_extlinux=if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}extlinux/extlinux.conf; then echo Found ${prefix}extlinux/extlinux.conf; run boot_extlinux; echo SCRIPT FAILED: continuing...; fi scan_dev_for_scripts=for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${script}; then echo Found U-Boot script ${prefix}${script}; run boot_a_script; echo SCRIPT FAILED: continuing...; fi; done scriptaddr=0xa8000000 stderr=serial stdin=serial stdout=serial vendor=armltd
Environment size: 3974/262140 bytes => bdinfo arch_number = 0x000008E0 boot_params = 0x80002000 DRAM bank = 0x00000000 -> start = 0x80000000 -> size = 0x08000000 DRAM bank = 0x00000001 -> start = 0xA0000000 -> size = 0x00000004 eth0name = smc911x-0 ethaddr = 52:54:00:12:34:56 current eth = smc911x-0 ip_addr = <NULL> baudrate = 38400 bps TLB addr = 0x87FF0000 relocaddr = 0x87F7B000 reloc off = 0x0777B000 irq_sp = 0x87EDAEF0 sp start = 0x87EDAEE0
With -m 512
U-Boot 2017.09-rc1 (Aug 02 2017 - 10:55:19 +1000)
DRAM: 512 MiB WARNING: Caches not enabled Flash: 128 MiB MMC: MMC: 0 *** Warning - bad CRC, using default environment
In: serial Out: serial Err: serial Net: smc911x-0 Hit any key to stop autoboot: 0 => bdinfo arch_number = 0x000008E0 boot_params = 0x80002000 DRAM bank = 0x00000000 -> start = 0x80000000 -> size = 0x20000000 DRAM bank = 0x00000001 -> start = 0xA0000000 -> size = 0x00000004 eth0name = smc911x-0 ethaddr = 52:54:00:12:34:56 current eth = smc911x-0 ip_addr = <NULL> baudrate = 38400 bps TLB addr = 0x9FFF0000 relocaddr = 0x9FF7B000 reloc off = 0x1F77B000 irq_sp = 0x9FEDAEF0 sp start = 0x9FEDAEE0
With -m 1024
U-Boot 2017.09-rc1 (Aug 02 2017 - 10:55:19 +1000)
DRAM: 1 GiB WARNING: Caches not enabled Flash: 128 MiB MMC: MMC: 0 *** Warning - bad CRC, using default environment
In: serial Out: serial Err: serial Net: smc911x-0 Hit any key to stop autoboot: 0 => bdinfo arch_number = 0x000008E0 boot_params = 0x80002000 DRAM bank = 0x00000000 -> start = 0x80000000 -> size = 0x20000000 DRAM bank = 0x00000001 -> start = 0xA0000000 -> size = 0x20000000 eth0name = smc911x-0 ethaddr = 52:54:00:12:34:56 current eth = smc911x-0 ip_addr = <NULL> baudrate = 38400 bps TLB addr = 0x9FFF0000 relocaddr = 0x9FF7B000 reloc off = 0x1F77B000 irq_sp = 0x9FEDAEF0 sp start = 0x9FEDAEE0
-m 1024 works with ${kernel_addr_r} instead of giving "efi_load_pe: Invalid DOS Signature"
=> setenv fdt_addr_r 0x81000000 => setenv fdtfile vexpress-v2p-ca15-tc1.dtb => load mmc 0:1 ${fdt_addr_r} ${fdtfile} reading vexpress-v2p-ca15-tc1.dtb 13384 bytes read in 22 ms (593.8 KiB/s) => load mmc 0:1 ${kernel_addr_r} efi/boot/bootarm.efi reading efi/boot/bootarm.efi 64908 bytes read in 49 ms (1.3 MiB/s) => bootefi ${kernel_addr_r} ${fdt_addr_r} ## Starting EFI application at a0008000 ... Scanning disks on mmc... MMC Device 1 not found MMC Device 2 not found MMC Device 3 not found Found 1 disks
OpenBSD/armv7 BOOTARM 0.8
boot> cannot open sd0a:/etc/random.seed: No such file or directory booting sd0a:/bsd: 2265112+7989364+447000 [80+314496+149702]=0xaad1c0
OpenBSD/armv7 booting ... arg0 0xc0dad1c0 arg1 0x8e0 arg2 0x88000000 Allocating page tables freestart = 0x80dae000, free_pages = 127570 (0x0001f252) IRQ stack: p0x80ddc000 v0xc0ddc000 ABT stack: p0x80ddd000 v0xc0ddd000 UND stack: p0x80dde000 v0xc0dde000 SVC stack: p0x80ddf000 v0xc0ddf000 Creating L1 page table at 0x80db0000 Mapping kernel Constructing L2 page tables undefined page pmap board type: 2272 Copyright (c) 1982, 1986, 1989, 1991, 1993 The Regents of the University of California. All rights reserved. Copyright (c) 1995-2017 OpenBSD. All rights reserved. https://www.OpenBSD.org
OpenBSD 6.1-current (RAMDISK) #30: Sat Aug 5 22:01:16 MDT 2017 deraadt@armv7.openbsd.org:/usr/src/sys/arch/armv7/compile/RAMDISK real mem = 1073741824 (1024MB) avail mem = 1038520320 (990MB) mainbus0 at root: V2P-CA15 cpu0 at mainbus0: ARM Cortex-A15 r2p1 (ARMv7) cpu0: DC enabled IC enabled WB disabled EABT branch prediction enabled cpu0: 32KB(64b/l,2way) I-cache, 32KB(64b/l,2way) wr-back D-cache cortex0 at mainbus0 ampintc0 at mainbus0 nirq 160, ncpu 1 agtimer0 at mainbus0: tick rate 62500 KHz simplebus0 at mainbus0: "smb" simplebus1 at simplebus0: "motherboard" simplebus2 at simplebus1: "iofpga" sysreg0 at simplebus2: ID 0x1190f500 PROCID0 0x14000237 pluart0 at simplebus2: console pluart1 at simplebus2 pluart2 at simplebus2 pluart3 at simplebus2 plrtc0 at simplebus2 simplebus3 at mainbus0: "hsb" boot device: lookup 'sd0a:/bsd' failed. root on rd0a swap on rd0b dump on rd0b e

On Sat, Aug 5, 2017 at 10:01 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
OpenBSD doesn't run on the db410c. However, our EFI bootloader should just run. You can download it from:
http://ftp.openbsd.org/pub/OpenBSD/snapshots/arm64/BOOTAA64.EFI
for the 64-bit (AArch64) and
http://ftp.openbsd.org/pub/OpenBSD/snapshots/armv7/BOOTARM.EFI
for the 32-bit version (AArch32).
Yup, this appears to work, I think this is about what you'd expect without having an openbsd fs:
dragonboard410c => load mmc 0:1 ${kernel_addr_r} efi/openbsd/bootaa64.efi reading efi/openbsd/bootaa64.efi 75732 bytes read in 35 ms (2.1 MiB/s) dragonboard410c => bootefi ${kernel_addr_r} ${fdt_addr_r} ## Starting EFI application at 81000000 ... Scanning disk sdhci@07864000.blk... Found 1 disks WARNING: Invalid device tree, expect boot to fail
OpenBSD/arm64 BOOTAA64 0.6
sd0: getdisklabel: no disk label open(sd0a:/etc/boot.conf): bad partition boot> sd0: getdisklabel: no disk label cannot open sd0a:/etc/random.seed: bad partition booting sd0a:/bsd: sd0: getdisklabel: no disk label open sd0a:/bsd: bad partition failed(95). will try /bsd boot> sd0: getdisklabel: no disk label cannot open sd0a:/etc/random.seed: bad partition booting sd0a:/bsd: sd0: getdisklabel: no disk label open sd0a:/bsd: bad partition failed(95). will try /bsd Turning timeout off. boot>

From: Rob Clark robdclark@gmail.com Date: Sat, 5 Aug 2017 10:28:34 -0400
On Sat, Aug 5, 2017 at 10:01 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
OpenBSD doesn't run on the db410c. However, our EFI bootloader should just run. You can download it from:
http://ftp.openbsd.org/pub/OpenBSD/snapshots/arm64/BOOTAA64.EFI
for the 64-bit (AArch64) and
http://ftp.openbsd.org/pub/OpenBSD/snapshots/armv7/BOOTARM.EFI
for the 32-bit version (AArch32).
Yup, this appears to work, I think this is about what you'd expect without having an openbsd fs:
dragonboard410c => load mmc 0:1 ${kernel_addr_r} efi/openbsd/bootaa64.efi reading efi/openbsd/bootaa64.efi 75732 bytes read in 35 ms (2.1 MiB/s) dragonboard410c => bootefi ${kernel_addr_r} ${fdt_addr_r} ## Starting EFI application at 81000000 ... Scanning disk sdhci@07864000.blk... Found 1 disks WARNING: Invalid device tree, expect boot to fail
OpenBSD/arm64 BOOTAA64 0.6
sd0: getdisklabel: no disk label open(sd0a:/etc/boot.conf): bad partition boot> sd0: getdisklabel: no disk label cannot open sd0a:/etc/random.seed: bad partition booting sd0a:/bsd: sd0: getdisklabel: no disk label open sd0a:/bsd: bad partition failed(95). will try /bsd boot> sd0: getdisklabel: no disk label cannot open sd0a:/etc/random.seed: bad partition booting sd0a:/bsd: sd0: getdisklabel: no disk label open sd0a:/bsd: bad partition failed(95). will try /bsd Turning timeout off. boot>
Right. At that point it is trying to load the OpenBSD kernel from a UFS/FFS filesystem, which fails because you don't have a BSD disklabel on your SD/MMC device. And even if it could, it would be game over pretty quickly as the OpenBSD kernel doesn't support the UART on your board (yet).

On Fri, Aug 04, 2017 at 10:41:32PM +0200, Mark Kettenis wrote: [...]
..and what you're sketching out here should work with recent enough versions of our bootloader.
However, to me having an ACPI Device Path component in there doesn't make much sense on a board without ACPI. It certainly doesn't help mapping a boot path back to an actual hardware device. Wouldn't it be more logical to a Hardware Device Path there instead? In particular a Memory Mapped Device Path would make a lot of sense as the start address would make it fairly easy to do the mapping.
It was really an arbitrary choice, as Rob said. I don't think there's any big problem with changing it, but I'm not sure Memory Mapped is correct. As I read it, StartingAddress and EndingAddress in that class should be pointing at some window into a memory map entry that is holding the thing described /by/ the node, but there's really nothing here. It's just an arbitrary root node.
If we want something other than the ACPI path I made up, we should probably just go with a Vendor Specific Device Path with a constant well-known GUID of our choosing. e61d73b9-a384-4acc-aeab-82e828f3628b, say.

Signed-off-by: Rob Clark robdclark@gmail.com --- lib/efi_loader/efi_net.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-)
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 0b949d86e8..aa0618fd3a 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -26,9 +26,6 @@ struct efi_net_obj { /* EFI Interface callback struct for network */ struct efi_simple_network net; struct efi_simple_network_mode net_mode; - /* Device path to the network adapter */ - struct efi_device_path_mac_addr dp_mac; - struct efi_device_path_file_path dp_end; /* PXE struct to transmit dhcp data */ struct efi_pxe pxe; struct efi_pxe_mode pxe_mode; @@ -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;

Get rid of the hacky fake boot-device and duplicate device-path constructing (which needs to match what efi_disk and efi_net do). Instead convert over to use efi_device_path helpers to construct device-paths, and use that to look up the actual boot device.
Also, extract out a helper to plug things in properly to the loaded_image. In a following patch we'll want to re-use this in efi_load_image() to handle the case of loading an image from a file_path.
Signed-off-by: Rob Clark robdclark@gmail.com --- cmd/bootefi.c | 201 +++++++++++++----------------------------- include/efi_loader.h | 5 +- lib/efi_loader/efi_boottime.c | 35 ++++++++ lib/efi_loader/efi_net.c | 5 +- 4 files changed, 99 insertions(+), 147 deletions(-)
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index d20775eccd..b9e1e5e131 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -22,97 +22,14 @@ DECLARE_GLOBAL_DATA_PTR;
static uint8_t efi_obj_list_initalized;
-/* - * When booting using the "bootefi" command, we don't know which - * physical device the file came from. So we create a pseudo-device - * called "bootefi" with the device path /bootefi. - * - * In addition to the originating device we also declare the file path - * of "bootefi" based loads to be /bootefi. - */ -static struct efi_device_path_file_path bootefi_image_path[] = { - { - .dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE, - .dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH, - .dp.length = sizeof(bootefi_image_path[0]), - .str = { 'b','o','o','t','e','f','i' }, - }, { - .dp.type = DEVICE_PATH_TYPE_END, - .dp.sub_type = DEVICE_PATH_SUB_TYPE_END, - .dp.length = sizeof(bootefi_image_path[0]), - } -}; - -static struct efi_device_path_file_path bootefi_device_path[] = { - { - .dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE, - .dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH, - .dp.length = sizeof(bootefi_image_path[0]), - .str = { 'b','o','o','t','e','f','i' }, - }, { - .dp.type = DEVICE_PATH_TYPE_END, - .dp.sub_type = DEVICE_PATH_SUB_TYPE_END, - .dp.length = sizeof(bootefi_image_path[0]), - } -}; - -/* The EFI loaded_image interface for the image executed via "bootefi" */ -static struct efi_loaded_image loaded_image_info = { - .device_handle = bootefi_device_path, - .file_path = bootefi_image_path, -}; - -/* The EFI object struct for the image executed via "bootefi" */ -static struct efi_object loaded_image_info_obj = { - .handle = &loaded_image_info, - .protocols = { - { - /* - * When asking for the loaded_image interface, just - * return handle which points to loaded_image_info - */ - .guid = &efi_guid_loaded_image, - .protocol_interface = &loaded_image_info, - }, - { - /* - * When asking for the device path interface, return - * bootefi_device_path - */ - .guid = &efi_guid_device_path, - .protocol_interface = bootefi_device_path, - }, - { - .guid = &efi_guid_console_control, - .protocol_interface = (void *) &efi_console_control - }, - { - .guid = &efi_guid_device_path_to_text_protocol, - .protocol_interface = (void *) &efi_device_path_to_text - }, - }, -}; - -/* The EFI object struct for the device the "bootefi" image was loaded from */ -static struct efi_object bootefi_device_obj = { - .handle = bootefi_device_path, - .protocols = { - { - /* When asking for the device path interface, return - * bootefi_device_path */ - .guid = &efi_guid_device_path, - .protocol_interface = bootefi_device_path - } - }, -}; +static struct efi_device_path *bootefi_image_path; +static struct efi_device_path *bootefi_device_path;
/* Initialize and populate EFI object list */ static void efi_init_obj_list(void) { efi_obj_list_initalized = 1;
- list_add_tail(&loaded_image_info_obj.link, &efi_obj_list); - list_add_tail(&bootefi_device_obj.link, &efi_obj_list); efi_console_register(); #ifdef CONFIG_PARTITIONS efi_disk_register(); @@ -121,13 +38,7 @@ static void efi_init_obj_list(void) efi_gop_register(); #endif #ifdef CONFIG_NET - void *nethandle = loaded_image_info.device_handle; - efi_net_register(&nethandle); - - if (!memcmp(bootefi_device_path[0].str, "N\0e\0t", 6)) - loaded_image_info.device_handle = nethandle; - else - loaded_image_info.device_handle = bootefi_device_path; + efi_net_register(); #endif #ifdef CONFIG_GENERATE_SMBIOS_TABLE efi_smbios_register(); @@ -210,14 +121,27 @@ static unsigned long efi_run_in_el2(asmlinkage ulong (*entry)( * Load an EFI payload into a newly allocated piece of memory, register all * EFI objects it would want to access and jump to it. */ -static unsigned long do_bootefi_exec(void *efi, void *fdt) +static unsigned long do_bootefi_exec(void *efi, void *fdt, + struct efi_device_path *device_path, + struct efi_device_path *image_path) { + struct efi_loaded_image loaded_image_info = {}; + struct efi_object loaded_image_info_obj = {}; + ulong ret; + ulong (*entry)(void *image_handle, struct efi_system_table *st) asmlinkage; ulong fdt_pages, fdt_size, fdt_start, fdt_end; const efi_guid_t fdt_guid = EFI_FDT_GUID; bootm_headers_t img = { 0 };
+ /* Initialize and populate EFI object list */ + if (!efi_obj_list_initalized) + efi_init_obj_list(); + + efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj, + device_path, image_path); + /* * gd lives in a fixed register which may get clobbered while we execute * the payload. So save it here and restore it on every callback entry @@ -252,18 +176,18 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt)
/* Load the EFI payload */ entry = efi_load_pe(efi, &loaded_image_info); - if (!entry) - return -ENOENT; - - /* Initialize and populate EFI object list */ - if (!efi_obj_list_initalized) - efi_init_obj_list(); + if (!entry) { + ret = -ENOENT; + goto exit; + }
/* Call our payload! */ debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry);
if (setjmp(&loaded_image_info.exit_jmp)) { - return loaded_image_info.exit_status; + ret = loaded_image_info.exit_status; + EFI_EXIT(ret); + goto exit; }
#ifdef CONFIG_ARM64 @@ -282,7 +206,13 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt) } #endif
- return efi_do_enter(&loaded_image_info, &systab, entry); + ret = efi_do_enter(&loaded_image_info, &systab, entry); + +exit: + /* image has returned, loaded-image obj goes *poof*: */ + list_del(&loaded_image_info_obj.link); + + return ret; }
@@ -315,7 +245,8 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) }
printf("## Starting EFI application at %08lx ...\n", addr); - r = do_bootefi_exec((void *)addr, (void*)fdt_addr); + r = do_bootefi_exec((void *)addr, (void*)fdt_addr, + bootefi_device_path, bootefi_image_path); printf("## Application terminated, r = %lu\n", r & ~EFI_ERROR_MASK);
@@ -344,58 +275,44 @@ U_BOOT_CMD( bootefi_help_text );
-void efi_set_bootdev(const char *dev, const char *devnr, const char *path) +static int parse_partnum(const char *devnr) { - __maybe_unused struct blk_desc *desc; - char devname[32] = { 0 }; /* dp->str is u16[32] long */ - char *colon, *s; - -#if defined(CONFIG_BLK) || CONFIG_IS_ENABLED(ISO_PARTITION) - desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10)); -#endif - -#ifdef CONFIG_BLK - if (desc) { - snprintf(devname, sizeof(devname), "%s", desc->bdev->name); - } else -#endif - - { - /* Assemble the condensed device name we use in efi_disk.c */ - snprintf(devname, sizeof(devname), "%s%s", dev, devnr); + const char *str = strchr(devnr, ':'); + if (str) { + str++; + return simple_strtoul(str, NULL, 16); } + return 0; +}
- colon = strchr(devname, ':'); - -#if CONFIG_IS_ENABLED(ISO_PARTITION) - /* For ISOs we create partition block devices */ - if (desc && (desc->type != DEV_TYPE_UNKNOWN) && - (desc->part_type == PART_TYPE_ISO)) { - if (!colon) - snprintf(devname, sizeof(devname), "%s:1", devname); +void efi_set_bootdev(const char *dev, const char *devnr, const char *path) +{ + char filename[32] = { 0 }; /* dp->str is u16[32] long */ + char *s;
- colon = NULL; - } -#endif + if (strcmp(dev, "Net")) { + struct blk_desc *desc; + int part;
- if (colon) - *colon = '\0'; + desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10)); + part = parse_partnum(devnr);
- /* Patch bootefi_device_path to the target device */ - memset(bootefi_device_path[0].str, 0, sizeof(bootefi_device_path[0].str)); - ascii2unicode(bootefi_device_path[0].str, devname); + bootefi_device_path = efi_dp_from_part(desc, part); + } else { +#ifdef CONFIG_NET + bootefi_device_path = efi_dp_from_eth(); +#endif + }
- /* Patch bootefi_image_path to the target file path */ - memset(bootefi_image_path[0].str, 0, sizeof(bootefi_image_path[0].str)); if (strcmp(dev, "Net")) { /* Add leading / to fs paths, because they're absolute */ - snprintf(devname, sizeof(devname), "/%s", path); + snprintf(filename, sizeof(filename), "/%s", path); } else { - snprintf(devname, sizeof(devname), "%s", path); + snprintf(filename, sizeof(filename), "%s", path); } /* DOS style file path: */ - s = devname; + s = filename; while ((s = strchr(s, '/'))) *s++ = '\'; - ascii2unicode(bootefi_image_path[0].str, devname); + bootefi_image_path = efi_dp_from_file(NULL, 0, filename); } diff --git a/include/efi_loader.h b/include/efi_loader.h index 1ab4af0f88..479fb6b63d 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -143,7 +143,7 @@ int efi_disk_register(void); /* Called by bootefi to make GOP (graphical) interface available */ int efi_gop_register(void); /* Called by bootefi to make the network interface available */ -int efi_net_register(void **handle); +int efi_net_register(void); /* Called by bootefi to make SMBIOS tables available */ void efi_smbios_register(void);
@@ -203,6 +203,9 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table efi_status_t efi_get_protocol(struct efi_object *efiobj, struct efi_handler *handler, void **protocol_interface); +void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *obj, + struct efi_device_path *device_path, + struct efi_device_path *file_path);
#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER extern void *efi_bounce_buffer; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 4b78f6d556..4a19fabdb3 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -714,6 +714,41 @@ static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid, return EFI_EXIT(efi_install_configuration_table(guid, table)); }
+/* Initialize a loaded_image_info + loaded_image_info object with correct + * protocols, boot-device, etc. + */ +void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *obj, + struct efi_device_path *device_path, + struct efi_device_path *file_path) +{ + obj->handle = info; + + /* + * When asking for the device path interface, return + * bootefi_device_path + */ + obj->protocols[0].guid = &efi_guid_device_path; + obj->protocols[0].protocol_interface = device_path; + + /* + * When asking for the loaded_image interface, just + * return handle which points to loaded_image_info + */ + obj->protocols[1].guid = &efi_guid_loaded_image; + obj->protocols[1].protocol_interface = info; + + obj->protocols[2].guid = &efi_guid_console_control; + obj->protocols[2].protocol_interface = (void *)&efi_console_control; + + obj->protocols[3].guid = &efi_guid_device_path_to_text_protocol; + obj->protocols[3].protocol_interface = (void *)&efi_guid_device_path_to_text_protocol; + + info->file_path = file_path; + info->device_handle = efi_dp_find_obj(device_path); + + list_add_tail(&obj->link, &efi_obj_list); +} + static efi_status_t EFIAPI efi_load_image(bool boot_policy, efi_handle_t parent_image, struct efi_device_path *file_path, diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index aa0618fd3a..91f1e4a69e 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -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; }

fallback.efi (and probably other things) use UEFI's simple-file-system protocol and file support to search for OS's to boot.
Signed-off-by: Rob Clark robdclark@gmail.com --- fs/fs.c | 21 ++ include/efi.h | 2 + include/efi_api.h | 65 ++++++ include/efi_loader.h | 13 ++ include/fs.h | 2 + lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_disk.c | 51 +++++ lib/efi_loader/efi_file.c | 468 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_image_loader.c | 3 + 9 files changed, 626 insertions(+) create mode 100644 lib/efi_loader/efi_file.c
diff --git a/fs/fs.c b/fs/fs.c index 42a028a6ce..a269619b51 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -247,6 +247,27 @@ int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype) return -1; }
+/* set current blk device w/ blk_desc + partition # */ +int fs_set_blk_dev2(struct blk_desc *desc, int part) +{ + struct fstype_info *info; + int ret, i; + + ret = part_get_info(desc, part, &fs_partition); + if (ret) + return ret; + fs_dev_desc = desc; + + for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) { + if (!info->probe(fs_dev_desc, &fs_partition)) { + fs_type = info->fstype; + return 0; + } + } + + return -1; +} + static void fs_close(void) { struct fstype_info *info = fs_get_info(fs_type); diff --git a/include/efi.h b/include/efi.h index 87b0b43f20..ddd2b96417 100644 --- a/include/efi.h +++ b/include/efi.h @@ -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; diff --git a/include/efi_api.h b/include/efi_api.h index dd79cace32..1a542846b3 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -657,4 +657,69 @@ struct efi_pxe { struct efi_pxe_mode *mode; };
+#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \ + EFI_GUID(0x964e5b22, 0x6459, 0x11d2, \ + 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b) +#define EFI_FILE_PROTOCOL_REVISION 0x00010000 + +struct efi_file_handle { + u64 rev; + efi_status_t (EFIAPI *open)(struct efi_file_handle *file, + struct efi_file_handle **new_handle, + s16 *file_name, u64 open_mode, u64 attributes); + efi_status_t (EFIAPI *close)(struct efi_file_handle *file); + efi_status_t (EFIAPI *delete)(struct efi_file_handle *file); + efi_status_t (EFIAPI *read)(struct efi_file_handle *file, + u64 *buffer_size, void *buffer); + efi_status_t (EFIAPI *write)(struct efi_file_handle *file, + u64 *buffer_size, void *buffer); + efi_status_t (EFIAPI *getpos)(struct efi_file_handle *file, + u64 *pos); + efi_status_t (EFIAPI *setpos)(struct efi_file_handle *file, + u64 pos); + efi_status_t (EFIAPI *getinfo)(struct efi_file_handle *file, + efi_guid_t *info_type, u64 *buffer_size, void *buffer); + efi_status_t (EFIAPI *setinfo)(struct efi_file_handle *file, + efi_guid_t *info_type, u64 buffer_size, void *buffer); + efi_status_t (EFIAPI *flush)(struct efi_file_handle *file); +}; + +#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \ + EFI_GUID(0x964e5b22, 0x6459, 0x11d2, \ + 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b) +#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION 0x00010000 + +struct efi_simple_file_system_protocol { + u64 rev; + efi_status_t (EFIAPI *open_volume)(struct efi_simple_file_system_protocol *this, + struct efi_file_handle **root); +}; + +#define EFI_FILE_INFO_GUID \ + EFI_GUID(0x9576e92, 0x6d3f, 0x11d2, \ + 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b) + +#define EFI_FILE_MODE_READ 0x0000000000000001 +#define EFI_FILE_MODE_WRITE 0x0000000000000002 +#define EFI_FILE_MODE_CREATE 0x8000000000000000 + +#define EFI_FILE_READ_ONLY 0x0000000000000001 +#define EFI_FILE_HIDDEN 0x0000000000000002 +#define EFI_FILE_SYSTEM 0x0000000000000004 +#define EFI_FILE_RESERVED 0x0000000000000008 +#define EFI_FILE_DIRECTORY 0x0000000000000010 +#define EFI_FILE_ARCHIVE 0x0000000000000020 +#define EFI_FILE_VALID_ATTR 0x0000000000000037 + +struct efi_file_info { + u64 size; + u64 file_size; + u64 physical_size; + struct efi_time create_time; + struct efi_time last_access_time; + struct efi_time modification_time; + u64 attribute; + s16 file_name[0]; +}; + #endif diff --git a/include/efi_loader.h b/include/efi_loader.h index 479fb6b63d..4210d5f19a 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -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; @@ -147,6 +149,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);
@@ -175,6 +180,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 */ diff --git a/include/fs.h b/include/fs.h index 71051d7c66..7e920916e3 100644 --- a/include/fs.h +++ b/include/fs.h @@ -26,6 +26,8 @@ */ int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype);
+int fs_set_blk_dev2(struct blk_desc *desc, int part); + /* * Print the list of files on the partition previously set by fs_set_blk_dev(), * in directory "dirname". diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index f35e5ce8a8..cce92cfeb5 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -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 diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index eea65a402a..f7565ad937 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -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,49 @@ static const struct efi_block_io block_io_disk_template = { .flush_blocks = &efi_disk_flush_blocks, };
+static efi_status_t EFIAPI efi_disk_open_fs(void *handle, + const efi_guid_t *protocol, + void **protocol_interface) +{ + struct efi_disk_obj *diskobj = handle; + + if (!diskobj->volume) { + diskobj->volume = + efi_simple_file_system(diskobj->desc, + diskobj->part, + diskobj->dp); + } + + *protocol_interface = diskobj->volume; + + return EFI_SUCCESS; +} + +/* + * 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_simple_file_system_protocol *volume; + struct efi_object *efiobj; + struct efi_disk_obj *diskobj; + + efiobj = efi_dp_find_obj(fp); + if (!efiobj) + return NULL; + + diskobj = container_of(efiobj, struct efi_disk_obj, parent); + + efi_disk_open_fs(diskobj, NULL, (void **)&volume); + + return volume; +} + static void efi_disk_add_dev(const char *name, const char *if_typename, struct blk_desc *desc, @@ -194,6 +239,12 @@ 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->parent.protocols[2].guid = + &efi_simple_file_system_protocol_guid; + diskobj->parent.protocols[2].open = + efi_disk_open_fs; + } diskobj->parent.handle = diskobj; diskobj->ops = block_io_disk_template; diskobj->ifname = if_typename; diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c new file mode 100644 index 0000000000..7bdd16c649 --- /dev/null +++ b/lib/efi_loader/efi_file.c @@ -0,0 +1,468 @@ +/* + * EFI utils + * + * Copyright (c) 2017 Rob Clark + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <charset.h> +#include <efi_loader.h> +#include <malloc.h> +#include <fs.h> + +struct file_system { + struct efi_simple_file_system_protocol base; + struct efi_device_path *dp; + struct blk_desc *desc; + int part; +}; +#define to_fs(x) container_of(x, struct file_system, base) + +struct file_handle { + struct efi_file_handle base; + struct file_system *fs; + loff_t offset; /* current file position/cursor */ + int isdir; + char path[0]; +}; +#define to_fh(x) container_of(x, struct file_handle, base) + +static const struct efi_file_handle efi_file_handle_protocol; + +static char *basename(struct file_handle *fh) +{ + char *s = strrchr(fh->path, '/'); + if (s) + return s + 1; + return fh->path; +} + +static int set_blk_dev(struct file_handle *fh) +{ + return fs_set_blk_dev2(fh->fs->desc, fh->fs->part); +} + +static int is_dir(struct file_handle *fh, const char *filename) +{ + char buf[256]; + struct fs_dirent d; + const char *path; + int ret; + + if (!filename) { + path = fh->path; + } else { + ret = snprintf(buf, sizeof(buf), "%s/%s", + fh->path, filename); + if (ret >= sizeof(buf)) + return 0; + path = buf; + } + + set_blk_dev(fh); + ret = fs_readdir(path, 0, &d); + if (ret == -ENOTDIR) { + return 0; + } else if (ret == -ENXIO) { + debug("WARNING: cannot read directories!\n"); + /* + * We don't know, assume regular file, but if + * the EFI app tries to read a directory, it + * won't work properly. This will be a problem + * for fallback.efi as it searches /EFI/ for + * OS installations. Too bad. + */ + return 0; + } else { + return 1; + } +} + +/* NOTE: despite what you would expect, 'file_name' is actually a path. + * With windoze style backlashes, ofc. + */ +static struct efi_file_handle *file_open(struct file_system *fs, + struct file_handle *parent, s16 *file_name) +{ + struct file_handle *fh; + char f0[MAX_UTF8_PER_UTF16] = {0}; + int plen = 0; + int flen = 0; + + if (file_name) + utf16_to_utf8((u8 *)f0, (u16 *)file_name, 1); + + /* we could have a parent, but also an absolute path: */ + if (f0[0] == '\') { + plen = 0; + flen = utf16_strlen((u16 *)file_name); + } else if (parent) { + plen = strlen(parent->path) + 1; + flen = utf16_strlen((u16 *)file_name); + } + + /* +2 is for null and '/' */ + fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2); + + fh->base = efi_file_handle_protocol; + fh->fs = fs; + + if (parent) { + char *p = fh->path; + + if (plen > 0) { + strcpy(p, parent->path); + p += plen - 1; + *p++ = '/'; + } + + utf16_to_utf8((u8 *)p, (u16 *)file_name, flen); + + /* sanitize path: */ + while ((p = strchr(p, '\'))) + *p++ = '/'; + + /* check if file exists: */ + if (set_blk_dev(fh)) + goto error; + if (!fs_exists(fh->path)) + goto error; + + /* figure out if file is a directory: */ + fh->isdir = is_dir(fh, NULL); + } else { + fh->isdir = 1; + strcpy(fh->path, ""); + } + + return &fh->base; + +error: + free(fh); + return NULL; +} + +static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file, + struct efi_file_handle **new_handle, + s16 *file_name, u64 open_mode, u64 attributes) +{ + struct file_handle *fh = to_fh(file); + + EFI_ENTRY("%p, %p, %p, %llu, %llu", file, new_handle, file_name, + open_mode, attributes); + + *new_handle = file_open(fh->fs, fh, file_name); + if (!*new_handle) + return EFI_EXIT(EFI_NOT_FOUND); + + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file) +{ + struct file_handle *fh = to_fh(file); + EFI_ENTRY("%p", file); + free(fh); + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file) +{ + efi_file_close(file); + return EFI_WARN_DELETE_FAILURE; +} + +static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size, + void *buffer) +{ + loff_t actread; + + if (fs_read(fh->path, (ulong)buffer, fh->offset, + *buffer_size, &actread)) + return EFI_DEVICE_ERROR; + + *buffer_size = actread; + fh->offset += actread; + + return EFI_SUCCESS; +} + +static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size, + void *buffer) +{ + struct efi_file_info *info = buffer; + struct fs_dirent dent; + unsigned required_size; + int ret; + + ret = fs_readdir(fh->path, fh->offset, &dent); + + if (ret == -ENOENT) { + /* no more files in directory: */ + /* workaround shim.efi bug/quirk.. as find_boot_csv() + * loops through directory contents, it initially calls + * read w/ zero length buffer to find out how much mem + * to allocate for the EFI_FILE_INFO, then allocates, + * and then calls a 2nd time. If we return size of + * zero the first time, it happily passes that to + * AllocateZeroPool(), and when that returns NULL it + * thinks it is EFI_OUT_OF_RESOURCES. So on first + * call return a non-zero size: + */ + if (*buffer_size == 0) + *buffer_size = sizeof(*info); + else + *buffer_size = 0; + return EFI_SUCCESS; + } else if (ret) { + return EFI_DEVICE_ERROR; + } + + /* check buffer size: */ + required_size = sizeof(*info) + 2 * (strlen(dent.name) + 1); + if (*buffer_size < required_size) { + *buffer_size = required_size; + return EFI_BUFFER_TOO_SMALL; + } + + *buffer_size = required_size; + memset(info, 0, required_size); + + info->size = required_size; + info->file_size = dent.size; + info->physical_size = dent.size; + + if (is_dir(fh, dent.name)) + info->attribute |= EFI_FILE_DIRECTORY; + + ascii2unicode((u16 *)info->file_name, dent.name); + + fh->offset++; + + return EFI_SUCCESS; +} + +static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file, + u64 *buffer_size, void *buffer) +{ + struct file_handle *fh = to_fh(file); + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer); + + if (set_blk_dev(fh)) { + ret = EFI_DEVICE_ERROR; + goto error; + } + + if (fh->isdir) { + ret = dir_read(fh, buffer_size, buffer); + } else { + ret = file_read(fh, buffer_size, buffer); + } + +error: + return EFI_EXIT(ret); +} + +static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file, + u64 *buffer_size, void *buffer) +{ + EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer); + return EFI_EXIT(EFI_WRITE_PROTECTED); +} + +static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file, + u64 *pos) +{ + struct file_handle *fh = to_fh(file); + EFI_ENTRY("%p, %p", file, pos); + *pos = fh->offset; + return EFI_EXIT(EFI_SUCCESS); +} + +static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file, + u64 pos) +{ + struct file_handle *fh = to_fh(file); + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %llu", file, pos); + + if (fh->isdir && (pos != 0)) { + ret = EFI_UNSUPPORTED; + goto error; + } + + if (pos == ~0ULL) { + loff_t file_size; + + if (set_blk_dev(fh)) { + ret = EFI_DEVICE_ERROR; + goto error; + } + + if (fs_size(fh->path, &file_size)) { + ret = EFI_DEVICE_ERROR; + goto error; + } + + pos = file_size; + } + + fh->offset = pos; + +error: + return EFI_EXIT(ret); +} + +static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file, + efi_guid_t *info_type, u64 *buffer_size, void *buffer) +{ + struct file_handle *fh = to_fh(file); + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %p, %p, %p", file, info_type, buffer_size, buffer); + + if (!guidcmp(info_type, &efi_file_info_guid)) { + struct efi_file_info *info = buffer; + char *filename = basename(fh); + unsigned required_size; + loff_t file_size; + + /* check buffer size: */ + required_size = sizeof(*info) + 2 * (strlen(filename) + 1); + if (*buffer_size < required_size) { + *buffer_size = required_size; + ret = EFI_BUFFER_TOO_SMALL; + goto error; + } + + if (set_blk_dev(fh)) { + ret = EFI_DEVICE_ERROR; + goto error; + } + + if (fs_size(fh->path, &file_size)) { + ret = EFI_DEVICE_ERROR; + goto error; + } + + memset(info, 0, required_size); + + info->size = required_size; + info->file_size = file_size; + info->physical_size = file_size; + + if (fh->isdir) + info->attribute |= EFI_FILE_DIRECTORY; + + ascii2unicode((u16 *)info->file_name, filename); + } else { + ret = EFI_UNSUPPORTED; + } + +error: + return EFI_EXIT(ret); +} + +static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file, + efi_guid_t *info_type, u64 buffer_size, void *buffer) +{ + EFI_ENTRY("%p, %p, %llu, %p", file, info_type, buffer_size, buffer); + return EFI_EXIT(EFI_UNSUPPORTED); +} + +static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file) +{ + EFI_ENTRY("%p", file); + return EFI_EXIT(EFI_SUCCESS); +} + +static const struct efi_file_handle efi_file_handle_protocol = { + .rev = EFI_FILE_PROTOCOL_REVISION, + .open = efi_file_open, + .close = efi_file_close, + .delete = efi_file_delete, + .read = efi_file_read, + .write = efi_file_write, + .getpos = efi_file_getpos, + .setpos = efi_file_setpos, + .getinfo = efi_file_getinfo, + .setinfo = efi_file_setinfo, + .flush = efi_file_flush, +}; + +struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp) +{ + struct efi_simple_file_system_protocol *v; + struct efi_file_handle *f; + efi_status_t ret; + + v = efi_fs_from_path(fp); + if (!v) + return NULL; + + EFI_CALL(ret = v->open_volume(v, &f)); + if (ret != EFI_SUCCESS) + return NULL; + + /* skip over device-path nodes before the file path: */ + while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) { + fp = efi_dp_next(fp); + } + + while (fp) { + struct efi_device_path_file_path *fdp = + container_of(fp, struct efi_device_path_file_path, dp); + struct efi_file_handle *f2; + + if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) { + printf("bad file path!\n"); + f->close(f); + return NULL; + } + + EFI_CALL(ret = f->open(f, &f2, (s16 *)fdp->str, EFI_FILE_MODE_READ, 0)); + if (ret != EFI_SUCCESS) + return NULL; + + fp = efi_dp_next(fp); + + EFI_CALL(f->close(f)); + f = f2; + } + + return f; +} + +static efi_status_t EFIAPI +efi_open_volume(struct efi_simple_file_system_protocol *this, + struct efi_file_handle **root) +{ + struct file_system *fs = to_fs(this); + + EFI_ENTRY("%p, %p", this, root); + + *root = file_open(fs, NULL, NULL); + + return EFI_EXIT(EFI_SUCCESS); +} + +struct efi_simple_file_system_protocol * +efi_simple_file_system(struct blk_desc *desc, int part, + struct efi_device_path *dp) +{ + struct file_system *fs; + + fs = calloc(1, sizeof(*fs)); + fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION; + fs->base.open_volume = efi_open_volume; + fs->desc = desc; + fs->part = part; + fs->dp = dp; + + return &fs->base; +} diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index f961407f50..469acae082 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -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)

Previously we only supported the case when the EFI application loaded the image into memory for us. But fallback.efi does not do this.
Signed-off-by: Rob Clark robdclark@gmail.com --- lib/efi_loader/efi_boottime.c | 83 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 15 deletions(-)
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 4a19fabdb3..6187065fdd 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -749,6 +749,45 @@ void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *ob list_add_tail(&obj->link, &efi_obj_list); }
+static efi_status_t load_image_from_path(struct efi_device_path *file_path, + void **buffer) +{ + struct efi_file_info *info = NULL; + struct efi_file_handle *f; + static efi_status_t ret; + uint64_t bs; + + f = efi_file_from_path(file_path); + if (!f) + return EFI_DEVICE_ERROR; + + bs = 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, @@ -756,25 +795,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); @@ -783,7 +837,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); }

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.
Signed-off-by: Rob Clark robdclark@gmail.com --- lib/efi_loader/efi_memory.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 9e079f1fa3..2ba8d8b42b 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -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 = DIV_ROUND_UP(size + sizeof(struct efi_pool_allocation), + EFI_PAGE_SIZE);
if (size == 0) { *buffer = NULL;

Signed-off-by: Rob Clark robdclark@gmail.com --- cmd/bootefi.c | 4 + include/efi.h | 19 +++ include/efi_loader.h | 10 ++ lib/efi_loader/Makefile | 2 +- lib/efi_loader/efi_boottime.c | 5 + lib/efi_loader/efi_runtime.c | 17 ++- lib/efi_loader/efi_variable.c | 342 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 394 insertions(+), 5 deletions(-) create mode 100644 lib/efi_loader/efi_variable.c
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index b9e1e5e131..80f52e9e35 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -181,6 +181,10 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt, goto exit; }
+ /* we don't support much: */ + setenv("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);
diff --git a/include/efi.h b/include/efi.h index ddd2b96417..dbd482a5db 100644 --- a/include/efi.h +++ b/include/efi.h @@ -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 * diff --git a/include/efi_loader.h b/include/efi_loader.h index 4210d5f19a..f14fdfa58e 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -296,6 +296,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 */ diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index cce92cfeb5..f58cb13337 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -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 diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 6187065fdd..19eaea8baf 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -927,6 +927,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: */ + saveenv(); +#endif + board_quiesce_devices();
/* Fix up caches for EFI payloads if necessary */ diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index dd52755d1d..7615090ba3 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -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, }; diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c new file mode 100644 index 0000000000..721b14807f --- /dev/null +++ b/lib/efi_loader/efi_variable.c @@ -0,0 +1,342 @@ +/* + * EFI utils + * + * Copyright (c) 2017 Rob Clark + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <malloc.h> +#include <charset.h> +#include <efi_loader.h> + +#define READ_ONLY BIT(31) + +/* 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 + * + * Maybe a utf16 type would be useful to for a string value to be auto + * converted to utf16? + */ + +#define MAX_VAR_NAME 31 +#define MAX_NATIVE_VAR_NAME \ + (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx_") + \ + (MAX_VAR_NAME * MAX_UTF8_PER_UTF16)) + +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +static const char * hex2mem(u8 *mem, const char *hexstr, int count) +{ + memset(mem, 0, count/2); + + do { + int nibble; + + *mem = 0; + + if (!count || !*hexstr) + break; + + nibble = hex(*hexstr); + if (nibble < 0) + break; + + *mem = nibble; + count--; + hexstr++; + + if (!count || !*hexstr) + break; + + nibble = hex(*hexstr); + if (nibble < 0) + break; + + *mem = (*mem << 4) | nibble; + count--; + hexstr++; + mem++; + + } while (1); + + if (*hexstr) + return hexstr; + + return NULL; +} + +static char * mem2hex(char *hexstr, const u8 *mem, int count) +{ + static const char hexchars[] = "0123456789abcdef"; + + while (count-- > 0) { + u8 ch = *mem++; + *hexstr++ = hexchars[ch >> 4]; + *hexstr++ = hexchars[ch & 0xf]; + } + + return hexstr; +} + +static efi_status_t efi_to_native(char *native, s16 *variable_name, + efi_guid_t *vendor) +{ + size_t len; + + len = utf16_strlen((u16 *)variable_name); + if (len >= MAX_VAR_NAME) + return EFI_DEVICE_ERROR; + + native += sprintf(native, "efi_"); + native += guidstr(native, vendor); + native += sprintf(native, "_"); + native = (char *)utf16_to_utf8((u8 *)native, (u16 *)variable_name, len); + *native = '\0'; + + return EFI_SUCCESS; +} + +static const char * prefix(const char *str, const char *prefix) +{ + while (*str && *prefix) { + if (*str != *prefix) + break; + str++; + prefix++; + } + + if (*prefix) + return NULL; + + return str; +} + +/* parse attributes part of variable value, if present: */ +static const char * parse_attr(const char *str, u32 *attrp) +{ + u32 attr = 0; + char sep = '{'; + + if (*str != '{') { + *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS; + return str; + } + + while (*str == sep) { + const char *s; + + str++; + + if ((s = prefix(str, "ro"))) { + attr |= READ_ONLY; + } else if ((s = prefix(str, "boot"))) { + attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS; + } else if ((s = prefix(str, "run"))) { + attr |= EFI_VARIABLE_RUNTIME_ACCESS; + } else { + printf("invalid attribute: %s\n", str); + break; + } + + str = s; + sep = ','; + } + + str++; + + *attrp = attr; + + return str; +} + +/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetVariable.28.2... */ +efi_status_t EFIAPI efi_get_variable(s16 *variable_name, + efi_guid_t *vendor, u32 *attributes, + unsigned long *data_size, void *data) +{ + char native_name[MAX_NATIVE_VAR_NAME + 1]; + efi_status_t ret; + unsigned long in_size; + const char *val, *s; + u32 attr; + + EFI_ENTRY("%p %p %p %p %p", variable_name, vendor, attributes, + data_size, data); + + if (!variable_name || !vendor || !data_size) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + ret = efi_to_native(native_name, variable_name, vendor); + if (ret) + return EFI_EXIT(ret); + + debug("%s: get '%s'\n", __func__, native_name); + + val = getenv(native_name); + if (!val) + return EFI_EXIT(EFI_NOT_FOUND); + + val = parse_attr(val, &attr); + + in_size = *data_size; + + if ((s = prefix(val, "(blob)"))) { + unsigned len = strlen(s); + + /* two characters per byte: */ + len = DIV_ROUND_UP(len, 2); + *data_size = len; + + if (in_size < len) + return EFI_EXIT(EFI_BUFFER_TOO_SMALL); + + if (!data) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + if (hex2mem(data, s, len * 2)) + return EFI_EXIT(EFI_DEVICE_ERROR); + + debug("%s: got value: "%s"\n", __func__, s); + } else if ((s = prefix(val, "(string)"))) { + unsigned len = strlen(s) + 1; + + *data_size = len; + + if (in_size < len) + return EFI_EXIT(EFI_BUFFER_TOO_SMALL); + + if (!data) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + memcpy(data, s, len); + ((char *)data)[len] = '\0'; + + debug("%s: got value: "%s"\n", __func__, (char *)data); + } else { + debug("%s: invalid value: '%s'\n", __func__, val); + return EFI_EXIT(EFI_DEVICE_ERROR); + } + + if (attributes) + *attributes = attr & EFI_VARIABLE_MASK; + + return EFI_EXIT(EFI_SUCCESS); +} + +/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#GetNextVariableN... */ +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); + + return EFI_EXIT(EFI_DEVICE_ERROR); +} + +/* http://wiki.phoenix.com/wiki/index.php/EFI_RUNTIME_SERVICES#SetVariable.28.2... */ +efi_status_t EFIAPI efi_set_variable(s16 *variable_name, + efi_guid_t *vendor, u32 attributes, + unsigned long data_size, void *data) +{ + char native_name[MAX_NATIVE_VAR_NAME + 1]; + efi_status_t ret = EFI_SUCCESS; + char *val, *s; + u32 attr; + + EFI_ENTRY("%p %p %x %lu %p", variable_name, vendor, attributes, + data_size, data); + + if (!variable_name || !vendor || !data_size) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + ret = efi_to_native(native_name, variable_name, vendor); + if (ret) + return EFI_EXIT(ret); + +#define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS) + + if ((data_size == 0) || !(attributes & ACCESS_ATTR)) { + /* delete the variable: */ + setenv(native_name, NULL); + return EFI_EXIT(EFI_SUCCESS); + } + + val = getenv(native_name); + if (val) { + parse_attr(val, &attr); + + if (attr & READ_ONLY) + return EFI_EXIT(EFI_WRITE_PROTECTED); + } + + val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1); + if (!val) + return EFI_EXIT(EFI_OUT_OF_RESOURCES); + + s = val; + + /* store attributes: */ + attributes &= (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS); + s += sprintf(s, "{"); + while (attributes) { + u32 attr = 1 << (ffs(attributes) - 1); + + if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) + s += sprintf(s, "boot"); + else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) + s += sprintf(s, "run"); + + attributes &= ~attr; + if (attributes) + s += sprintf(s, ","); + } + s += sprintf(s, "}"); + + /* store payload: */ + s += sprintf(s, "(blob)"); + s = mem2hex(s, data, data_size); + *s = '\0'; + + debug("%s: setting: %s=%s\n", __func__, native_name, val); + + if (setenv(native_name, val)) + ret = EFI_DEVICE_ERROR; + + free(val); + + return EFI_EXIT(ret); +}

On 4 August 2017 at 13:32, Rob Clark robdclark@gmail.com wrote:
Commit message?
Signed-off-by: Rob Clark robdclark@gmail.com
cmd/bootefi.c | 4 + include/efi.h | 19 +++ include/efi_loader.h | 10 ++ lib/efi_loader/Makefile | 2 +- lib/efi_loader/efi_boottime.c | 5 + lib/efi_loader/efi_runtime.c | 17 ++- lib/efi_loader/efi_variable.c | 342 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 394 insertions(+), 5 deletions(-) create mode 100644 lib/efi_loader/efi_variable.c

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.)
Signed-off-by: Rob Clark robdclark@gmail.com --- 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 | 169 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_boottime.c | 6 +- lib/efi_loader/efi_image_loader.c | 1 + 8 files changed, 235 insertions(+), 6 deletions(-) create mode 100644 lib/efi_loader/efi_bootmgr.c
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 80f52e9e35..02a0dd159b 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -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); diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h index d8dab8e46a..94ccab02d2 100644 --- a/include/config_distro_bootcmd.h +++ b/include/config_distro_bootcmd.h @@ -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 " \ diff --git a/include/efi_api.h b/include/efi_api.h index 1a542846b3..ef91e34c7b 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -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) diff --git a/include/efi_loader.h b/include/efi_loader.h index f14fdfa58e..e3eb932f54 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -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; @@ -219,6 +220,8 @@ efi_status_t efi_get_protocol(struct efi_object *efiobj, 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; @@ -306,6 +309,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 */ diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index f58cb13337..930c0e218e 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -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 diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c new file mode 100644 index 0000000000..8246ddd48f --- /dev/null +++ b/lib/efi_loader/efi_bootmgr.c @@ -0,0 +1,169 @@ +/* + * EFI utils + * + * Copyright (c) 2017 Rob Clark + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <charset.h> +#include <malloc.h> +#include <efi_loader.h> + +static const struct efi_boot_services *bs; +static const struct efi_runtime_services *rs; + +#define LOAD_OPTION_ACTIVE 0x00000001 +#define LOAD_OPTION_FORCE_RECONNECT 0x00000002 +#define LOAD_OPTION_HIDDEN 0x00000008 + +/* + * bootmgr implements the logic of trying to find a payload to boot + * based on the BootOrder + BootXXXX variables, and then loading it. + * + * TODO detecting a special key held (f9?) and displaying a boot menu + * like you would get on a PC would be clever. + * + * TODO if we had a way to write and persist variables after the OS + * has started, we'd also want to check OsIndications to see if we + * should do normal or recovery boot. + */ + + +/* + * See section 3.1.3 in the v2.7 UEFI spec for more details on + * the layout of EFI_LOAD_OPTION. In short it is: + * + * typedef struct _EFI_LOAD_OPTION { + * UINT32 Attributes; + * UINT16 FilePathListLength; + * // CHAR16 Description[]; <-- variable length, NULL terminated + * // EFI_DEVICE_PATH_PROTOCOL FilePathList[]; <-- FilePathListLength bytes + * // UINT8 OptionalData[]; + * } EFI_LOAD_OPTION; + */ +struct load_option { + u32 attributes; + u16 file_path_length; + u16 *label; + struct efi_device_path *file_path; + u8 *optional_data; +}; + +static void parse_load_option(struct load_option *lo, void *ptr) +{ + lo->attributes = *(u32 *)ptr; + ptr += sizeof(u32); + + lo->file_path_length = *(u16 *)ptr; + ptr += sizeof(u16); + + lo->label = ptr; + ptr += (utf16_strlen(lo->label) + 1) * 2; + + lo->file_path = ptr; + ptr += lo->file_path_length; + + lo->optional_data = ptr; +} + +/* free() the result */ +static void *get_var(u16 *name, const efi_guid_t *vendor, + unsigned long *size) +{ + efi_guid_t *v = (efi_guid_t *)vendor; + efi_status_t ret; + void *buf = NULL; + + *size = 0; + EFI_CALL(ret = rs->get_variable((s16 *)name, v, NULL, size, buf)); + if (ret == EFI_BUFFER_TOO_SMALL) { + buf = malloc(*size); + EFI_CALL(ret = rs->get_variable((s16 *)name, v, NULL, size, buf)); + } + + if (ret != EFI_SUCCESS) { + free(buf); + *size = 0; + return NULL; + } + + return buf; +} + +static void *try_load_entry(uint16_t n, struct efi_device_path **device_path, + struct efi_device_path **file_path) +{ + struct load_option lo; + u16 varname[] = L"Boot0000"; + u16 hexmap[] = L"0123456789ABCDEF"; + void *load_option, *image = NULL; + unsigned long size; + + varname[4] = hexmap[(n & 0xf000) >> 12]; + varname[5] = hexmap[(n & 0x0f00) >> 8]; + varname[6] = hexmap[(n & 0x00f0) >> 4]; + varname[7] = hexmap[(n & 0x000f) >> 0]; + + load_option = get_var(varname, &efi_global_variable_guid, &size); + if (!load_option) + return NULL; + + parse_load_option(&lo, load_option); + + if (lo.attributes & LOAD_OPTION_ACTIVE) { + efi_status_t ret; + u16 *str = NULL; + + debug("%s: trying to load "%ls" from: %ls\n", __func__, + lo.label, (str = efi_dp_str(lo.file_path))); + efi_free_pool(str); + + ret = efi_load_image_from_path(lo.file_path, &image); + + if (ret != EFI_SUCCESS) + goto error; + + printf("Booting: %ls\n", lo.label); + efi_dp_split_file_path(lo.file_path, device_path, file_path); + } + +error: + free(load_option); + + return image; +} + +void *efi_bootmgr_load(struct efi_device_path **device_path, + struct efi_device_path **file_path) +{ + uint16_t *bootorder; + unsigned long size; + void *image = NULL; + int i, num; + + __efi_entry_check(); + + bs = systab.boottime; + rs = systab.runtime; + + bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size); + if (!bootorder) + goto error; + + num = size / sizeof(uint16_t); + for (i = 0; i < num; i++) { + debug("%s: trying to load Boot%04X\n", __func__, bootorder[i]); + image = try_load_entry(bootorder[i], device_path, file_path); + if (image) + break; + } + + free(bootorder); + +error: + __efi_exit_check(); + + return image; +} diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 19eaea8baf..5ff2d2d4b0 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -749,8 +749,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; @@ -808,7 +808,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); diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 469acae082..242e6a504b 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -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 =

On 08/04/2017 09:32 PM, Rob Clark wrote:
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.)
I wonder if this implementation is Fedora specific.
For Debian I could not find any reference to boot.csv. And it is not mentioned in the UEFI 2.7 spec.
Please, provide the specification that your work is based on.
Best regards
Heinrich
Signed-off-by: Rob Clark robdclark@gmail.com
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 | 169 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_boottime.c | 6 +- lib/efi_loader/efi_image_loader.c | 1 + 8 files changed, 235 insertions(+), 6 deletions(-) create mode 100644 lib/efi_loader/efi_bootmgr.c
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 80f52e9e35..02a0dd159b 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -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);
diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h index d8dab8e46a..94ccab02d2 100644 --- a/include/config_distro_bootcmd.h +++ b/include/config_distro_bootcmd.h @@ -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};" \
"load ${devtype} ${devnum}:${distro_bootpart} " \ "${kernel_addr_r} efi/boot/"BOOTEFI_NAME"; " \ "if fdt addr ${fdt_addr_r}; then " \"fi;" \
diff --git a/include/efi_api.h b/include/efi_api.h index 1a542846b3..ef91e34c7b 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -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) diff --git a/include/efi_loader.h b/include/efi_loader.h index f14fdfa58e..e3eb932f54 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -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; @@ -219,6 +220,8 @@ efi_status_t efi_get_protocol(struct efi_object *efiobj, 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; @@ -306,6 +309,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 */ diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index f58cb13337..930c0e218e 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -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 diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c new file mode 100644 index 0000000000..8246ddd48f --- /dev/null +++ b/lib/efi_loader/efi_bootmgr.c @@ -0,0 +1,169 @@ +/*
- EFI utils
- Copyright (c) 2017 Rob Clark
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <charset.h> +#include <malloc.h> +#include <efi_loader.h>
+static const struct efi_boot_services *bs; +static const struct efi_runtime_services *rs;
+#define LOAD_OPTION_ACTIVE 0x00000001 +#define LOAD_OPTION_FORCE_RECONNECT 0x00000002 +#define LOAD_OPTION_HIDDEN 0x00000008
+/*
- bootmgr implements the logic of trying to find a payload to boot
- based on the BootOrder + BootXXXX variables, and then loading it.
- TODO detecting a special key held (f9?) and displaying a boot menu
- like you would get on a PC would be clever.
- TODO if we had a way to write and persist variables after the OS
- has started, we'd also want to check OsIndications to see if we
- should do normal or recovery boot.
- */
+/*
- See section 3.1.3 in the v2.7 UEFI spec for more details on
- the layout of EFI_LOAD_OPTION. In short it is:
- typedef struct _EFI_LOAD_OPTION {
UINT32 Attributes;
UINT16 FilePathListLength;
// CHAR16 Description[]; <-- variable length, NULL terminated
// EFI_DEVICE_PATH_PROTOCOL FilePathList[]; <-- FilePathListLength bytes
// UINT8 OptionalData[];
- } EFI_LOAD_OPTION;
- */
+struct load_option {
- u32 attributes;
- u16 file_path_length;
- u16 *label;
- struct efi_device_path *file_path;
- u8 *optional_data;
+};
+static void parse_load_option(struct load_option *lo, void *ptr) +{
- lo->attributes = *(u32 *)ptr;
- ptr += sizeof(u32);
- lo->file_path_length = *(u16 *)ptr;
- ptr += sizeof(u16);
- lo->label = ptr;
- ptr += (utf16_strlen(lo->label) + 1) * 2;
- lo->file_path = ptr;
- ptr += lo->file_path_length;
- lo->optional_data = ptr;
+}
+/* free() the result */ +static void *get_var(u16 *name, const efi_guid_t *vendor,
unsigned long *size)
+{
- efi_guid_t *v = (efi_guid_t *)vendor;
- efi_status_t ret;
- void *buf = NULL;
- *size = 0;
- EFI_CALL(ret = rs->get_variable((s16 *)name, v, NULL, size, buf));
- if (ret == EFI_BUFFER_TOO_SMALL) {
buf = malloc(*size);
EFI_CALL(ret = rs->get_variable((s16 *)name, v, NULL, size, buf));
- }
- if (ret != EFI_SUCCESS) {
free(buf);
*size = 0;
return NULL;
- }
- return buf;
+}
+static void *try_load_entry(uint16_t n, struct efi_device_path **device_path,
struct efi_device_path **file_path)
+{
- struct load_option lo;
- u16 varname[] = L"Boot0000";
- u16 hexmap[] = L"0123456789ABCDEF";
- void *load_option, *image = NULL;
- unsigned long size;
- varname[4] = hexmap[(n & 0xf000) >> 12];
- varname[5] = hexmap[(n & 0x0f00) >> 8];
- varname[6] = hexmap[(n & 0x00f0) >> 4];
- varname[7] = hexmap[(n & 0x000f) >> 0];
- load_option = get_var(varname, &efi_global_variable_guid, &size);
- if (!load_option)
return NULL;
- parse_load_option(&lo, load_option);
- if (lo.attributes & LOAD_OPTION_ACTIVE) {
efi_status_t ret;
u16 *str = NULL;
debug("%s: trying to load \"%ls\" from: %ls\n", __func__,
lo.label, (str = efi_dp_str(lo.file_path)));
efi_free_pool(str);
ret = efi_load_image_from_path(lo.file_path, &image);
if (ret != EFI_SUCCESS)
goto error;
printf("Booting: %ls\n", lo.label);
efi_dp_split_file_path(lo.file_path, device_path, file_path);
- }
+error:
- free(load_option);
- return image;
+}
+void *efi_bootmgr_load(struct efi_device_path **device_path,
struct efi_device_path **file_path)
+{
- uint16_t *bootorder;
- unsigned long size;
- void *image = NULL;
- int i, num;
- __efi_entry_check();
- bs = systab.boottime;
- rs = systab.runtime;
- bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size);
- if (!bootorder)
goto error;
- num = size / sizeof(uint16_t);
- for (i = 0; i < num; i++) {
debug("%s: trying to load Boot%04X\n", __func__, bootorder[i]);
image = try_load_entry(bootorder[i], device_path, file_path);
if (image)
break;
- }
- free(bootorder);
+error:
- __efi_exit_check();
- return image;
+} diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 19eaea8baf..5ff2d2d4b0 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -749,8 +749,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; @@ -808,7 +808,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);
if (ret != EFI_SUCCESS) { free(info); free(obj);ret = efi_load_image_from_path(file_path, &source_buffer);
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 469acae082..242e6a504b 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -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 =

On Fri, Aug 4, 2017 at 4:06 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:32 PM, Rob Clark wrote:
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.)
I wonder if this implementation is Fedora specific.
For Debian I could not find any reference to boot.csv. And it is not mentioned in the UEFI 2.7 spec.
Please, provide the specification that your work is based on.
The references to boot.csv are based on looking at how shim/fallback work.. perhaps that is not standardized. I'll let Peter Jones comment on that if he knows better what windows and other linux distro's do.
The bootmanager implementation is based on UEFI spec (sect 3.1 in v2.7), which does not depend on boot.csv or how shim/fallback program the BootOrder/BootXXXX variables. But simply that they do. I'm not particularly familiar with the boot chain on Debian, it is entirely possible that it works differently. My comments about boot.csv where merely to try to provide context (and are quite possibly misleading on some distro's), but are irrelevant to the bootmgr implementation which only cares about BootOrder/BootXXXX, as described in sect 3.1.
(There are a lot of details that I skipped over in the bootmgr implementation, simply because secure boot or setting of efi variables from the OS is not implemented, so they are not yet relevant.)
BR, -R
Best regards
Heinrich
Signed-off-by: Rob Clark robdclark@gmail.com
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 | 169 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_boottime.c | 6 +- lib/efi_loader/efi_image_loader.c | 1 + 8 files changed, 235 insertions(+), 6 deletions(-) create mode 100644 lib/efi_loader/efi_bootmgr.c
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 80f52e9e35..02a0dd159b 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -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);
diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h index d8dab8e46a..94ccab02d2 100644 --- a/include/config_distro_bootcmd.h +++ b/include/config_distro_bootcmd.h @@ -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 " \
diff --git a/include/efi_api.h b/include/efi_api.h index 1a542846b3..ef91e34c7b 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -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) diff --git a/include/efi_loader.h b/include/efi_loader.h index f14fdfa58e..e3eb932f54 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -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; @@ -219,6 +220,8 @@ efi_status_t efi_get_protocol(struct efi_object *efiobj, 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; @@ -306,6 +309,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 */ diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index f58cb13337..930c0e218e 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -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 diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c new file mode 100644 index 0000000000..8246ddd48f --- /dev/null +++ b/lib/efi_loader/efi_bootmgr.c @@ -0,0 +1,169 @@ +/*
- EFI utils
- Copyright (c) 2017 Rob Clark
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <charset.h> +#include <malloc.h> +#include <efi_loader.h>
+static const struct efi_boot_services *bs; +static const struct efi_runtime_services *rs;
+#define LOAD_OPTION_ACTIVE 0x00000001 +#define LOAD_OPTION_FORCE_RECONNECT 0x00000002 +#define LOAD_OPTION_HIDDEN 0x00000008
+/*
- bootmgr implements the logic of trying to find a payload to boot
- based on the BootOrder + BootXXXX variables, and then loading it.
- TODO detecting a special key held (f9?) and displaying a boot menu
- like you would get on a PC would be clever.
- TODO if we had a way to write and persist variables after the OS
- has started, we'd also want to check OsIndications to see if we
- should do normal or recovery boot.
- */
+/*
- See section 3.1.3 in the v2.7 UEFI spec for more details on
- the layout of EFI_LOAD_OPTION. In short it is:
- typedef struct _EFI_LOAD_OPTION {
UINT32 Attributes;
UINT16 FilePathListLength;
// CHAR16 Description[]; <-- variable length, NULL terminated
// EFI_DEVICE_PATH_PROTOCOL FilePathList[]; <-- FilePathListLength bytes
// UINT8 OptionalData[];
- } EFI_LOAD_OPTION;
- */
+struct load_option {
u32 attributes;
u16 file_path_length;
u16 *label;
struct efi_device_path *file_path;
u8 *optional_data;
+};
+static void parse_load_option(struct load_option *lo, void *ptr) +{
lo->attributes = *(u32 *)ptr;
ptr += sizeof(u32);
lo->file_path_length = *(u16 *)ptr;
ptr += sizeof(u16);
lo->label = ptr;
ptr += (utf16_strlen(lo->label) + 1) * 2;
lo->file_path = ptr;
ptr += lo->file_path_length;
lo->optional_data = ptr;
+}
+/* free() the result */ +static void *get_var(u16 *name, const efi_guid_t *vendor,
unsigned long *size)
+{
efi_guid_t *v = (efi_guid_t *)vendor;
efi_status_t ret;
void *buf = NULL;
*size = 0;
EFI_CALL(ret = rs->get_variable((s16 *)name, v, NULL, size, buf));
if (ret == EFI_BUFFER_TOO_SMALL) {
buf = malloc(*size);
EFI_CALL(ret = rs->get_variable((s16 *)name, v, NULL, size, buf));
}
if (ret != EFI_SUCCESS) {
free(buf);
*size = 0;
return NULL;
}
return buf;
+}
+static void *try_load_entry(uint16_t n, struct efi_device_path **device_path,
struct efi_device_path **file_path)
+{
struct load_option lo;
u16 varname[] = L"Boot0000";
u16 hexmap[] = L"0123456789ABCDEF";
void *load_option, *image = NULL;
unsigned long size;
varname[4] = hexmap[(n & 0xf000) >> 12];
varname[5] = hexmap[(n & 0x0f00) >> 8];
varname[6] = hexmap[(n & 0x00f0) >> 4];
varname[7] = hexmap[(n & 0x000f) >> 0];
load_option = get_var(varname, &efi_global_variable_guid, &size);
if (!load_option)
return NULL;
parse_load_option(&lo, load_option);
if (lo.attributes & LOAD_OPTION_ACTIVE) {
efi_status_t ret;
u16 *str = NULL;
debug("%s: trying to load \"%ls\" from: %ls\n", __func__,
lo.label, (str = efi_dp_str(lo.file_path)));
efi_free_pool(str);
ret = efi_load_image_from_path(lo.file_path, &image);
if (ret != EFI_SUCCESS)
goto error;
printf("Booting: %ls\n", lo.label);
efi_dp_split_file_path(lo.file_path, device_path, file_path);
}
+error:
free(load_option);
return image;
+}
+void *efi_bootmgr_load(struct efi_device_path **device_path,
struct efi_device_path **file_path)
+{
uint16_t *bootorder;
unsigned long size;
void *image = NULL;
int i, num;
__efi_entry_check();
bs = systab.boottime;
rs = systab.runtime;
bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size);
if (!bootorder)
goto error;
num = size / sizeof(uint16_t);
for (i = 0; i < num; i++) {
debug("%s: trying to load Boot%04X\n", __func__, bootorder[i]);
image = try_load_entry(bootorder[i], device_path, file_path);
if (image)
break;
}
free(bootorder);
+error:
__efi_exit_check();
return image;
+} diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 19eaea8baf..5ff2d2d4b0 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -749,8 +749,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; @@ -808,7 +808,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);
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 469acae082..242e6a504b 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -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 =

On Fri, Aug 4, 2017 at 4:28 PM, Rob Clark robdclark@gmail.com wrote:
On Fri, Aug 4, 2017 at 4:06 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:32 PM, Rob Clark wrote:
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.)
I wonder if this implementation is Fedora specific.
For Debian I could not find any reference to boot.csv. And it is not mentioned in the UEFI 2.7 spec.
Please, provide the specification that your work is based on.
The references to boot.csv are based on looking at how shim/fallback work.. perhaps that is not standardized. I'll let Peter Jones comment on that if he knows better what windows and other linux distro's do.
The bootmanager implementation is based on UEFI spec (sect 3.1 in v2.7), which does not depend on boot.csv or how shim/fallback program the BootOrder/BootXXXX variables. But simply that they do. I'm not particularly familiar with the boot chain on Debian, it is entirely possible that it works differently. My comments about boot.csv where merely to try to provide context (and are quite possibly misleading on some distro's), but are irrelevant to the bootmgr implementation which only cares about BootOrder/BootXXXX, as described in sect 3.1.
(There are a lot of details that I skipped over in the bootmgr implementation, simply because secure boot or setting of efi variables from the OS is not implemented, so they are not yet relevant.)
and in case it wasn't clear, that means the implementation is not fedora specific, even if my description in the cover letter is.
BR, -R
BR, -R
Best regards
Heinrich
Signed-off-by: Rob Clark robdclark@gmail.com
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 | 169 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_boottime.c | 6 +- lib/efi_loader/efi_image_loader.c | 1 + 8 files changed, 235 insertions(+), 6 deletions(-) create mode 100644 lib/efi_loader/efi_bootmgr.c
diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 80f52e9e35..02a0dd159b 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -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);
diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h index d8dab8e46a..94ccab02d2 100644 --- a/include/config_distro_bootcmd.h +++ b/include/config_distro_bootcmd.h @@ -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 " \
diff --git a/include/efi_api.h b/include/efi_api.h index 1a542846b3..ef91e34c7b 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -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) diff --git a/include/efi_loader.h b/include/efi_loader.h index f14fdfa58e..e3eb932f54 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -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; @@ -219,6 +220,8 @@ efi_status_t efi_get_protocol(struct efi_object *efiobj, 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; @@ -306,6 +309,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 */ diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index f58cb13337..930c0e218e 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -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 diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c new file mode 100644 index 0000000000..8246ddd48f --- /dev/null +++ b/lib/efi_loader/efi_bootmgr.c @@ -0,0 +1,169 @@ +/*
- EFI utils
- Copyright (c) 2017 Rob Clark
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <charset.h> +#include <malloc.h> +#include <efi_loader.h>
+static const struct efi_boot_services *bs; +static const struct efi_runtime_services *rs;
+#define LOAD_OPTION_ACTIVE 0x00000001 +#define LOAD_OPTION_FORCE_RECONNECT 0x00000002 +#define LOAD_OPTION_HIDDEN 0x00000008
+/*
- bootmgr implements the logic of trying to find a payload to boot
- based on the BootOrder + BootXXXX variables, and then loading it.
- TODO detecting a special key held (f9?) and displaying a boot menu
- like you would get on a PC would be clever.
- TODO if we had a way to write and persist variables after the OS
- has started, we'd also want to check OsIndications to see if we
- should do normal or recovery boot.
- */
+/*
- See section 3.1.3 in the v2.7 UEFI spec for more details on
- the layout of EFI_LOAD_OPTION. In short it is:
- typedef struct _EFI_LOAD_OPTION {
UINT32 Attributes;
UINT16 FilePathListLength;
// CHAR16 Description[]; <-- variable length, NULL terminated
// EFI_DEVICE_PATH_PROTOCOL FilePathList[]; <-- FilePathListLength bytes
// UINT8 OptionalData[];
- } EFI_LOAD_OPTION;
- */
+struct load_option {
u32 attributes;
u16 file_path_length;
u16 *label;
struct efi_device_path *file_path;
u8 *optional_data;
+};
+static void parse_load_option(struct load_option *lo, void *ptr) +{
lo->attributes = *(u32 *)ptr;
ptr += sizeof(u32);
lo->file_path_length = *(u16 *)ptr;
ptr += sizeof(u16);
lo->label = ptr;
ptr += (utf16_strlen(lo->label) + 1) * 2;
lo->file_path = ptr;
ptr += lo->file_path_length;
lo->optional_data = ptr;
+}
+/* free() the result */ +static void *get_var(u16 *name, const efi_guid_t *vendor,
unsigned long *size)
+{
efi_guid_t *v = (efi_guid_t *)vendor;
efi_status_t ret;
void *buf = NULL;
*size = 0;
EFI_CALL(ret = rs->get_variable((s16 *)name, v, NULL, size, buf));
if (ret == EFI_BUFFER_TOO_SMALL) {
buf = malloc(*size);
EFI_CALL(ret = rs->get_variable((s16 *)name, v, NULL, size, buf));
}
if (ret != EFI_SUCCESS) {
free(buf);
*size = 0;
return NULL;
}
return buf;
+}
+static void *try_load_entry(uint16_t n, struct efi_device_path **device_path,
struct efi_device_path **file_path)
+{
struct load_option lo;
u16 varname[] = L"Boot0000";
u16 hexmap[] = L"0123456789ABCDEF";
void *load_option, *image = NULL;
unsigned long size;
varname[4] = hexmap[(n & 0xf000) >> 12];
varname[5] = hexmap[(n & 0x0f00) >> 8];
varname[6] = hexmap[(n & 0x00f0) >> 4];
varname[7] = hexmap[(n & 0x000f) >> 0];
load_option = get_var(varname, &efi_global_variable_guid, &size);
if (!load_option)
return NULL;
parse_load_option(&lo, load_option);
if (lo.attributes & LOAD_OPTION_ACTIVE) {
efi_status_t ret;
u16 *str = NULL;
debug("%s: trying to load \"%ls\" from: %ls\n", __func__,
lo.label, (str = efi_dp_str(lo.file_path)));
efi_free_pool(str);
ret = efi_load_image_from_path(lo.file_path, &image);
if (ret != EFI_SUCCESS)
goto error;
printf("Booting: %ls\n", lo.label);
efi_dp_split_file_path(lo.file_path, device_path, file_path);
}
+error:
free(load_option);
return image;
+}
+void *efi_bootmgr_load(struct efi_device_path **device_path,
struct efi_device_path **file_path)
+{
uint16_t *bootorder;
unsigned long size;
void *image = NULL;
int i, num;
__efi_entry_check();
bs = systab.boottime;
rs = systab.runtime;
bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size);
if (!bootorder)
goto error;
num = size / sizeof(uint16_t);
for (i = 0; i < num; i++) {
debug("%s: trying to load Boot%04X\n", __func__, bootorder[i]);
image = try_load_entry(bootorder[i], device_path, file_path);
if (image)
break;
}
free(bootorder);
+error:
__efi_exit_check();
return image;
+} diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 19eaea8baf..5ff2d2d4b0 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -749,8 +749,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; @@ -808,7 +808,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);
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 469acae082..242e6a504b 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -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 =

On Fri, Aug 04, 2017 at 04:28:05PM -0400, Rob Clark wrote:
On Fri, Aug 4, 2017 at 4:06 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/04/2017 09:32 PM, Rob Clark wrote:
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.)
I wonder if this implementation is Fedora specific.
For Debian I could not find any reference to boot.csv. And it is not mentioned in the UEFI 2.7 spec.
Please, provide the specification that your work is based on.
The references to boot.csv are based on looking at how shim/fallback work.. perhaps that is not standardized. I'll let Peter Jones comment on that if he knows better what windows and other linux distro's do.
The boot.csv parts are part of fallback.efi, which is part of shim. It's not distro specific, though not every distro uses it (though I thought debian had started doing so very recently), and fallback.efi + boot.csv is still strictly optional. But as Rob says below - the boot methodology fallback employs is based on what the spec says the boot manager will do. A distro could also just stick a second copy of their normal bootloader in /EFI/BOOT/BOOTAA64.EFI and it would boot through the recovery path every time until somebody manually created a boot variable. fallback is just shim's method of setting the variables automatically, and it is fairly widely deployed. I'm pretty sure SuSE and Ubuntu both use it, for example.
The bootmanager implementation is based on UEFI spec (sect 3.1 in v2.7), which does not depend on boot.csv or how shim/fallback program the BootOrder/BootXXXX variables. But simply that they do. I'm not particularly familiar with the boot chain on Debian, it is entirely possible that it works differently. My comments about boot.csv where merely to try to provide context (and are quite possibly misleading on some distro's), but are irrelevant to the bootmgr implementation which only cares about BootOrder/BootXXXX, as described in sect 3.1.
(There are a lot of details that I skipped over in the bootmgr implementation, simply because secure boot or setting of efi variables from the OS is not implemented, so they are not yet relevant.)
This is still all correct.

Some arch's have trouble with unaligned accesses. Technically EFI device-path structs should be byte aligned, and the next node in the path starts immediately after the previous. Meaning that a pointer to an 'struct efi_device_path' is not necessarily word aligned. See section 10.3.1 in v2.7 of UEFI spec.
This causes problems not just for u-boot, but also most/all EFI payloads loaded by u-boot on these archs. Fortunately the common practice for traversing a device path is to rely on the length field in the header, rather than the specified length of the particular device path type+subtype. So the EFI_DP_PAD() macro will add the specified number of bytes to the tail of device path structs to pad them to word alignment.
Technically this is non-compliant, BROKEN_UNALIGNED should *only* be defined on archs that cannot do unaligned accesses.
Signed-off-by: Rob Clark robdclark@gmail.com --- I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED
Mark, this is untested but I think it should solve your crash on the Banana Pi. Could you give it a try when you get a chance?
arch/arm/config.mk | 2 +- include/efi_api.h | 30 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_device_path.c | 3 +++ 3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 1a77779db4..067dc93a9d 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ $(call cc-option,-arm-use-movt=0,) PLATFORM_RELFLAGS += $(LLVM_RELFLAGS)
-PLATFORM_CPPFLAGS += -D__ARM__ +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED
ifdef CONFIG_ARM64 PLATFORM_ELFFLAGS += -B aarch64 -O elf64-littleaarch64 diff --git a/include/efi_api.h b/include/efi_api.h index ef91e34c7b..ddd1e6100a 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -284,6 +284,31 @@ struct efi_loaded_image { #define DEVICE_PATH_TYPE_END 0x7f # define DEVICE_PATH_SUB_TYPE_END 0xff
+/* + * Some arch's have trouble with unaligned accesses. Technically + * EFI device-path structs should be byte aligned, and the next node + * in the path starts immediately after the previous. Meaning that + * a pointer to an 'struct efi_device_path' is not necessarily word + * aligned. See section 10.3.1 in v2.7 of UEFI spec. + * + * This causes problems not just for u-boot, but also most/all EFI + * payloads loaded by u-boot on these archs. Fortunately the common + * practice for traversing a device path is to rely on the length + * field in the header, rather than the specified length of the + * particular device path type+subtype. So the EFI_DP_PAD() macro + * will add the specified number of bytes to the tail of device path + * structs to pad them to word alignment. + * + * Technically this is non-compliant, BROKEN_UNALIGNED should *only* + * be defined on archs that cannot do unaligned accesses. + */ + +#ifdef BROKEN_UNALIGNED +# define EFI_DP_PAD(n) u8 __pad[n] +#else +# define EFI_DP_PAD(n) +#endif + struct efi_device_path { u8 type; u8 sub_type; @@ -318,12 +343,14 @@ struct efi_device_path_usb { struct efi_device_path dp; u8 parent_port_number; u8 usb_interface; + EFI_DP_PAD(2); } __packed;
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type; + EFI_DP_PAD(3); } __packed;
struct efi_device_path_usb_class { @@ -333,11 +360,13 @@ struct efi_device_path_usb_class { u8 device_class; u8 device_subclass; u8 device_protocol; + EFI_DP_PAD(1); } __packed;
struct efi_device_path_sd_mmc_path { struct efi_device_path dp; u8 slot_number; + EFI_DP_PAD(3); } __packed;
#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04 @@ -353,6 +382,7 @@ struct efi_device_path_hard_drive_path { u8 partition_signature[16]; u8 partmap_type; u8 signature_type; + EFI_DP_PAD(1); } __packed;
struct efi_device_path_cdrom_path { diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index b5acf73f98..515a1f4737 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -402,6 +402,9 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
// TODO efi_device_path_file_path should be variable length: fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1); +#ifdef BROKEN_UNALIGNED + fpsize = ALIGN(fpsize, 4); +#endif dpsize += fpsize;
start = buf = calloc(1, dpsize + sizeof(END));

On 08/05/2017 05:58 PM, Rob Clark wrote:
Some arch's have trouble with unaligned accesses. Technically EFI device-path structs should be byte aligned, and the next node in the path starts immediately after the previous. Meaning that a pointer to an 'struct efi_device_path' is not necessarily word aligned. See section 10.3.1 in v2.7 of UEFI spec.
This causes problems not just for u-boot, but also most/all EFI payloads loaded by u-boot on these archs. Fortunately the common practice for traversing a device path is to rely on the length field in the header, rather than the specified length of the particular device path type+subtype. So the EFI_DP_PAD() macro will add the specified number of bytes to the tail of device path structs to pad them to word alignment.
Technically this is non-compliant, BROKEN_UNALIGNED should *only* be defined on archs that cannot do unaligned accesses.
Signed-off-by: Rob Clark robdclark@gmail.com
I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED
Mark, this is untested but I think it should solve your crash on the Banana Pi. Could you give it a try when you get a chance?
arch/arm/config.mk | 2 +- include/efi_api.h | 30 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_device_path.c | 3 +++ 3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 1a77779db4..067dc93a9d 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ $(call cc-option,-arm-use-movt=0,) PLATFORM_RELFLAGS += $(LLVM_RELFLAGS)
-PLATFORM_CPPFLAGS += -D__ARM__ +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED
NAK
We have more then ARM. And other architectures also create exceptions for unaligned access.
I hate platform specific code. It should not be used outside /arch.
To play it save you should not use _packed at all! Use memcpy to transfer between aligned and unaligned memory.
Best regards
Heinrich
ifdef CONFIG_ARM64 PLATFORM_ELFFLAGS += -B aarch64 -O elf64-littleaarch64 diff --git a/include/efi_api.h b/include/efi_api.h index ef91e34c7b..ddd1e6100a 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -284,6 +284,31 @@ struct efi_loaded_image { #define DEVICE_PATH_TYPE_END 0x7f # define DEVICE_PATH_SUB_TYPE_END 0xff
+/*
- Some arch's have trouble with unaligned accesses. Technically
- EFI device-path structs should be byte aligned, and the next node
- in the path starts immediately after the previous. Meaning that
- a pointer to an 'struct efi_device_path' is not necessarily word
- aligned. See section 10.3.1 in v2.7 of UEFI spec.
- This causes problems not just for u-boot, but also most/all EFI
- payloads loaded by u-boot on these archs. Fortunately the common
- practice for traversing a device path is to rely on the length
- field in the header, rather than the specified length of the
- particular device path type+subtype. So the EFI_DP_PAD() macro
- will add the specified number of bytes to the tail of device path
- structs to pad them to word alignment.
- Technically this is non-compliant, BROKEN_UNALIGNED should *only*
- be defined on archs that cannot do unaligned accesses.
- */
+#ifdef BROKEN_UNALIGNED +# define EFI_DP_PAD(n) u8 __pad[n] +#else +# define EFI_DP_PAD(n) +#endif
struct efi_device_path { u8 type; u8 sub_type; @@ -318,12 +343,14 @@ struct efi_device_path_usb { struct efi_device_path dp; u8 parent_port_number; u8 usb_interface;
- EFI_DP_PAD(2);
} __packed;
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type;
- EFI_DP_PAD(3);
} __packed;
struct efi_device_path_usb_class { @@ -333,11 +360,13 @@ struct efi_device_path_usb_class { u8 device_class; u8 device_subclass; u8 device_protocol;
- EFI_DP_PAD(1);
} __packed;
struct efi_device_path_sd_mmc_path { struct efi_device_path dp; u8 slot_number;
- EFI_DP_PAD(3);
} __packed;
#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04 @@ -353,6 +382,7 @@ struct efi_device_path_hard_drive_path { u8 partition_signature[16]; u8 partmap_type; u8 signature_type;
- EFI_DP_PAD(1);
} __packed;
struct efi_device_path_cdrom_path { diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index b5acf73f98..515a1f4737 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -402,6 +402,9 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
// TODO efi_device_path_file_path should be variable length: fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1); +#ifdef BROKEN_UNALIGNED
- fpsize = ALIGN(fpsize, 4);
+#endif dpsize += fpsize;
start = buf = calloc(1, dpsize + sizeof(END));

On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 05:58 PM, Rob Clark wrote:
Some arch's have trouble with unaligned accesses. Technically EFI device-path structs should be byte aligned, and the next node in the path starts immediately after the previous. Meaning that a pointer to an 'struct efi_device_path' is not necessarily word aligned. See section 10.3.1 in v2.7 of UEFI spec.
This causes problems not just for u-boot, but also most/all EFI payloads loaded by u-boot on these archs. Fortunately the common practice for traversing a device path is to rely on the length field in the header, rather than the specified length of the particular device path type+subtype. So the EFI_DP_PAD() macro will add the specified number of bytes to the tail of device path structs to pad them to word alignment.
Technically this is non-compliant, BROKEN_UNALIGNED should *only* be defined on archs that cannot do unaligned accesses.
Signed-off-by: Rob Clark robdclark@gmail.com
I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED
Mark, this is untested but I think it should solve your crash on the Banana Pi. Could you give it a try when you get a chance?
arch/arm/config.mk | 2 +- include/efi_api.h | 30 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_device_path.c | 3 +++ 3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 1a77779db4..067dc93a9d 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ $(call cc-option,-arm-use-movt=0,) PLATFORM_RELFLAGS += $(LLVM_RELFLAGS)
-PLATFORM_CPPFLAGS += -D__ARM__ +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED
NAK
We have more then ARM. And other architectures also create exceptions for unaligned access.
I hate platform specific code. It should not be used outside /arch.
To play it save you should not use _packed at all! Use memcpy to transfer between aligned and unaligned memory.
except for reasons I explained in the thread on the patch that added the __packed in the first place. Sorry, this is ugly but we have to do it.
BR, -R
Best regards
Heinrich
ifdef CONFIG_ARM64 PLATFORM_ELFFLAGS += -B aarch64 -O elf64-littleaarch64 diff --git a/include/efi_api.h b/include/efi_api.h index ef91e34c7b..ddd1e6100a 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -284,6 +284,31 @@ struct efi_loaded_image { #define DEVICE_PATH_TYPE_END 0x7f # define DEVICE_PATH_SUB_TYPE_END 0xff
+/*
- Some arch's have trouble with unaligned accesses. Technically
- EFI device-path structs should be byte aligned, and the next node
- in the path starts immediately after the previous. Meaning that
- a pointer to an 'struct efi_device_path' is not necessarily word
- aligned. See section 10.3.1 in v2.7 of UEFI spec.
- This causes problems not just for u-boot, but also most/all EFI
- payloads loaded by u-boot on these archs. Fortunately the common
- practice for traversing a device path is to rely on the length
- field in the header, rather than the specified length of the
- particular device path type+subtype. So the EFI_DP_PAD() macro
- will add the specified number of bytes to the tail of device path
- structs to pad them to word alignment.
- Technically this is non-compliant, BROKEN_UNALIGNED should *only*
- be defined on archs that cannot do unaligned accesses.
- */
+#ifdef BROKEN_UNALIGNED +# define EFI_DP_PAD(n) u8 __pad[n] +#else +# define EFI_DP_PAD(n) +#endif
struct efi_device_path { u8 type; u8 sub_type; @@ -318,12 +343,14 @@ struct efi_device_path_usb { struct efi_device_path dp; u8 parent_port_number; u8 usb_interface;
EFI_DP_PAD(2);
} __packed;
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type;
EFI_DP_PAD(3);
} __packed;
struct efi_device_path_usb_class { @@ -333,11 +360,13 @@ struct efi_device_path_usb_class { u8 device_class; u8 device_subclass; u8 device_protocol;
EFI_DP_PAD(1);
} __packed;
struct efi_device_path_sd_mmc_path { struct efi_device_path dp; u8 slot_number;
EFI_DP_PAD(3);
} __packed;
#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04 @@ -353,6 +382,7 @@ struct efi_device_path_hard_drive_path { u8 partition_signature[16]; u8 partmap_type; u8 signature_type;
EFI_DP_PAD(1);
} __packed;
struct efi_device_path_cdrom_path { diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index b5acf73f98..515a1f4737 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -402,6 +402,9 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
// TODO efi_device_path_file_path should be variable length: fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
+#ifdef BROKEN_UNALIGNED
fpsize = ALIGN(fpsize, 4);
+#endif dpsize += fpsize;
start = buf = calloc(1, dpsize + sizeof(END));

On Sat, Aug 5, 2017 at 12:16 PM, Rob Clark robdclark@gmail.com wrote:
On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 05:58 PM, Rob Clark wrote:
Some arch's have trouble with unaligned accesses. Technically EFI device-path structs should be byte aligned, and the next node in the path starts immediately after the previous. Meaning that a pointer to an 'struct efi_device_path' is not necessarily word aligned. See section 10.3.1 in v2.7 of UEFI spec.
This causes problems not just for u-boot, but also most/all EFI payloads loaded by u-boot on these archs. Fortunately the common practice for traversing a device path is to rely on the length field in the header, rather than the specified length of the particular device path type+subtype. So the EFI_DP_PAD() macro will add the specified number of bytes to the tail of device path structs to pad them to word alignment.
Technically this is non-compliant, BROKEN_UNALIGNED should *only* be defined on archs that cannot do unaligned accesses.
Signed-off-by: Rob Clark robdclark@gmail.com
I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED
Mark, this is untested but I think it should solve your crash on the Banana Pi. Could you give it a try when you get a chance?
arch/arm/config.mk | 2 +- include/efi_api.h | 30 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_device_path.c | 3 +++ 3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 1a77779db4..067dc93a9d 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ $(call cc-option,-arm-use-movt=0,) PLATFORM_RELFLAGS += $(LLVM_RELFLAGS)
-PLATFORM_CPPFLAGS += -D__ARM__ +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED
NAK
We have more then ARM. And other architectures also create exceptions for unaligned access.
I hate platform specific code. It should not be used outside /arch.
To play it save you should not use _packed at all! Use memcpy to transfer between aligned and unaligned memory.
except for reasons I explained in the thread on the patch that added the __packed in the first place. Sorry, this is ugly but we have to do it.
well, to be fair, we don't *have* to do it. The alternative is disable EFI_LOADER on archs that cannot do unaligned accesses. But this seemed like the better option.
BR, -R
BR, -R
Best regards
Heinrich
ifdef CONFIG_ARM64 PLATFORM_ELFFLAGS += -B aarch64 -O elf64-littleaarch64 diff --git a/include/efi_api.h b/include/efi_api.h index ef91e34c7b..ddd1e6100a 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -284,6 +284,31 @@ struct efi_loaded_image { #define DEVICE_PATH_TYPE_END 0x7f # define DEVICE_PATH_SUB_TYPE_END 0xff
+/*
- Some arch's have trouble with unaligned accesses. Technically
- EFI device-path structs should be byte aligned, and the next node
- in the path starts immediately after the previous. Meaning that
- a pointer to an 'struct efi_device_path' is not necessarily word
- aligned. See section 10.3.1 in v2.7 of UEFI spec.
- This causes problems not just for u-boot, but also most/all EFI
- payloads loaded by u-boot on these archs. Fortunately the common
- practice for traversing a device path is to rely on the length
- field in the header, rather than the specified length of the
- particular device path type+subtype. So the EFI_DP_PAD() macro
- will add the specified number of bytes to the tail of device path
- structs to pad them to word alignment.
- Technically this is non-compliant, BROKEN_UNALIGNED should *only*
- be defined on archs that cannot do unaligned accesses.
- */
+#ifdef BROKEN_UNALIGNED +# define EFI_DP_PAD(n) u8 __pad[n] +#else +# define EFI_DP_PAD(n) +#endif
struct efi_device_path { u8 type; u8 sub_type; @@ -318,12 +343,14 @@ struct efi_device_path_usb { struct efi_device_path dp; u8 parent_port_number; u8 usb_interface;
EFI_DP_PAD(2);
} __packed;
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type;
EFI_DP_PAD(3);
} __packed;
struct efi_device_path_usb_class { @@ -333,11 +360,13 @@ struct efi_device_path_usb_class { u8 device_class; u8 device_subclass; u8 device_protocol;
EFI_DP_PAD(1);
} __packed;
struct efi_device_path_sd_mmc_path { struct efi_device_path dp; u8 slot_number;
EFI_DP_PAD(3);
} __packed;
#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04 @@ -353,6 +382,7 @@ struct efi_device_path_hard_drive_path { u8 partition_signature[16]; u8 partmap_type; u8 signature_type;
EFI_DP_PAD(1);
} __packed;
struct efi_device_path_cdrom_path { diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index b5acf73f98..515a1f4737 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -402,6 +402,9 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
// TODO efi_device_path_file_path should be variable length: fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
+#ifdef BROKEN_UNALIGNED
fpsize = ALIGN(fpsize, 4);
+#endif dpsize += fpsize;
start = buf = calloc(1, dpsize + sizeof(END));

On 08/05/2017 06:19 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 12:16 PM, Rob Clark robdclark@gmail.com wrote:
On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 05:58 PM, Rob Clark wrote:
Some arch's have trouble with unaligned accesses. Technically EFI device-path structs should be byte aligned, and the next node in the path starts immediately after the previous. Meaning that a pointer to an 'struct efi_device_path' is not necessarily word aligned. See section 10.3.1 in v2.7 of UEFI spec.
This causes problems not just for u-boot, but also most/all EFI payloads loaded by u-boot on these archs. Fortunately the common practice for traversing a device path is to rely on the length field in the header, rather than the specified length of the particular device path type+subtype. So the EFI_DP_PAD() macro will add the specified number of bytes to the tail of device path structs to pad them to word alignment.
Technically this is non-compliant, BROKEN_UNALIGNED should *only* be defined on archs that cannot do unaligned accesses.
Signed-off-by: Rob Clark robdclark@gmail.com
I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED
Mark, this is untested but I think it should solve your crash on the Banana Pi. Could you give it a try when you get a chance?
arch/arm/config.mk | 2 +- include/efi_api.h | 30 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_device_path.c | 3 +++ 3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 1a77779db4..067dc93a9d 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ $(call cc-option,-arm-use-movt=0,) PLATFORM_RELFLAGS += $(LLVM_RELFLAGS)
-PLATFORM_CPPFLAGS += -D__ARM__ +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED
NAK
We have more then ARM. And other architectures also create exceptions for unaligned access.
I hate platform specific code. It should not be used outside /arch.
To play it save you should not use _packed at all! Use memcpy to transfer between aligned and unaligned memory.
except for reasons I explained in the thread on the patch that added the __packed in the first place. Sorry, this is ugly but we have to do it.
well, to be fair, we don't *have* to do it. The alternative is disable EFI_LOADER on archs that cannot do unaligned accesses. But this seemed like the better option.
In which UEFI protocol do you need the packed structures? Why can't you use memcpy to provide/read the data in these protocols? Why can't you use padding on all architectures?
Best regards
Heinrich
BR, -R
Best regards
Heinrich
ifdef CONFIG_ARM64 PLATFORM_ELFFLAGS += -B aarch64 -O elf64-littleaarch64 diff --git a/include/efi_api.h b/include/efi_api.h index ef91e34c7b..ddd1e6100a 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -284,6 +284,31 @@ struct efi_loaded_image { #define DEVICE_PATH_TYPE_END 0x7f # define DEVICE_PATH_SUB_TYPE_END 0xff
+/*
- Some arch's have trouble with unaligned accesses. Technically
- EFI device-path structs should be byte aligned, and the next node
- in the path starts immediately after the previous. Meaning that
- a pointer to an 'struct efi_device_path' is not necessarily word
- aligned. See section 10.3.1 in v2.7 of UEFI spec.
- This causes problems not just for u-boot, but also most/all EFI
- payloads loaded by u-boot on these archs. Fortunately the common
- practice for traversing a device path is to rely on the length
- field in the header, rather than the specified length of the
- particular device path type+subtype. So the EFI_DP_PAD() macro
- will add the specified number of bytes to the tail of device path
- structs to pad them to word alignment.
- Technically this is non-compliant, BROKEN_UNALIGNED should *only*
- be defined on archs that cannot do unaligned accesses.
- */
+#ifdef BROKEN_UNALIGNED +# define EFI_DP_PAD(n) u8 __pad[n] +#else +# define EFI_DP_PAD(n) +#endif
struct efi_device_path { u8 type; u8 sub_type; @@ -318,12 +343,14 @@ struct efi_device_path_usb { struct efi_device_path dp; u8 parent_port_number; u8 usb_interface;
EFI_DP_PAD(2);
} __packed;
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type;
EFI_DP_PAD(3);
} __packed;
struct efi_device_path_usb_class { @@ -333,11 +360,13 @@ struct efi_device_path_usb_class { u8 device_class; u8 device_subclass; u8 device_protocol;
EFI_DP_PAD(1);
} __packed;
struct efi_device_path_sd_mmc_path { struct efi_device_path dp; u8 slot_number;
EFI_DP_PAD(3);
} __packed;
#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04 @@ -353,6 +382,7 @@ struct efi_device_path_hard_drive_path { u8 partition_signature[16]; u8 partmap_type; u8 signature_type;
EFI_DP_PAD(1);
} __packed;
struct efi_device_path_cdrom_path { diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index b5acf73f98..515a1f4737 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -402,6 +402,9 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
// TODO efi_device_path_file_path should be variable length: fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
+#ifdef BROKEN_UNALIGNED
fpsize = ALIGN(fpsize, 4);
+#endif dpsize += fpsize;
start = buf = calloc(1, dpsize + sizeof(END));

On Sat, Aug 5, 2017 at 12:26 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 06:19 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 12:16 PM, Rob Clark robdclark@gmail.com wrote:
On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 05:58 PM, Rob Clark wrote:
Some arch's have trouble with unaligned accesses. Technically EFI device-path structs should be byte aligned, and the next node in the path starts immediately after the previous. Meaning that a pointer to an 'struct efi_device_path' is not necessarily word aligned. See section 10.3.1 in v2.7 of UEFI spec.
This causes problems not just for u-boot, but also most/all EFI payloads loaded by u-boot on these archs. Fortunately the common practice for traversing a device path is to rely on the length field in the header, rather than the specified length of the particular device path type+subtype. So the EFI_DP_PAD() macro will add the specified number of bytes to the tail of device path structs to pad them to word alignment.
Technically this is non-compliant, BROKEN_UNALIGNED should *only* be defined on archs that cannot do unaligned accesses.
Signed-off-by: Rob Clark robdclark@gmail.com
I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED
Mark, this is untested but I think it should solve your crash on the Banana Pi. Could you give it a try when you get a chance?
arch/arm/config.mk | 2 +- include/efi_api.h | 30 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_device_path.c | 3 +++ 3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 1a77779db4..067dc93a9d 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ $(call cc-option,-arm-use-movt=0,) PLATFORM_RELFLAGS += $(LLVM_RELFLAGS)
-PLATFORM_CPPFLAGS += -D__ARM__ +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED
NAK
We have more then ARM. And other architectures also create exceptions for unaligned access.
I hate platform specific code. It should not be used outside /arch.
To play it save you should not use _packed at all! Use memcpy to transfer between aligned and unaligned memory.
except for reasons I explained in the thread on the patch that added the __packed in the first place. Sorry, this is ugly but we have to do it.
well, to be fair, we don't *have* to do it. The alternative is disable EFI_LOADER on archs that cannot do unaligned accesses. But this seemed like the better option.
In which UEFI protocol do you need the packed structures? Why can't you use memcpy to provide/read the data in these protocols? Why can't you use padding on all architectures?
The device-path protocol, as described in section 10.3 in the UEFI spec. And as I already explained, the problem isn't just u-boot but also all the various efi payloads loaded and executed directly or indirectly by u-boot. Please feel free to send patches to these various projects to fix those problems on armv7/etc. I guess it will take a while to get them all upstream, if they are accepted (really, *why* are we using efi on these archs?) and trickle out into distros.
I'll repeat the section I previously quoted from 10.3.1 in the other email thread:
A Device Path is a series of generic Device Path nodes. The first Device Path node starts at byte offset zero of the Device Path. The next Device Path node starts at the end of the previous Device Path node. Therefore all nodes are byte-packed data structures that may appear on any byte boundary. All code references to device path notes must assume all fields are unaligned. Since every Device Path node contains a length field in a known place, it is possible to traverse Device Path nodes that are of an unknown type. There is no limit to the number, type, or sequence of nodes in a Device Path.
What we were doing before was incorrect. Sorry, but if we are implementing the UEFI spec, we need to implement the UEFI spec, not what we wish the UEFI spec was. I fixed the bug in u-boot, and that exposed the problem in openbsd's bootaa64.efi. But I've spent enough time looking at shim/fallback/grub to say that they probably have the same problem on armv7 (and any other arch's that cannot do unaligned access).
This patch allows us to not break stuff that currently works by accident on these archs, but do things correctly on aarch64 and archs that *can* do unaligned access. This is better than doing things incorrectly on aarch64, and better than breaking archs that relied on this existing bug in u-boot's UEFI implementation.
BR, -R

On 08/05/2017 06:16 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 05:58 PM, Rob Clark wrote:
Some arch's have trouble with unaligned accesses. Technically EFI device-path structs should be byte aligned, and the next node in the path starts immediately after the previous. Meaning that a pointer to an 'struct efi_device_path' is not necessarily word aligned. See section 10.3.1 in v2.7 of UEFI spec.
This causes problems not just for u-boot, but also most/all EFI payloads loaded by u-boot on these archs. Fortunately the common practice for traversing a device path is to rely on the length field in the header, rather than the specified length of the particular device path type+subtype. So the EFI_DP_PAD() macro will add the specified number of bytes to the tail of device path structs to pad them to word alignment.
Technically this is non-compliant, BROKEN_UNALIGNED should *only* be defined on archs that cannot do unaligned accesses.
Signed-off-by: Rob Clark robdclark@gmail.com
I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED
Mark, this is untested but I think it should solve your crash on the Banana Pi. Could you give it a try when you get a chance?
arch/arm/config.mk | 2 +- include/efi_api.h | 30 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_device_path.c | 3 +++ 3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 1a77779db4..067dc93a9d 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ $(call cc-option,-arm-use-movt=0,) PLATFORM_RELFLAGS += $(LLVM_RELFLAGS)
-PLATFORM_CPPFLAGS += -D__ARM__ +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED
NAK
We have more then ARM. And other architectures also create exceptions for unaligned access.
I hate platform specific code. It should not be used outside /arch.
To play it save you should not use _packed at all! Use memcpy to transfer between aligned and unaligned memory.
except for reasons I explained in the thread on the patch that added the __packed in the first place. Sorry, this is ugly but we have to do it.
BR, -R
According to the UEFI standard the nodes in paths are not to be assumed to be aligned.
So even if you use padding bytes in paths that you pass to the EFI application you should not expect that the EFI application does the same. Expect the EFI application either to remove them or send new nodes without padding.
To the idea of padding bytes and __packed does not make sense.
Best regards
Heinrich
Best regards
Heinrich
ifdef CONFIG_ARM64 PLATFORM_ELFFLAGS += -B aarch64 -O elf64-littleaarch64 diff --git a/include/efi_api.h b/include/efi_api.h index ef91e34c7b..ddd1e6100a 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -284,6 +284,31 @@ struct efi_loaded_image { #define DEVICE_PATH_TYPE_END 0x7f # define DEVICE_PATH_SUB_TYPE_END 0xff
+/*
- Some arch's have trouble with unaligned accesses. Technically
- EFI device-path structs should be byte aligned, and the next node
- in the path starts immediately after the previous. Meaning that
- a pointer to an 'struct efi_device_path' is not necessarily word
- aligned. See section 10.3.1 in v2.7 of UEFI spec.
- This causes problems not just for u-boot, but also most/all EFI
- payloads loaded by u-boot on these archs. Fortunately the common
- practice for traversing a device path is to rely on the length
- field in the header, rather than the specified length of the
- particular device path type+subtype. So the EFI_DP_PAD() macro
- will add the specified number of bytes to the tail of device path
- structs to pad them to word alignment.
- Technically this is non-compliant, BROKEN_UNALIGNED should *only*
- be defined on archs that cannot do unaligned accesses.
- */
+#ifdef BROKEN_UNALIGNED +# define EFI_DP_PAD(n) u8 __pad[n] +#else +# define EFI_DP_PAD(n) +#endif
struct efi_device_path { u8 type; u8 sub_type; @@ -318,12 +343,14 @@ struct efi_device_path_usb { struct efi_device_path dp; u8 parent_port_number; u8 usb_interface;
EFI_DP_PAD(2);
} __packed;
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type;
EFI_DP_PAD(3);
} __packed;
struct efi_device_path_usb_class { @@ -333,11 +360,13 @@ struct efi_device_path_usb_class { u8 device_class; u8 device_subclass; u8 device_protocol;
EFI_DP_PAD(1);
} __packed;
struct efi_device_path_sd_mmc_path { struct efi_device_path dp; u8 slot_number;
EFI_DP_PAD(3);
} __packed;
#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04 @@ -353,6 +382,7 @@ struct efi_device_path_hard_drive_path { u8 partition_signature[16]; u8 partmap_type; u8 signature_type;
EFI_DP_PAD(1);
} __packed;
struct efi_device_path_cdrom_path { diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index b5acf73f98..515a1f4737 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -402,6 +402,9 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
// TODO efi_device_path_file_path should be variable length: fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
+#ifdef BROKEN_UNALIGNED
fpsize = ALIGN(fpsize, 4);
+#endif dpsize += fpsize;
start = buf = calloc(1, dpsize + sizeof(END));

On Sat, Aug 5, 2017 at 12:52 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 06:16 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 05:58 PM, Rob Clark wrote:
Some arch's have trouble with unaligned accesses. Technically EFI device-path structs should be byte aligned, and the next node in the path starts immediately after the previous. Meaning that a pointer to an 'struct efi_device_path' is not necessarily word aligned. See section 10.3.1 in v2.7 of UEFI spec.
This causes problems not just for u-boot, but also most/all EFI payloads loaded by u-boot on these archs. Fortunately the common practice for traversing a device path is to rely on the length field in the header, rather than the specified length of the particular device path type+subtype. So the EFI_DP_PAD() macro will add the specified number of bytes to the tail of device path structs to pad them to word alignment.
Technically this is non-compliant, BROKEN_UNALIGNED should *only* be defined on archs that cannot do unaligned accesses.
Signed-off-by: Rob Clark robdclark@gmail.com
I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED
Mark, this is untested but I think it should solve your crash on the Banana Pi. Could you give it a try when you get a chance?
arch/arm/config.mk | 2 +- include/efi_api.h | 30 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_device_path.c | 3 +++ 3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 1a77779db4..067dc93a9d 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ $(call cc-option,-arm-use-movt=0,) PLATFORM_RELFLAGS += $(LLVM_RELFLAGS)
-PLATFORM_CPPFLAGS += -D__ARM__ +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED
NAK
We have more then ARM. And other architectures also create exceptions for unaligned access.
I hate platform specific code. It should not be used outside /arch.
To play it save you should not use _packed at all! Use memcpy to transfer between aligned and unaligned memory.
except for reasons I explained in the thread on the patch that added the __packed in the first place. Sorry, this is ugly but we have to do it.
BR, -R
According to the UEFI standard the nodes in paths are not to be assumed to be aligned.
So even if you use padding bytes in paths that you pass to the EFI application you should not expect that the EFI application does the same. Expect the EFI application either to remove them or send new nodes without padding.
To the idea of padding bytes and __packed does not make sense.
Ok, to be fair, you are right about device-paths passed too u-boot. On BROKEN_UNALIGNED archs, we should sanitise with a copy to an aligned device-path in *addition* to what I proposed. I can make a patch to add a helper to do this a bit later.
But the padding bytes + __packed does make total sense because it avoids breaking efi payloads that already exist in the field. The crash that Mark saw was not in u-boot, but in openbsd's bootaa64.efi. (It is possibly that we just get lucky here in u-boot since I add the /End node after the mac address by something the compiler turns into a memcpy.)
My proposal simply preserves the bug that we already have on BROKEN_UNALIGNED archs (but makes the improvement that it fixes the bug on aarch64 and any other arch that can do unaligned access), to keep existing efi payloads working.
BR, -R
Best regards
Heinrich
Best regards
Heinrich
ifdef CONFIG_ARM64 PLATFORM_ELFFLAGS += -B aarch64 -O elf64-littleaarch64 diff --git a/include/efi_api.h b/include/efi_api.h index ef91e34c7b..ddd1e6100a 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -284,6 +284,31 @@ struct efi_loaded_image { #define DEVICE_PATH_TYPE_END 0x7f # define DEVICE_PATH_SUB_TYPE_END 0xff
+/*
- Some arch's have trouble with unaligned accesses. Technically
- EFI device-path structs should be byte aligned, and the next node
- in the path starts immediately after the previous. Meaning that
- a pointer to an 'struct efi_device_path' is not necessarily word
- aligned. See section 10.3.1 in v2.7 of UEFI spec.
- This causes problems not just for u-boot, but also most/all EFI
- payloads loaded by u-boot on these archs. Fortunately the common
- practice for traversing a device path is to rely on the length
- field in the header, rather than the specified length of the
- particular device path type+subtype. So the EFI_DP_PAD() macro
- will add the specified number of bytes to the tail of device path
- structs to pad them to word alignment.
- Technically this is non-compliant, BROKEN_UNALIGNED should *only*
- be defined on archs that cannot do unaligned accesses.
- */
+#ifdef BROKEN_UNALIGNED +# define EFI_DP_PAD(n) u8 __pad[n] +#else +# define EFI_DP_PAD(n) +#endif
struct efi_device_path { u8 type; u8 sub_type; @@ -318,12 +343,14 @@ struct efi_device_path_usb { struct efi_device_path dp; u8 parent_port_number; u8 usb_interface;
EFI_DP_PAD(2);
} __packed;
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type;
EFI_DP_PAD(3);
} __packed;
struct efi_device_path_usb_class { @@ -333,11 +360,13 @@ struct efi_device_path_usb_class { u8 device_class; u8 device_subclass; u8 device_protocol;
EFI_DP_PAD(1);
} __packed;
struct efi_device_path_sd_mmc_path { struct efi_device_path dp; u8 slot_number;
EFI_DP_PAD(3);
} __packed;
#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04 @@ -353,6 +382,7 @@ struct efi_device_path_hard_drive_path { u8 partition_signature[16]; u8 partmap_type; u8 signature_type;
EFI_DP_PAD(1);
} __packed;
struct efi_device_path_cdrom_path { diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index b5acf73f98..515a1f4737 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -402,6 +402,9 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
// TODO efi_device_path_file_path should be variable length: fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
+#ifdef BROKEN_UNALIGNED
fpsize = ALIGN(fpsize, 4);
+#endif dpsize += fpsize;
start = buf = calloc(1, dpsize + sizeof(END));

On Sat, Aug 5, 2017 at 1:06 PM, Rob Clark robdclark@gmail.com wrote:
On Sat, Aug 5, 2017 at 12:52 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 06:16 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 05:58 PM, Rob Clark wrote:
Some arch's have trouble with unaligned accesses. Technically EFI device-path structs should be byte aligned, and the next node in the path starts immediately after the previous. Meaning that a pointer to an 'struct efi_device_path' is not necessarily word aligned. See section 10.3.1 in v2.7 of UEFI spec.
This causes problems not just for u-boot, but also most/all EFI payloads loaded by u-boot on these archs. Fortunately the common practice for traversing a device path is to rely on the length field in the header, rather than the specified length of the particular device path type+subtype. So the EFI_DP_PAD() macro will add the specified number of bytes to the tail of device path structs to pad them to word alignment.
Technically this is non-compliant, BROKEN_UNALIGNED should *only* be defined on archs that cannot do unaligned accesses.
Signed-off-by: Rob Clark robdclark@gmail.com
I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED
Mark, this is untested but I think it should solve your crash on the Banana Pi. Could you give it a try when you get a chance?
arch/arm/config.mk | 2 +- include/efi_api.h | 30 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_device_path.c | 3 +++ 3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 1a77779db4..067dc93a9d 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ $(call cc-option,-arm-use-movt=0,) PLATFORM_RELFLAGS += $(LLVM_RELFLAGS)
-PLATFORM_CPPFLAGS += -D__ARM__ +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED
NAK
We have more then ARM. And other architectures also create exceptions for unaligned access.
I hate platform specific code. It should not be used outside /arch.
To play it save you should not use _packed at all! Use memcpy to transfer between aligned and unaligned memory.
except for reasons I explained in the thread on the patch that added the __packed in the first place. Sorry, this is ugly but we have to do it.
BR, -R
According to the UEFI standard the nodes in paths are not to be assumed to be aligned.
So even if you use padding bytes in paths that you pass to the EFI application you should not expect that the EFI application does the same. Expect the EFI application either to remove them or send new nodes without padding.
To the idea of padding bytes and __packed does not make sense.
Ok, to be fair, you are right about device-paths passed too u-boot. On BROKEN_UNALIGNED archs, we should sanitise with a copy to an aligned device-path in *addition* to what I proposed. I can make a patch to add a helper to do this a bit later.
so thinking about this a bit, I have two options in mind:
+ efi_dp_sanitize() + efi_dp_free() helpers that are no-ops on archs that can do unaligned access, but efi_dp_sanitize() always allocates and copies on BROKEN_UNALIGNED archs and efi_dp_free() always free's on BROKEN_UNALIGNED archs, even if the dp passed from efi payload doesn't require it.
+ efi_dp_sanitize() that is no-op on archs that can do unaligned access but only allocates/copies when passed a device path that would result in unaligned access, plus hook some mechanism to auto-free on EFI_EXIT().. which is slightly tricky w/ nesting of efi calls, but not impossible and at least avoids the problem of missing calls to free the dup'd device-path.. which is the sort of leak I might miss since I'm not using EFI_LOADER on any BROKEN_UNALIGNED arch
anyone who cares about armv7 + efi have a preference between always doing an extra alloc+copy+free vs EFI_EXIT() magic?
BR, -R
But the padding bytes + __packed does make total sense because it avoids breaking efi payloads that already exist in the field. The crash that Mark saw was not in u-boot, but in openbsd's bootaa64.efi. (It is possibly that we just get lucky here in u-boot since I add the /End node after the mac address by something the compiler turns into a memcpy.)
My proposal simply preserves the bug that we already have on BROKEN_UNALIGNED archs (but makes the improvement that it fixes the bug on aarch64 and any other arch that can do unaligned access), to keep existing efi payloads working.
BR, -R
Best regards
Heinrich
Best regards
Heinrich
ifdef CONFIG_ARM64 PLATFORM_ELFFLAGS += -B aarch64 -O elf64-littleaarch64 diff --git a/include/efi_api.h b/include/efi_api.h index ef91e34c7b..ddd1e6100a 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -284,6 +284,31 @@ struct efi_loaded_image { #define DEVICE_PATH_TYPE_END 0x7f # define DEVICE_PATH_SUB_TYPE_END 0xff
+/*
- Some arch's have trouble with unaligned accesses. Technically
- EFI device-path structs should be byte aligned, and the next node
- in the path starts immediately after the previous. Meaning that
- a pointer to an 'struct efi_device_path' is not necessarily word
- aligned. See section 10.3.1 in v2.7 of UEFI spec.
- This causes problems not just for u-boot, but also most/all EFI
- payloads loaded by u-boot on these archs. Fortunately the common
- practice for traversing a device path is to rely on the length
- field in the header, rather than the specified length of the
- particular device path type+subtype. So the EFI_DP_PAD() macro
- will add the specified number of bytes to the tail of device path
- structs to pad them to word alignment.
- Technically this is non-compliant, BROKEN_UNALIGNED should *only*
- be defined on archs that cannot do unaligned accesses.
- */
+#ifdef BROKEN_UNALIGNED +# define EFI_DP_PAD(n) u8 __pad[n] +#else +# define EFI_DP_PAD(n) +#endif
struct efi_device_path { u8 type; u8 sub_type; @@ -318,12 +343,14 @@ struct efi_device_path_usb { struct efi_device_path dp; u8 parent_port_number; u8 usb_interface;
EFI_DP_PAD(2);
} __packed;
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type;
EFI_DP_PAD(3);
} __packed;
struct efi_device_path_usb_class { @@ -333,11 +360,13 @@ struct efi_device_path_usb_class { u8 device_class; u8 device_subclass; u8 device_protocol;
EFI_DP_PAD(1);
} __packed;
struct efi_device_path_sd_mmc_path { struct efi_device_path dp; u8 slot_number;
EFI_DP_PAD(3);
} __packed;
#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04 @@ -353,6 +382,7 @@ struct efi_device_path_hard_drive_path { u8 partition_signature[16]; u8 partmap_type; u8 signature_type;
EFI_DP_PAD(1);
} __packed;
struct efi_device_path_cdrom_path { diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index b5acf73f98..515a1f4737 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -402,6 +402,9 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
// TODO efi_device_path_file_path should be variable length: fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
+#ifdef BROKEN_UNALIGNED
fpsize = ALIGN(fpsize, 4);
+#endif dpsize += fpsize;
start = buf = calloc(1, dpsize + sizeof(END));

On 08/05/2017 08:43 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 1:06 PM, Rob Clark robdclark@gmail.com wrote:
On Sat, Aug 5, 2017 at 12:52 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 06:16 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 05:58 PM, Rob Clark wrote:
Some arch's have trouble with unaligned accesses. Technically EFI device-path structs should be byte aligned, and the next node in the path starts immediately after the previous. Meaning that a pointer to an 'struct efi_device_path' is not necessarily word aligned. See section 10.3.1 in v2.7 of UEFI spec.
This causes problems not just for u-boot, but also most/all EFI payloads loaded by u-boot on these archs. Fortunately the common practice for traversing a device path is to rely on the length field in the header, rather than the specified length of the particular device path type+subtype. So the EFI_DP_PAD() macro will add the specified number of bytes to the tail of device path structs to pad them to word alignment.
Technically this is non-compliant, BROKEN_UNALIGNED should *only* be defined on archs that cannot do unaligned accesses.
Signed-off-by: Rob Clark robdclark@gmail.com
I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED
Mark, this is untested but I think it should solve your crash on the Banana Pi. Could you give it a try when you get a chance?
arch/arm/config.mk | 2 +- include/efi_api.h | 30 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_device_path.c | 3 +++ 3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 1a77779db4..067dc93a9d 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ $(call cc-option,-arm-use-movt=0,) PLATFORM_RELFLAGS += $(LLVM_RELFLAGS)
-PLATFORM_CPPFLAGS += -D__ARM__ +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED
NAK
We have more then ARM. And other architectures also create exceptions for unaligned access.
I hate platform specific code. It should not be used outside /arch.
To play it save you should not use _packed at all! Use memcpy to transfer between aligned and unaligned memory.
except for reasons I explained in the thread on the patch that added the __packed in the first place. Sorry, this is ugly but we have to do it.
BR, -R
According to the UEFI standard the nodes in paths are not to be assumed to be aligned.
So even if you use padding bytes in paths that you pass to the EFI application you should not expect that the EFI application does the same. Expect the EFI application either to remove them or send new nodes without padding.
To the idea of padding bytes and __packed does not make sense.
Ok, to be fair, you are right about device-paths passed too u-boot. On BROKEN_UNALIGNED archs, we should sanitise with a copy to an aligned device-path in *addition* to what I proposed. I can make a patch to add a helper to do this a bit later.
so thinking about this a bit, I have two options in mind:
efi_dp_sanitize() + efi_dp_free() helpers that are no-ops on archs that can do unaligned access, but efi_dp_sanitize() always allocates and copies on BROKEN_UNALIGNED archs and efi_dp_free() always free's on BROKEN_UNALIGNED archs, even if the dp passed from efi payload doesn't require it.
efi_dp_sanitize() that is no-op on archs that can do unaligned access but only allocates/copies when passed a device path that would result in unaligned access, plus hook some mechanism to auto-free on EFI_EXIT().. which is slightly tricky w/ nesting of efi calls, but not impossible and at least avoids the problem of missing calls to free the dup'd device-path.. which is the sort of leak I might miss since I'm not using EFI_LOADER on any BROKEN_UNALIGNED arch
anyone who cares about armv7 + efi have a preference between always doing an extra alloc+copy+free vs EFI_EXIT() magic?
BR, -R
But the padding bytes + __packed does make total sense because it avoids breaking efi payloads that already exist in the field. The crash that Mark saw was not in u-boot, but in openbsd's bootaa64.efi. (It is possibly that we just get lucky here in u-boot since I add the /End node after the mac address by something the compiler turns into a memcpy.)
My proposal simply preserves the bug that we already have on BROKEN_UNALIGNED archs (but makes the improvement that it fixes the bug on aarch64 and any other arch that can do unaligned access), to keep existing efi payloads working.
BR, -R
Please, go for the simplest code. I cannot imagine that copying takes more than 10ms for starting grub on any architecture. So the user will not notice it anyway. Assume every architecture requiring alignment for which you do not have proof of the contrary.
Even on ARM64 there are op codes that fail without alignment.
Regards
Heinrich
ifdef CONFIG_ARM64 PLATFORM_ELFFLAGS += -B aarch64 -O elf64-littleaarch64 diff --git a/include/efi_api.h b/include/efi_api.h index ef91e34c7b..ddd1e6100a 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -284,6 +284,31 @@ struct efi_loaded_image { #define DEVICE_PATH_TYPE_END 0x7f # define DEVICE_PATH_SUB_TYPE_END 0xff
+/*
- Some arch's have trouble with unaligned accesses. Technically
- EFI device-path structs should be byte aligned, and the next node
- in the path starts immediately after the previous. Meaning that
- a pointer to an 'struct efi_device_path' is not necessarily word
- aligned. See section 10.3.1 in v2.7 of UEFI spec.
- This causes problems not just for u-boot, but also most/all EFI
- payloads loaded by u-boot on these archs. Fortunately the common
- practice for traversing a device path is to rely on the length
- field in the header, rather than the specified length of the
- particular device path type+subtype. So the EFI_DP_PAD() macro
- will add the specified number of bytes to the tail of device path
- structs to pad them to word alignment.
- Technically this is non-compliant, BROKEN_UNALIGNED should *only*
- be defined on archs that cannot do unaligned accesses.
- */
+#ifdef BROKEN_UNALIGNED +# define EFI_DP_PAD(n) u8 __pad[n] +#else +# define EFI_DP_PAD(n) +#endif
struct efi_device_path { u8 type; u8 sub_type; @@ -318,12 +343,14 @@ struct efi_device_path_usb { struct efi_device_path dp; u8 parent_port_number; u8 usb_interface;
EFI_DP_PAD(2);
} __packed;
struct efi_device_path_mac_addr { struct efi_device_path dp; struct efi_mac_addr mac; u8 if_type;
EFI_DP_PAD(3);
} __packed;
struct efi_device_path_usb_class { @@ -333,11 +360,13 @@ struct efi_device_path_usb_class { u8 device_class; u8 device_subclass; u8 device_protocol;
EFI_DP_PAD(1);
} __packed;
struct efi_device_path_sd_mmc_path { struct efi_device_path dp; u8 slot_number;
EFI_DP_PAD(3);
} __packed;
#define DEVICE_PATH_TYPE_MEDIA_DEVICE 0x04 @@ -353,6 +382,7 @@ struct efi_device_path_hard_drive_path { u8 partition_signature[16]; u8 partmap_type; u8 signature_type;
EFI_DP_PAD(1);
} __packed;
struct efi_device_path_cdrom_path { diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index b5acf73f98..515a1f4737 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -402,6 +402,9 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part,
// TODO efi_device_path_file_path should be variable length: fpsize = sizeof(struct efi_device_path) + 2 * (strlen(path) + 1);
+#ifdef BROKEN_UNALIGNED
fpsize = ALIGN(fpsize, 4);
+#endif dpsize += fpsize;
start = buf = calloc(1, dpsize + sizeof(END));

On Sat, Aug 5, 2017 at 4:05 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 08:43 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 1:06 PM, Rob Clark robdclark@gmail.com wrote:
On Sat, Aug 5, 2017 at 12:52 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 06:16 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 05:58 PM, Rob Clark wrote: > Some arch's have trouble with unaligned accesses. Technically > EFI device-path structs should be byte aligned, and the next node > in the path starts immediately after the previous. Meaning that > a pointer to an 'struct efi_device_path' is not necessarily word > aligned. See section 10.3.1 in v2.7 of UEFI spec. > > This causes problems not just for u-boot, but also most/all EFI > payloads loaded by u-boot on these archs. Fortunately the common > practice for traversing a device path is to rely on the length > field in the header, rather than the specified length of the > particular device path type+subtype. So the EFI_DP_PAD() macro > will add the specified number of bytes to the tail of device path > structs to pad them to word alignment. > > Technically this is non-compliant, BROKEN_UNALIGNED should *only* > be defined on archs that cannot do unaligned accesses. > > Signed-off-by: Rob Clark robdclark@gmail.com > --- > I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED > > Mark, this is untested but I think it should solve your crash on the > Banana Pi. Could you give it a try when you get a chance? > > arch/arm/config.mk | 2 +- > include/efi_api.h | 30 ++++++++++++++++++++++++++++++ > lib/efi_loader/efi_device_path.c | 3 +++ > 3 files changed, 34 insertions(+), 1 deletion(-) > > diff --git a/arch/arm/config.mk b/arch/arm/config.mk > index 1a77779db4..067dc93a9d 100644 > --- a/arch/arm/config.mk > +++ b/arch/arm/config.mk > @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ > $(call cc-option,-arm-use-movt=0,) > PLATFORM_RELFLAGS += $(LLVM_RELFLAGS) > > -PLATFORM_CPPFLAGS += -D__ARM__ > +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED
NAK
We have more then ARM. And other architectures also create exceptions for unaligned access.
I hate platform specific code. It should not be used outside /arch.
To play it save you should not use _packed at all! Use memcpy to transfer between aligned and unaligned memory.
except for reasons I explained in the thread on the patch that added the __packed in the first place. Sorry, this is ugly but we have to do it.
BR, -R
According to the UEFI standard the nodes in paths are not to be assumed to be aligned.
So even if you use padding bytes in paths that you pass to the EFI application you should not expect that the EFI application does the same. Expect the EFI application either to remove them or send new nodes without padding.
To the idea of padding bytes and __packed does not make sense.
Ok, to be fair, you are right about device-paths passed too u-boot. On BROKEN_UNALIGNED archs, we should sanitise with a copy to an aligned device-path in *addition* to what I proposed. I can make a patch to add a helper to do this a bit later.
so thinking about this a bit, I have two options in mind:
efi_dp_sanitize() + efi_dp_free() helpers that are no-ops on archs that can do unaligned access, but efi_dp_sanitize() always allocates and copies on BROKEN_UNALIGNED archs and efi_dp_free() always free's on BROKEN_UNALIGNED archs, even if the dp passed from efi payload doesn't require it.
efi_dp_sanitize() that is no-op on archs that can do unaligned access but only allocates/copies when passed a device path that would result in unaligned access, plus hook some mechanism to auto-free on EFI_EXIT().. which is slightly tricky w/ nesting of efi calls, but not impossible and at least avoids the problem of missing calls to free the dup'd device-path.. which is the sort of leak I might miss since I'm not using EFI_LOADER on any BROKEN_UNALIGNED arch
anyone who cares about armv7 + efi have a preference between always doing an extra alloc+copy+free vs EFI_EXIT() magic?
Please, go for the simplest code.
well, the source of my question was there are two definitions of "simplest" here, ie "simplest to use" and "simplest helpers".. I'm mostly worried about future patches that use the helpers introducing leaks. But actually I think a reasonable middle ground is "simplest helpers plus always make the helpers not no-ops for DEBUG builds"..
I cannot imagine that copying takes more than 10ms for starting grub on any architecture. So the user will not notice it anyway. Assume every architecture requiring alignment for which you do not have proof of the contrary.
Even on ARM64 there are op codes that fail without alignment.
iirc the restrictions where mostly about device memory, and atomics. Which should not apply here. (And we have aarch64 servers in production with grub, which doesn't take any protections about unaligned device-path nodes, using non u-boot EFI implementations. So maybe that counts as proof to the contrary ;-))
BR, -R

On 05.08.17 21:31, Rob Clark wrote:
On Sat, Aug 5, 2017 at 4:05 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 08:43 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 1:06 PM, Rob Clark robdclark@gmail.com wrote:
On Sat, Aug 5, 2017 at 12:52 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 06:16 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: > On 08/05/2017 05:58 PM, Rob Clark wrote: >> Some arch's have trouble with unaligned accesses. Technically >> EFI device-path structs should be byte aligned, and the next node >> in the path starts immediately after the previous. Meaning that >> a pointer to an 'struct efi_device_path' is not necessarily word >> aligned. See section 10.3.1 in v2.7 of UEFI spec. >> >> This causes problems not just for u-boot, but also most/all EFI >> payloads loaded by u-boot on these archs. Fortunately the common >> practice for traversing a device path is to rely on the length >> field in the header, rather than the specified length of the >> particular device path type+subtype. So the EFI_DP_PAD() macro >> will add the specified number of bytes to the tail of device path >> structs to pad them to word alignment. >> >> Technically this is non-compliant, BROKEN_UNALIGNED should *only* >> be defined on archs that cannot do unaligned accesses. >> >> Signed-off-by: Rob Clark robdclark@gmail.com >> --- >> I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED >> >> Mark, this is untested but I think it should solve your crash on the >> Banana Pi. Could you give it a try when you get a chance? >> >> arch/arm/config.mk | 2 +- >> include/efi_api.h | 30 ++++++++++++++++++++++++++++++ >> lib/efi_loader/efi_device_path.c | 3 +++ >> 3 files changed, 34 insertions(+), 1 deletion(-) >> >> diff --git a/arch/arm/config.mk b/arch/arm/config.mk >> index 1a77779db4..067dc93a9d 100644 >> --- a/arch/arm/config.mk >> +++ b/arch/arm/config.mk >> @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ >> $(call cc-option,-arm-use-movt=0,) >> PLATFORM_RELFLAGS += $(LLVM_RELFLAGS) >> >> -PLATFORM_CPPFLAGS += -D__ARM__ >> +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED > > NAK > > We have more then ARM. And other architectures also create exceptions > for unaligned access. > > I hate platform specific code. It should not be used outside /arch. > > To play it save you should not use _packed at all! > Use memcpy to transfer between aligned and unaligned memory.
except for reasons I explained in the thread on the patch that added the __packed in the first place. Sorry, this is ugly but we have to do it.
BR, -R
According to the UEFI standard the nodes in paths are not to be assumed to be aligned.
So even if you use padding bytes in paths that you pass to the EFI application you should not expect that the EFI application does the same. Expect the EFI application either to remove them or send new nodes without padding.
To the idea of padding bytes and __packed does not make sense.
Ok, to be fair, you are right about device-paths passed too u-boot. On BROKEN_UNALIGNED archs, we should sanitise with a copy to an aligned device-path in *addition* to what I proposed. I can make a patch to add a helper to do this a bit later.
so thinking about this a bit, I have two options in mind:
efi_dp_sanitize() + efi_dp_free() helpers that are no-ops on archs that can do unaligned access, but efi_dp_sanitize() always allocates and copies on BROKEN_UNALIGNED archs and efi_dp_free() always free's on BROKEN_UNALIGNED archs, even if the dp passed from efi payload doesn't require it.
efi_dp_sanitize() that is no-op on archs that can do unaligned access but only allocates/copies when passed a device path that would result in unaligned access, plus hook some mechanism to auto-free on EFI_EXIT().. which is slightly tricky w/ nesting of efi calls, but not impossible and at least avoids the problem of missing calls to free the dup'd device-path.. which is the sort of leak I might miss since I'm not using EFI_LOADER on any BROKEN_UNALIGNED arch
anyone who cares about armv7 + efi have a preference between always doing an extra alloc+copy+free vs EFI_EXIT() magic?
Please, go for the simplest code.
well, the source of my question was there are two definitions of "simplest" here, ie "simplest to use" and "simplest helpers".. I'm mostly worried about future patches that use the helpers introducing leaks. But actually I think a reasonable middle ground is "simplest helpers plus always make the helpers not no-ops for DEBUG builds"..
I cannot imagine that copying takes more than 10ms for starting grub on any architecture. So the user will not notice it anyway. Assume every architecture requiring alignment for which you do not have proof of the contrary.
Even on ARM64 there are op codes that fail without alignment.
iirc the restrictions where mostly about device memory, and atomics. Which should not apply here. (And we have aarch64 servers in production with grub, which doesn't take any protections about unaligned device-path nodes, using non u-boot EFI implementations. So maybe that counts as proof to the contrary ;-))
The UEFI spec mandates that unaligned access are fixed up properly (usually by hardware). We violate the spec on 32bit ARM, but do fulfill it on AArch64.
The reason things worked before really was just that both U-Boot and grub were compiled with -mno-unaligned-access, so they never issued unaligned accesses.
Which system broke for Mark? Banana pi mostly has 32bit SBCs, so I assume it's one of those? In that case, we would need to either
1) hack up the openbsd loader to also get compiled with -mno-unaligned-access
or
2) rework the 32bit arm mmu code to enable the MMU plus hardware unaligned fixups on all systems. (that's a *lot* of work)
To me personally, UEFI in 32bit ARM land is a nice to have standardized platform, but not something where we really need to be fully standards compliant in every aspect. I think "making things work" is good enough there.
For AArch64 things are different. There we should strive for full UEFI compliance as it's "the future" :).
Alex

From: Alexander Graf agraf@suse.de Date: Mon, 7 Aug 2017 21:19:37 +0100
On 05.08.17 21:31, Rob Clark wrote:
On Sat, Aug 5, 2017 at 4:05 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 08:43 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 1:06 PM, Rob Clark robdclark@gmail.com wrote:
On Sat, Aug 5, 2017 at 12:52 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 06:16 PM, Rob Clark wrote: > On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: >> On 08/05/2017 05:58 PM, Rob Clark wrote: >>> Some arch's have trouble with unaligned accesses. Technically >>> EFI device-path structs should be byte aligned, and the next node >>> in the path starts immediately after the previous. Meaning that >>> a pointer to an 'struct efi_device_path' is not necessarily word >>> aligned. See section 10.3.1 in v2.7 of UEFI spec. >>> >>> This causes problems not just for u-boot, but also most/all EFI >>> payloads loaded by u-boot on these archs. Fortunately the common >>> practice for traversing a device path is to rely on the length >>> field in the header, rather than the specified length of the >>> particular device path type+subtype. So the EFI_DP_PAD() macro >>> will add the specified number of bytes to the tail of device path >>> structs to pad them to word alignment. >>> >>> Technically this is non-compliant, BROKEN_UNALIGNED should *only* >>> be defined on archs that cannot do unaligned accesses. >>> >>> Signed-off-by: Rob Clark robdclark@gmail.com >>> --- >>> I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED >>> >>> Mark, this is untested but I think it should solve your crash on the >>> Banana Pi. Could you give it a try when you get a chance? >>> >>> arch/arm/config.mk | 2 +- >>> include/efi_api.h | 30 ++++++++++++++++++++++++++++++ >>> lib/efi_loader/efi_device_path.c | 3 +++ >>> 3 files changed, 34 insertions(+), 1 deletion(-) >>> >>> diff --git a/arch/arm/config.mk b/arch/arm/config.mk >>> index 1a77779db4..067dc93a9d 100644 >>> --- a/arch/arm/config.mk >>> +++ b/arch/arm/config.mk >>> @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ >>> $(call cc-option,-arm-use-movt=0,) >>> PLATFORM_RELFLAGS += $(LLVM_RELFLAGS) >>> >>> -PLATFORM_CPPFLAGS += -D__ARM__ >>> +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED >> >> NAK >> >> We have more then ARM. And other architectures also create exceptions >> for unaligned access. >> >> I hate platform specific code. It should not be used outside /arch. >> >> To play it save you should not use _packed at all! >> Use memcpy to transfer between aligned and unaligned memory. > > except for reasons I explained in the thread on the patch that added > the __packed in the first place. Sorry, this is ugly but we have to > do it. > > BR, > -R
According to the UEFI standard the nodes in paths are not to be assumed to be aligned.
So even if you use padding bytes in paths that you pass to the EFI application you should not expect that the EFI application does the same. Expect the EFI application either to remove them or send new nodes without padding.
To the idea of padding bytes and __packed does not make sense.
Ok, to be fair, you are right about device-paths passed too u-boot. On BROKEN_UNALIGNED archs, we should sanitise with a copy to an aligned device-path in *addition* to what I proposed. I can make a patch to add a helper to do this a bit later.
so thinking about this a bit, I have two options in mind:
efi_dp_sanitize() + efi_dp_free() helpers that are no-ops on archs that can do unaligned access, but efi_dp_sanitize() always allocates and copies on BROKEN_UNALIGNED archs and efi_dp_free() always free's on BROKEN_UNALIGNED archs, even if the dp passed from efi payload doesn't require it.
efi_dp_sanitize() that is no-op on archs that can do unaligned access but only allocates/copies when passed a device path that would result in unaligned access, plus hook some mechanism to auto-free on EFI_EXIT().. which is slightly tricky w/ nesting of efi calls, but not impossible and at least avoids the problem of missing calls to free the dup'd device-path.. which is the sort of leak I might miss since I'm not using EFI_LOADER on any BROKEN_UNALIGNED arch
anyone who cares about armv7 + efi have a preference between always doing an extra alloc+copy+free vs EFI_EXIT() magic?
Please, go for the simplest code.
well, the source of my question was there are two definitions of "simplest" here, ie "simplest to use" and "simplest helpers".. I'm mostly worried about future patches that use the helpers introducing leaks. But actually I think a reasonable middle ground is "simplest helpers plus always make the helpers not no-ops for DEBUG builds"..
I cannot imagine that copying takes more than 10ms for starting grub on any architecture. So the user will not notice it anyway. Assume every architecture requiring alignment for which you do not have proof of the contrary.
Even on ARM64 there are op codes that fail without alignment.
iirc the restrictions where mostly about device memory, and atomics. Which should not apply here. (And we have aarch64 servers in production with grub, which doesn't take any protections about unaligned device-path nodes, using non u-boot EFI implementations. So maybe that counts as proof to the contrary ;-))
The UEFI spec mandates that unaligned access are fixed up properly (usually by hardware). We violate the spec on 32bit ARM, but do fulfill it on AArch64.
Actually for AArch32 the spec says:
Unaligned access should be enabled if supported; Alignment faults are enabled otherwise.
So UEFI payload that wants to support pre-ARMv7 hardware should make sure it doesn't do any unaligned access.
The reason things worked before really was just that both U-Boot and grub were compiled with -mno-unaligned-access, so they never issued unaligned accesses.
That in itself is not enough to prevent unaligned access. If you explicitly cast an unaligned pointer to say (uint32_t *) and then dereference it, you'll create an unaligned access.
Which system broke for Mark? Banana pi mostly has 32bit SBCs, so I assume it's one of those? In that case, we would need to either
- hack up the openbsd loader to also get compiled with
-mno-unaligned-access
or
- rework the 32bit arm mmu code to enable the MMU plus hardware
unaligned fixups on all systems. (that's a *lot* of work)
The system that broke was Cortex-A7 based, so ARMv7.
*However*, the OpenBSD bootloader does not perform any unaligned access. *All* cases of unaligned access that I encountered while testing/debugging Rob's code were in U-Boot itself. This was mostly a consequence from a diff in the series that added __packed to the device path node structs and then passed members of those structs to functions that accessed the contents of tose (now byte aligned structs) through (uint16_t *) or (uint32_t *) pointers.
There was initially some confusion because I used the pc reported by U-Boot to look up the faulting instruction whereas I should have been using the reloc_pc.
I think Rob is worried about grub/shim/fallback.efi doing unaligned access of device path nodes on armv7. But I'm not sure he has any evidence beyond looking at code.
To me personally, UEFI in 32bit ARM land is a nice to have standardized platform, but not something where we really need to be fully standards compliant in every aspect. I think "making things work" is good enough there.
Right. For us UEFI us just a convenient way to re-use the U-Boot device driver code to load a kernel from a UFS/FFS filesystem.
For AArch64 things are different. There we should strive for full UEFI compliance as it's "the future" :).
Even there just making things work would be good enough for me ;). Our AArach64 bootloader is almost identical to the AArch32 one and works on a real UEFI implementation as well (SoftIron Overdrive 1000).
Cheers,
Mark

On Mon, Aug 7, 2017 at 5:14 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Alexander Graf agraf@suse.de Date: Mon, 7 Aug 2017 21:19:37 +0100
On 05.08.17 21:31, Rob Clark wrote:
On Sat, Aug 5, 2017 at 4:05 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 08:43 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 1:06 PM, Rob Clark robdclark@gmail.com wrote:
On Sat, Aug 5, 2017 at 12:52 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: > On 08/05/2017 06:16 PM, Rob Clark wrote: >> On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: >>> On 08/05/2017 05:58 PM, Rob Clark wrote: >>>> Some arch's have trouble with unaligned accesses. Technically >>>> EFI device-path structs should be byte aligned, and the next node >>>> in the path starts immediately after the previous. Meaning that >>>> a pointer to an 'struct efi_device_path' is not necessarily word >>>> aligned. See section 10.3.1 in v2.7 of UEFI spec. >>>> >>>> This causes problems not just for u-boot, but also most/all EFI >>>> payloads loaded by u-boot on these archs. Fortunately the common >>>> practice for traversing a device path is to rely on the length >>>> field in the header, rather than the specified length of the >>>> particular device path type+subtype. So the EFI_DP_PAD() macro >>>> will add the specified number of bytes to the tail of device path >>>> structs to pad them to word alignment. >>>> >>>> Technically this is non-compliant, BROKEN_UNALIGNED should *only* >>>> be defined on archs that cannot do unaligned accesses. >>>> >>>> Signed-off-by: Rob Clark robdclark@gmail.com >>>> --- >>>> I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED >>>> >>>> Mark, this is untested but I think it should solve your crash on the >>>> Banana Pi. Could you give it a try when you get a chance? >>>> >>>> arch/arm/config.mk | 2 +- >>>> include/efi_api.h | 30 ++++++++++++++++++++++++++++++ >>>> lib/efi_loader/efi_device_path.c | 3 +++ >>>> 3 files changed, 34 insertions(+), 1 deletion(-) >>>> >>>> diff --git a/arch/arm/config.mk b/arch/arm/config.mk >>>> index 1a77779db4..067dc93a9d 100644 >>>> --- a/arch/arm/config.mk >>>> +++ b/arch/arm/config.mk >>>> @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ >>>> $(call cc-option,-arm-use-movt=0,) >>>> PLATFORM_RELFLAGS += $(LLVM_RELFLAGS) >>>> >>>> -PLATFORM_CPPFLAGS += -D__ARM__ >>>> +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED >>> >>> NAK >>> >>> We have more then ARM. And other architectures also create exceptions >>> for unaligned access. >>> >>> I hate platform specific code. It should not be used outside /arch. >>> >>> To play it save you should not use _packed at all! >>> Use memcpy to transfer between aligned and unaligned memory. >> >> except for reasons I explained in the thread on the patch that added >> the __packed in the first place. Sorry, this is ugly but we have to >> do it. >> >> BR, >> -R > > According to the UEFI standard the nodes in paths are not to be assumed > to be aligned. > > So even if you use padding bytes in paths that you pass to the EFI > application you should not expect that the EFI application does the > same. Expect the EFI application either to remove them or send new nodes > without padding. > > To the idea of padding bytes and __packed does not make sense.
Ok, to be fair, you are right about device-paths passed too u-boot. On BROKEN_UNALIGNED archs, we should sanitise with a copy to an aligned device-path in *addition* to what I proposed. I can make a patch to add a helper to do this a bit later.
so thinking about this a bit, I have two options in mind:
efi_dp_sanitize() + efi_dp_free() helpers that are no-ops on archs that can do unaligned access, but efi_dp_sanitize() always allocates and copies on BROKEN_UNALIGNED archs and efi_dp_free() always free's on BROKEN_UNALIGNED archs, even if the dp passed from efi payload doesn't require it.
efi_dp_sanitize() that is no-op on archs that can do unaligned access but only allocates/copies when passed a device path that would result in unaligned access, plus hook some mechanism to auto-free on EFI_EXIT().. which is slightly tricky w/ nesting of efi calls, but not impossible and at least avoids the problem of missing calls to free the dup'd device-path.. which is the sort of leak I might miss since I'm not using EFI_LOADER on any BROKEN_UNALIGNED arch
anyone who cares about armv7 + efi have a preference between always doing an extra alloc+copy+free vs EFI_EXIT() magic?
Please, go for the simplest code.
well, the source of my question was there are two definitions of "simplest" here, ie "simplest to use" and "simplest helpers".. I'm mostly worried about future patches that use the helpers introducing leaks. But actually I think a reasonable middle ground is "simplest helpers plus always make the helpers not no-ops for DEBUG builds"..
I cannot imagine that copying takes more than 10ms for starting grub on any architecture. So the user will not notice it anyway. Assume every architecture requiring alignment for which you do not have proof of the contrary.
Even on ARM64 there are op codes that fail without alignment.
iirc the restrictions where mostly about device memory, and atomics. Which should not apply here. (And we have aarch64 servers in production with grub, which doesn't take any protections about unaligned device-path nodes, using non u-boot EFI implementations. So maybe that counts as proof to the contrary ;-))
The UEFI spec mandates that unaligned access are fixed up properly (usually by hardware). We violate the spec on 32bit ARM, but do fulfill it on AArch64.
Actually for AArch32 the spec says:
Unaligned access should be enabled if supported; Alignment faults are enabled otherwise.
It is a bit strange to me that alignment faults are disabled by default, but presumably is disabled out of reset.. I don't totally understand the connection to having mmu disabled, unless all memory is treated like device memory without mmu enabled. (Which, also, wouldn't that be super-slow?) But anyway, I guess that is a bit beside the point.
So UEFI payload that wants to support pre-ARMv7 hardware should make sure it doesn't do any unaligned access.
The reason things worked before really was just that both U-Boot and grub were compiled with -mno-unaligned-access, so they never issued unaligned accesses.
That in itself is not enough to prevent unaligned access. If you explicitly cast an unaligned pointer to say (uint32_t *) and then dereference it, you'll create an unaligned access.
This is problematic around file nodes in the device path. Adding the padding bytes to the end of each device-path struct would "solve" that, and if pre-aarch64 we are aiming at "good enough to work", I kinda think that this the approach we should go for. Other than file-path nodes, the rest of the issues in u-boot should be solved by addition of missing __packed on 'struct efi_device_path' (which I've added locally and will be in the next revision of the patchset).
Which system broke for Mark? Banana pi mostly has 32bit SBCs, so I assume it's one of those? In that case, we would need to either
- hack up the openbsd loader to also get compiled with
-mno-unaligned-access
or
- rework the 32bit arm mmu code to enable the MMU plus hardware
unaligned fixups on all systems. (that's a *lot* of work)
The system that broke was Cortex-A7 based, so ARMv7.
*However*, the OpenBSD bootloader does not perform any unaligned access. *All* cases of unaligned access that I encountered while testing/debugging Rob's code were in U-Boot itself. This was mostly a consequence from a diff in the series that added __packed to the device path node structs and then passed members of those structs to functions that accessed the contents of tose (now byte aligned structs) through (uint16_t *) or (uint32_t *) pointers.
Yeah.. the padding byte approach I suggested would solve this. Not *really* compliant, but should work for all the payload code I've come across. We definitely shouldn't enable the padding bytes on aarch64, but I think padding bytes would be a pragmatic solution for armv7 and earlier. (And hopefully someday someone enables mmu on armv7 so we can turn off the hack there too.)
There was initially some confusion because I used the pc reported by U-Boot to look up the faulting instruction whereas I should have been using the reloc_pc.
I think Rob is worried about grub/shim/fallback.efi doing unaligned access of device path nodes on armv7. But I'm not sure he has any evidence beyond looking at code.
It will definitely be an issue (if grub/shim/fallback were not compiled with -mno-unaligned-access) in some of the debug code that prints out device-paths. I haven't checked the compiler flags used w/ shim/fallback.
Parsing MEDIA_DEVICE:HARD_DRIVE nodes would be an issue regardless without -mno-unaligned-access, although we might be getting lucky by just not hitting debug paths. Some of the other issues would be hidden by padding bytes at the end of the DP structs which would keep the file-path nodes word aligned.
To me personally, UEFI in 32bit ARM land is a nice to have standardized platform, but not something where we really need to be fully standards compliant in every aspect. I think "making things work" is good enough there.
Right. For us UEFI us just a convenient way to re-use the U-Boot device driver code to load a kernel from a UFS/FFS filesystem.
For AArch64 things are different. There we should strive for full UEFI compliance as it's "the future" :).
Even there just making things work would be good enough for me ;). Our AArach64 bootloader is almost identical to the AArch32 one and works on a real UEFI implementation as well (SoftIron Overdrive 1000).
I think we should make the aarch64 implementation fully compliant (ie. addition of missing __packed's and no extra padding bytes). I won't loose sleep if some efi payloads don't work on pre-aarch64 (we should be able to keep things that were working before working). If adding missing __packed breaks things on platforms that can't do unaligned access, the solution is not to remove __packed, but to conditionally add padding bytes at the end of the struct. That way we keep things working as before on the old platforms, but make things work correctly on aarch64.
I'll add back my patch for EFI_DP_PAD() to this patchset, since this seems like the sensible way forward.
BR, -R

Am 07.08.2017 um 23:18 schrieb Rob Clark robdclark@gmail.com:
On Mon, Aug 7, 2017 at 5:14 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Alexander Graf agraf@suse.de Date: Mon, 7 Aug 2017 21:19:37 +0100
On 05.08.17 21:31, Rob Clark wrote:
On Sat, Aug 5, 2017 at 4:05 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 08:43 PM, Rob Clark wrote: > On Sat, Aug 5, 2017 at 1:06 PM, Rob Clark robdclark@gmail.com wrote: >> On Sat, Aug 5, 2017 at 12:52 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: >>> On 08/05/2017 06:16 PM, Rob Clark wrote: >>>> On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: >>>>> On 08/05/2017 05:58 PM, Rob Clark wrote: >>>>> Some arch's have trouble with unaligned accesses. Technically >>>>> EFI device-path structs should be byte aligned, and the next node >>>>> in the path starts immediately after the previous. Meaning that >>>>> a pointer to an 'struct efi_device_path' is not necessarily word >>>>> aligned. See section 10.3.1 in v2.7 of UEFI spec. >>>>> >>>>> This causes problems not just for u-boot, but also most/all EFI >>>>> payloads loaded by u-boot on these archs. Fortunately the common >>>>> practice for traversing a device path is to rely on the length >>>>> field in the header, rather than the specified length of the >>>>> particular device path type+subtype. So the EFI_DP_PAD() macro >>>>> will add the specified number of bytes to the tail of device path >>>>> structs to pad them to word alignment. >>>>> >>>>> Technically this is non-compliant, BROKEN_UNALIGNED should *only* >>>>> be defined on archs that cannot do unaligned accesses. >>>>> >>>>> Signed-off-by: Rob Clark robdclark@gmail.com >>>>> --- >>>>> I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED >>>>> >>>>> Mark, this is untested but I think it should solve your crash on the >>>>> Banana Pi. Could you give it a try when you get a chance? >>>>> >>>>> arch/arm/config.mk | 2 +- >>>>> include/efi_api.h | 30 ++++++++++++++++++++++++++++++ >>>>> lib/efi_loader/efi_device_path.c | 3 +++ >>>>> 3 files changed, 34 insertions(+), 1 deletion(-) >>>>> >>>>> diff --git a/arch/arm/config.mk b/arch/arm/config.mk >>>>> index 1a77779db4..067dc93a9d 100644 >>>>> --- a/arch/arm/config.mk >>>>> +++ b/arch/arm/config.mk >>>>> @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ >>>>> $(call cc-option,-arm-use-movt=0,) >>>>> PLATFORM_RELFLAGS += $(LLVM_RELFLAGS) >>>>> >>>>> -PLATFORM_CPPFLAGS += -D__ARM__ >>>>> +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED >>>> >>>> NAK >>>> >>>> We have more then ARM. And other architectures also create exceptions >>>> for unaligned access. >>>> >>>> I hate platform specific code. It should not be used outside /arch. >>>> >>>> To play it save you should not use _packed at all! >>>> Use memcpy to transfer between aligned and unaligned memory. >>> >>> except for reasons I explained in the thread on the patch that added >>> the __packed in the first place. Sorry, this is ugly but we have to >>> do it. >>> >>> BR, >>> -R >> >> According to the UEFI standard the nodes in paths are not to be assumed >> to be aligned. >> >> So even if you use padding bytes in paths that you pass to the EFI >> application you should not expect that the EFI application does the >> same. Expect the EFI application either to remove them or send new nodes >> without padding. >> >> To the idea of padding bytes and __packed does not make sense. > > Ok, to be fair, you are right about device-paths passed too u-boot. > On BROKEN_UNALIGNED archs, we should sanitise with a copy to an > aligned device-path in *addition* to what I proposed. I can make a > patch to add a helper to do this a bit later.
so thinking about this a bit, I have two options in mind:
efi_dp_sanitize() + efi_dp_free() helpers that are no-ops on archs that can do unaligned access, but efi_dp_sanitize() always allocates and copies on BROKEN_UNALIGNED archs and efi_dp_free() always free's on BROKEN_UNALIGNED archs, even if the dp passed from efi payload doesn't require it.
efi_dp_sanitize() that is no-op on archs that can do unaligned access but only allocates/copies when passed a device path that would result in unaligned access, plus hook some mechanism to auto-free on EFI_EXIT().. which is slightly tricky w/ nesting of efi calls, but not impossible and at least avoids the problem of missing calls to free the dup'd device-path.. which is the sort of leak I might miss since I'm not using EFI_LOADER on any BROKEN_UNALIGNED arch
anyone who cares about armv7 + efi have a preference between always doing an extra alloc+copy+free vs EFI_EXIT() magic?
Please, go for the simplest code.
well, the source of my question was there are two definitions of "simplest" here, ie "simplest to use" and "simplest helpers".. I'm mostly worried about future patches that use the helpers introducing leaks. But actually I think a reasonable middle ground is "simplest helpers plus always make the helpers not no-ops for DEBUG builds"..
I cannot imagine that copying takes more than 10ms for starting grub on any architecture. So the user will not notice it anyway. Assume every architecture requiring alignment for which you do not have proof of the contrary.
Even on ARM64 there are op codes that fail without alignment.
iirc the restrictions where mostly about device memory, and atomics. Which should not apply here. (And we have aarch64 servers in production with grub, which doesn't take any protections about unaligned device-path nodes, using non u-boot EFI implementations. So maybe that counts as proof to the contrary ;-))
The UEFI spec mandates that unaligned access are fixed up properly (usually by hardware). We violate the spec on 32bit ARM, but do fulfill it on AArch64.
Actually for AArch32 the spec says:
Unaligned access should be enabled if supported; Alignment faults are enabled otherwise.
It is a bit strange to me that alignment faults are disabled by default, but presumably is disabled out of reset.. I don't totally understand the connection to having mmu disabled, unless all memory is treated like device memory without mmu enabled. (Which, also, wouldn't that be super-slow?) But anyway, I guess that is a bit beside the point.
It is treated as device memory with mmu disabled, yes.
So UEFI payload that wants to support pre-ARMv7 hardware should make sure it doesn't do any unaligned access.
The reason things worked before really was just that both U-Boot and grub were compiled with -mno-unaligned-access, so they never issued unaligned accesses.
That in itself is not enough to prevent unaligned access. If you explicitly cast an unaligned pointer to say (uint32_t *) and then dereference it, you'll create an unaligned access.
This is problematic around file nodes in the device path. Adding the padding bytes to the end of each device-path struct would "solve" that, and if pre-aarch64 we are aiming at "good enough to work", I kinda think that this the approach we should go for. Other than file-path nodes, the rest of the issues in u-boot should be solved by addition of missing __packed on 'struct efi_device_path' (which I've added locally and will be in the next revision of the patchset).
Let's ask Leif and Ard if that is actually correct. If I remember correctly, edk2 some times leverages automatic padding from the compiler on structs.
Which system broke for Mark? Banana pi mostly has 32bit SBCs, so I assume it's one of those? In that case, we would need to either
- hack up the openbsd loader to also get compiled with
-mno-unaligned-access
or
- rework the 32bit arm mmu code to enable the MMU plus hardware
unaligned fixups on all systems. (that's a *lot* of work)
The system that broke was Cortex-A7 based, so ARMv7.
*However*, the OpenBSD bootloader does not perform any unaligned access. *All* cases of unaligned access that I encountered while testing/debugging Rob's code were in U-Boot itself. This was mostly a consequence from a diff in the series that added __packed to the device path node structs and then passed members of those structs to functions that accessed the contents of tose (now byte aligned structs) through (uint16_t *) or (uint32_t *) pointers.
Yeah.. the padding byte approach I suggested would solve this. Not *really* compliant, but should work for all the payload code I've come across. We definitely shouldn't enable the padding bytes on aarch64, but I think padding bytes would be a pragmatic solution for armv7 and earlier. (And hopefully someday someone enables mmu on armv7 so we can turn off the hack there too.)
The problem really is inconsistency. Some 32bit arm platforms enable the mmu, some don't.
There was initially some confusion because I used the pc reported by U-Boot to look up the faulting instruction whereas I should have been using the reloc_pc.
I think Rob is worried about grub/shim/fallback.efi doing unaligned access of device path nodes on armv7. But I'm not sure he has any evidence beyond looking at code.
It will definitely be an issue (if grub/shim/fallback were not compiled with -mno-unaligned-access) in some of the debug code that prints out device-paths. I haven't checked the compiler flags used w/ shim/fallback.
Parsing MEDIA_DEVICE:HARD_DRIVE nodes would be an issue regardless without -mno-unaligned-access, although we might be getting lucky by just not hitting debug paths. Some of the other issues would be hidden by padding bytes at the end of the DP structs which would keep the file-path nodes word aligned.
To me personally, UEFI in 32bit ARM land is a nice to have standardized platform, but not something where we really need to be fully standards compliant in every aspect. I think "making things work" is good enough there.
Right. For us UEFI us just a convenient way to re-use the U-Boot device driver code to load a kernel from a UFS/FFS filesystem.
For AArch64 things are different. There we should strive for full UEFI compliance as it's "the future" :).
Even there just making things work would be good enough for me ;). Our AArach64 bootloader is almost identical to the AArch32 one and works on a real UEFI implementation as well (SoftIron Overdrive 1000).
I think we should make the aarch64 implementation fully compliant (ie. addition of missing __packed's and no extra padding bytes). I won't loose sleep if some efi payloads don't work on pre-aarch64 (we should be able to keep things that were working before working). If adding missing __packed breaks things on platforms that can't do unaligned access, the solution is not to remove __packed, but to conditionally add padding bytes at the end of the struct. That way we keep things working as before on the old platforms, but make things work correctly on aarch64.
I'll add back my patch for EFI_DP_PAD() to this patchset, since this seems like the sensible way forward.
Let's make sure we get an ack from Leif/Ard for that approach :)
Alex

On 8 August 2017 at 07:52, Alexander Graf agraf@suse.de wrote:
Am 07.08.2017 um 23:18 schrieb Rob Clark robdclark@gmail.com:
On Mon, Aug 7, 2017 at 5:14 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Alexander Graf agraf@suse.de Date: Mon, 7 Aug 2017 21:19:37 +0100
On 05.08.17 21:31, Rob Clark wrote:
On Sat, Aug 5, 2017 at 4:05 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: > On 08/05/2017 08:43 PM, Rob Clark wrote: >> On Sat, Aug 5, 2017 at 1:06 PM, Rob Clark robdclark@gmail.com wrote: >>> On Sat, Aug 5, 2017 at 12:52 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: >>>> On 08/05/2017 06:16 PM, Rob Clark wrote: >>>>> On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: >>>>>> On 08/05/2017 05:58 PM, Rob Clark wrote: >>>>>> Some arch's have trouble with unaligned accesses. Technically >>>>>> EFI device-path structs should be byte aligned, and the next node >>>>>> in the path starts immediately after the previous. Meaning that >>>>>> a pointer to an 'struct efi_device_path' is not necessarily word >>>>>> aligned. See section 10.3.1 in v2.7 of UEFI spec. >>>>>> >>>>>> This causes problems not just for u-boot, but also most/all EFI >>>>>> payloads loaded by u-boot on these archs. Fortunately the common >>>>>> practice for traversing a device path is to rely on the length >>>>>> field in the header, rather than the specified length of the >>>>>> particular device path type+subtype. So the EFI_DP_PAD() macro >>>>>> will add the specified number of bytes to the tail of device path >>>>>> structs to pad them to word alignment. >>>>>> >>>>>> Technically this is non-compliant, BROKEN_UNALIGNED should *only* >>>>>> be defined on archs that cannot do unaligned accesses. >>>>>> >>>>>> Signed-off-by: Rob Clark robdclark@gmail.com >>>>>> --- >>>>>> I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED >>>>>> >>>>>> Mark, this is untested but I think it should solve your crash on the >>>>>> Banana Pi. Could you give it a try when you get a chance? >>>>>> >>>>>> arch/arm/config.mk | 2 +- >>>>>> include/efi_api.h | 30 ++++++++++++++++++++++++++++++ >>>>>> lib/efi_loader/efi_device_path.c | 3 +++ >>>>>> 3 files changed, 34 insertions(+), 1 deletion(-) >>>>>> >>>>>> diff --git a/arch/arm/config.mk b/arch/arm/config.mk >>>>>> index 1a77779db4..067dc93a9d 100644 >>>>>> --- a/arch/arm/config.mk >>>>>> +++ b/arch/arm/config.mk >>>>>> @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ >>>>>> $(call cc-option,-arm-use-movt=0,) >>>>>> PLATFORM_RELFLAGS += $(LLVM_RELFLAGS) >>>>>> >>>>>> -PLATFORM_CPPFLAGS += -D__ARM__ >>>>>> +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED >>>>> >>>>> NAK >>>>> >>>>> We have more then ARM. And other architectures also create exceptions >>>>> for unaligned access. >>>>> >>>>> I hate platform specific code. It should not be used outside /arch. >>>>> >>>>> To play it save you should not use _packed at all! >>>>> Use memcpy to transfer between aligned and unaligned memory. >>>> >>>> except for reasons I explained in the thread on the patch that added >>>> the __packed in the first place. Sorry, this is ugly but we have to >>>> do it. >>>> >>>> BR, >>>> -R >>> >>> According to the UEFI standard the nodes in paths are not to be assumed >>> to be aligned. >>> >>> So even if you use padding bytes in paths that you pass to the EFI >>> application you should not expect that the EFI application does the >>> same. Expect the EFI application either to remove them or send new nodes >>> without padding. >>> >>> To the idea of padding bytes and __packed does not make sense. >> >> Ok, to be fair, you are right about device-paths passed too u-boot. >> On BROKEN_UNALIGNED archs, we should sanitise with a copy to an >> aligned device-path in *addition* to what I proposed. I can make a >> patch to add a helper to do this a bit later. > > so thinking about this a bit, I have two options in mind: > > + efi_dp_sanitize() + efi_dp_free() helpers that are no-ops on > archs that can do unaligned access, but efi_dp_sanitize() always > allocates and copies on BROKEN_UNALIGNED archs and efi_dp_free() > always free's on BROKEN_UNALIGNED archs, even if the dp passed > from efi payload doesn't require it. > > + efi_dp_sanitize() that is no-op on archs that can do unaligned > access but only allocates/copies when passed a device path that > would result in unaligned access, plus hook some mechanism to > auto-free on EFI_EXIT().. which is slightly tricky w/ nesting of > efi calls, but not impossible and at least avoids the problem > of missing calls to free the dup'd device-path.. which is the > sort of leak I might miss since I'm not using EFI_LOADER on any > BROKEN_UNALIGNED arch > > anyone who cares about armv7 + efi have a preference between always > doing an extra alloc+copy+free vs EFI_EXIT() magic? >
Please, go for the simplest code.
well, the source of my question was there are two definitions of "simplest" here, ie "simplest to use" and "simplest helpers".. I'm mostly worried about future patches that use the helpers introducing leaks. But actually I think a reasonable middle ground is "simplest helpers plus always make the helpers not no-ops for DEBUG builds"..
I cannot imagine that copying takes more than 10ms for starting grub on any architecture. So the user will not notice it anyway. Assume every architecture requiring alignment for which you do not have proof of the contrary.
Even on ARM64 there are op codes that fail without alignment.
iirc the restrictions where mostly about device memory, and atomics. Which should not apply here. (And we have aarch64 servers in production with grub, which doesn't take any protections about unaligned device-path nodes, using non u-boot EFI implementations. So maybe that counts as proof to the contrary ;-))
The UEFI spec mandates that unaligned access are fixed up properly (usually by hardware). We violate the spec on 32bit ARM, but do fulfill it on AArch64.
Actually for AArch32 the spec says:
Unaligned access should be enabled if supported; Alignment faults are enabled otherwise.
It is a bit strange to me that alignment faults are disabled by default, but presumably is disabled out of reset.. I don't totally understand the connection to having mmu disabled, unless all memory is treated like device memory without mmu enabled. (Which, also, wouldn't that be super-slow?) But anyway, I guess that is a bit beside the point.
It is treated as device memory with mmu disabled, yes.
Yes, and this is really the only sane thing that can be done, given that the page tables not only contain VA/PA translations (which I guess U-boot, like UEFI, does not use), but also the memory types of the various regions. So without page tables, the CPU has no clue which accesses it can cache (to memory) and which accesses it cannot (to devices).
Actually, my recommendation is to always run non-trivial code (i.e., C code) with caching enabled. In UEFI, we do rely on -mno-unaligned access (or -mstrict-align) to ensure the C code does not get optimized to use uncached accesses, but as Alex pointed out, this is not a complete solution.
So UEFI payload that wants to support pre-ARMv7 hardware should make sure it doesn't do any unaligned access.
The reason things worked before really was just that both U-Boot and grub were compiled with -mno-unaligned-access, so they never issued unaligned accesses.
That in itself is not enough to prevent unaligned access. If you explicitly cast an unaligned pointer to say (uint32_t *) and then dereference it, you'll create an unaligned access.
This is problematic around file nodes in the device path. Adding the padding bytes to the end of each device-path struct would "solve" that, and if pre-aarch64 we are aiming at "good enough to work", I kinda think that this the approach we should go for. Other than file-path nodes, the rest of the issues in u-boot should be solved by addition of missing __packed on 'struct efi_device_path' (which I've added locally and will be in the next revision of the patchset).
Let's ask Leif and Ard if that is actually correct. If I remember correctly, edk2 some times leverages automatic padding from the compiler on structs.
I guess that that might work, if U-boot is the only agent instantiating device path structures. But what do you mean by 'correct' in this context?
Which system broke for Mark? Banana pi mostly has 32bit SBCs, so I assume it's one of those? In that case, we would need to either
- hack up the openbsd loader to also get compiled with
-mno-unaligned-access
or
- rework the 32bit arm mmu code to enable the MMU plus hardware
unaligned fixups on all systems. (that's a *lot* of work)
The system that broke was Cortex-A7 based, so ARMv7.
*However*, the OpenBSD bootloader does not perform any unaligned access. *All* cases of unaligned access that I encountered while testing/debugging Rob's code were in U-Boot itself. This was mostly a consequence from a diff in the series that added __packed to the device path node structs and then passed members of those structs to functions that accessed the contents of tose (now byte aligned structs) through (uint16_t *) or (uint32_t *) pointers.
Yeah.. the padding byte approach I suggested would solve this. Not *really* compliant, but should work for all the payload code I've come across. We definitely shouldn't enable the padding bytes on aarch64, but I think padding bytes would be a pragmatic solution for armv7 and earlier. (And hopefully someday someone enables mmu on armv7 so we can turn off the hack there too.)
The problem really is inconsistency. Some 32bit arm platforms enable the mmu, some don't.
There was initially some confusion because I used the pc reported by U-Boot to look up the faulting instruction whereas I should have been using the reloc_pc.
I think Rob is worried about grub/shim/fallback.efi doing unaligned access of device path nodes on armv7. But I'm not sure he has any evidence beyond looking at code.
It will definitely be an issue (if grub/shim/fallback were not compiled with -mno-unaligned-access) in some of the debug code that prints out device-paths. I haven't checked the compiler flags used w/ shim/fallback.
Parsing MEDIA_DEVICE:HARD_DRIVE nodes would be an issue regardless without -mno-unaligned-access, although we might be getting lucky by just not hitting debug paths. Some of the other issues would be hidden by padding bytes at the end of the DP structs which would keep the file-path nodes word aligned.
To me personally, UEFI in 32bit ARM land is a nice to have standardized platform, but not something where we really need to be fully standards compliant in every aspect. I think "making things work" is good enough there.
Right. For us UEFI us just a convenient way to re-use the U-Boot device driver code to load a kernel from a UFS/FFS filesystem.
For AArch64 things are different. There we should strive for full UEFI compliance as it's "the future" :).
Even there just making things work would be good enough for me ;). Our AArach64 bootloader is almost identical to the AArch32 one and works on a real UEFI implementation as well (SoftIron Overdrive 1000).
I think we should make the aarch64 implementation fully compliant (ie. addition of missing __packed's and no extra padding bytes). I won't loose sleep if some efi payloads don't work on pre-aarch64 (we should be able to keep things that were working before working). If adding missing __packed breaks things on platforms that can't do unaligned access, the solution is not to remove __packed, but to conditionally add padding bytes at the end of the struct. That way we keep things working as before on the old platforms, but make things work correctly on aarch64.
I'll add back my patch for EFI_DP_PAD() to this patchset, since this seems like the sensible way forward.
Let's make sure we get an ack from Leif/Ard for that approach :)
Alex

On Tue, Aug 08, 2017 at 09:11:14AM +0100, Ard Biesheuvel wrote:
On 8 August 2017 at 07:52, Alexander Graf agraf@suse.de wrote:
Am 07.08.2017 um 23:18 schrieb Rob Clark robdclark@gmail.com:
On Mon, Aug 7, 2017 at 5:14 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Alexander Graf agraf@suse.de Date: Mon, 7 Aug 2017 21:19:37 +0100
On 05.08.17 21:31, Rob Clark wrote: > On Sat, Aug 5, 2017 at 4:05 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: >> On 08/05/2017 08:43 PM, Rob Clark wrote: >>> On Sat, Aug 5, 2017 at 1:06 PM, Rob Clark robdclark@gmail.com wrote: >>>> On Sat, Aug 5, 2017 at 12:52 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: >>>>> On 08/05/2017 06:16 PM, Rob Clark wrote: >>>>>> On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: >>>>>>> On 08/05/2017 05:58 PM, Rob Clark wrote: >>>>>>> Some arch's have trouble with unaligned accesses. Technically >>>>>>> EFI device-path structs should be byte aligned, and the next node >>>>>>> in the path starts immediately after the previous. Meaning that >>>>>>> a pointer to an 'struct efi_device_path' is not necessarily word >>>>>>> aligned. See section 10.3.1 in v2.7 of UEFI spec. >>>>>>> >>>>>>> This causes problems not just for u-boot, but also most/all EFI >>>>>>> payloads loaded by u-boot on these archs. Fortunately the common >>>>>>> practice for traversing a device path is to rely on the length >>>>>>> field in the header, rather than the specified length of the >>>>>>> particular device path type+subtype. So the EFI_DP_PAD() macro >>>>>>> will add the specified number of bytes to the tail of device path >>>>>>> structs to pad them to word alignment. >>>>>>> >>>>>>> Technically this is non-compliant, BROKEN_UNALIGNED should *only* >>>>>>> be defined on archs that cannot do unaligned accesses. >>>>>>> >>>>>>> Signed-off-by: Rob Clark robdclark@gmail.com >>>>>>> --- >>>>>>> I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED >>>>>>> >>>>>>> Mark, this is untested but I think it should solve your crash on the >>>>>>> Banana Pi. Could you give it a try when you get a chance? >>>>>>> >>>>>>> arch/arm/config.mk | 2 +- >>>>>>> include/efi_api.h | 30 ++++++++++++++++++++++++++++++ >>>>>>> lib/efi_loader/efi_device_path.c | 3 +++ >>>>>>> 3 files changed, 34 insertions(+), 1 deletion(-) >>>>>>> >>>>>>> diff --git a/arch/arm/config.mk b/arch/arm/config.mk >>>>>>> index 1a77779db4..067dc93a9d 100644 >>>>>>> --- a/arch/arm/config.mk >>>>>>> +++ b/arch/arm/config.mk >>>>>>> @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ >>>>>>> $(call cc-option,-arm-use-movt=0,) >>>>>>> PLATFORM_RELFLAGS += $(LLVM_RELFLAGS) >>>>>>> >>>>>>> -PLATFORM_CPPFLAGS += -D__ARM__ >>>>>>> +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED >>>>>> >>>>>> NAK >>>>>> >>>>>> We have more then ARM. And other architectures also create exceptions >>>>>> for unaligned access. >>>>>> >>>>>> I hate platform specific code. It should not be used outside /arch. >>>>>> >>>>>> To play it save you should not use _packed at all! >>>>>> Use memcpy to transfer between aligned and unaligned memory. >>>>> >>>>> except for reasons I explained in the thread on the patch that added >>>>> the __packed in the first place. Sorry, this is ugly but we have to >>>>> do it. >>>>> >>>>> BR, >>>>> -R >>>> >>>> According to the UEFI standard the nodes in paths are not to be assumed >>>> to be aligned. >>>> >>>> So even if you use padding bytes in paths that you pass to the EFI >>>> application you should not expect that the EFI application does the >>>> same. Expect the EFI application either to remove them or send new nodes >>>> without padding. >>>> >>>> To the idea of padding bytes and __packed does not make sense. >>> >>> Ok, to be fair, you are right about device-paths passed too u-boot. >>> On BROKEN_UNALIGNED archs, we should sanitise with a copy to an >>> aligned device-path in *addition* to what I proposed. I can make a >>> patch to add a helper to do this a bit later. >> >> so thinking about this a bit, I have two options in mind: >> >> + efi_dp_sanitize() + efi_dp_free() helpers that are no-ops on >> archs that can do unaligned access, but efi_dp_sanitize() always >> allocates and copies on BROKEN_UNALIGNED archs and efi_dp_free() >> always free's on BROKEN_UNALIGNED archs, even if the dp passed >> from efi payload doesn't require it. >> >> + efi_dp_sanitize() that is no-op on archs that can do unaligned >> access but only allocates/copies when passed a device path that >> would result in unaligned access, plus hook some mechanism to >> auto-free on EFI_EXIT().. which is slightly tricky w/ nesting of >> efi calls, but not impossible and at least avoids the problem >> of missing calls to free the dup'd device-path.. which is the >> sort of leak I might miss since I'm not using EFI_LOADER on any >> BROKEN_UNALIGNED arch >> >> anyone who cares about armv7 + efi have a preference between always >> doing an extra alloc+copy+free vs EFI_EXIT() magic? >> > > Please, go for the simplest code.
well, the source of my question was there are two definitions of "simplest" here, ie "simplest to use" and "simplest helpers".. I'm mostly worried about future patches that use the helpers introducing leaks. But actually I think a reasonable middle ground is "simplest helpers plus always make the helpers not no-ops for DEBUG builds"..
> I cannot imagine that copying takes more than 10ms for starting grub on > any architecture. So the user will not notice it anyway. > Assume every architecture requiring alignment for which you do not have > proof of the contrary. > > Even on ARM64 there are op codes that fail without alignment.
iirc the restrictions where mostly about device memory, and atomics. Which should not apply here. (And we have aarch64 servers in production with grub, which doesn't take any protections about unaligned device-path nodes, using non u-boot EFI implementations. So maybe that counts as proof to the contrary ;-))
The UEFI spec mandates that unaligned access are fixed up properly (usually by hardware). We violate the spec on 32bit ARM, but do fulfill it on AArch64.
Actually for AArch32 the spec says:
Unaligned access should be enabled if supported; Alignment faults are enabled otherwise.
It is a bit strange to me that alignment faults are disabled by default, but presumably is disabled out of reset.. I don't totally understand the connection to having mmu disabled, unless all memory is treated like device memory without mmu enabled. (Which, also, wouldn't that be super-slow?) But anyway, I guess that is a bit beside the point.
It is treated as device memory with mmu disabled, yes.
Yes, and this is really the only sane thing that can be done, given that the page tables not only contain VA/PA translations (which I guess U-boot, like UEFI, does not use), but also the memory types of the various regions. So without page tables, the CPU has no clue which accesses it can cache (to memory) and which accesses it cannot (to devices).
Actually, my recommendation is to always run non-trivial code (i.e., C code) with caching enabled. In UEFI, we do rely on -mno-unaligned access (or -mstrict-align) to ensure the C code does not get optimized to use uncached accesses, but as Alex pointed out, this is not a complete solution.
To nitpick: in EDK2, we rely on -mstrict-align/-mno-unaligned-access in the PEI (Pre-EFI Initilization) phase. Once we present a UEFI-compliant interface, unaligned accesses are used.
So UEFI payload that wants to support pre-ARMv7 hardware should make sure it doesn't do any unaligned access.
The reason things worked before really was just that both U-Boot and grub were compiled with -mno-unaligned-access, so they never issued unaligned accesses.
That in itself is not enough to prevent unaligned access. If you explicitly cast an unaligned pointer to say (uint32_t *) and then dereference it, you'll create an unaligned access.
This is problematic around file nodes in the device path. Adding the padding bytes to the end of each device-path struct would "solve" that, and if pre-aarch64 we are aiming at "good enough to work", I kinda think that this the approach we should go for. Other than file-path nodes, the rest of the issues in u-boot should be solved by addition of missing __packed on 'struct efi_device_path' (which I've added locally and will be in the next revision of the patchset).
Let's ask Leif and Ard if that is actually correct. If I remember correctly, edk2 some times leverages automatic padding from the compiler on structs.
I guess that that might work, if U-boot is the only agent instantiating device path structures. But what do you mean by 'correct' in this context?
Well, if that is the case, are we risking the ability to in the future support loading drivers or protocols at runtime. (This would for example exclude Shim compatibility or running the UEFI Shell.)
/ Leif

On Tue, Aug 8, 2017 at 7:32 AM, Leif Lindholm leif.lindholm@linaro.org wrote:
On Tue, Aug 08, 2017 at 09:11:14AM +0100, Ard Biesheuvel wrote:
On 8 August 2017 at 07:52, Alexander Graf agraf@suse.de wrote:
Am 07.08.2017 um 23:18 schrieb Rob Clark robdclark@gmail.com:
This is problematic around file nodes in the device path. Adding the padding bytes to the end of each device-path struct would "solve" that, and if pre-aarch64 we are aiming at "good enough to work", I kinda think that this the approach we should go for. Other than file-path nodes, the rest of the issues in u-boot should be solved by addition of missing __packed on 'struct efi_device_path' (which I've added locally and will be in the next revision of the patchset).
Let's ask Leif and Ard if that is actually correct. If I remember correctly, edk2 some times leverages automatic padding from the compiler on structs.
I guess that that might work, if U-boot is the only agent instantiating device path structures. But what do you mean by 'correct' in this context?
Well, if that is the case, are we risking the ability to in the future support loading drivers or protocols at runtime. (This would for example exclude Shim compatibility or running the UEFI Shell.)
My proposal (and this is only for <=armv6 and armv7 until someone gets around to enabling mmu and disabling alignment faults) is to add padding bytes at the end of the various device-path structs to at least keep the structs (and things like utf16 string in file-path struct) aligned, and rely on efi payload and u-boot to be compiled with -mno-unaligned-access if it needs to access fields within the device-path structs.
This is *essentially* what u-boot does implicitly at the moment (by missing __packed attribute on certain structs). I want to fix that on aarch64, but without the padding bytes it causes a some unaligned accesses in u-boot on armv7 devices.
I think the goal for armv7 is more to have enough uefi for grub and OpenBSD's bootloader. If fancy things like loading drivers and protocols at runtime doesn't work, well these didn't work before so I won't loose much sleep. But I would like that to work properly on aarch64.
BR, -R

On Tue, Aug 08, 2017 at 08:01:10AM -0400, Rob Clark wrote:
On Tue, Aug 8, 2017 at 7:32 AM, Leif Lindholm leif.lindholm@linaro.org wrote:
On Tue, Aug 08, 2017 at 09:11:14AM +0100, Ard Biesheuvel wrote:
On 8 August 2017 at 07:52, Alexander Graf agraf@suse.de wrote:
Am 07.08.2017 um 23:18 schrieb Rob Clark robdclark@gmail.com:
This is problematic around file nodes in the device path. Adding the padding bytes to the end of each device-path struct would "solve" that, and if pre-aarch64 we are aiming at "good enough to work", I kinda think that this the approach we should go for. Other than file-path nodes, the rest of the issues in u-boot should be solved by addition of missing __packed on 'struct efi_device_path' (which I've added locally and will be in the next revision of the patchset).
Let's ask Leif and Ard if that is actually correct. If I remember correctly, edk2 some times leverages automatic padding from the compiler on structs.
I guess that that might work, if U-boot is the only agent instantiating device path structures. But what do you mean by 'correct' in this context?
Well, if that is the case, are we risking the ability to in the future support loading drivers or protocols at runtime. (This would for example exclude Shim compatibility or running the UEFI Shell.)
My proposal (and this is only for <=armv6 and armv7 until someone gets around to enabling mmu and disabling alignment faults) is to add padding bytes at the end of the various device-path structs to at least keep the structs (and things like utf16 string in file-path struct) aligned, and rely on efi payload and u-boot to be compiled with -mno-unaligned-access if it needs to access fields within the device-path structs.
This is *essentially* what u-boot does implicitly at the moment (by missing __packed attribute on certain structs). I want to fix that on aarch64, but without the padding bytes it causes a some unaligned accesses in u-boot on armv7 devices.
I think the goal for armv7 is more to have enough uefi for grub and OpenBSD's bootloader. If fancy things like loading drivers and protocols at runtime doesn't work, well these didn't work before so I won't loose much sleep. But I would like that to work properly on aarch64.
I'm all for the just enough approach (I just keep hoping for feature creep). If this means certain aspects will not be supportable, what I want is for it to be not supportable in a predictable manner.
So I guess what I'd like is that if we do this, then we either turn the efi_*install_* functions back into just returning EFI_OUT_OF_RESOURCES on these platforms, or worst case make them scream bloody murder (but progress and hope for the best - like [1]).
[1] https://lists.denx.de/pipermail/u-boot/2016-January/241454.html
/ Leif

On Tue, Aug 8, 2017 at 2:52 AM, Alexander Graf agraf@suse.de wrote:
Am 07.08.2017 um 23:18 schrieb Rob Clark robdclark@gmail.com:
On Mon, Aug 7, 2017 at 5:14 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Alexander Graf agraf@suse.de Date: Mon, 7 Aug 2017 21:19:37 +0100
On 05.08.17 21:31, Rob Clark wrote:
On Sat, Aug 5, 2017 at 4:05 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: > On 08/05/2017 08:43 PM, Rob Clark wrote: >> On Sat, Aug 5, 2017 at 1:06 PM, Rob Clark robdclark@gmail.com wrote: >>> On Sat, Aug 5, 2017 at 12:52 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: >>>> On 08/05/2017 06:16 PM, Rob Clark wrote: >>>>> On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: >>>>>> On 08/05/2017 05:58 PM, Rob Clark wrote: >>>>>> Some arch's have trouble with unaligned accesses. Technically >>>>>> EFI device-path structs should be byte aligned, and the next node >>>>>> in the path starts immediately after the previous. Meaning that >>>>>> a pointer to an 'struct efi_device_path' is not necessarily word >>>>>> aligned. See section 10.3.1 in v2.7 of UEFI spec. >>>>>> >>>>>> This causes problems not just for u-boot, but also most/all EFI >>>>>> payloads loaded by u-boot on these archs. Fortunately the common >>>>>> practice for traversing a device path is to rely on the length >>>>>> field in the header, rather than the specified length of the >>>>>> particular device path type+subtype. So the EFI_DP_PAD() macro >>>>>> will add the specified number of bytes to the tail of device path >>>>>> structs to pad them to word alignment. >>>>>> >>>>>> Technically this is non-compliant, BROKEN_UNALIGNED should *only* >>>>>> be defined on archs that cannot do unaligned accesses. >>>>>> >>>>>> Signed-off-by: Rob Clark robdclark@gmail.com >>>>>> --- >>>>>> I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED >>>>>> >>>>>> Mark, this is untested but I think it should solve your crash on the >>>>>> Banana Pi. Could you give it a try when you get a chance? >>>>>> >>>>>> arch/arm/config.mk | 2 +- >>>>>> include/efi_api.h | 30 ++++++++++++++++++++++++++++++ >>>>>> lib/efi_loader/efi_device_path.c | 3 +++ >>>>>> 3 files changed, 34 insertions(+), 1 deletion(-) >>>>>> >>>>>> diff --git a/arch/arm/config.mk b/arch/arm/config.mk >>>>>> index 1a77779db4..067dc93a9d 100644 >>>>>> --- a/arch/arm/config.mk >>>>>> +++ b/arch/arm/config.mk >>>>>> @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ >>>>>> $(call cc-option,-arm-use-movt=0,) >>>>>> PLATFORM_RELFLAGS += $(LLVM_RELFLAGS) >>>>>> >>>>>> -PLATFORM_CPPFLAGS += -D__ARM__ >>>>>> +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED >>>>> >>>>> NAK >>>>> >>>>> We have more then ARM. And other architectures also create exceptions >>>>> for unaligned access. >>>>> >>>>> I hate platform specific code. It should not be used outside /arch. >>>>> >>>>> To play it save you should not use _packed at all! >>>>> Use memcpy to transfer between aligned and unaligned memory. >>>> >>>> except for reasons I explained in the thread on the patch that added >>>> the __packed in the first place. Sorry, this is ugly but we have to >>>> do it. >>>> >>>> BR, >>>> -R >>> >>> According to the UEFI standard the nodes in paths are not to be assumed >>> to be aligned. >>> >>> So even if you use padding bytes in paths that you pass to the EFI >>> application you should not expect that the EFI application does the >>> same. Expect the EFI application either to remove them or send new nodes >>> without padding. >>> >>> To the idea of padding bytes and __packed does not make sense. >> >> Ok, to be fair, you are right about device-paths passed too u-boot. >> On BROKEN_UNALIGNED archs, we should sanitise with a copy to an >> aligned device-path in *addition* to what I proposed. I can make a >> patch to add a helper to do this a bit later. > > so thinking about this a bit, I have two options in mind: > > + efi_dp_sanitize() + efi_dp_free() helpers that are no-ops on > archs that can do unaligned access, but efi_dp_sanitize() always > allocates and copies on BROKEN_UNALIGNED archs and efi_dp_free() > always free's on BROKEN_UNALIGNED archs, even if the dp passed > from efi payload doesn't require it. > > + efi_dp_sanitize() that is no-op on archs that can do unaligned > access but only allocates/copies when passed a device path that > would result in unaligned access, plus hook some mechanism to > auto-free on EFI_EXIT().. which is slightly tricky w/ nesting of > efi calls, but not impossible and at least avoids the problem > of missing calls to free the dup'd device-path.. which is the > sort of leak I might miss since I'm not using EFI_LOADER on any > BROKEN_UNALIGNED arch > > anyone who cares about armv7 + efi have a preference between always > doing an extra alloc+copy+free vs EFI_EXIT() magic? >
Please, go for the simplest code.
well, the source of my question was there are two definitions of "simplest" here, ie "simplest to use" and "simplest helpers".. I'm mostly worried about future patches that use the helpers introducing leaks. But actually I think a reasonable middle ground is "simplest helpers plus always make the helpers not no-ops for DEBUG builds"..
I cannot imagine that copying takes more than 10ms for starting grub on any architecture. So the user will not notice it anyway. Assume every architecture requiring alignment for which you do not have proof of the contrary.
Even on ARM64 there are op codes that fail without alignment.
iirc the restrictions where mostly about device memory, and atomics. Which should not apply here. (And we have aarch64 servers in production with grub, which doesn't take any protections about unaligned device-path nodes, using non u-boot EFI implementations. So maybe that counts as proof to the contrary ;-))
The UEFI spec mandates that unaligned access are fixed up properly (usually by hardware). We violate the spec on 32bit ARM, but do fulfill it on AArch64.
Actually for AArch32 the spec says:
Unaligned access should be enabled if supported; Alignment faults are enabled otherwise.
It is a bit strange to me that alignment faults are disabled by default, but presumably is disabled out of reset.. I don't totally understand the connection to having mmu disabled, unless all memory is treated like device memory without mmu enabled. (Which, also, wouldn't that be super-slow?) But anyway, I guess that is a bit beside the point.
It is treated as device memory with mmu disabled, yes.
So UEFI payload that wants to support pre-ARMv7 hardware should make sure it doesn't do any unaligned access.
The reason things worked before really was just that both U-Boot and grub were compiled with -mno-unaligned-access, so they never issued unaligned accesses.
That in itself is not enough to prevent unaligned access. If you explicitly cast an unaligned pointer to say (uint32_t *) and then dereference it, you'll create an unaligned access.
This is problematic around file nodes in the device path. Adding the padding bytes to the end of each device-path struct would "solve" that, and if pre-aarch64 we are aiming at "good enough to work", I kinda think that this the approach we should go for. Other than file-path nodes, the rest of the issues in u-boot should be solved by addition of missing __packed on 'struct efi_device_path' (which I've added locally and will be in the next revision of the patchset).
Let's ask Leif and Ard if that is actually correct. If I remember correctly, edk2 some times leverages automatic padding from the compiler on structs.
in edk2's DevicePath.h it does '#pragma pack(1)' before all the device path structs (aka equiv of adding packed attribute to each struct) and then '#pragma pack()' after (disabling the packing)
Also, fwiw I couldn't find anywhere in edk2 that was enabling alignment faults, but probably they have the mmu enabled and configured 1:1 similar to aarch64 in u-boot.
BR, -R

From: Rob Clark robdclark@gmail.com Date: Mon, 7 Aug 2017 18:18:50 -0400
On Mon, Aug 7, 2017 at 5:14 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Alexander Graf agraf@suse.de Date: Mon, 7 Aug 2017 21:19:37 +0100
On 05.08.17 21:31, Rob Clark wrote:
On Sat, Aug 5, 2017 at 4:05 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 08:43 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 1:06 PM, Rob Clark robdclark@gmail.com wrote: > On Sat, Aug 5, 2017 at 12:52 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: >> On 08/05/2017 06:16 PM, Rob Clark wrote: >>> On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote: >>>> On 08/05/2017 05:58 PM, Rob Clark wrote: >>>>> Some arch's have trouble with unaligned accesses. Technically >>>>> EFI device-path structs should be byte aligned, and the next node >>>>> in the path starts immediately after the previous. Meaning that >>>>> a pointer to an 'struct efi_device_path' is not necessarily word >>>>> aligned. See section 10.3.1 in v2.7 of UEFI spec. >>>>> >>>>> This causes problems not just for u-boot, but also most/all EFI >>>>> payloads loaded by u-boot on these archs. Fortunately the common >>>>> practice for traversing a device path is to rely on the length >>>>> field in the header, rather than the specified length of the >>>>> particular device path type+subtype. So the EFI_DP_PAD() macro >>>>> will add the specified number of bytes to the tail of device path >>>>> structs to pad them to word alignment. >>>>> >>>>> Technically this is non-compliant, BROKEN_UNALIGNED should *only* >>>>> be defined on archs that cannot do unaligned accesses. >>>>> >>>>> Signed-off-by: Rob Clark robdclark@gmail.com >>>>> --- >>>>> I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED >>>>> >>>>> Mark, this is untested but I think it should solve your crash on the >>>>> Banana Pi. Could you give it a try when you get a chance? >>>>> >>>>> arch/arm/config.mk | 2 +- >>>>> include/efi_api.h | 30 ++++++++++++++++++++++++++++++ >>>>> lib/efi_loader/efi_device_path.c | 3 +++ >>>>> 3 files changed, 34 insertions(+), 1 deletion(-) >>>>> >>>>> diff --git a/arch/arm/config.mk b/arch/arm/config.mk >>>>> index 1a77779db4..067dc93a9d 100644 >>>>> --- a/arch/arm/config.mk >>>>> +++ b/arch/arm/config.mk >>>>> @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ >>>>> $(call cc-option,-arm-use-movt=0,) >>>>> PLATFORM_RELFLAGS += $(LLVM_RELFLAGS) >>>>> >>>>> -PLATFORM_CPPFLAGS += -D__ARM__ >>>>> +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED >>>> >>>> NAK >>>> >>>> We have more then ARM. And other architectures also create exceptions >>>> for unaligned access. >>>> >>>> I hate platform specific code. It should not be used outside /arch. >>>> >>>> To play it save you should not use _packed at all! >>>> Use memcpy to transfer between aligned and unaligned memory. >>> >>> except for reasons I explained in the thread on the patch that added >>> the __packed in the first place. Sorry, this is ugly but we have to >>> do it. >>> >>> BR, >>> -R >> >> According to the UEFI standard the nodes in paths are not to be assumed >> to be aligned. >> >> So even if you use padding bytes in paths that you pass to the EFI >> application you should not expect that the EFI application does the >> same. Expect the EFI application either to remove them or send new nodes >> without padding. >> >> To the idea of padding bytes and __packed does not make sense. > > Ok, to be fair, you are right about device-paths passed too u-boot. > On BROKEN_UNALIGNED archs, we should sanitise with a copy to an > aligned device-path in *addition* to what I proposed. I can make a > patch to add a helper to do this a bit later.
so thinking about this a bit, I have two options in mind:
efi_dp_sanitize() + efi_dp_free() helpers that are no-ops on archs that can do unaligned access, but efi_dp_sanitize() always allocates and copies on BROKEN_UNALIGNED archs and efi_dp_free() always free's on BROKEN_UNALIGNED archs, even if the dp passed from efi payload doesn't require it.
efi_dp_sanitize() that is no-op on archs that can do unaligned access but only allocates/copies when passed a device path that would result in unaligned access, plus hook some mechanism to auto-free on EFI_EXIT().. which is slightly tricky w/ nesting of efi calls, but not impossible and at least avoids the problem of missing calls to free the dup'd device-path.. which is the sort of leak I might miss since I'm not using EFI_LOADER on any BROKEN_UNALIGNED arch
anyone who cares about armv7 + efi have a preference between always doing an extra alloc+copy+free vs EFI_EXIT() magic?
Please, go for the simplest code.
well, the source of my question was there are two definitions of "simplest" here, ie "simplest to use" and "simplest helpers".. I'm mostly worried about future patches that use the helpers introducing leaks. But actually I think a reasonable middle ground is "simplest helpers plus always make the helpers not no-ops for DEBUG builds"..
I cannot imagine that copying takes more than 10ms for starting grub on any architecture. So the user will not notice it anyway. Assume every architecture requiring alignment for which you do not have proof of the contrary.
Even on ARM64 there are op codes that fail without alignment.
iirc the restrictions where mostly about device memory, and atomics. Which should not apply here. (And we have aarch64 servers in production with grub, which doesn't take any protections about unaligned device-path nodes, using non u-boot EFI implementations. So maybe that counts as proof to the contrary ;-))
The UEFI spec mandates that unaligned access are fixed up properly (usually by hardware). We violate the spec on 32bit ARM, but do fulfill it on AArch64.
Actually for AArch32 the spec says:
Unaligned access should be enabled if supported; Alignment faults are enabled otherwise.
It is a bit strange to me that alignment faults are disabled by default, but presumably is disabled out of reset.. I don't totally understand the connection to having mmu disabled, unless all memory is treated like device memory without mmu enabled. (Which, also, wouldn't that be super-slow?) But anyway, I guess that is a bit beside the point.
So UEFI payload that wants to support pre-ARMv7 hardware should make sure it doesn't do any unaligned access.
The reason things worked before really was just that both U-Boot and grub were compiled with -mno-unaligned-access, so they never issued unaligned accesses.
That in itself is not enough to prevent unaligned access. If you explicitly cast an unaligned pointer to say (uint32_t *) and then dereference it, you'll create an unaligned access.
This is problematic around file nodes in the device path. Adding the padding bytes to the end of each device-path struct would "solve" that, and if pre-aarch64 we are aiming at "good enough to work", I kinda think that this the approach we should go for. Other than file-path nodes, the rest of the issues in u-boot should be solved by addition of missing __packed on 'struct efi_device_path' (which I've added locally and will be in the next revision of the patchset).
Which system broke for Mark? Banana pi mostly has 32bit SBCs, so I assume it's one of those? In that case, we would need to either
- hack up the openbsd loader to also get compiled with
-mno-unaligned-access
or
- rework the 32bit arm mmu code to enable the MMU plus hardware
unaligned fixups on all systems. (that's a *lot* of work)
The system that broke was Cortex-A7 based, so ARMv7.
*However*, the OpenBSD bootloader does not perform any unaligned access. *All* cases of unaligned access that I encountered while testing/debugging Rob's code were in U-Boot itself. This was mostly a consequence from a diff in the series that added __packed to the device path node structs and then passed members of those structs to functions that accessed the contents of tose (now byte aligned structs) through (uint16_t *) or (uint32_t *) pointers.
Yeah.. the padding byte approach I suggested would solve this. Not *really* compliant, but should work for all the payload code I've come across. We definitely shouldn't enable the padding bytes on aarch64, but I think padding bytes would be a pragmatic solution for armv7 and earlier. (And hopefully someday someone enables mmu on armv7 so we can turn off the hack there too.)
There was initially some confusion because I used the pc reported by U-Boot to look up the faulting instruction whereas I should have been using the reloc_pc.
I think Rob is worried about grub/shim/fallback.efi doing unaligned access of device path nodes on armv7. But I'm not sure he has any evidence beyond looking at code.
It will definitely be an issue (if grub/shim/fallback were not compiled with -mno-unaligned-access) in some of the debug code that prints out device-paths. I haven't checked the compiler flags used w/ shim/fallback.
Parsing MEDIA_DEVICE:HARD_DRIVE nodes would be an issue regardless without -mno-unaligned-access, although we might be getting lucky by just not hitting debug paths. Some of the other issues would be hidden by padding bytes at the end of the DP structs which would keep the file-path nodes word aligned.
To me personally, UEFI in 32bit ARM land is a nice to have standardized platform, but not something where we really need to be fully standards compliant in every aspect. I think "making things work" is good enough there.
Right. For us UEFI us just a convenient way to re-use the U-Boot device driver code to load a kernel from a UFS/FFS filesystem.
For AArch64 things are different. There we should strive for full UEFI compliance as it's "the future" :).
Even there just making things work would be good enough for me ;). Our AArach64 bootloader is almost identical to the AArch32 one and works on a real UEFI implementation as well (SoftIron Overdrive 1000).
I think we should make the aarch64 implementation fully compliant (ie. addition of missing __packed's and no extra padding bytes). I won't loose sleep if some efi payloads don't work on pre-aarch64 (we should be able to keep things that were working before working). If adding missing __packed breaks things on platforms that can't do unaligned access, the solution is not to remove __packed, but to conditionally add padding bytes at the end of the struct. That way we keep things working as before on the old platforms, but make things work correctly on aarch64.
I'll add back my patch for EFI_DP_PAD() to this patchset, since this seems like the sensible way forward.
Adding padding does not magically align things on a >8-bit boundary. And as Peter pointed out, the hard drive device path type is inherently unalignable. The UEFI payload simply has to deal with that. If grub/shim/fallback.efi is currently broken, it will have to be fixed to be usable with U-Boot on 32-bit ARM.
The problem that needs to be fixed is the manipulation of device path nodes in U-Boot itself. Using __packed and -mno-aligned-access should take care of any parsing done through the device path node struct types. But whenever a member of such a struct is passed to some other function that function should be careful not to do unaligned access. After you fixed the GUID printing code it seems ascii2unicode() is the only function left that does an unaligned access. Perhaps the solution here is to simply stop using file path nodes in U-Boot like you already suggested. Or otherwise just do the ascii2unicode() conversion into a suitably aligned buffer and memcpy() that into place.
Cheers,
Mark

On Tue, Aug 8, 2017 at 8:26 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Mon, 7 Aug 2017 18:18:50 -0400
On Mon, Aug 7, 2017 at 5:14 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Alexander Graf agraf@suse.de Date: Mon, 7 Aug 2017 21:19:37 +0100
For AArch64 things are different. There we should strive for full UEFI compliance as it's "the future" :).
Even there just making things work would be good enough for me ;). Our AArach64 bootloader is almost identical to the AArch32 one and works on a real UEFI implementation as well (SoftIron Overdrive 1000).
I think we should make the aarch64 implementation fully compliant (ie. addition of missing __packed's and no extra padding bytes). I won't loose sleep if some efi payloads don't work on pre-aarch64 (we should be able to keep things that were working before working). If adding missing __packed breaks things on platforms that can't do unaligned access, the solution is not to remove __packed, but to conditionally add padding bytes at the end of the struct. That way we keep things working as before on the old platforms, but make things work correctly on aarch64.
I'll add back my patch for EFI_DP_PAD() to this patchset, since this seems like the sensible way forward.
Adding padding does not magically align things on a >8-bit boundary. And as Peter pointed out, the hard drive device path type is inherently unalignable. The UEFI payload simply has to deal with that. If grub/shim/fallback.efi is currently broken, it will have to be fixed to be usable with U-Boot on 32-bit ARM.
The problem that needs to be fixed is the manipulation of device path nodes in U-Boot itself. Using __packed and -mno-aligned-access should take care of any parsing done through the device path node struct types. But whenever a member of such a struct is passed to some other function that function should be careful not to do unaligned access. After you fixed the GUID printing code it seems ascii2unicode() is the only function left that does an unaligned access. Perhaps the solution here is to simply stop using file path nodes in U-Boot like you already suggested. Or otherwise just do the ascii2unicode() conversion into a suitably aligned buffer and memcpy() that into place.
It is also a problem with all the utf16 string handling, which shows up all over the place. For example if efi payload (or efi_load_image()) took the file path string from a device-path, which was unaligned. I'm not really a fan of trying to deal with this everywhere, since it is making the code much more cumbersome for the benefit of platforms that are broken (and still will be broken in ways we cannot prevent) from an EFI standpoint.
So I'm pretty much NAK on playing whack-a-mole with unaligned utf16 string handling in u-boot. Padding bytes at end of device-path structs won't solve everything.. that can only be done by making unaligned accesses work. (And hey, maybe we can trap the faults and emulate on armv6 if someone cares enough?) But the padding bytes will keep things working to the extent that they work today, in an non-obtrusive way, that is good enough for armv6/armv7 and it lets us fix things properly on aarch64. So that is progress.
(And I kind of agree with Leif from the other email on this thread that efi_*install* should probably scream scary warning msgs on BROKEN_UNALIGNED platforms.)
BR, -R

From: Rob Clark robdclark@gmail.com Date: Tue, 8 Aug 2017 08:54:26 -0400
On Tue, Aug 8, 2017 at 8:26 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Mon, 7 Aug 2017 18:18:50 -0400
On Mon, Aug 7, 2017 at 5:14 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Alexander Graf agraf@suse.de Date: Mon, 7 Aug 2017 21:19:37 +0100
For AArch64 things are different. There we should strive for full UEFI compliance as it's "the future" :).
Even there just making things work would be good enough for me ;). Our AArach64 bootloader is almost identical to the AArch32 one and works on a real UEFI implementation as well (SoftIron Overdrive 1000).
I think we should make the aarch64 implementation fully compliant (ie. addition of missing __packed's and no extra padding bytes). I won't loose sleep if some efi payloads don't work on pre-aarch64 (we should be able to keep things that were working before working). If adding missing __packed breaks things on platforms that can't do unaligned access, the solution is not to remove __packed, but to conditionally add padding bytes at the end of the struct. That way we keep things working as before on the old platforms, but make things work correctly on aarch64.
I'll add back my patch for EFI_DP_PAD() to this patchset, since this seems like the sensible way forward.
Adding padding does not magically align things on a >8-bit boundary. And as Peter pointed out, the hard drive device path type is inherently unalignable. The UEFI payload simply has to deal with that. If grub/shim/fallback.efi is currently broken, it will have to be fixed to be usable with U-Boot on 32-bit ARM.
The problem that needs to be fixed is the manipulation of device path nodes in U-Boot itself. Using __packed and -mno-aligned-access should take care of any parsing done through the device path node struct types. But whenever a member of such a struct is passed to some other function that function should be careful not to do unaligned access. After you fixed the GUID printing code it seems ascii2unicode() is the only function left that does an unaligned access. Perhaps the solution here is to simply stop using file path nodes in U-Boot like you already suggested. Or otherwise just do the ascii2unicode() conversion into a suitably aligned buffer and memcpy() that into place.
It is also a problem with all the utf16 string handling, which shows up all over the place. For example if efi payload (or efi_load_image()) took the file path string from a device-path, which was unaligned. I'm not really a fan of trying to deal with this everywhere, since it is making the code much more cumbersome for the benefit of platforms that are broken (and still will be broken in ways we cannot prevent) from an EFI standpoint.
Sorry, but I object to using the term broken here. Alignment is a rather fundamental aspect of how computers work and even on x86 you can't completely ignore that aspect. And from an EFI standard...
The EFI standard clearly states that for Aarch32 (2.3.5 AArch32 Platforms):
Unaligned access should be enabled if supported; Alignment faults are enabled otherwise.
In other words, a generic Aarch32 EFI payload should expect that unaligned access faults.
The EFI standard also says (2.3.1 Data Types):
Unless otherwise specified all data types are naturally aligned. Structures are aligned on boundaries equal to the largest internal datum of the structure and internal data are implicitly padded to achieve natural alignment.
The "unless otherwise specified" clearly applies to device path nodes, but outside that scope utf16 strings should be 16-bit aligned. So if the file path passed to efi_load_image() isn't 16-bit aligned that is a U-Boot bug. Note that this became a bug in patch 13/20 where you added __packed. I understand that you introduced __packed to fix another bug, but you'll have to deal with the consequences.
So I'm pretty much NAK on playing whack-a-mole with unaligned utf16 string handling in u-boot. Padding bytes at end of device-path structs won't solve everything.. that can only be done by making unaligned accesses work. (And hey, maybe we can trap the faults and emulate on armv6 if someone cares enough?) But the padding bytes will keep things working to the extent that they work today, in an non-obtrusive way, that is good enough for armv6/armv7 and it lets us fix things properly on aarch64. So that is progress.
Adding the padding *will not* fix anything. It just makes the structures bigger. The compiler is still free to align them on an odd boundary.
Sorry there is no way around it. You'll need to be careful about alignment anywhere where you convert data into and out of device paths. But you certainly don't need to check every place where utf16 strings are used. The compiler will make sure things will be naturally aligned unless you explicitly tell it not to do that.

On Tue, Aug 8, 2017 at 9:33 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Tue, 8 Aug 2017 08:54:26 -0400
On Tue, Aug 8, 2017 at 8:26 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Mon, 7 Aug 2017 18:18:50 -0400
On Mon, Aug 7, 2017 at 5:14 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Alexander Graf agraf@suse.de Date: Mon, 7 Aug 2017 21:19:37 +0100
For AArch64 things are different. There we should strive for full UEFI compliance as it's "the future" :).
Even there just making things work would be good enough for me ;). Our AArach64 bootloader is almost identical to the AArch32 one and works on a real UEFI implementation as well (SoftIron Overdrive 1000).
I think we should make the aarch64 implementation fully compliant (ie. addition of missing __packed's and no extra padding bytes). I won't loose sleep if some efi payloads don't work on pre-aarch64 (we should be able to keep things that were working before working). If adding missing __packed breaks things on platforms that can't do unaligned access, the solution is not to remove __packed, but to conditionally add padding bytes at the end of the struct. That way we keep things working as before on the old platforms, but make things work correctly on aarch64.
I'll add back my patch for EFI_DP_PAD() to this patchset, since this seems like the sensible way forward.
Adding padding does not magically align things on a >8-bit boundary. And as Peter pointed out, the hard drive device path type is inherently unalignable. The UEFI payload simply has to deal with that. If grub/shim/fallback.efi is currently broken, it will have to be fixed to be usable with U-Boot on 32-bit ARM.
The problem that needs to be fixed is the manipulation of device path nodes in U-Boot itself. Using __packed and -mno-aligned-access should take care of any parsing done through the device path node struct types. But whenever a member of such a struct is passed to some other function that function should be careful not to do unaligned access. After you fixed the GUID printing code it seems ascii2unicode() is the only function left that does an unaligned access. Perhaps the solution here is to simply stop using file path nodes in U-Boot like you already suggested. Or otherwise just do the ascii2unicode() conversion into a suitably aligned buffer and memcpy() that into place.
It is also a problem with all the utf16 string handling, which shows up all over the place. For example if efi payload (or efi_load_image()) took the file path string from a device-path, which was unaligned. I'm not really a fan of trying to deal with this everywhere, since it is making the code much more cumbersome for the benefit of platforms that are broken (and still will be broken in ways we cannot prevent) from an EFI standpoint.
Sorry, but I object to using the term broken here. Alignment is a rather fundamental aspect of how computers work and even on x86 you can't completely ignore that aspect. And from an EFI standard...
ok, s/broken/defies expectations/ if you prefer then. I think it amounts to the same thing.
The EFI standard clearly states that for Aarch32 (2.3.5 AArch32 Platforms):
Unaligned access should be enabled if supported; Alignment faults are enabled otherwise.
Yes, unaligned access should be enabled if supported.. not "if the UEFI implementation supports it". Presumably the "alignment faults are enabled otherwise" is referring to armv4 and armv5.
It also says that MMU should be enabled and SCTLR.A=0 on armv6 and armv7 (so I guess even armv6 supports unaligned access).
In that regard, I still think the term "broken" fits.
In other words, a generic Aarch32 EFI payload should expect that unaligned access faults.
The EFI standard also says (2.3.1 Data Types):
Unless otherwise specified all data types are naturally aligned. Structures are aligned on boundaries equal to the largest internal datum of the structure and internal data are implicitly padded to achieve natural alignment.
The "unless otherwise specified" clearly applies to device path nodes, but outside that scope utf16 strings should be 16-bit aligned. So if the file path passed to efi_load_image() isn't 16-bit aligned that is a U-Boot bug. Note that this became a bug in patch 13/20 where you added __packed. I understand that you introduced __packed to fix another bug, but you'll have to deal with the consequences.
So I'm pretty much NAK on playing whack-a-mole with unaligned utf16 string handling in u-boot. Padding bytes at end of device-path structs won't solve everything.. that can only be done by making unaligned accesses work. (And hey, maybe we can trap the faults and emulate on armv6 if someone cares enough?) But the padding bytes will keep things working to the extent that they work today, in an non-obtrusive way, that is good enough for armv6/armv7 and it lets us fix things properly on aarch64. So that is progress.
Adding the padding *will not* fix anything. It just makes the structures bigger. The compiler is still free to align them on an odd boundary.
It will "fix" what was "broken" by adding __packed ;-)
It makes the structures bigger to align the next structure to a word boundary, which *mostly* aligns fields within the structure to their natural alignment.
I agree it isn't really a fix. It's a workaround. But it has the consequence that utf16 strings in a device-path will be naturally aligned, in case they get passed back into some other efi api. (And that also applies to USB WWID nodes, we we aren't using for anything else yet.. from a quick look I don't see anywhere else where utf16 strings are used in a device-path.)
Sorry there is no way around it. You'll need to be careful about alignment anywhere where you convert data into and out of device paths. But you certainly don't need to check every place where utf16 strings are used. The compiler will make sure things will be naturally aligned unless you explicitly tell it not to do that.
I'm worried about the case where efi payload takes a utf16 path out of a device-path and passes it back to file->open().
I suppose special case handling in efi_load_image() wouldn't be too bad. It won't fix every theoretical problem on armv7 without unaligned access disabled.
BR, -R

On Tue, Aug 8, 2017 at 10:03 AM, Rob Clark robdclark@gmail.com wrote:
I suppose special case handling in efi_load_image() wouldn't be too bad. It won't fix every theoretical problem on armv7 without unaligned access disabled.
s/disabled/enabled/
BR, -R

On Tue, Aug 8, 2017 at 10:03 AM, Rob Clark robdclark@gmail.com wrote:
On Tue, Aug 8, 2017 at 9:33 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Tue, 8 Aug 2017 08:54:26 -0400
On Tue, Aug 8, 2017 at 8:26 AM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
From: Rob Clark robdclark@gmail.com Date: Mon, 7 Aug 2017 18:18:50 -0400
On Mon, Aug 7, 2017 at 5:14 PM, Mark Kettenis mark.kettenis@xs4all.nl wrote:
> From: Alexander Graf agraf@suse.de > Date: Mon, 7 Aug 2017 21:19:37 +0100 > > For AArch64 things are different. There we should strive for full UEFI > compliance as it's "the future" :).
Even there just making things work would be good enough for me ;). Our AArach64 bootloader is almost identical to the AArch32 one and works on a real UEFI implementation as well (SoftIron Overdrive 1000).
I think we should make the aarch64 implementation fully compliant (ie. addition of missing __packed's and no extra padding bytes). I won't loose sleep if some efi payloads don't work on pre-aarch64 (we should be able to keep things that were working before working). If adding missing __packed breaks things on platforms that can't do unaligned access, the solution is not to remove __packed, but to conditionally add padding bytes at the end of the struct. That way we keep things working as before on the old platforms, but make things work correctly on aarch64.
I'll add back my patch for EFI_DP_PAD() to this patchset, since this seems like the sensible way forward.
Adding padding does not magically align things on a >8-bit boundary. And as Peter pointed out, the hard drive device path type is inherently unalignable. The UEFI payload simply has to deal with that. If grub/shim/fallback.efi is currently broken, it will have to be fixed to be usable with U-Boot on 32-bit ARM.
The problem that needs to be fixed is the manipulation of device path nodes in U-Boot itself. Using __packed and -mno-aligned-access should take care of any parsing done through the device path node struct types. But whenever a member of such a struct is passed to some other function that function should be careful not to do unaligned access. After you fixed the GUID printing code it seems ascii2unicode() is the only function left that does an unaligned access. Perhaps the solution here is to simply stop using file path nodes in U-Boot like you already suggested. Or otherwise just do the ascii2unicode() conversion into a suitably aligned buffer and memcpy() that into place.
It is also a problem with all the utf16 string handling, which shows up all over the place. For example if efi payload (or efi_load_image()) took the file path string from a device-path, which was unaligned. I'm not really a fan of trying to deal with this everywhere, since it is making the code much more cumbersome for the benefit of platforms that are broken (and still will be broken in ways we cannot prevent) from an EFI standpoint.
Sorry, but I object to using the term broken here. Alignment is a rather fundamental aspect of how computers work and even on x86 you can't completely ignore that aspect. And from an EFI standard...
ok, s/broken/defies expectations/ if you prefer then. I think it amounts to the same thing.
The EFI standard clearly states that for Aarch32 (2.3.5 AArch32 Platforms):
Unaligned access should be enabled if supported; Alignment faults are enabled otherwise.
Yes, unaligned access should be enabled if supported.. not "if the UEFI implementation supports it". Presumably the "alignment faults are enabled otherwise" is referring to armv4 and armv5.
It also says that MMU should be enabled and SCTLR.A=0 on armv6 and armv7 (so I guess even armv6 supports unaligned access).
In that regard, I still think the term "broken" fits.
In other words, a generic Aarch32 EFI payload should expect that unaligned access faults.
The EFI standard also says (2.3.1 Data Types):
Unless otherwise specified all data types are naturally aligned. Structures are aligned on boundaries equal to the largest internal datum of the structure and internal data are implicitly padded to achieve natural alignment.
The "unless otherwise specified" clearly applies to device path nodes, but outside that scope utf16 strings should be 16-bit aligned. So if the file path passed to efi_load_image() isn't 16-bit aligned that is a U-Boot bug. Note that this became a bug in patch 13/20 where you added __packed. I understand that you introduced __packed to fix another bug, but you'll have to deal with the consequences.
So I'm pretty much NAK on playing whack-a-mole with unaligned utf16 string handling in u-boot. Padding bytes at end of device-path structs won't solve everything.. that can only be done by making unaligned accesses work. (And hey, maybe we can trap the faults and emulate on armv6 if someone cares enough?) But the padding bytes will keep things working to the extent that they work today, in an non-obtrusive way, that is good enough for armv6/armv7 and it lets us fix things properly on aarch64. So that is progress.
Adding the padding *will not* fix anything. It just makes the structures bigger. The compiler is still free to align them on an odd boundary.
It will "fix" what was "broken" by adding __packed ;-)
It makes the structures bigger to align the next structure to a word boundary, which *mostly* aligns fields within the structure to their natural alignment.
I agree it isn't really a fix. It's a workaround. But it has the consequence that utf16 strings in a device-path will be naturally aligned, in case they get passed back into some other efi api. (And that also applies to USB WWID nodes, we we aren't using for anything else yet.. from a quick look I don't see anywhere else where utf16 strings are used in a device-path.)
Sorry there is no way around it. You'll need to be careful about alignment anywhere where you convert data into and out of device paths. But you certainly don't need to check every place where utf16 strings are used. The compiler will make sure things will be naturally aligned unless you explicitly tell it not to do that.
I'm worried about the case where efi payload takes a utf16 path out of a device-path and passes it back to file->open().
I suppose special case handling in efi_load_image() wouldn't be too bad. It won't fix every theoretical problem on armv7 without unaligned access enabled.
Since I don't have an armv7 board w/ u-boot to test, I've done some experiments w/ SCTLR.A=1 on armv8:
GRUB:
Note, by default built w/out -mstrict-align on aarch64, which crashes. With -mstrict-align, it seems to work with or without extra padding.
Kernel does crash relatively early, I guess not expecting to have unaligned accesses disabled.
SHIM/FALLBACK:
Both seem to crash in both shim and fallback with and without padding. Note that shim and fallback do not seem to ever be built with -mstrict-align, which is probably the issue. So I wouldn't expect these to work on armv7 right now without at least recompiling.
fallback.efi is the one where I half suspect we'll see issues if there are any, but without my patchset it won't run on u-boot at all, and with my patchset it will run on aarch64 (but not armv7, at least until it is recompiled). So I'm tempted not to care at this point.
OpenBSD bootloader:
Works both with and without padding, at least until the point where it can't find an OpenBSD filesystem and then sits there at boot prompt.
SUMMARY:
Mark's initial problem wasn't even with device-paths, but was with guidstr(), iirc. Which is superceded by the patch adding vsprintf support for %pG (which works bytewise, so it doesn't have the same problem as guidstr).
Beyond that, right now the only place we construct a file-path (and call ascii2unicode()) is the EFI_LOADED_IMAGE::FilePath (which is not preceded by the "device" part of a device-path, so it is already word aligned).
So tl;dr: we can skip the padding for now, it doesn't fix anything enough to go from broken to working. And without it nothing that was working before stops working.
BR, -R

On Sat, Aug 5, 2017 at 1:06 PM, Rob Clark robdclark@gmail.com wrote:
On Sat, Aug 5, 2017 at 12:52 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 06:16 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 05:58 PM, Rob Clark wrote:
Some arch's have trouble with unaligned accesses. Technically EFI device-path structs should be byte aligned, and the next node in the path starts immediately after the previous. Meaning that a pointer to an 'struct efi_device_path' is not necessarily word aligned. See section 10.3.1 in v2.7 of UEFI spec.
This causes problems not just for u-boot, but also most/all EFI payloads loaded by u-boot on these archs. Fortunately the common practice for traversing a device path is to rely on the length field in the header, rather than the specified length of the particular device path type+subtype. So the EFI_DP_PAD() macro will add the specified number of bytes to the tail of device path structs to pad them to word alignment.
Technically this is non-compliant, BROKEN_UNALIGNED should *only* be defined on archs that cannot do unaligned accesses.
Signed-off-by: Rob Clark robdclark@gmail.com
I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED
Mark, this is untested but I think it should solve your crash on the Banana Pi. Could you give it a try when you get a chance?
arch/arm/config.mk | 2 +- include/efi_api.h | 30 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_device_path.c | 3 +++ 3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 1a77779db4..067dc93a9d 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ $(call cc-option,-arm-use-movt=0,) PLATFORM_RELFLAGS += $(LLVM_RELFLAGS)
-PLATFORM_CPPFLAGS += -D__ARM__ +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED
NAK
We have more then ARM. And other architectures also create exceptions for unaligned access.
I hate platform specific code. It should not be used outside /arch.
To play it save you should not use _packed at all! Use memcpy to transfer between aligned and unaligned memory.
except for reasons I explained in the thread on the patch that added the __packed in the first place. Sorry, this is ugly but we have to do it.
BR, -R
According to the UEFI standard the nodes in paths are not to be assumed to be aligned.
So even if you use padding bytes in paths that you pass to the EFI application you should not expect that the EFI application does the same. Expect the EFI application either to remove them or send new nodes without padding.
To the idea of padding bytes and __packed does not make sense.
Ok, to be fair, you are right about device-paths passed too u-boot. On BROKEN_UNALIGNED archs, we should sanitise with a copy to an aligned device-path in *addition* to what I proposed. I can make a patch to add a helper to do this a bit later.
But the padding bytes + __packed does make total sense because it avoids breaking efi payloads that already exist in the field. The crash that Mark saw was not in u-boot, but in openbsd's bootaa64.efi. (It is possibly that we just get lucky here in u-boot since I add the /End node after the mac address by something the compiler turns into a memcpy.)
My proposal simply preserves the bug that we already have on BROKEN_UNALIGNED archs (but makes the improvement that it fixes the bug on aarch64 and any other arch that can do unaligned access), to keep existing efi payloads working.
Ok, so I took a closer look at the assembly generated, and I realized that with __packed structs, gcc seems to be generating ldrb+orr's for *all* the fields, in other words it isn't assuming alignment of the device-path pointer. The only potential problem right now is that we are missing __packed on 'struct efi_device_path' itself, so dereferencing the length field could cause unaligned access. Adding the missing __packed annotation turns the generated code into a pair of ldrb's plus an orr. I'll add the missing __packed on efi_device_path to my patchset.
(I'm basing this on the asm generated for vexpress_ca15_tc2 build.)
That is the good news.. the bad news is this probably still ends up being a problem in a few places w/ utf16 strings (ie. ascii2unicode()'ing into a filepath node, or converting filenames passed from efi payload from utf16..).. I'll have to think a bit about how best to handle that. But at least this is all stuff that never worked before in the first place, so I guess we don't have to solve it immediately.
BR, -R

On Sat, Aug 05, 2017 at 06:52:59PM +0200, Heinrich Schuchardt wrote:
On 08/05/2017 06:16 PM, Rob Clark wrote:
On Sat, Aug 5, 2017 at 12:12 PM, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 08/05/2017 05:58 PM, Rob Clark wrote:
Some arch's have trouble with unaligned accesses. Technically EFI device-path structs should be byte aligned, and the next node in the path starts immediately after the previous. Meaning that a pointer to an 'struct efi_device_path' is not necessarily word aligned. See section 10.3.1 in v2.7 of UEFI spec.
This causes problems not just for u-boot, but also most/all EFI payloads loaded by u-boot on these archs. Fortunately the common practice for traversing a device path is to rely on the length field in the header, rather than the specified length of the particular device path type+subtype. So the EFI_DP_PAD() macro will add the specified number of bytes to the tail of device path structs to pad them to word alignment.
Technically this is non-compliant, BROKEN_UNALIGNED should *only* be defined on archs that cannot do unaligned accesses.
Signed-off-by: Rob Clark robdclark@gmail.com
I'm not sure if there are other arch's that need -DBROKEN_UNALIGNED
Mark, this is untested but I think it should solve your crash on the Banana Pi. Could you give it a try when you get a chance?
arch/arm/config.mk | 2 +- include/efi_api.h | 30 ++++++++++++++++++++++++++++++ lib/efi_loader/efi_device_path.c | 3 +++ 3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 1a77779db4..067dc93a9d 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -28,7 +28,7 @@ LLVMS_RELFLAGS := $(call cc-option,-mllvm,) \ $(call cc-option,-arm-use-movt=0,) PLATFORM_RELFLAGS += $(LLVM_RELFLAGS)
-PLATFORM_CPPFLAGS += -D__ARM__ +PLATFORM_CPPFLAGS += -D__ARM__ -DBROKEN_UNALIGNED
NAK
We have more then ARM. And other architectures also create exceptions for unaligned access.
I hate platform specific code. It should not be used outside /arch.
To play it save you should not use _packed at all! Use memcpy to transfer between aligned and unaligned memory.
except for reasons I explained in the thread on the patch that added the __packed in the first place. Sorry, this is ugly but we have to do it.
BR, -R
According to the UEFI standard the nodes in paths are not to be assumed to be aligned.
So even if you use padding bytes in paths that you pass to the EFI application you should not expect that the EFI application does the same. Expect the EFI application either to remove them or send new nodes without padding.
Well, you do need it to be packed though. We've tried in recent years to make sure all the fields pack naturally in new device paths types, but unfortunately some of the ones that have been in use for more than a decade were defined poorly; in particular, Hard Drive device paths do not pack naturally. In Rob's patch he's got this:
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; EFI_DP_PAD(1); } __packed;
If it's not packed, there's no circumstance that partition_number and partition_start will ever both be correctly aligned. Without the padding (which is questionable under the spec), the next header won't be. That implies any code that's given this structure will have to do fixups when checking the length, just to traverse the device path, even when you don't do anything with the payload data.
Here's a quick test case that shows the different ways it can be packed wrong, as well as the right way (I wrote this before looking at Rob's definition above, but they're materially the same):
https://pjones.fedorapeople.org/efidp_packed/dp.c https://pjones.fedorapeople.org/efidp_packed/output
There are a couple of different ways to get partition_number correct, but only if both the header and the payload are packed does payload.start ever get aligned correctly.
To the idea of padding bytes and __packed does not make sense.
Padding I'm not so sure about; if everything is packed, the compiler should generate direct accesses correctly. So the only cases we actually have to worry about are when a pointer to a field >u8 is passed some place that dereferences it.

On Fri, Aug 04, 2017 at 03:31:42PM -0400, Rob Clark wrote:
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.
The first seven patches add dependencies that will be needed later in the series. Patches 8-15 make u-boot work with upstream grub, without relying on distro patches. Patches 16-19 add missing bits of the UEFI implementation needed to support shim/fallback. And finally patch 20 adds bootmanager support to avoid shim/fallback after first boot.
In concept, I am in agreement with the goals of this patch series. In specifics, I'm going to skim around v0 and see if there's anything in particular that I feel I need to jump in and comment on at this point. Thanks for working on this!

On Wed, Aug 9, 2017 at 9:32 PM, Tom Rini trini@konsulko.com wrote:
On Fri, Aug 04, 2017 at 03:31:42PM -0400, Rob Clark wrote:
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.
The first seven patches add dependencies that will be needed later in the series. Patches 8-15 make u-boot work with upstream grub, without relying on distro patches. Patches 16-19 add missing bits of the UEFI implementation needed to support shim/fallback. And finally patch 20 adds bootmanager support to avoid shim/fallback after first boot.
In concept, I am in agreement with the goals of this patch series. In specifics, I'm going to skim around v0 and see if there's anything in particular that I feel I need to jump in and comment on at this point. Thanks for working on this!
There is one remaining regression in travis (well, actually two identical ones) on vexpress which I am pretty stumped by:
https://travis-ci.org/robclark/u-boot/jobs/262841862
I cannot reproduce that outside of travis (although I am using distro qemu). I "bisected" it a few days back by cutting down the travis config to just those two vexpress platforms and pushing different stages of this patchset to a test branch. It seemed to be 'efi_loader: refactor boot device and loaded_image handling' that was triggering it. Although that patch (or really any of them) don't touch anything near printenv. Possibly it could be a build size issue with EFI_LOADER getting slightly bigger? I tried updating travis to use qemu 2.9.0 (which modulo any possible distro patches should match what I have when I run locally on fedora 26), but that didn't help.
I'm at a bit of a loss about what to do.. I guess I can have a closer look and see if we have any distro patches on qemu that look somehow related. I don't see how this can possibly be a problem w/ the efi_loader patchset. Any suggestions welcome.
BR, -R
participants (13)
-
Alexander Graf
-
Ard Biesheuvel
-
Brüns, Stefan
-
Heinrich Schuchardt
-
Jonathan Gray
-
Leif Lindholm
-
Mark Kettenis
-
Peter Jones
-
Peter Robinson
-
rick@andestech.com
-
Rob Clark
-
Simon Glass
-
Tom Rini