From 04592adbdb997081acccdd7191a9bd661a791d91 Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Fri, 10 Nov 2023 13:25:34 +0900 Subject: net: wget: prevent overwriting reserved memory This introduces the valid range check to store the received blocks using lmb. The same logic is implemented in tftp. Signed-off-by: Masahisa Kojima Acked-by: Ilias Apalodimas Reviewed-by: Simon Glass Reviewed-by: Ramon Fried Signed-off-by: Ilias Apalodimas --- net/wget.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/net/wget.c b/net/wget.c index 8bb4d72db1a..6f97eb1d125 100644 --- a/net/wget.c +++ b/net/wget.c @@ -4,16 +4,20 @@ * Copyright Duncan Hare 2017 */ +#include #include #include #include #include #include +#include #include #include #include #include +DECLARE_GLOBAL_DATA_PTR; + static const char bootfile1[] = "GET "; static const char bootfile3[] = " HTTP/1.0\r\n\r\n"; static const char http_eom[] = "\r\n\r\n"; @@ -56,6 +60,29 @@ static unsigned int retry_tcp_ack_num; /* TCP retry acknowledge number*/ static unsigned int retry_tcp_seq_num; /* TCP retry sequence number */ static int retry_len; /* TCP retry length */ +static ulong wget_load_size; + +/** + * wget_init_max_size() - initialize maximum load size + * + * Return: 0 if success, -1 if fails + */ +static int wget_init_load_size(void) +{ + struct lmb lmb; + phys_size_t max_size; + + lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob); + + max_size = lmb_get_free_size(&lmb, image_load_addr); + if (!max_size) + return -1; + + wget_load_size = max_size; + + return 0; +} + /** * store_block() - store block in memory * @src: source of data @@ -64,10 +91,25 @@ static int retry_len; /* TCP retry length */ */ static inline int store_block(uchar *src, unsigned int offset, unsigned int len) { + ulong store_addr = image_load_addr + offset; ulong newsize = offset + len; uchar *ptr; - ptr = map_sysmem(image_load_addr + offset, len); + if (IS_ENABLED(CONFIG_LMB)) { + ulong end_addr = image_load_addr + wget_load_size; + + if (!end_addr) + end_addr = ULONG_MAX; + + if (store_addr < image_load_addr || + store_addr + len > end_addr) { + printf("\nwget error: "); + printf("trying to overwrite reserved memory...\n"); + return -1; + } + } + + ptr = map_sysmem(store_addr, len); memcpy(ptr, src, len); unmap_sysmem(ptr); @@ -248,25 +290,39 @@ static void wget_connected(uchar *pkt, unsigned int tcp_seq_num, net_boot_file_size = 0; - if (len > hlen) - store_block(pkt + hlen, 0, len - hlen); + if (len > hlen) { + if (store_block(pkt + hlen, 0, len - hlen) != 0) { + wget_loop_state = NETLOOP_FAIL; + wget_fail("wget: store error\n", tcp_seq_num, tcp_ack_num, action); + net_set_state(NETLOOP_FAIL); + return; + } + } debug_cond(DEBUG_WGET, "wget: Connected Pkt %p hlen %x\n", pkt, hlen); for (i = 0; i < pkt_q_idx; i++) { + int err; + ptr1 = map_sysmem( (phys_addr_t)(pkt_q[i].pkt), pkt_q[i].len); - store_block(ptr1, - pkt_q[i].tcp_seq_num - - initial_data_seq_num, - pkt_q[i].len); + err = store_block(ptr1, + pkt_q[i].tcp_seq_num - + initial_data_seq_num, + pkt_q[i].len); unmap_sysmem(ptr1); debug_cond(DEBUG_WGET, "wget: Connctd pkt Q %p len %x\n", pkt_q[i].pkt, pkt_q[i].len); + if (err) { + wget_loop_state = NETLOOP_FAIL; + wget_fail("wget: store error\n", tcp_seq_num, tcp_ack_num, action); + net_set_state(NETLOOP_FAIL); + return; + } } } } @@ -338,6 +394,7 @@ static void wget_handler(uchar *pkt, u16 dport, len) != 0) { wget_fail("wget: store error\n", tcp_seq_num, tcp_ack_num, action); + net_set_state(NETLOOP_FAIL); return; } @@ -428,6 +485,15 @@ void wget_start(void) debug_cond(DEBUG_WGET, "\nwget:Load address: 0x%lx\nLoading: *\b", image_load_addr); + if (IS_ENABLED(CONFIG_LMB)) { + if (wget_init_load_size()) { + printf("\nwget error: "); + printf("trying to overwrite reserved memory...\n"); + net_set_state(NETLOOP_FAIL); + return; + } + } + net_set_timeout_handler(wget_timeout, wget_timeout_handler); tcp_set_tcp_handler(wget_handler); -- cgit v1.2.3 From 8cf18da1a971dbfe121c1fc1836bb4af942d06dc Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Fri, 10 Nov 2023 13:25:35 +0900 Subject: net: wget: add wget with dns utility function Current wget takes the target uri in this format: ":" e.g.) 192.168.1.1:/bar The http server ip address must be resolved before calling wget. This commit adds the utility function runs wget with dhs. User can call wget with the uri like "http://foo/bar". Signed-off-by: Masahisa Kojima Reviewed-by: Ilias Apalodimas Reviewed-by: Ramon Fried Reviewed-by: Simon Glass Signed-off-by: Ilias Apalodimas --- include/net.h | 9 +++++++++ net/wget.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/include/net.h b/include/net.h index e63a946002d..61f9018769a 100644 --- a/include/net.h +++ b/include/net.h @@ -930,4 +930,13 @@ void eth_set_enable_bootdevs(bool enable); static inline void eth_set_enable_bootdevs(bool enable) {} #endif +/** + * wget_with_dns() - runs dns host IP address resulution before wget + * + * @dst_addr: destination address to download the file + * @uri: uri string of target file of wget + * Return: downloaded file size, negative if failed + */ +int wget_with_dns(ulong dst_addr, char *uri); + #endif /* __NET_H__ */ diff --git a/net/wget.c b/net/wget.c index 6f97eb1d125..2087146b377 100644 --- a/net/wget.c +++ b/net/wget.c @@ -15,6 +15,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -512,3 +513,56 @@ void wget_start(void) wget_send(TCP_SYN, 0, 0, 0); } + +#if (IS_ENABLED(CONFIG_CMD_DNS)) +int wget_with_dns(ulong dst_addr, char *uri) +{ + int ret; + char *s, *host_name, *file_name, *str_copy; + + /* + * Download file using wget. + * + * U-Boot wget takes the target uri in this format. + * ":" e.g.) 192.168.1.1:/sample/test.iso + * Need to resolve the http server ip address before starting wget. + */ + str_copy = strdup(uri); + if (!str_copy) + return -ENOMEM; + + s = str_copy + strlen("http://"); + host_name = strsep(&s, "/"); + if (!s) { + log_err("Error: invalied uri, no file path\n"); + ret = -EINVAL; + goto out; + } + file_name = s; + + /* TODO: If the given uri has ip address for the http server, skip dns */ + net_dns_resolve = host_name; + net_dns_env_var = "httpserverip"; + if (net_loop(DNS) < 0) { + log_err("Error: dns lookup of %s failed, check setup\n", net_dns_resolve); + ret = -EINVAL; + goto out; + } + s = env_get("httpserverip"); + if (!s) { + ret = -EINVAL; + goto out; + } + + strlcpy(net_boot_file_name, s, sizeof(net_boot_file_name)); + strlcat(net_boot_file_name, ":/", sizeof(net_boot_file_name)); /* append '/' which is removed by strsep() */ + strlcat(net_boot_file_name, file_name, sizeof(net_boot_file_name)); + image_load_addr = dst_addr; + ret = net_loop(WGET); + +out: + free(str_copy); + + return ret; +} +#endif -- cgit v1.2.3 From d822255d653421bdcda9d6f443929a5b67ca4454 Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Fri, 10 Nov 2023 13:25:36 +0900 Subject: blk: blkmap: add ramdisk creation utility function User needs to call several functions to create the ramdisk with blkmap. This adds the utility function to create blkmap device and mount the ramdisk. Signed-off-by: Masahisa Kojima Reviewed-by: Simon Glass Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- drivers/block/Makefile | 3 ++- drivers/block/blkmap.c | 15 ------------ drivers/block/blkmap_helper.c | 53 +++++++++++++++++++++++++++++++++++++++++++ include/blkmap.h | 29 +++++++++++++++++++++++ 4 files changed, 84 insertions(+), 16 deletions(-) create mode 100644 drivers/block/blkmap_helper.c diff --git a/drivers/block/Makefile b/drivers/block/Makefile index fdcba5c8318..fe6a1fcf486 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -15,7 +15,8 @@ obj-$(CONFIG_RKMTD) += rkmtd.o endif obj-$(CONFIG_SANDBOX) += sandbox.o host-uclass.o host_dev.o obj-$(CONFIG_$(SPL_TPL_)BLOCK_CACHE) += blkcache.o -obj-$(CONFIG_BLKMAP) += blkmap.o +obj-$(CONFIG_$(SPL_TPL_)BLKMAP) += blkmap.o +obj-$(CONFIG_$(SPL_TPL_)BLKMAP) += blkmap_helper.o obj-$(CONFIG_EFI_MEDIA) += efi-media-uclass.o obj-$(CONFIG_EFI_MEDIA_SANDBOX) += sb_efi_media.o diff --git a/drivers/block/blkmap.c b/drivers/block/blkmap.c index 149a4cac3ea..21201409ed4 100644 --- a/drivers/block/blkmap.c +++ b/drivers/block/blkmap.c @@ -66,21 +66,6 @@ struct blkmap_slice { void (*destroy)(struct blkmap *bm, struct blkmap_slice *bms); }; -/** - * struct blkmap - Block map - * - * Data associated with a blkmap. - * - * @label: Human readable name of this blkmap - * @blk: Underlying block device - * @slices: List of slices associated with this blkmap - */ -struct blkmap { - char *label; - struct udevice *blk; - struct list_head slices; -}; - static bool blkmap_slice_contains(struct blkmap_slice *bms, lbaint_t blknr) { return (blknr >= bms->blknr) && (blknr < (bms->blknr + bms->blkcnt)); diff --git a/drivers/block/blkmap_helper.c b/drivers/block/blkmap_helper.c new file mode 100644 index 00000000000..bfba14110d2 --- /dev/null +++ b/drivers/block/blkmap_helper.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * blkmap helper function + * + * Copyright (c) 2023, Linaro Limited + */ + +#include +#include +#include +#include + +int blkmap_create_ramdisk(const char *label, ulong image_addr, ulong image_size, + struct udevice **devp) +{ + int ret; + lbaint_t blknum; + struct blkmap *bm; + struct blk_desc *desc; + struct udevice *bm_dev; + + ret = blkmap_create(label, &bm_dev); + if (ret) { + log_err("failed to create blkmap\n"); + return ret; + } + + bm = dev_get_plat(bm_dev); + desc = dev_get_uclass_plat(bm->blk); + blknum = image_size >> desc->log2blksz; + ret = blkmap_map_pmem(bm_dev, 0, blknum, image_addr); + if (ret) { + log_err("Unable to map %#llx at block %d : %d\n", + (unsigned long long)image_addr, 0, ret); + goto err; + } + log_info("Block %d+0x" LBAF " mapped to %#llx\n", 0, blknum, + (unsigned long long)image_addr); + + ret = device_probe(bm->blk); + if (ret) + goto err; + + if (devp) + *devp = bm_dev; + + return 0; + +err: + blkmap_destroy(bm_dev); + + return ret; +} diff --git a/include/blkmap.h b/include/blkmap.h index af54583c7dd..30dc84a7da8 100644 --- a/include/blkmap.h +++ b/include/blkmap.h @@ -7,6 +7,23 @@ #ifndef _BLKMAP_H #define _BLKMAP_H +#include + +/** + * struct blkmap - Block map + * + * Data associated with a blkmap. + * + * @label: Human readable name of this blkmap + * @blk: Underlying block device + * @slices: List of slices associated with this blkmap + */ +struct blkmap { + char *label; + struct udevice *blk; + struct list_head slices; +}; + /** * blkmap_map_linear() - Map region of other block device * @@ -74,4 +91,16 @@ int blkmap_create(const char *label, struct udevice **devp); */ int blkmap_destroy(struct udevice *dev); +/** + * blkmap_create_ramdisk() - Create new ramdisk with blkmap + * + * @label: Label of the new blkmap + * @image_addr: Target memory start address of this mapping + * @image_size: Target memory size of this mapping + * @devp: Updated with the address of the created blkmap device + * Returns: 0 on success, negative error code on failure + */ +int blkmap_create_ramdisk(const char *label, ulong image_addr, ulong image_size, + struct udevice **devp); + #endif /* _BLKMAP_H */ -- cgit v1.2.3 From 550862bc1279278a029c8c17871a0c6241c522d8 Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Fri, 10 Nov 2023 13:25:37 +0900 Subject: efi_loader: Boot var automatic management Changes for complying to EFI spec ยง3.5.1.1 'Removable Media Boot Behavior'. Boot variables can be automatically generated during a removable media is probed. At the same time, unused boot variables will be detected and removed. Please note that currently the function 'efi_disk_remove' has no ability to distinguish below two scenarios a) Unplugging of a removable media under U-Boot b) U-Boot exiting and booting an OS Thus currently the boot variables management is not added into 'efi_disk_remove' to avoid boot options being added/erased repeatedly under scenario b) during power cycles See TODO comments under function 'efi_disk_remove' for more details The original efi_secboot tests expect that BootOrder EFI variable is not defined. With this commit, the BootOrder EFI variable is automatically added when the disk is detected. The original efi_secboot tests end up with unexpected failure. The efi_secboot tests need to be modified to explicitly set the BootOrder EFI variable. squashfs and erofs ls tests are also affected by this modification, need to clear the previous state before squashfs ls test starts. Co-developed-by: Masahisa Kojima Signed-off-by: Masahisa Kojima Signed-off-by: Raymond Mao Reviewed-by: Heinrich Schuchardt Reviewed-by: Ilias Apalodimas Reviewed-by: Joao Marcos Costa Signed-off-by: Ilias Apalodimas --- lib/efi_loader/efi_disk.c | 18 ++++++++++ lib/efi_loader/efi_setup.c | 7 ++++ test/py/tests/test_efi_secboot/test_signed.py | 42 +++++++++++----------- .../py/tests/test_efi_secboot/test_signed_intca.py | 14 ++++---- test/py/tests/test_efi_secboot/test_unsigned.py | 14 ++++---- test/py/tests/test_fs/test_erofs.py | 9 +++++ .../py/tests/test_fs/test_squashfs/test_sqfs_ls.py | 9 +++++ 7 files changed, 78 insertions(+), 35 deletions(-) diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index f0d76113b00..b808a7fe627 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -690,6 +690,13 @@ int efi_disk_probe(void *ctx, struct event *event) return -1; } + /* only do the boot option management when UEFI sub-system is initialized */ + if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && efi_obj_list_initialized == EFI_SUCCESS) { + ret = efi_bootmgr_update_media_device_boot_option(); + if (ret != EFI_SUCCESS) + return -1; + } + return 0; } @@ -742,6 +749,17 @@ int efi_disk_remove(void *ctx, struct event *event) dev_tag_del(dev, DM_TAG_EFI); return 0; + + /* + * TODO A flag to distinguish below 2 different scenarios of this + * function call is needed: + * a) Unplugging of a removable media under U-Boot + * b) U-Boot exiting and booting an OS + * In case of scenario a), efi_bootmgr_update_media_device_boot_option() + * needs to be invoked here to update the boot options and remove the + * unnecessary ones. + */ + } /** diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index e6de685e879..37359a77bbc 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -245,6 +245,13 @@ efi_status_t efi_init_obj_list(void) if (ret != EFI_SUCCESS) goto out; + if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR)) { + /* update boot option after variable service initialized */ + ret = efi_bootmgr_update_media_device_boot_option(); + if (ret != EFI_SUCCESS) + goto out; + } + /* Define supported languages */ ret = efi_init_platform_lang(); if (ret != EFI_SUCCESS) diff --git a/test/py/tests/test_efi_secboot/test_signed.py b/test/py/tests/test_efi_secboot/test_signed.py index ca52e853d8f..2f862a259ad 100644 --- a/test/py/tests/test_efi_secboot/test_signed.py +++ b/test/py/tests/test_efi_secboot/test_signed.py @@ -29,7 +29,7 @@ class TestEfiSignedImage(object): output = u_boot_console.run_command_list([ 'host bind 0 %s' % disk_img, 'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'bootefi bootmgr']) assert 'Hello, world!' in ''.join(output) @@ -37,7 +37,7 @@ class TestEfiSignedImage(object): # Test Case 1b, run unsigned image if no PK output = u_boot_console.run_command_list([ 'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi -s ""', - 'efidebug boot next 2', + 'efidebug boot order 2', 'bootefi bootmgr']) assert 'Hello, world!' in ''.join(output) @@ -59,13 +59,13 @@ class TestEfiSignedImage(object): assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert('\'HELLO1\' failed' in ''.join(output)) assert('efi_start_image() returned: 26' in ''.join(output)) output = u_boot_console.run_command_list([ 'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi -s ""', - 'efidebug boot next 2', + 'efidebug boot order 2', 'efidebug test bootmgr']) assert '\'HELLO2\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) @@ -77,12 +77,12 @@ class TestEfiSignedImage(object): 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db']) assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ - 'efidebug boot next 2', + 'efidebug boot order 2', 'efidebug test bootmgr']) assert '\'HELLO2\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) output = u_boot_console.run_command_list([ - 'efidebug boot next 1', + 'efidebug boot order 1', 'bootefi bootmgr']) assert 'Hello, world!' in ''.join(output) @@ -105,7 +105,7 @@ class TestEfiSignedImage(object): assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert '\'HELLO\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) @@ -117,7 +117,7 @@ class TestEfiSignedImage(object): 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db']) assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert '\'HELLO\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) @@ -143,7 +143,7 @@ class TestEfiSignedImage(object): assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert '\'HELLO\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) @@ -170,7 +170,7 @@ class TestEfiSignedImage(object): assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert 'Hello, world!' in ''.join(output) @@ -181,7 +181,7 @@ class TestEfiSignedImage(object): 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db']) assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert 'Hello, world!' in ''.join(output) @@ -193,7 +193,7 @@ class TestEfiSignedImage(object): 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx']) assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert '\'HELLO\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) @@ -205,7 +205,7 @@ class TestEfiSignedImage(object): 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize dbx']) assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert '\'HELLO\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) @@ -230,7 +230,7 @@ class TestEfiSignedImage(object): assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert '\'HELLO\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) @@ -254,7 +254,7 @@ class TestEfiSignedImage(object): assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'bootefi bootmgr']) assert 'Hello, world!' in ''.join(output) @@ -265,7 +265,7 @@ class TestEfiSignedImage(object): 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx']) assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert '\'HELLO\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) @@ -279,7 +279,7 @@ class TestEfiSignedImage(object): 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx']) assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert '\'HELLO\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) @@ -307,7 +307,7 @@ class TestEfiSignedImage(object): assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert '\'HELLO\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) @@ -330,7 +330,7 @@ class TestEfiSignedImage(object): assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert '\'HELLO\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) @@ -349,7 +349,7 @@ class TestEfiSignedImage(object): output = u_boot_console.run_command_list([ 'host bind 0 %s' % disk_img, 'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld_forged.efi.signed -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert('hELLO, world!' in ''.join(output)) @@ -364,7 +364,7 @@ class TestEfiSignedImage(object): 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK']) assert 'Failed to set EFI variable' not in ''.join(output) output = u_boot_console.run_command_list([ - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert(not 'hELLO, world!' in ''.join(output)) assert('\'HELLO1\' failed' in ''.join(output)) diff --git a/test/py/tests/test_efi_secboot/test_signed_intca.py b/test/py/tests/test_efi_secboot/test_signed_intca.py index d8d599d22f3..8d9a5f3e7fe 100644 --- a/test/py/tests/test_efi_secboot/test_signed_intca.py +++ b/test/py/tests/test_efi_secboot/test_signed_intca.py @@ -40,7 +40,7 @@ class TestEfiSignedImageIntca(object): output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO_a host 0:1 /helloworld.efi.signed_a -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert '\'HELLO_a\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) @@ -49,7 +49,7 @@ class TestEfiSignedImageIntca(object): # Test Case 1b, signed and authenticated by root CA output = u_boot_console.run_command_list([ 'efidebug boot add -b 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab -s ""', - 'efidebug boot next 2', + 'efidebug boot order 2', 'bootefi bootmgr']) assert 'Hello, world!' in ''.join(output) @@ -71,7 +71,7 @@ class TestEfiSignedImageIntca(object): output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert '\'HELLO_abc\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) @@ -81,7 +81,7 @@ class TestEfiSignedImageIntca(object): output = u_boot_console.run_command_list([ 'fatload host 0:1 4000000 db_b.auth', 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert '\'HELLO_abc\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) @@ -91,7 +91,7 @@ class TestEfiSignedImageIntca(object): output = u_boot_console.run_command_list([ 'fatload host 0:1 4000000 db_c.auth', 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert 'Hello, world!' in ''.join(output) @@ -117,7 +117,7 @@ class TestEfiSignedImageIntca(object): output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert 'Hello, world!' in ''.join(output) # Or, @@ -129,7 +129,7 @@ class TestEfiSignedImageIntca(object): output = u_boot_console.run_command_list([ 'fatload host 0:1 4000000 dbx_c.auth', 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx', - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert '\'HELLO_abc\' failed' in ''.join(output) assert 'efi_start_image() returned: 26' in ''.join(output) diff --git a/test/py/tests/test_efi_secboot/test_unsigned.py b/test/py/tests/test_efi_secboot/test_unsigned.py index df63f0df081..7c078f220d0 100644 --- a/test/py/tests/test_efi_secboot/test_unsigned.py +++ b/test/py/tests/test_efi_secboot/test_unsigned.py @@ -36,11 +36,11 @@ class TestEfiUnsignedImage(object): output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'bootefi bootmgr']) assert '\'HELLO\' failed' in ''.join(output) output = u_boot_console.run_command_list([ - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert 'efi_start_image() returned: 26' in ''.join(output) assert 'Hello, world!' not in ''.join(output) @@ -65,7 +65,7 @@ class TestEfiUnsignedImage(object): output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'bootefi bootmgr']) assert 'Hello, world!' in ''.join(output) @@ -89,11 +89,11 @@ class TestEfiUnsignedImage(object): output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'bootefi bootmgr']) assert '\'HELLO\' failed' in ''.join(output) output = u_boot_console.run_command_list([ - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert 'efi_start_image() returned: 26' in ''.join(output) assert 'Hello, world!' not in ''.join(output) @@ -107,11 +107,11 @@ class TestEfiUnsignedImage(object): output = u_boot_console.run_command_list([ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""', - 'efidebug boot next 1', + 'efidebug boot order 1', 'bootefi bootmgr']) assert '\'HELLO\' failed' in ''.join(output) output = u_boot_console.run_command_list([ - 'efidebug boot next 1', + 'efidebug boot order 1', 'efidebug test bootmgr']) assert 'efi_start_image() returned: 26' in ''.join(output) assert 'Hello, world!' not in ''.join(output) diff --git a/test/py/tests/test_fs/test_erofs.py b/test/py/tests/test_fs/test_erofs.py index 458a52ba79d..87ad8f2d5fd 100644 --- a/test/py/tests/test_fs/test_erofs.py +++ b/test/py/tests/test_fs/test_erofs.py @@ -196,6 +196,15 @@ def test_erofs(u_boot_console): """ build_dir = u_boot_console.config.build_dir + # If the EFI subsystem is enabled and initialized, EFI subsystem tries to + # add EFI boot option when the new disk is detected. If there is no EFI + # System Partition exists, EFI subsystem outputs error messages and + # it ends up with test failure. + # Restart U-Boot to clear the previous state. + # TODO: Ideally EFI test cases need to be fixed, but it will + # increase the number of system reset. + u_boot_console.restart_uboot() + try: # setup test environment make_erofs_image(build_dir) diff --git a/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py b/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py index 527a556ed80..a20a7d1a663 100644 --- a/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py +++ b/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py @@ -118,6 +118,15 @@ def test_sqfs_ls(u_boot_console): """ build_dir = u_boot_console.config.build_dir + # If the EFI subsystem is enabled and initialized, EFI subsystem tries to + # add EFI boot option when the new disk is detected. If there is no EFI + # System Partition exists, EFI subsystem outputs error messages and + # it ends up with test failure. + # Restart U-Boot to clear the previous state. + # TODO: Ideally EFI test cases need to be fixed, but it will + # increase the number of system reset. + u_boot_console.restart_uboot() + # setup test environment check_mksquashfs_version() generate_sqfs_src_dir(build_dir) -- cgit v1.2.3 From e23c8e81ebc97bbe9d4037b1324994446c2bc6c4 Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Fri, 10 Nov 2023 13:25:38 +0900 Subject: efi_loader: add missing const classifier for event service const classifier is missing in EventGroup parameter of CreateEventEx(). Fix it to remove the compiler warning. NotifyContext parameter of CreateEventEx() is also defined with const in UEFI specification, but NotifyContext parameter of CreateEvent() is defined without const. Since current implementation calls the common efi_create_event() function from both CreateEventEx() and CreateEvent() services, NotifyContext parameter leaves as is. Signed-off-by: Masahisa Kojima Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- include/efi_api.h | 2 +- include/efi_loader.h | 2 +- lib/efi_loader/efi_boottime.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index 8f5ef5f680f..c2d69644679 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -192,7 +192,7 @@ struct efi_boot_services { struct efi_event *event, void *context), void *notify_context, - efi_guid_t *event_group, + const efi_guid_t *event_group, struct efi_event **event); }; diff --git a/include/efi_loader.h b/include/efi_loader.h index 247be060e1c..1fd7b0b73c9 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -684,7 +684,7 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, void (EFIAPI *notify_function) ( struct efi_event *event, void *context), - void *notify_context, efi_guid_t *group, + void *notify_context, const efi_guid_t *group, struct efi_event **event); /* Call this to set a timer */ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 0b7579cb5af..f9b10a9763b 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -712,7 +712,7 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, void (EFIAPI *notify_function) ( struct efi_event *event, void *context), - void *notify_context, efi_guid_t *group, + void *notify_context, const efi_guid_t *group, struct efi_event **event) { struct efi_event *evt; @@ -790,7 +790,7 @@ efi_status_t EFIAPI efi_create_event_ex(uint32_t type, efi_uintn_t notify_tpl, struct efi_event *event, void *context), void *notify_context, - efi_guid_t *event_group, + const efi_guid_t *event_group, struct efi_event **event) { efi_status_t ret; -- cgit v1.2.3 From e0d1a1ea68c47b29d93bfc375d984fe0aee21093 Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Fri, 10 Nov 2023 13:25:39 +0900 Subject: efi_loader: add return to efibootmgr event group When the image loaded by efibootmgr returns, efibootmgr needs to clean the resources. Adding the event of returning to efibootmgr is useful to simplify the implementation. Signed-off-by: Masahisa Kojima Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- cmd/bootefi.c | 12 ++++++++++++ include/efi_api.h | 3 +++ include/efi_loader.h | 2 ++ lib/efi_loader/efi_boottime.c | 3 +++ 4 files changed, 20 insertions(+) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 20e5c94a33a..4d74969ad62 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -356,6 +356,7 @@ static efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options) efi_status_t ret; efi_uintn_t exit_data_size = 0; u16 *exit_data = NULL; + struct efi_event *evt; /* On ARM switch from EL3 or secure mode to EL2 or non-secure mode */ switch_to_non_secure_mode(); @@ -394,6 +395,17 @@ out: log_err("Failed to remove loadfile2 for initrd\n"); } + /* Notify EFI_EVENT_GROUP_RETURN_TO_EFIBOOTMGR event group. */ + list_for_each_entry(evt, &efi_events, link) { + if (evt->group && + !guidcmp(evt->group, + &efi_guid_event_group_return_to_efibootmgr)) { + efi_signal_event(evt); + EFI_CALL(systab.boottime->close_event(evt)); + break; + } + } + /* Control is returned to U-Boot, disable EFI watchdog */ efi_set_watchdog(0); diff --git a/include/efi_api.h b/include/efi_api.h index c2d69644679..0e92cb8a7f6 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -404,6 +404,9 @@ struct efi_runtime_services { #define EFI_EVENT_GROUP_RESET_SYSTEM \ EFI_GUID(0x62da6a56, 0x13fb, 0x485a, 0xa8, 0xda, \ 0xa3, 0xdd, 0x79, 0x12, 0xcb, 0x6b) +#define EFI_EVENT_GROUP_RETURN_TO_EFIBOOTMGR \ + EFI_GUID(0xb4a40fe6, 0x9149, 0x4f29, 0x94, 0x47, \ + 0x49, 0x38, 0x7a, 0x7f, 0xab, 0x87) /* EFI Configuration Table and GUID definitions */ #define NULL_GUID \ diff --git a/include/efi_loader.h b/include/efi_loader.h index 1fd7b0b73c9..664dae28f88 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -289,6 +289,8 @@ extern const efi_guid_t efi_guid_event_group_memory_map_change; extern const efi_guid_t efi_guid_event_group_ready_to_boot; /* event group ResetSystem() invoked (before ExitBootServices) */ extern const efi_guid_t efi_guid_event_group_reset_system; +/* event group return to efibootmgr */ +extern const efi_guid_t efi_guid_event_group_return_to_efibootmgr; /* GUID of the device tree table */ extern const efi_guid_t efi_guid_fdt; extern const efi_guid_t efi_guid_loaded_image; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index f9b10a9763b..3767fa2db42 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -90,6 +90,9 @@ const efi_guid_t efi_guid_event_group_ready_to_boot = /* event group ResetSystem() invoked (before ExitBootServices) */ const efi_guid_t efi_guid_event_group_reset_system = EFI_EVENT_GROUP_RESET_SYSTEM; +/* event group return to efibootmgr */ +const efi_guid_t efi_guid_event_group_return_to_efibootmgr = + EFI_EVENT_GROUP_RETURN_TO_EFIBOOTMGR; /* GUIDs of the Load File and Load File2 protocols */ const efi_guid_t efi_guid_load_file_protocol = EFI_LOAD_FILE_PROTOCOL_GUID; const efi_guid_t efi_guid_load_file2_protocol = EFI_LOAD_FILE2_PROTOCOL_GUID; -- cgit v1.2.3 From d7d07a8b508bb8af564f6de1ea763be0f67f2456 Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Fri, 10 Nov 2023 13:25:40 +0900 Subject: efi_loader: support boot from URI device path This supports to boot from the URI device path. When user selects the URI device path, bootmgr downloads the file using wget into the address specified by loadaddr env variable. If the file is .iso or .img file, mount the image with blkmap then try to boot with the default file(e.g. EFI/BOOT/BOOTAA64.EFI). Since boot option indicating the default file is automatically created when new disk is detected, system can boot by selecting the automatically created blkmap boot option. If the file is PE-COFF file, load and start the downloaded file. The buffer used to download the ISO image file must be reserved to avoid the unintended access to the image and expose the ramdisk to the OS. For PE-COFF file case, this memory reservation is done in LoadImage Boot Service. [Ilias fix a few memory leaks by replacing returns with gotos] Lore: https://lore.kernel.org/u-boot/20231110042542.3797301-1-masahisa.kojima@linaro.org/T/#mbac31da301ff465b60894b38f3a587b2868cf817 Signed-off-by: Masahisa Kojima Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- lib/efi_loader/Kconfig | 9 + lib/efi_loader/efi_bootmgr.c | 386 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 395 insertions(+) diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 547dfd76ef5..2913d1c9dfe 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -477,4 +477,13 @@ config EFI_RISCV_BOOT_PROTOCOL replace the transfer via the device-tree. The latter is not possible on systems using ACPI. +config EFI_HTTP_BOOT + bool "EFI HTTP Boot support" + select CMD_DNS + select CMD_WGET + select BLKMAP + help + Enabling this option adds EFI HTTP Boot support. It allows to + directly boot from network. + endif diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index a40762c74c8..48153bd5ffb 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -7,10 +7,14 @@ #define LOG_CATEGORY LOGC_EFI +#include +#include #include #include +#include #include #include +#include #include #include #include @@ -19,6 +23,23 @@ static const struct efi_boot_services *bs; static const struct efi_runtime_services *rs; +/** + * struct uridp_context - uri device path resource + * + * @image_size: image size + * @image_addr: image address + * @loaded_dp: pointer to loaded device path + * @ramdisk_blk_dev: pointer to the ramdisk blk device + * @mem_handle: efi_handle to the loaded PE-COFF image + */ +struct uridp_context { + ulong image_size; + ulong image_addr; + struct efi_device_path *loaded_dp; + struct udevice *ramdisk_blk_dev; + efi_handle_t mem_handle; +}; + const efi_guid_t efi_guid_bootmenu_auto_generated = EFICONFIG_AUTO_GENERATED_ENTRY_GUID; @@ -168,6 +189,364 @@ out: return ret; } +/** + * mount_image() - mount the image with blkmap + * + * @lo_label: u16 label string of load option + * @addr: image address + * @size: image size + * Return: pointer to the UCLASS_BLK udevice, NULL if failed + */ +static struct udevice *mount_image(u16 *lo_label, ulong addr, ulong size) +{ + int err; + struct blkmap *bm; + struct udevice *bm_dev; + char *label = NULL, *p; + + label = efi_alloc(utf16_utf8_strlen(lo_label) + 1); + if (!label) + return NULL; + + p = label; + utf16_utf8_strcpy(&p, lo_label); + err = blkmap_create_ramdisk(label, addr, size, &bm_dev); + if (err) { + efi_free_pool(label); + return NULL; + } + bm = dev_get_plat(bm_dev); + + efi_free_pool(label); + + return bm->blk; +} + +/** + * search_default_file() - search default file + * + * @dev: pointer to the UCLASS_BLK or UCLASS_PARTITION udevice + * @loaded_dp: pointer to default file device path + * Return: status code + */ +static efi_status_t search_default_file(struct udevice *dev, + struct efi_device_path **loaded_dp) +{ + efi_status_t ret; + efi_handle_t handle; + u16 *default_file_name = NULL; + struct efi_file_handle *root, *f; + struct efi_device_path *dp = NULL, *fp = NULL; + struct efi_simple_file_system_protocol *file_system; + struct efi_device_path *device_path, *full_path = NULL; + + if (dev_tag_get_ptr(dev, DM_TAG_EFI, (void **)&handle)) { + log_warning("DM_TAG_EFI not found\n"); + return EFI_INVALID_PARAMETER; + } + + ret = EFI_CALL(bs->open_protocol(handle, &efi_guid_device_path, + (void **)&device_path, efi_root, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL)); + if (ret != EFI_SUCCESS) + return ret; + + ret = EFI_CALL(bs->open_protocol(handle, &efi_simple_file_system_protocol_guid, + (void **)&file_system, efi_root, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL)); + if (ret != EFI_SUCCESS) + return ret; + + ret = EFI_CALL(file_system->open_volume(file_system, &root)); + if (ret != EFI_SUCCESS) + return ret; + + full_path = expand_media_path(device_path); + ret = efi_dp_split_file_path(full_path, &dp, &fp); + if (ret != EFI_SUCCESS) + goto err; + + default_file_name = efi_dp_str(fp); + efi_free_pool(dp); + efi_free_pool(fp); + if (!default_file_name) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + + ret = EFI_CALL(root->open(root, &f, default_file_name, + EFI_FILE_MODE_READ, 0)); + efi_free_pool(default_file_name); + if (ret != EFI_SUCCESS) + goto err; + + EFI_CALL(f->close(f)); + EFI_CALL(root->close(root)); + + *loaded_dp = full_path; + + return EFI_SUCCESS; + +err: + EFI_CALL(root->close(root)); + efi_free_pool(full_path); + + return ret; +} + +/** + * check_disk_has_default_file() - load the default file + * + * @blk: pointer to the UCLASS_BLK udevice + * @dp: pointer to default file device path + * Return: status code + */ +static efi_status_t check_disk_has_default_file(struct udevice *blk, + struct efi_device_path **dp) +{ + efi_status_t ret; + struct udevice *partition; + + /* image that has no partition table but a file system */ + ret = search_default_file(blk, dp); + if (ret == EFI_SUCCESS) + return ret; + + /* try the partitions */ + device_foreach_child(partition, blk) { + enum uclass_id id; + + id = device_get_uclass_id(partition); + if (id != UCLASS_PARTITION) + continue; + + ret = search_default_file(partition, dp); + if (ret == EFI_SUCCESS) + return ret; + } + + return EFI_NOT_FOUND; +} + +/** + * prepare_loaded_image() - prepare ramdisk for downloaded image + * + * @label: label of load option + * @addr: image address + * @size: image size + * @dp: pointer to default file device path + * @blk: pointer to created blk udevice + * Return: status code + */ +static efi_status_t prepare_loaded_image(u16 *label, ulong addr, ulong size, + struct efi_device_path **dp, + struct udevice **blk) +{ + efi_status_t ret; + struct udevice *ramdisk_blk; + + ramdisk_blk = mount_image(label, addr, size); + if (!ramdisk_blk) + return EFI_LOAD_ERROR; + + ret = check_disk_has_default_file(ramdisk_blk, dp); + if (ret != EFI_SUCCESS) { + log_info("Cannot boot from downloaded image\n"); + goto err; + } + + /* + * TODO: expose the ramdisk to OS. + * Need to pass the ramdisk information by the architecture-specific + * methods such as 'pmem' device-tree node. + */ + ret = efi_add_memory_map(addr, size, EFI_RESERVED_MEMORY_TYPE); + if (ret != EFI_SUCCESS) { + log_err("Memory reservation failed\n"); + goto err; + } + + *blk = ramdisk_blk; + + return EFI_SUCCESS; + +err: + if (blkmap_destroy(ramdisk_blk->parent)) + log_err("Destroying blkmap failed\n"); + + return ret; +} + +/** + * efi_bootmgr_release_uridp_resource() - cleanup uri device path resource + * + * @ctx: event context + * Return: status code + */ +efi_status_t efi_bootmgr_release_uridp_resource(struct uridp_context *ctx) +{ + efi_status_t ret = EFI_SUCCESS; + + if (!ctx) + return ret; + + /* cleanup for iso or img image */ + if (ctx->ramdisk_blk_dev) { + ret = efi_add_memory_map(ctx->image_addr, ctx->image_size, + EFI_CONVENTIONAL_MEMORY); + if (ret != EFI_SUCCESS) + log_err("Reclaiming memory failed\n"); + + if (blkmap_destroy(ctx->ramdisk_blk_dev->parent)) { + log_err("Destroying blkmap failed\n"); + ret = EFI_DEVICE_ERROR; + } + } + + /* cleanup for PE-COFF image */ + if (ctx->mem_handle) { + ret = efi_uninstall_multiple_protocol_interfaces( + ctx->mem_handle, &efi_guid_device_path, ctx->loaded_dp, + NULL); + if (ret != EFI_SUCCESS) + log_err("Uninstall device_path protocol failed\n"); + } + + efi_free_pool(ctx->loaded_dp); + free(ctx); + + return ret; +} + +/** + * efi_bootmgr_image_return_notify() - return to efibootmgr callback + * + * @event: the event for which this notification function is registered + * @context: event context + */ +static void EFIAPI efi_bootmgr_image_return_notify(struct efi_event *event, + void *context) +{ + efi_status_t ret; + + EFI_ENTRY("%p, %p", event, context); + ret = efi_bootmgr_release_uridp_resource(context); + EFI_EXIT(ret); +} + +/** + * try_load_from_uri_path() - Handle the URI device path + * + * @uridp: uri device path + * @lo_label: label of load option + * @handle: pointer to handle for newly installed image + * Return: status code + */ +static efi_status_t try_load_from_uri_path(struct efi_device_path_uri *uridp, + u16 *lo_label, + efi_handle_t *handle) +{ + char *s; + int err; + int uri_len; + efi_status_t ret; + void *source_buffer; + efi_uintn_t source_size; + struct uridp_context *ctx; + struct udevice *blk = NULL; + struct efi_event *event = NULL; + efi_handle_t mem_handle = NULL; + struct efi_device_path *loaded_dp; + static ulong image_size, image_addr; + + ctx = calloc(1, sizeof(struct uridp_context)); + if (!ctx) + return EFI_OUT_OF_RESOURCES; + + s = env_get("loadaddr"); + if (!s) { + log_err("Error: loadaddr is not set\n"); + ret = EFI_INVALID_PARAMETER; + goto err; + } + + image_addr = hextoul(s, NULL); + err = wget_with_dns(image_addr, uridp->uri); + if (err < 0) { + ret = EFI_INVALID_PARAMETER; + goto err; + } + + image_size = env_get_hex("filesize", 0); + if (!image_size) { + ret = EFI_INVALID_PARAMETER; + goto err; + } + + /* + * If the file extension is ".iso" or ".img", mount it and try to load + * the default file. + * If the file is PE-COFF image, load the downloaded file. + */ + uri_len = strlen(uridp->uri); + if (!strncmp(&uridp->uri[uri_len - 4], ".iso", 4) || + !strncmp(&uridp->uri[uri_len - 4], ".img", 4)) { + ret = prepare_loaded_image(lo_label, image_addr, image_size, + &loaded_dp, &blk); + if (ret != EFI_SUCCESS) + goto err; + + source_buffer = NULL; + source_size = 0; + } else if (efi_check_pe((void *)image_addr, image_size, NULL) == EFI_SUCCESS) { + /* + * loaded_dp must exist until efi application returns, + * will be freed in return_to_efibootmgr event callback. + */ + loaded_dp = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, + (uintptr_t)image_addr, image_size); + ret = efi_install_multiple_protocol_interfaces( + &mem_handle, &efi_guid_device_path, loaded_dp, NULL); + if (ret != EFI_SUCCESS) + goto err; + + source_buffer = (void *)image_addr; + source_size = image_size; + } else { + log_err("Error: file type is not supported\n"); + ret = EFI_UNSUPPORTED; + goto err; + } + + ctx->image_size = image_size; + ctx->image_addr = image_addr; + ctx->loaded_dp = loaded_dp; + ctx->ramdisk_blk_dev = blk; + ctx->mem_handle = mem_handle; + + ret = EFI_CALL(efi_load_image(false, efi_root, loaded_dp, source_buffer, + source_size, handle)); + if (ret != EFI_SUCCESS) + goto err; + + /* create event for cleanup when the image returns or error occurs */ + ret = efi_create_event(EVT_NOTIFY_SIGNAL, TPL_CALLBACK, + efi_bootmgr_image_return_notify, ctx, + &efi_guid_event_group_return_to_efibootmgr, + &event); + if (ret != EFI_SUCCESS) { + log_err("Creating event failed\n"); + goto err; + } + + return ret; + +err: + efi_bootmgr_release_uridp_resource(ctx); + + return ret; +} + /** * try_load_entry() - try to load image for boot option * @@ -211,6 +590,13 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle, if (EFI_DP_TYPE(lo.file_path, MEDIA_DEVICE, FILE_PATH)) { /* file_path doesn't contain a device path */ ret = try_load_from_short_path(lo.file_path, handle); + } else if (EFI_DP_TYPE(lo.file_path, MESSAGING_DEVICE, MSG_URI)) { + if (IS_ENABLED(CONFIG_EFI_HTTP_BOOT)) + ret = try_load_from_uri_path( + (struct efi_device_path_uri *)lo.file_path, + lo.label, handle); + else + ret = EFI_LOAD_ERROR; } else { file_path = expand_media_path(lo.file_path); ret = EFI_CALL(efi_load_image(true, efi_root, file_path, -- cgit v1.2.3 From f01c961ee34c0124ce53020970ba11b0ab316d9e Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Fri, 10 Nov 2023 13:25:41 +0900 Subject: cmd: efidebug: add uri device path This adds the URI device path option for 'boot add' subcommand. User can add the URI load option for downloading ISO image file or EFI application through network. Currently HTTP is only supported. Signed-off-by: Masahisa Kojima Signed-off-by: Ilias Apalodimas --- cmd/efidebug.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/net.h | 8 ++++++ net/wget.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 201531ac19f..78ef16f4cb5 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -707,6 +708,65 @@ out: return initrd_dp; } +/** + * efi_boot_add_uri() - set URI load option + * + * @argc: Number of arguments + * @argv: Argument array + * @var_name16: variable name buffer + * @var_name16_size: variable name buffer size + * @lo: pointer to the load option + * @file_path: buffer to set the generated device path pointer + * @fp_size: file_path size + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure + */ +static int efi_boot_add_uri(int argc, char *const argv[], u16 *var_name16, + size_t var_name16_size, struct efi_load_option *lo, + struct efi_device_path **file_path, + efi_uintn_t *fp_size) +{ + int id; + char *pos; + char *endp; + u16 *label; + efi_uintn_t uridp_len; + struct efi_device_path_uri *uridp; + + if (argc < 3 || lo->label) + return CMD_RET_USAGE; + + id = (int)hextoul(argv[1], &endp); + if (*endp != '\0' || id > 0xffff) + return CMD_RET_USAGE; + + label = efi_convert_string(argv[2]); + if (!label) + return CMD_RET_FAILURE; + + if (!wget_validate_uri(argv[3])) { + printf("ERROR: invalid URI\n"); + return CMD_RET_FAILURE; + } + + efi_create_indexed_name(var_name16, var_name16_size, "Boot", id); + lo->label = label; + + uridp_len = sizeof(struct efi_device_path) + strlen(argv[3]) + 1; + uridp = efi_alloc(uridp_len + sizeof(END)); + uridp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + uridp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_URI; + uridp->dp.length = uridp_len; + strcpy(uridp->uri, argv[3]); + pos = (char *)uridp + uridp_len; + memcpy(pos, &END, sizeof(END)); + + *file_path = &uridp->dp; + *fp_size += uridp_len + sizeof(END); + + return CMD_RET_SUCCESS; +} + /** * do_efi_boot_add() - set UEFI load option * @@ -829,6 +889,21 @@ static int do_efi_boot_add(struct cmd_tbl *cmdtp, int flag, argc -= 1; argv += 1; break; + case 'u': + if (IS_ENABLED(CONFIG_EFI_HTTP_BOOT)) { + r = efi_boot_add_uri(argc, argv, var_name16, + sizeof(var_name16), &lo, + &file_path, &fp_size); + if (r != CMD_RET_SUCCESS) + goto out; + fp_free = file_path; + argc -= 3; + argv += 3; + } else{ + r = CMD_RET_USAGE; + goto out; + } + break; default: r = CMD_RET_USAGE; goto out; @@ -1491,6 +1566,9 @@ U_BOOT_LONGHELP(efidebug, " -b|-B