From 49b5b19f862bea5da5887f5d58411dafeaff1ae9 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 24 Dec 2019 17:19:12 +0200 Subject: [PATCH 01/38] test/py: Fix broken 'notbuildconfigspec' marker Consider the following test sample: @pytest.mark.buildconfigspec('fit') @pytest.mark.notbuildconfigspec('generate_acpi_table') def test_sample(u_boot_console): Whatever the argument of the 'notbuildconfigspec' is, the test ends up being skipped with the message: ('/uboot/test/py/conftest.py', 463, 'Skipped: .config feature "fit" enabled') Signed-off-by: Cristian Ciocaltea Signed-off-by: Heinrich Schuchardt --- test/py/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/py/conftest.py b/test/py/conftest.py index 472dd0545d..34ac4fb062 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -472,7 +472,7 @@ def setup_buildconfigspec(item): option = options.args[0] if not ubconfig.buildconfig.get('config_' + option.lower(), None): pytest.skip('.config feature "%s" not enabled' % option.lower()) - for option in item.iter_markers('notbuildconfigspec'): + for options in item.iter_markers('notbuildconfigspec'): option = options.args[0] if ubconfig.buildconfig.get('config_' + option.lower(), None): pytest.skip('.config feature "%s" enabled' % option.lower()) From 72a60feab025dcf420f2b2137849d2f0bcbb13b5 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 29 Dec 2019 12:06:29 +0100 Subject: [PATCH 02/38] configs: qemu: enable FIT images on qemu_arm(64)_defconfig For testing UEFI FIT images we need FIT image support on QEMU. Signed-off-by: Heinrich Schuchardt --- configs/qemu_arm64_defconfig | 5 +++++ configs/qemu_arm_defconfig | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/configs/qemu_arm64_defconfig b/configs/qemu_arm64_defconfig index b7c320743a..50d0aa5bf5 100644 --- a/configs/qemu_arm64_defconfig +++ b/configs/qemu_arm64_defconfig @@ -6,6 +6,11 @@ CONFIG_NR_DRAM_BANKS=1 CONFIG_ENV_SECT_SIZE=0x40000 CONFIG_AHCI=y CONFIG_DISTRO_DEFAULTS=y +CONFIG_FIT=y +CONFIG_FIT_SIGNATURE=y +CONFIG_FIT_VERBOSE=y +CONFIG_FIT_BEST_MATCH=y +CONFIG_LEGACY_IMAGE_FORMAT=y CONFIG_USE_PREBOOT=y CONFIG_PREBOOT="pci enum" # CONFIG_DISPLAY_CPUINFO is not set diff --git a/configs/qemu_arm_defconfig b/configs/qemu_arm_defconfig index 521c6793a3..ff97d6bd83 100644 --- a/configs/qemu_arm_defconfig +++ b/configs/qemu_arm_defconfig @@ -7,6 +7,11 @@ CONFIG_NR_DRAM_BANKS=1 CONFIG_ENV_SECT_SIZE=0x40000 CONFIG_AHCI=y CONFIG_DISTRO_DEFAULTS=y +CONFIG_FIT=y +CONFIG_FIT_SIGNATURE=y +CONFIG_FIT_VERBOSE=y +CONFIG_FIT_BEST_MATCH=y +CONFIG_LEGACY_IMAGE_FORMAT=y CONFIG_USE_PREBOOT=y CONFIG_PREBOOT="pci enum" # CONFIG_DISPLAY_CPUINFO is not set From 75fe571a4194ac26de7deffb2aa6db494596f58f Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Tue, 26 Nov 2019 09:51:05 +0900 Subject: [PATCH 03/38] include: pe.h: add signature-related definitions The index (IMAGE_DIRECTORY_ENTRY_SECURITY) in a table points to a region containing authentication information (image's signature) in PE format. WIN_CERTIFICATE structure defines an embedded signature format. Those definitions will be used in my UEFI secure boot patch. Signed-off-by: AKASHI Takahiro Reviewed-by: Heinrich Schuchardt --- include/pe.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/pe.h b/include/pe.h index bff3b0aa7a..086f2b860e 100644 --- a/include/pe.h +++ b/include/pe.h @@ -155,6 +155,8 @@ typedef struct _IMAGE_SECTION_HEADER { uint32_t Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; +/* Indices for Optional Header Data Directories */ +#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 typedef struct _IMAGE_BASE_RELOCATION @@ -252,4 +254,20 @@ typedef struct _IMAGE_RELOCATION #define IMAGE_REL_AMD64_PAIR 0x000F #define IMAGE_REL_AMD64_SSPAN32 0x0010 +/* certificate appended to PE image */ +typedef struct _WIN_CERTIFICATE { + uint32_t dwLength; + uint16_t wRevision; + uint16_t wCertificateType; + uint8_t bCertificate[]; +} WIN_CERTIFICATE, *LPWIN_CERTIFICATE; + +/* Definitions for the contents of the certs data block */ +#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 +#define WIN_CERT_TYPE_EFI_OKCS115 0x0EF0 +#define WIN_CERT_TYPE_EFI_GUID 0x0EF1 + +#define WIN_CERT_REVISION_1_0 0x0100 +#define WIN_CERT_REVISION_2_0 0x0200 + #endif /* _PE_H */ From 8876e1bc8800e76a6865b22a62a4c0edeac0855c Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 4 Dec 2019 22:58:58 +0100 Subject: [PATCH 04/38] efi_loader: imply USB_KEYBOARD_FN_KEYS UEFI applications like GRUB and SCT assume that function keys are enabled on the keyboard. Let EFI_LOADER imply USB_KEYBOARD_FN_KEYS. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 21ef440341..e2cb729303 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -15,6 +15,7 @@ config EFI_LOADER select HAVE_BLOCK_DEVICE select REGEX imply CFB_CONSOLE_ANSI + imply USB_KEYBOARD_FN_KEYS help Select this option if you want to run UEFI applications (like GNU GRUB or iPXE) on top of U-Boot. If this option is enabled, U-Boot From 570147275c00ce64e0aaa956b1dff73c7422985d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 8 Dec 2019 10:02:37 +0100 Subject: [PATCH 05/38] efi_loader: adjust file system info When the GetInfo() method of the EFI_FILE_PROTOCOL is called to retrieve the file system info we claim that the volume is read only and has no free space. This leads to failures in programs that check this information before writing to the volume like SCT's InstallSct.efi. Currently there is no function to determine these parameters in U-Boot. So let's return optimistic values: Return that the volume is writable. Return the volume size as free space. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_file.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index 6d3f680e56..140116ddc4 100644 --- a/lib/efi_loader/efi_file.c +++ b/lib/efi_loader/efi_file.c @@ -656,9 +656,16 @@ static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file, memset(info, 0, required_size); info->size = required_size; - info->read_only = true; + /* + * TODO: We cannot determine if the volume can be written to. + */ + info->read_only = false; info->volume_size = part.size * part.blksz; - info->free_space = 0; + /* + * TODO: We currently have no function to determine the free + * space. The volume size is the best upper bound we have. + */ + info->free_space = info->volume_size; info->block_size = part.blksz; /* * TODO: The volume label is not available in U-Boot. From 7a597259d26f84a63350b6a1af5b29445e9d451b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 28 Nov 2019 06:46:09 +0100 Subject: [PATCH 06/38] efi_loader: pass address to efi_install_fdt() As part of moving the parsing of command line arguments to do_bootefi() call efi_install_fdt() with the address of the device tree instead of a string. If the address is EFI_FDT_USE_INTERNAL (= 0), the internal device tree is used. Signed-off-by: Heinrich Schuchardt --- cmd/bootefi.c | 32 ++++++++++++++++++-------------- include/efi_loader.h | 3 +++ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index f613cce7e2..2b190a3edd 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -196,40 +196,39 @@ static void *get_config_table(const efi_guid_t *guid) #endif /* !CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) */ /** - * efi_install_fdt() - install fdt passed by a command argument + * efi_install_fdt() - install device tree * - * If fdt_opt is available, the device tree located at that memory address will + * If fdt_addr is available, the device tree located at that memory address will * will be installed as configuration table, otherwise the device tree located * at the address indicated by environment variable fdtcontroladdr will be used. * - * On architectures (x86) using ACPI tables device trees shall not be installed - * as configuration table. + * On architectures using ACPI tables device trees shall not be installed as + * configuration table. * - * @fdt_opt: pointer to argument + * @fdt_addr: address of device tree or EFI_FDT_USE_INTERNAL to use the + * internal device tree as indicated by environment variable + * fdtcontroladdr * Return: status code */ -static efi_status_t efi_install_fdt(const char *fdt_opt) +static efi_status_t efi_install_fdt(uintptr_t fdt_addr) { /* * The EBBR spec requires that we have either an FDT or an ACPI table * but not both. */ #if CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) - if (fdt_opt) { + if (fdt_addr) { printf("ERROR: can't have ACPI table and device tree.\n"); return EFI_LOAD_ERROR; } #else - unsigned long fdt_addr; void *fdt; bootm_headers_t img = { 0 }; efi_status_t ret; - if (fdt_opt) { - fdt_addr = simple_strtoul(fdt_opt, NULL, 16); - if (!fdt_addr) - return EFI_INVALID_PARAMETER; - } else { + if (fdt_addr == EFI_FDT_USE_INTERNAL) { + const char *fdt_opt; + /* Look for device tree that is already installed */ if (get_config_table(&efi_guid_fdt)) return EFI_SUCCESS; @@ -556,6 +555,7 @@ static int do_efi_selftest(void) static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { efi_status_t ret; + uintptr_t fdt_addr; if (argc < 2) return CMD_RET_USAGE; @@ -568,7 +568,11 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return CMD_RET_FAILURE; } - ret = efi_install_fdt(argc > 2 ? argv[2] : NULL); + if (argc > 2) + fdt_addr = simple_strtoul(argv[2], NULL, 16); + else + fdt_addr = EFI_FDT_USE_INTERNAL; + ret = efi_install_fdt(fdt_addr); if (ret == EFI_INVALID_PARAMETER) return CMD_RET_USAGE; else if (ret != EFI_SUCCESS) diff --git a/include/efi_loader.h b/include/efi_loader.h index 16a1b258b1..3a22089329 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -34,6 +34,9 @@ static inline int guidcmp(const void *g1, const void *g2) EFI_GUID(0xbbe4e671, 0x5773, 0x4ea1, \ 0x9a, 0xab, 0x3a, 0x7d, 0xbf, 0x40, 0xc4, 0x82) +/* Use internal device tree when starting UEFI application */ +#define EFI_FDT_USE_INTERNAL 0UL + /* Root node */ extern efi_handle_t efi_root; From 753aa18f176af214b8f054c45f99bc20dfb15938 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 4 Dec 2019 12:31:12 +0100 Subject: [PATCH 07/38] efi_loader: use hardware device tree by default If the bootefi command is called without passing the address of a device tree, the internal device tree is used. For devices with a hardware device tree it is preferable to used the hardware device tree in this case. Signed-off-by: Heinrich Schuchardt --- cmd/bootefi.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 2b190a3edd..a3e2a05126 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -200,14 +200,16 @@ static void *get_config_table(const efi_guid_t *guid) * * If fdt_addr is available, the device tree located at that memory address will * will be installed as configuration table, otherwise the device tree located - * at the address indicated by environment variable fdtcontroladdr will be used. + * at the address indicated by environment variable fdt_addr or as fallback + * fdtcontroladdr will be used. * * On architectures using ACPI tables device trees shall not be installed as * configuration table. * * @fdt_addr: address of device tree or EFI_FDT_USE_INTERNAL to use the - * internal device tree as indicated by environment variable - * fdtcontroladdr + * the hardware device tree as indicated by environment variable + * fdt_addr or as fallback the internal device tree as indicated by + * the environment variable fdtcontroladdr * Return: status code */ static efi_status_t efi_install_fdt(uintptr_t fdt_addr) @@ -232,15 +234,19 @@ static efi_status_t efi_install_fdt(uintptr_t fdt_addr) /* Look for device tree that is already installed */ if (get_config_table(&efi_guid_fdt)) return EFI_SUCCESS; - /* Use our own device tree as default */ - fdt_opt = env_get("fdtcontroladdr"); + /* Check if there is a hardware device tree */ + fdt_opt = env_get("fdt_addr"); + /* Use our own device tree as fallback */ if (!fdt_opt) { - printf("ERROR: need device tree\n"); - return EFI_NOT_FOUND; + fdt_opt = env_get("fdtcontroladdr"); + if (!fdt_opt) { + printf("ERROR: need device tree\n"); + return EFI_NOT_FOUND; + } } fdt_addr = simple_strtoul(fdt_opt, NULL, 16); if (!fdt_addr) { - printf("ERROR: invalid $fdtcontroladdr\n"); + printf("ERROR: invalid $fdt_addr or $fdtcontroladdr\n"); return EFI_LOAD_ERROR; } } From f9ceb6ac1443b824e94a9df9ec1dfb2bc742e451 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 7 Dec 2019 20:51:06 +0100 Subject: [PATCH 08/38] efi_loader: carve out efi_run_image() Provide public function efi_run_imager() which can be used to run an UEFI image from memory. Signed-off-by: Heinrich Schuchardt --- cmd/bootefi.c | 51 +++++++++++++++++++++++++++----------------- include/efi_loader.h | 2 ++ 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index a3e2a05126..15b4ff9515 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -360,11 +360,8 @@ static int do_efibootmgr(void) static int do_bootefi_image(const char *image_opt) { void *image_buf; - struct efi_device_path *device_path, *image_path; - struct efi_device_path *file_path = NULL; unsigned long addr, size; const char *size_str; - efi_handle_t mem_handle = NULL, handle; efi_status_t ret; #ifdef CONFIG_CMD_BOOTEFI_HELLO @@ -382,8 +379,10 @@ static int do_bootefi_image(const char *image_opt) image_buf = map_sysmem(addr, size); memcpy(image_buf, __efi_helloworld_begin, size); - device_path = NULL; - image_path = NULL; + efi_free_pool(bootefi_device_path); + efi_free_pool(bootefi_image_path); + bootefi_device_path = NULL; + bootefi_image_path = NULL; } else #endif { @@ -399,19 +398,37 @@ static int do_bootefi_image(const char *image_opt) return CMD_RET_USAGE; image_buf = map_sysmem(addr, size); - - device_path = bootefi_device_path; - image_path = bootefi_image_path; } + ret = efi_run_image(image_buf, size); - if (!device_path && !image_path) { + if (ret != EFI_SUCCESS) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +/** + * efi_run_image() - run loaded UEFI image + * + * @source_buffer: memory address of the UEFI image + * @source_size: size of the UEFI image + * Return: status code + */ +efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size) +{ + efi_handle_t mem_handle = NULL, handle; + struct efi_device_path *file_path = NULL; + efi_status_t ret; + + if (!bootefi_device_path || !bootefi_image_path) { /* * Special case for efi payload not loaded from disk, * such as 'bootefi hello' or for example payload * loaded directly into memory via JTAG, etc: */ file_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, - (uintptr_t)image_buf, size); + (uintptr_t)source_buffer, + source_size); /* * Make sure that device for device_path exist * in load_image(). Otherwise, shell and grub will fail. @@ -425,12 +442,12 @@ static int do_bootefi_image(const char *image_opt) if (ret != EFI_SUCCESS) goto out; } else { - assert(device_path && image_path); - file_path = efi_dp_append(device_path, image_path); + file_path = efi_dp_append(bootefi_device_path, + bootefi_image_path); } - ret = EFI_CALL(efi_load_image(false, efi_root, - file_path, image_buf, size, &handle)); + ret = EFI_CALL(efi_load_image(false, efi_root, file_path, source_buffer, + source_size, &handle)); if (ret != EFI_SUCCESS) goto out; @@ -441,11 +458,7 @@ out: efi_delete_handle(mem_handle); if (file_path) efi_free_pool(file_path); - - if (ret != EFI_SUCCESS) - return CMD_RET_FAILURE; - - return CMD_RET_SUCCESS; + return ret; } #ifdef CONFIG_CMD_BOOTEFI_SELFTEST diff --git a/include/efi_loader.h b/include/efi_loader.h index 3a22089329..1e1fe52bc0 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -341,6 +341,8 @@ extern struct list_head efi_register_notify_events; /* Initialize efi execution environment */ efi_status_t efi_init_obj_list(void); +/* Run loaded UEFI image */ +efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size); /* Initialize variable services */ efi_status_t efi_init_variables(void); /* Notify ExitBootServices() is called */ From f64f223256f32e86d97ec32eea7dc36d5e9c5fd9 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 8 Dec 2019 01:07:01 +0100 Subject: [PATCH 09/38] efi_loader: export efi_install_fdt() Use a pointer to addressable memory instead of a "physical" address in the virtual address space of the sandbox to efi_install_fdt(). Export the efi_install_fdt() function. Signed-off-by: Heinrich Schuchardt --- cmd/bootefi.c | 24 ++++++++++++++---------- include/efi_loader.h | 4 +++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 15b4ff9515..78c8b8dbd1 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -212,24 +212,24 @@ static void *get_config_table(const efi_guid_t *guid) * the environment variable fdtcontroladdr * Return: status code */ -static efi_status_t efi_install_fdt(uintptr_t fdt_addr) +efi_status_t efi_install_fdt(void *fdt) { /* * The EBBR spec requires that we have either an FDT or an ACPI table * but not both. */ #if CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) - if (fdt_addr) { + if (fdt) { printf("ERROR: can't have ACPI table and device tree.\n"); return EFI_LOAD_ERROR; } #else - void *fdt; bootm_headers_t img = { 0 }; efi_status_t ret; - if (fdt_addr == EFI_FDT_USE_INTERNAL) { + if (fdt == EFI_FDT_USE_INTERNAL) { const char *fdt_opt; + uintptr_t fdt_addr; /* Look for device tree that is already installed */ if (get_config_table(&efi_guid_fdt)) @@ -249,10 +249,10 @@ static efi_status_t efi_install_fdt(uintptr_t fdt_addr) printf("ERROR: invalid $fdt_addr or $fdtcontroladdr\n"); return EFI_LOAD_ERROR; } + fdt = map_sysmem(fdt_addr, 0); } /* Install device tree */ - fdt = map_sysmem(fdt_addr, 0); if (fdt_check_header(fdt)) { printf("ERROR: invalid device tree\n"); return EFI_LOAD_ERROR; @@ -574,7 +574,7 @@ static int do_efi_selftest(void) static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { efi_status_t ret; - uintptr_t fdt_addr; + void *fdt; if (argc < 2) return CMD_RET_USAGE; @@ -587,11 +587,15 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return CMD_RET_FAILURE; } - if (argc > 2) + if (argc > 2) { + uintptr_t fdt_addr; + fdt_addr = simple_strtoul(argv[2], NULL, 16); - else - fdt_addr = EFI_FDT_USE_INTERNAL; - ret = efi_install_fdt(fdt_addr); + fdt = map_sysmem(fdt_addr, 0); + } else { + fdt = EFI_FDT_USE_INTERNAL; + } + ret = efi_install_fdt(fdt); if (ret == EFI_INVALID_PARAMETER) return CMD_RET_USAGE; else if (ret != EFI_SUCCESS) diff --git a/include/efi_loader.h b/include/efi_loader.h index 1e1fe52bc0..4d401f69d7 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -35,7 +35,7 @@ static inline int guidcmp(const void *g1, const void *g2) 0x9a, 0xab, 0x3a, 0x7d, 0xbf, 0x40, 0xc4, 0x82) /* Use internal device tree when starting UEFI application */ -#define EFI_FDT_USE_INTERNAL 0UL +#define EFI_FDT_USE_INTERNAL NULL /* Root node */ extern efi_handle_t efi_root; @@ -341,6 +341,8 @@ extern struct list_head efi_register_notify_events; /* Initialize efi execution environment */ efi_status_t efi_init_obj_list(void); +/* Install device tree */ +efi_status_t efi_install_fdt(void *fdt); /* Run loaded UEFI image */ efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size); /* Initialize variable services */ From 362b991cbe203cd4795e4231aeb7b3388776da0f Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Mon, 16 Dec 2019 12:01:32 +0530 Subject: [PATCH 10/38] efi_selftest: Update .gitignore Add the following file to .gitignore efi_miniapp_file_image_exception.h Signed-off-by: Sughosh Ganu Use efi_miniapp_*.h instead of file enumeration. Signed-off-by: Heinrich Schuchardt --- lib/efi_selftest/.gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/efi_selftest/.gitignore b/lib/efi_selftest/.gitignore index 293a17b818..5b25169e6e 100644 --- a/lib/efi_selftest/.gitignore +++ b/lib/efi_selftest/.gitignore @@ -1,4 +1,3 @@ -efi_miniapp_file_image_exit.h -efi_miniapp_file_image_return.h +efi_miniapp_*.h *.efi *.so From 07b57ef1ebcb1098080385392197c3b9971e00e1 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Mon, 16 Dec 2019 12:05:58 +0100 Subject: [PATCH 11/38] efi_loader: git ignore helloworld_efi.S Add *.S to .gitignore. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/efi_loader/.gitignore b/lib/efi_loader/.gitignore index 634a600f84..f2d7c14447 100644 --- a/lib/efi_loader/.gitignore +++ b/lib/efi_loader/.gitignore @@ -1,2 +1,3 @@ *.efi *.so +*.S From 3510280960ead6947a296d1d9b8744d3336381ea Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 19 Dec 2019 13:39:30 +0100 Subject: [PATCH 12/38] test/py: describe env__efi_loader_helloworld_file Describe the components of environment variable env__efi_loader_helloworld_file. Signed-off-by: Heinrich Schuchardt --- test/py/tests/test_efi_loader.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/py/tests/test_efi_loader.py b/test/py/tests/test_efi_loader.py index d6b214f845..adf9d77452 100644 --- a/test/py/tests/test_efi_loader.py +++ b/test/py/tests/test_efi_loader.py @@ -43,9 +43,10 @@ env__net_static_env_vars = [ # Details regarding a file that may be read from a TFTP server. This variable # may be omitted or set to None if TFTP testing is not possible or desired. env__efi_loader_helloworld_file = { - 'fn': 'lib/efi_loader/helloworld.efi', - 'size': 5058624, - 'crc32': 'c2244b26', + 'fn': 'lib/efi_loader/helloworld.efi', # file name + 'size': 5058624, # file length in bytes + 'crc32': 'c2244b26', # CRC32 check sum + 'addr': 0x40400000, # load address } """ From b0ad9b5b2a60583dc44a171d7dfdaac1b4518aa7 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 22 Dec 2019 07:15:55 +0000 Subject: [PATCH 13/38] efi_loader: clear screen should move cursor to home On a VT100 terminal [2J should be enough to both clear the whole screen and set the cursor to position (1, 1). But the Linux console does not behave like this. So send an extra [H. For reference see the console_codes(4) man page. Add a function description. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_console.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 218f7caa12..8494044799 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -360,12 +360,26 @@ static efi_status_t EFIAPI efi_cout_set_attribute( return EFI_EXIT(EFI_SUCCESS); } +/** + * efi_cout_clear_screen() - clear screen + * + * This function implements the ClearScreen service of the + * EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL. See the Unified Extensible Firmware + * Interface (UEFI) specification for details. + * + * @this: pointer to the protocol instance + * Return: status code + */ static efi_status_t EFIAPI efi_cout_clear_screen( struct efi_simple_text_output_protocol *this) { EFI_ENTRY("%p", this); - printf(ESC"[2J"); + /* + * The Linux console wants both a clear and a home command. The video + * uclass does not support [H without coordinates, yet. + */ + printf(ESC "[2J" ESC "[1;1H"); efi_con_mode.cursor_column = 0; efi_con_mode.cursor_row = 0; From 9b8d264b5af801a56e06aceab34dc74cb66121b1 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 24 Dec 2019 08:11:01 +0100 Subject: [PATCH 14/38] part: efi: comment for GPT_HEADER_SIGNATURE_UBOOT Add a comment indicating that the value of GPT_HEADER_SIGNATURE_UBOOT equals the ASCII string 'EFI PART'. Signed-off-by: Heinrich Schuchardt --- include/part_efi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/part_efi.h b/include/part_efi.h index eb5797af74..1929e4400f 100644 --- a/include/part_efi.h +++ b/include/part_efi.h @@ -24,7 +24,7 @@ #define EFI_PMBR_OSTYPE_EFI 0xEF #define EFI_PMBR_OSTYPE_EFI_GPT 0xEE -#define GPT_HEADER_SIGNATURE_UBOOT 0x5452415020494645ULL +#define GPT_HEADER_SIGNATURE_UBOOT 0x5452415020494645ULL // 'EFI PART' #define GPT_HEADER_CHROMEOS_IGNORE 0x454d45524f4e4749ULL // 'IGNOREME' #define GPT_HEADER_REVISION_V1 0x00010000 From 9bb758aab63dd5972b27b8dc1a0f22224b54f885 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 1 Jan 2020 13:19:12 +0100 Subject: [PATCH 15/38] efi_loader: __cyg_profile_func_enter/_exit U-Boot can be compiled with function tracing enabled. When compiling with FTRACE __cyg_profile_func_enter() is called when a function is entered and __cyg_profile_func_exit() when the function is left. To avoid a crash we have to define these function for the free-standing UEFI binaries. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_freestanding.c | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/efi_loader/efi_freestanding.c b/lib/efi_loader/efi_freestanding.c index bd9da5bbc8..dcf5d1c49a 100644 --- a/lib/efi_loader/efi_freestanding.c +++ b/lib/efi_loader/efi_freestanding.c @@ -88,3 +88,35 @@ void *memset(void *s, int c, size_t n) *d++ = c; return s; } + +/** + * __cyg_profile_func_enter() - record function entry + * + * This is called on every function entry when compiling with + * -finstrument-functions. + * + * We do nothing here. + * + * @param func_ptr Pointer to function being entered + * @param caller Pointer to function which called this function + */ +void __attribute__((no_instrument_function)) +__cyg_profile_func_enter(void *func_ptr, void *caller) +{ +} + +/** + * __cyg_profile_func_exit() - record function exit + * + * This is called on every function exit when compiling with + * -finstrument-functions. + * + * We do nothing here. + * + * @param func_ptr Pointer to function being entered + * @param caller Pointer to function which called this function + */ +void __attribute__((no_instrument_function)) +__cyg_profile_func_exit(void *func_ptr, void *caller) +{ +} From 61e42d9465ef36857693cc3db615438241a06bf6 Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Sun, 29 Dec 2019 00:01:04 +0530 Subject: [PATCH 16/38] efi_loader: Add guidcpy function Add guidcpy function to copy the source guid to the destination guid. Use this function instead of memcpy for copying to the destination guid. Signed-off-by: Sughosh Ganu Use void * instead of efi_guid_t * for arguments to allow copying unaligned GUIDs. The GUIDs of configuration tables are __packed. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 5 +++++ lib/efi_loader/efi_boottime.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 4d401f69d7..e1c9b1fd6a 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -17,6 +17,11 @@ static inline int guidcmp(const void *g1, const void *g2) return memcmp(g1, g2, sizeof(efi_guid_t)); } +static inline void *guidcpy(void *dst, const void *src) +{ + return memcpy(dst, src, sizeof(efi_guid_t)); +} + /* No need for efi loader support in SPL */ #if CONFIG_IS_ENABLED(EFI_LOADER) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 88a7604bbf..3103a50158 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1401,7 +1401,7 @@ static efi_status_t EFIAPI efi_register_protocol_notify( } item->event = event; - memcpy(&item->protocol, protocol, sizeof(efi_guid_t)); + guidcpy(&item->protocol, protocol); INIT_LIST_HEAD(&item->handles); list_add_tail(&item->link, &efi_register_notify_events); @@ -1632,7 +1632,7 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, return EFI_OUT_OF_RESOURCES; /* Add a new entry */ - memcpy(&systab.tables[i].guid, guid, sizeof(*guid)); + guidcpy(&systab.tables[i].guid, guid); systab.tables[i].table = table; systab.nr_tables = i + 1; From a3850e40e1bd64f99f91960953003ca31ee6fd0b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 3 Jan 2020 22:53:42 +0100 Subject: [PATCH 17/38] efi_loader: free load options after execution When be launch a binary via bootefi the bootargs environment variable is used to set the load options in the loaded image protocol. Free memory allocated for load options when the UEFI binary exits. Signed-off-by: Heinrich Schuchardt --- cmd/bootefi.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 78c8b8dbd1..56bdff33c6 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -28,11 +28,13 @@ static struct efi_device_path *bootefi_device_path; /** * Set the load options of an image from an environment variable. * - * @handle: the image handle - * @env_var: name of the environment variable - * Return: status code + * @handle: the image handle + * @env_var: name of the environment variable + * @load_options: pointer to load options (output) + * Return: status code */ -static efi_status_t set_load_options(efi_handle_t handle, const char *env_var) +static efi_status_t set_load_options(efi_handle_t handle, const char *env_var, + u16 **load_options) { struct efi_loaded_image *loaded_image_info; size_t size; @@ -40,6 +42,7 @@ static efi_status_t set_load_options(efi_handle_t handle, const char *env_var) u16 *pos; efi_status_t ret; + *load_options = NULL; ret = EFI_CALL(systab.boottime->open_protocol( handle, &efi_guid_loaded_image, @@ -64,6 +67,7 @@ static efi_status_t set_load_options(efi_handle_t handle, const char *env_var) return EFI_OUT_OF_RESOURCES; } pos = loaded_image_info->load_options; + *load_options = pos; utf8_utf16_strcpy(&pos, env); loaded_image_info->load_options_size = size * 2; @@ -298,9 +302,10 @@ static efi_status_t do_bootefi_exec(efi_handle_t handle) efi_status_t ret; efi_uintn_t exit_data_size = 0; u16 *exit_data = NULL; + u16 *load_options; /* Transfer environment variable as load options */ - ret = set_load_options(handle, "bootargs"); + ret = set_load_options(handle, "bootargs", &load_options); if (ret != EFI_SUCCESS) return ret; @@ -314,12 +319,7 @@ static efi_status_t do_bootefi_exec(efi_handle_t handle) efi_restore_gd(); - /* - * FIXME: Who is responsible for - * free(loaded_image_info->load_options); - * Once efi_exit() is implemented correctly, - * handle itself doesn't exist here. - */ + free(load_options); return ret; } @@ -469,6 +469,7 @@ static efi_status_t bootefi_run_prepare(const char *load_options_path, struct efi_loaded_image **loaded_image_infop) { efi_status_t ret; + u16 *load_options; ret = efi_setup_loaded_image(device_path, image_path, image_objp, loaded_image_infop); @@ -476,7 +477,8 @@ static efi_status_t bootefi_run_prepare(const char *load_options_path, return ret; /* Transfer environment variable as load options */ - return set_load_options((efi_handle_t)*image_objp, load_options_path); + return set_load_options((efi_handle_t)*image_objp, load_options_path, + &load_options); } /** From 7b31efc54c3fba33d1305a0f9b730472bb0f63eb Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 3 Jan 2020 22:47:19 +0100 Subject: [PATCH 18/38] efi_loader: define all known warning status codes Of all warning status codes up to now only EFI_WARN_DELETE_FAILURE is defined. The patch adds the missing definitions for later usage. Signed-off-by: Heinrich Schuchardt --- include/efi.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/efi.h b/include/efi.h index 5f415a99cc..e12697a5d5 100644 --- a/include/efi.h +++ b/include/efi.h @@ -91,7 +91,13 @@ 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 +#define EFI_WARN_UNKNOWN_GLYPH 1 +#define EFI_WARN_DELETE_FAILURE 2 +#define EFI_WARN_WRITE_FAILURE 3 +#define EFI_WARN_BUFFER_TOO_SMALL 4 +#define EFI_WARN_STALE_DATA 5 +#define EFI_WARN_FILE_SYSTEM 6 +#define EFI_WARN_RESET_REQUIRED 7 typedef unsigned long efi_status_t; typedef u64 efi_physical_addr_t; From 173cd9e73ad833bf479cc5b400de5ca738ef7510 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 7 Jan 2020 06:02:33 +0100 Subject: [PATCH 19/38] cmd: efidebug: simplify get_guid_text() When we hit a matching GUID we can directly return the text. There is no need for a check after the loop. efi_guid_t is defined as 8 byte aligned but GUIDs in packed structures do not follow this alignment. Do not require the argument of get_guid_text() to be correctly aligned. Signed-off-by: Heinrich Schuchardt --- cmd/efidebug.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 1fff4390de..45ed5be885 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -254,24 +254,27 @@ static const struct { }; /** - * get_guid_text - get string of protocol guid - * @guid: Protocol guid - * Return: String + * get_guid_text - get string of GUID * - * Return string for display to represent the protocol. + * Return description of GUID. + * + * @guid: GUID + * Return: description of GUID or NULL */ -static const char *get_guid_text(const efi_guid_t *guid) +static const char *get_guid_text(const void *guid) { int i; - for (i = 0; i < ARRAY_SIZE(guid_list); i++) + for (i = 0; i < ARRAY_SIZE(guid_list); i++) { + /* + * As guidcmp uses memcmp() we can safely accept unaligned + * GUIDs. + */ if (!guidcmp(&guid_list[i].guid, guid)) - break; + return guid_list[i].text; + } - if (i != ARRAY_SIZE(guid_list)) - return guid_list[i].text; - else - return NULL; + return NULL; } /** From 986e0648840710b55a93587fc166296365e03f24 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 7 Jan 2020 05:57:47 +0100 Subject: [PATCH 20/38] cmd: efidebug: new sub-command tables Provide sub-command for efidebug to list configuration tables. Signed-off-by: Heinrich Schuchardt --- cmd/efidebug.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 45ed5be885..78fc649782 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -251,6 +251,19 @@ static const struct { "PXE Base Code", EFI_PXE_BASE_CODE_PROTOCOL_GUID, }, + /* Configuration table GUIDs */ + { + "ACPI table", + EFI_ACPI_TABLE_GUID, + }, + { + "device tree", + EFI_FDT_GUID, + }, + { + "SMBIOS table", + SMBIOS_TABLE_GUID, + }, }; /** @@ -480,6 +493,34 @@ static int do_efi_show_memmap(cmd_tbl_t *cmdtp, int flag, return CMD_RET_SUCCESS; } +/** + * do_efi_show_tables() - show UEFI configuration tables + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "tables" sub-command. + * Show UEFI configuration tables. + */ +static int do_efi_show_tables(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + efi_uintn_t i; + const char *guid_str; + + for (i = 0; i < systab.nr_tables; ++i) { + guid_str = get_guid_text(&systab.tables[i].guid); + if (!guid_str) + guid_str = ""; + printf("%pUl %s\n", &systab.tables[i].guid, guid_str); + } + + return CMD_RET_SUCCESS; +} + /** * do_efi_boot_add() - set UEFI load option * @@ -1047,6 +1088,8 @@ static cmd_tbl_t cmd_efidebug_sub[] = { "", ""), U_BOOT_CMD_MKENT(memmap, CONFIG_SYS_MAXARGS, 1, do_efi_show_memmap, "", ""), + U_BOOT_CMD_MKENT(tables, CONFIG_SYS_MAXARGS, 1, do_efi_show_tables, + "", ""), }; /** @@ -1114,7 +1157,9 @@ static char efidebug_help_text[] = "efidebug images\n" " - show loaded images\n" "efidebug memmap\n" - " - show uefi memory map\n"; + " - show uefi memory map\n" + "efidebug tables\n" + " - show UEFI configuration tables\n"; #endif U_BOOT_CMD( From 07e2fe797115b1239c513ba12caa017978322da0 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 7 Jan 2020 07:48:15 +0100 Subject: [PATCH 21/38] cmd: efidebug: capitalize UEFI %s/uefi/UEFI/g Signed-off-by: Heinrich Schuchardt --- cmd/efidebug.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 78fc649782..576e95b395 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -1149,15 +1149,15 @@ static char efidebug_help_text[] = " - set/show UEFI boot order\n" "\n" "efidebug devices\n" - " - show uefi devices\n" + " - show UEFI devices\n" "efidebug drivers\n" - " - show uefi drivers\n" + " - show UEFI drivers\n" "efidebug dh\n" - " - show uefi handles\n" + " - show UEFI handles\n" "efidebug images\n" " - show loaded images\n" "efidebug memmap\n" - " - show uefi memory map\n" + " - show UEFI memory map\n" "efidebug tables\n" " - show UEFI configuration tables\n"; #endif From a031b03f6448fafba46d08f7a88fa33690d50858 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 24 Dec 2019 18:05:38 +0200 Subject: [PATCH 22/38] image: Add IH_OS_EFI for EFI chain-load boot Add a new OS type to be used for chain-loading an EFI compatible firmware or boot loader like GRUB2, possibly in a verified boot scenario. Bellow is sample ITS file that generates a FIT image supporting secure boot. Please note the presence of 'os = "efi";' line, which identifies the currently introduced OS type: / { #address-cells = <1>; images { efi-grub { description = "GRUB EFI"; data = /incbin/("bootarm.efi"); type = "kernel_noload"; arch = "arm"; os = "efi"; compression = "none"; load = <0x0>; entry = <0x0>; hash-1 { algo = "sha256"; }; }; }; configurations { default = "config-grub"; config-grub { kernel = "efi-grub"; signature-1 { algo = "sha256,rsa2048"; sign-images = "kernel"; }; }; }; }; Signed-off-by: Cristian Ciocaltea Reviewed-by: Heinrich Schuchardt --- common/image-fit.c | 3 ++- common/image.c | 1 + include/image.h | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/common/image-fit.c b/common/image-fit.c index c52f945120..231612ff5f 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -1926,7 +1926,8 @@ int fit_image_load(bootm_headers_t *images, ulong addr, image_type == IH_TYPE_FPGA || fit_image_check_os(fit, noffset, IH_OS_LINUX) || fit_image_check_os(fit, noffset, IH_OS_U_BOOT) || - fit_image_check_os(fit, noffset, IH_OS_OPENRTOS); + fit_image_check_os(fit, noffset, IH_OS_OPENRTOS) || + fit_image_check_os(fit, noffset, IH_OS_EFI); /* * If either of the checks fail, we should report an error, but diff --git a/common/image.c b/common/image.c index eb626dcac9..75d5dd944f 100644 --- a/common/image.c +++ b/common/image.c @@ -137,6 +137,7 @@ static const table_entry_t uimage_os[] = { { IH_OS_OPENRTOS, "openrtos", "OpenRTOS", }, #endif { IH_OS_OPENSBI, "opensbi", "RISC-V OpenSBI", }, + { IH_OS_EFI, "efi", "EFI Firmware" }, { -1, "", "", }, }; diff --git a/include/image.h b/include/image.h index f4d2aaf53e..4a280b78e7 100644 --- a/include/image.h +++ b/include/image.h @@ -157,6 +157,7 @@ enum { IH_OS_ARM_TRUSTED_FIRMWARE, /* ARM Trusted Firmware */ IH_OS_TEE, /* Trusted Execution Environment */ IH_OS_OPENSBI, /* RISC-V OpenSBI */ + IH_OS_EFI, /* EFI Firmware (e.g. GRUB2) */ IH_OS_COUNT, }; From ecc7fdaa9ef191e023fbc138e3466ccf05f6af20 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 24 Dec 2019 18:05:39 +0200 Subject: [PATCH 23/38] bootm: Add a bootm command for type IH_OS_EFI Add support for booting EFI binaries contained in FIT images. A typical usage scenario is chain-loading GRUB2 in a verified boot environment. Signed-off-by: Cristian Ciocaltea Reviewed-by: Heinrich Schuchardt --- cmd/Kconfig | 7 ++++++ common/bootm_os.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/cmd/Kconfig b/cmd/Kconfig index 4e29e7b3c5..a6bfa87e08 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -259,6 +259,13 @@ config CMD_BOOTM help Boot an application image from the memory. +config BOOTM_EFI + bool "Support booting UEFI FIT images" + depends on CMD_BOOTEFI && CMD_BOOTM && FIT + default y + help + Support booting UEFI FIT images via the bootm command. + config CMD_BOOTZ bool "bootz" help diff --git a/common/bootm_os.c b/common/bootm_os.c index d89ddc32b0..1d58462509 100644 --- a/common/bootm_os.c +++ b/common/bootm_os.c @@ -7,10 +7,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -498,6 +500,57 @@ static int do_bootm_tee(int flag, int argc, char * const argv[], } #endif +#ifdef CONFIG_BOOTM_EFI +static int do_bootm_efi(int flag, int argc, char * const argv[], + bootm_headers_t *images) +{ + int ret; + efi_status_t efi_ret; + void *image_buf; + + if (flag != BOOTM_STATE_OS_GO) + return 0; + + /* Locate FDT, if provided */ + ret = bootm_find_images(flag, argc, argv); + if (ret) + return ret; + + /* Initialize EFI drivers */ + efi_ret = efi_init_obj_list(); + if (efi_ret != EFI_SUCCESS) { + printf("## Failed to initialize UEFI sub-system: r = %lu\n", + efi_ret & ~EFI_ERROR_MASK); + return 1; + } + + /* Install device tree */ + efi_ret = efi_install_fdt(images->ft_len + ? images->ft_addr : EFI_FDT_USE_INTERNAL); + if (efi_ret != EFI_SUCCESS) { + printf("## Failed to install device tree: r = %lu\n", + efi_ret & ~EFI_ERROR_MASK); + return 1; + } + + /* Run EFI image */ + printf("## Transferring control to EFI (at address %08lx) ...\n", + images->ep); + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + image_buf = map_sysmem(images->ep, images->os.image_len); + + efi_ret = efi_run_image(image_buf, images->os.image_len); + if (efi_ret != EFI_SUCCESS) { + printf("## Failed to run EFI image: r = %lu\n", + efi_ret & ~EFI_ERROR_MASK); + return 1; + } + + return 0; +} +#endif + static boot_os_fn *boot_os[] = { [IH_OS_U_BOOT] = do_bootm_standalone, #ifdef CONFIG_BOOTM_LINUX @@ -534,6 +587,9 @@ static boot_os_fn *boot_os[] = { #ifdef CONFIG_BOOTM_OPTEE [IH_OS_TEE] = do_bootm_tee, #endif +#ifdef CONFIG_BOOTM_EFI + [IH_OS_EFI] = do_bootm_efi, +#endif }; /* Allow for arch specific config before we boot */ From b533386dc1bb67672d6c45d7e3c689d80546e0c8 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 24 Dec 2019 18:05:40 +0200 Subject: [PATCH 24/38] doc: Add sample uefi.its image description file This patch adds an example FIT image description file demonstrating the usage of bootm command to securely launch UEFI binaries. Signed-off-by: Cristian Ciocaltea Reviewed-by: Heinrich Schuchardt --- doc/uImage.FIT/uefi.its | 67 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 doc/uImage.FIT/uefi.its diff --git a/doc/uImage.FIT/uefi.its b/doc/uImage.FIT/uefi.its new file mode 100644 index 0000000000..378ca4ed8d --- /dev/null +++ b/doc/uImage.FIT/uefi.its @@ -0,0 +1,67 @@ +/* + * Example FIT image description file demonstrating the usage of the + * bootm command to launch UEFI binaries. + * + * Two boot configurations are available to enable booting GRUB2 on QEMU, + * the former uses a FDT blob contained in the FIT image, while the later + * relies on the FDT provided by the board emulator. + */ + +/dts-v1/; + +/ { + description = "GRUB2 EFI and QEMU FDT blob"; + #address-cells = <1>; + + images { + efi-grub { + description = "GRUB EFI Firmware"; + data = /incbin/("bootarm.efi"); + type = "kernel_noload"; + arch = "arm"; + os = "efi"; + compression = "none"; + load = <0x0>; + entry = <0x0>; + hash-1 { + algo = "sha256"; + }; + }; + + fdt-qemu { + description = "QEMU DTB"; + data = /incbin/("qemu-arm.dtb"); + type = "flat_dt"; + arch = "arm"; + compression = "none"; + hash-1 { + algo = "sha256"; + }; + }; + }; + + configurations { + default = "config-grub-fdt"; + + config-grub-fdt { + description = "GRUB EFI Boot w/ FDT"; + kernel = "efi-grub"; + fdt = "fdt-qemu"; + signature-1 { + algo = "sha256,rsa2048"; + key-name-hint = "dev"; + sign-images = "kernel", "fdt"; + }; + }; + + config-grub-nofdt { + description = "GRUB EFI Boot w/o FDT"; + kernel = "efi-grub"; + signature-1 { + algo = "sha256,rsa2048"; + key-name-hint = "dev"; + sign-images = "kernel"; + }; + }; + }; +}; From 2dbab878ba3dfbb94cfda8e08c3d7ec1f0d1835d Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 24 Dec 2019 18:05:41 +0200 Subject: [PATCH 25/38] doc: uefi.rst: Document launching UEFI binaries from FIT images This patch adds a new section "Launching a UEFI binary from a FIT image" documenting the usage of the CONFIG_BOOTM_EFI extension to bootm command that offers a verified boot alternative for UEFI binaries such as GRUB2. Signed-off-by: Cristian Ciocaltea Reviewed-by: Heinrich Schuchardt --- doc/uefi/uefi.rst | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst index db942df694..a8fd886d6b 100644 --- a/doc/uefi/uefi.rst +++ b/doc/uefi/uefi.rst @@ -63,6 +63,40 @@ The environment variable 'bootargs' is passed as load options in the UEFI system table. The Linux kernel EFI stub uses the load options as command line arguments. +Launching a UEFI binary from a FIT image +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A signed FIT image can be used to securely boot a UEFI image via the +bootm command. This feature is available if U-Boot is configured with:: + + CONFIG_BOOTM_EFI=y + +A sample configuration is provided as file doc/uImage.FIT/uefi.its. + +Below you find the output of an example session starting GRUB:: + + => load mmc 0:1 ${kernel_addr_r} image.fit + 4620426 bytes read in 83 ms (53.1 MiB/s) + => bootm ${kernel_addr_r}#config-grub-nofdt + ## Loading kernel from FIT Image at 40400000 ... + Using 'config-grub-nofdt' configuration + Verifying Hash Integrity ... sha256,rsa2048:dev+ OK + Trying 'efi-grub' kernel subimage + Description: GRUB EFI Firmware + Created: 2019-11-20 8:18:16 UTC + Type: Kernel Image (no loading done) + Compression: uncompressed + Data Start: 0x404000d0 + Data Size: 450560 Bytes = 440 KiB + Hash algo: sha256 + Hash value: 4dbee00021112df618f58b3f7cf5e1595533d543094064b9ce991e8b054a9eec + Verifying Hash Integrity ... sha256+ OK + XIP Kernel Image (no loading done) + ## Transferring control to EFI (at address 404000d0) ... + Welcome to GRUB! + +See doc/uImage.FIT/howto.txt for an introduction to FIT images. + Executing the boot manager ~~~~~~~~~~~~~~~~~~~~~~~~~~ From 8391f955494efc0dbe136e1557e9606bbf55d046 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Mon, 30 Dec 2019 03:34:27 +0200 Subject: [PATCH 26/38] test/py: Create a test for launching UEFI binaries from FIT images This test verifies the implementation of the 'bootm' extension that handles UEFI binaries inside FIT images (enabled via CONFIG_BOOTM_EFI). Signed-off-by: Cristian Ciocaltea Reviewed-by: Heinrich Schuchardt --- test/py/tests/test_efi_fit.py | 458 ++++++++++++++++++++++++++++++++++ 1 file changed, 458 insertions(+) create mode 100644 test/py/tests/test_efi_fit.py diff --git a/test/py/tests/test_efi_fit.py b/test/py/tests/test_efi_fit.py new file mode 100644 index 0000000000..6986b2d35c --- /dev/null +++ b/test/py/tests/test_efi_fit.py @@ -0,0 +1,458 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019, Cristian Ciocaltea +# +# Work based on: +# - test_net.py +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +# - test_fit.py +# Copyright (c) 2013, Google Inc. +# +# Test launching UEFI binaries from FIT images. + +import os.path +import pytest +import u_boot_utils as util + +""" +Note: This test relies on boardenv_* containing configuration values to define +which network environment is available for testing. Without this, the parts +that rely on network will be automatically skipped. + +For example: + +# Boolean indicating whether the Ethernet device is attached to USB, and hence +# USB enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_usb = False + +# Boolean indicating whether the Ethernet device is attached to PCI, and hence +# PCI enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_pci = True + +# True if a DHCP server is attached to the network, and should be tested. +# If DHCP testing is not possible or desired, this variable may be omitted or +# set to False. +env__net_dhcp_server = True + +# A list of environment variables that should be set in order to configure a +# static IP. If solely relying on DHCP, this variable may be omitted or set to +# an empty list. +env__net_static_env_vars = [ + ('ipaddr', '10.0.0.100'), + ('netmask', '255.255.255.0'), + ('serverip', '10.0.0.1'), +] + +# Details regarding a file that may be read from a TFTP server. This variable +# may be omitted or set to None if TFTP testing is not possible or desired. +# Additionally, when the 'size' is not available, the file will be generated +# automatically in the TFTP root directory, as specified by the 'dn' field. +env__efi_fit_tftp_file = { + 'fn': 'test-efi-fit.img', # File path relative to TFTP root + 'size': 3831, # File size + 'crc32': '9fa3f79c', # Checksum using CRC-32 algorithm, optional + 'addr': 0x40400000, # Loading address, integer, optional + 'dn': 'tftp/root/dir', # TFTP root directory path, optional +} +""" + +# Define the parametrized ITS data to be used for FIT images generation. +its_data = ''' +/dts-v1/; + +/ { + description = "EFI image with FDT blob"; + #address-cells = <1>; + + images { + efi { + description = "Test EFI"; + data = /incbin/("%(efi-bin)s"); + type = "%(kernel-type)s"; + arch = "%(sys-arch)s"; + os = "efi"; + compression = "%(efi-comp)s"; + load = <0x0>; + entry = <0x0>; + }; + fdt { + description = "Test FDT"; + data = /incbin/("%(fdt-bin)s"); + type = "flat_dt"; + arch = "%(sys-arch)s"; + compression = "%(fdt-comp)s"; + }; + }; + + configurations { + default = "config-efi-fdt"; + config-efi-fdt { + description = "EFI FIT w/ FDT"; + kernel = "efi"; + fdt = "fdt"; + }; + config-efi-nofdt { + description = "EFI FIT w/o FDT"; + kernel = "efi"; + }; + }; +}; +''' + +# Define the parametrized FDT data to be used for DTB images generation. +fdt_data = ''' +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <0>; + + model = "%(sys-arch)s %(fdt_type)s EFI FIT Boot Test"; + compatible = "%(sys-arch)s"; + + reset@0 { + compatible = "%(sys-arch)s,reset"; + reg = <0>; + }; +}; +''' + +@pytest.mark.buildconfigspec('bootm_efi') +@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.notbuildconfigspec('generate_acpi_table') +@pytest.mark.requiredtool('dtc') +def test_efi_fit_launch(u_boot_console): + """Test handling of UEFI binaries inside FIT images. + + The tests are trying to launch U-Boot's helloworld.efi embedded into + FIT images, in uncompressed or gzip compressed format. + + Additionally, a sample FDT blob is created and embedded into the above + mentioned FIT images, in uncompressed or gzip compressed format. + + For more details, see launch_efi(). + + The following test cases are currently defined and enabled: + - Launch uncompressed FIT EFI & internal FDT + - Launch uncompressed FIT EFI & FIT FDT + - Launch compressed FIT EFI & internal FDT + - Launch compressed FIT EFI & FIT FDT + """ + + def net_pre_commands(): + """Execute any commands required to enable network hardware. + + These commands are provided by the boardenv_* file; see the comment + at the beginning of this file. + """ + + init_usb = cons.config.env.get('env__net_uses_usb', False) + if init_usb: + cons.run_command('usb start') + + init_pci = cons.config.env.get('env__net_uses_pci', False) + if init_pci: + cons.run_command('pci enum') + + def net_dhcp(): + """Execute the dhcp command. + + The boardenv_* file may be used to enable/disable DHCP; see the + comment at the beginning of this file. + """ + + has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y' + if not has_dhcp: + cons.log.warning('CONFIG_CMD_DHCP != y: Skipping DHCP network setup') + return False + + test_dhcp = cons.config.env.get('env__net_dhcp_server', False) + if not test_dhcp: + cons.log.info('No DHCP server available') + return False + + cons.run_command('setenv autoload no') + output = cons.run_command('dhcp') + assert 'DHCP client bound to address ' in output + return True + + def net_setup_static(): + """Set up a static IP configuration. + + The configuration is provided by the boardenv_* file; see the comment at + the beginning of this file. + """ + + has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y' + if not has_dhcp: + cons.log.warning('CONFIG_NET != y: Skipping static network setup') + return False + + env_vars = cons.config.env.get('env__net_static_env_vars', None) + if not env_vars: + cons.log.info('No static network configuration is defined') + return False + + for (var, val) in env_vars: + cons.run_command('setenv %s %s' % (var, val)) + return True + + def make_fpath(fname): + """Compute the path of a given (temporary) file. + + Args: + fname: The name of a file within U-Boot build dir. + Return: + The computed file path. + """ + + return os.path.join(cons.config.build_dir, fname) + + def make_efi(fname, comp): + """Create an UEFI binary. + + This simply copies lib/efi_loader/helloworld.efi into U-Boot + build dir and, optionally, compresses the file using gzip. + + Args: + fname: The target file name within U-Boot build dir. + comp: Flag to enable gzip compression. + Return: + The path of the created file. + """ + + bin_path = make_fpath(fname) + util.run_and_log(cons, + ['cp', make_fpath('lib/efi_loader/helloworld.efi'), bin_path]) + if comp: + util.run_and_log(cons, ['gzip', '-f', bin_path]) + bin_path += '.gz' + return bin_path + + def make_dtb(fdt_type, comp): + """Create a sample DTB file. + + Creates a DTS file and compiles it to a DTB. + + Args: + fdt_type: The type of the FDT, i.e. internal, user. + comp: Flag to enable gzip compression. + Return: + The path of the created file. + """ + + # Generate resources referenced by FDT. + fdt_params = { + 'sys-arch': sys_arch, + 'fdt_type': fdt_type, + } + + # Generate a test FDT file. + dts = make_fpath('test-efi-fit-%s.dts' % fdt_type) + with open(dts, 'w') as fd: + fd.write(fdt_data % fdt_params) + + # Build the test FDT. + dtb = make_fpath('test-efi-fit-%s.dtb' % fdt_type) + util.run_and_log(cons, ['dtc', '-I', 'dts', '-O', 'dtb', '-o', dtb, dts]) + if comp: + util.run_and_log(cons, ['gzip', '-f', dtb]) + dtb += '.gz' + return dtb + + def make_fit(comp): + """Create a sample FIT image. + + Runs 'mkimage' to create a FIT image within U-Boot build dir. + Args: + comp: Enable gzip compression for the EFI binary and FDT blob. + Return: + The path of the created file. + """ + + # Generate resources referenced by ITS. + its_params = { + 'sys-arch': sys_arch, + 'efi-bin': os.path.basename(make_efi('test-efi-fit-helloworld.efi', comp)), + 'kernel-type': 'kernel' if comp else 'kernel_noload', + 'efi-comp': 'gzip' if comp else 'none', + 'fdt-bin': os.path.basename(make_dtb('user', comp)), + 'fdt-comp': 'gzip' if comp else 'none', + } + + # Generate a test ITS file. + its_path = make_fpath('test-efi-fit-helloworld.its') + with open(its_path, 'w') as fd: + fd.write(its_data % its_params) + + # Build the test ITS. + fit_path = make_fpath('test-efi-fit-helloworld.fit') + util.run_and_log( + cons, [make_fpath('tools/mkimage'), '-f', its_path, fit_path]) + return fit_path + + def load_fit_from_host(f): + """Load the FIT image using the 'host load' command and return its address. + + Args: + f: Dictionary describing the FIT image to load, see env__efi_fit_test_file + in the comment at the beginning of this file. + Return: + The address where the file has been loaded. + """ + + addr = f.get('addr', None) + if not addr: + addr = util.find_ram_base(cons) + + output = cons.run_command( + 'host load hostfs - %x %s/%s' % (addr, f['dn'], f['fn'])) + expected_text = ' bytes read' + sz = f.get('size', None) + if sz: + expected_text = '%d' % sz + expected_text + assert(expected_text in output) + + return addr + + def load_fit_from_tftp(f): + """Load the FIT image using the tftpboot command and return its address. + + The file is downloaded from the TFTP server, its size and optionally its + CRC32 are validated. + + Args: + f: Dictionary describing the FIT image to load, see env__efi_fit_tftp_file + in the comment at the beginning of this file. + Return: + The address where the file has been loaded. + """ + + addr = f.get('addr', None) + if not addr: + addr = util.find_ram_base(cons) + + fn = f['fn'] + output = cons.run_command('tftpboot %x %s' % (addr, fn)) + expected_text = 'Bytes transferred = ' + sz = f.get('size', None) + if sz: + expected_text += '%d' % sz + assert expected_text in output + + expected_crc = f.get('crc32', None) + if not expected_crc: + return addr + + if cons.config.buildconfig.get('config_cmd_crc32', 'n') != 'y': + return addr + + output = cons.run_command('crc32 $fileaddr $filesize') + assert expected_crc in output + + return addr + + def launch_efi(enable_fdt, enable_comp): + """Launch U-Boot's helloworld.efi binary from a FIT image. + + An external image file can be downloaded from TFTP, when related + details are provided by the boardenv_* file; see the comment at the + beginning of this file. + + If the size of the TFTP file is not provided within env__efi_fit_tftp_file, + the test image is generated automatically and placed in the TFTP root + directory specified via the 'dn' field. + + When running the tests on Sandbox, the image file is loaded directly + from the host filesystem. + + Once the load address is available on U-Boot console, the 'bootm' + command is executed for either 'config-efi-fdt' or 'config-efi-nofdt' + FIT configuration, depending on the value of the 'enable_fdt' function + argument. + + Eventually the 'Hello, world' message is expected in the U-Boot console. + + Args: + enable_fdt: Flag to enable using the FDT blob inside FIT image. + enable_comp: Flag to enable GZIP compression on EFI and FDT + generated content. + """ + + with cons.log.section('FDT=%s;COMP=%s' % (enable_fdt, enable_comp)): + if is_sandbox: + fit = { + 'dn': cons.config.build_dir, + } + else: + # Init networking. + net_pre_commands() + net_set_up = net_dhcp() + net_set_up = net_setup_static() or net_set_up + if not net_set_up: + pytest.skip('Network not initialized') + + fit = cons.config.env.get('env__efi_fit_tftp_file', None) + if not fit: + pytest.skip('No env__efi_fit_tftp_file binary specified in environment') + + sz = fit.get('size', None) + if not sz: + if not fit.get('dn', None): + pytest.skip('Neither "size", nor "dn" info provided in env__efi_fit_tftp_file') + + # Create test FIT image. + fit_path = make_fit(enable_comp) + fit['fn'] = os.path.basename(fit_path) + fit['size'] = os.path.getsize(fit_path) + + # Copy image to TFTP root directory. + if fit['dn'] != cons.config.build_dir: + util.run_and_log(cons, ['mv', '-f', fit_path, '%s/' % fit['dn']]) + + # Load FIT image. + addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit) + + # Select boot configuration. + fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt' + + # Try booting. + cons.run_command( + 'bootm %x#%s' % (addr, fit_config), wait_for_prompt=False) + if enable_fdt: + cons.wait_for('Booting using the fdt blob') + cons.wait_for('Hello, world') + cons.wait_for('## Application terminated, r = 0') + cons.restart_uboot(); + + cons = u_boot_console + # Array slice removes leading/trailing quotes. + sys_arch = cons.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1] + is_sandbox = sys_arch == 'sandbox' + + try: + if is_sandbox: + # Use our own device tree file, will be restored afterwards. + control_dtb = make_dtb('internal', False) + old_dtb = cons.config.dtb + cons.config.dtb = control_dtb + + # Run tests + # - fdt OFF, gzip OFF + launch_efi(False, False) + # - fdt ON, gzip OFF + launch_efi(True, False) + + if is_sandbox: + # - fdt OFF, gzip ON + launch_efi(False, True) + # - fdt ON, gzip ON + launch_efi(True, True) + + finally: + if is_sandbox: + # Go back to the original U-Boot with the correct dtb. + cons.config.dtb = old_dtb + cons.restart_uboot() From a2487684003b0bc380955e1a38cdd71da3ca4366 Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Sat, 28 Dec 2019 23:58:27 +0530 Subject: [PATCH 27/38] dm: rng: Add random number generator(rng) uclass Add a uclass for reading a random number seed from a random number generator device. Signed-off-by: Sughosh Ganu Reviewed-by: Patrice Chotard Reviewed-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- drivers/Kconfig | 2 ++ drivers/Makefile | 1 + drivers/rng/Kconfig | 7 +++++++ drivers/rng/Makefile | 6 ++++++ drivers/rng/rng-uclass.c | 23 +++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/rng.h | 33 +++++++++++++++++++++++++++++++++ 7 files changed, 73 insertions(+) create mode 100644 drivers/rng/Kconfig create mode 100644 drivers/rng/Makefile create mode 100644 drivers/rng/rng-uclass.c create mode 100644 include/rng.h diff --git a/drivers/Kconfig b/drivers/Kconfig index 9d99ce0226..e34a22708c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -90,6 +90,8 @@ source "drivers/remoteproc/Kconfig" source "drivers/reset/Kconfig" +source "drivers/rng/Kconfig" + source "drivers/rtc/Kconfig" source "drivers/scsi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index cb8c215e76..8eec4e8176 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -116,4 +116,5 @@ obj-$(CONFIG_W1_EEPROM) += w1-eeprom/ obj-$(CONFIG_MACH_PIC32) += ddr/microchip/ obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock/ +obj-$(CONFIG_DM_RNG) += rng/ endif diff --git a/drivers/rng/Kconfig b/drivers/rng/Kconfig new file mode 100644 index 0000000000..dd44cc0242 --- /dev/null +++ b/drivers/rng/Kconfig @@ -0,0 +1,7 @@ +config DM_RNG + bool "Driver support for Random Number Generator devices" + depends on DM + help + Enable driver model for random number generator(rng) devices. + This interface is used to initialise the rng device and to + read the random seed from the device. diff --git a/drivers/rng/Makefile b/drivers/rng/Makefile new file mode 100644 index 0000000000..311705b6e6 --- /dev/null +++ b/drivers/rng/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2019, Linaro Limited +# + +obj-$(CONFIG_DM_RNG) += rng-uclass.o diff --git a/drivers/rng/rng-uclass.c b/drivers/rng/rng-uclass.c new file mode 100644 index 0000000000..b6af3b8606 --- /dev/null +++ b/drivers/rng/rng-uclass.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019, Linaro Limited + */ + +#include +#include +#include + +int dm_rng_read(struct udevice *dev, void *buffer, size_t size) +{ + const struct dm_rng_ops *ops = device_get_ops(dev); + + if (!ops->read) + return -ENOSYS; + + return ops->read(dev, buffer, size); +} + +UCLASS_DRIVER(rng) = { + .name = "rng", + .id = UCLASS_RNG, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index c1bab17ad1..67f5d673cb 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -88,6 +88,7 @@ enum uclass_id { UCLASS_REGULATOR, /* Regulator device */ UCLASS_REMOTEPROC, /* Remote Processor device */ UCLASS_RESET, /* Reset controller device */ + UCLASS_RNG, /* Random Number Generator */ UCLASS_RTC, /* Real time clock device */ UCLASS_SCSI, /* SCSI device */ UCLASS_SERIAL, /* Serial UART */ diff --git a/include/rng.h b/include/rng.h new file mode 100644 index 0000000000..d2c0f9af62 --- /dev/null +++ b/include/rng.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019, Linaro Limited + */ + +#if !defined _RNG_H_ +#define _RNG_H_ + +struct udevice; + +/** + * dm_rng_read() - read a random number seed from the rng device + * @buffer: input buffer to put the read random seed into + * @size: number of bytes of random seed read + * + * Return: 0 if OK, -ve on error + */ +int dm_rng_read(struct udevice *dev, void *buffer, size_t size); + +/* struct dm_rng_ops - Operations for the hwrng uclass */ +struct dm_rng_ops { + /** + * @read() - read a random number seed + * + * @data: input buffer to read the random seed + * @max: total number of bytes to read + * + * Return: 0 if OK, -ve on error + */ + int (*read)(struct udevice *dev, void *data, size_t max); +}; + +#endif /* _RNG_H_ */ From 82ebf0f6a01c79aa4059bdaf3ddc6522adcc56b5 Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Sat, 28 Dec 2019 23:58:28 +0530 Subject: [PATCH 28/38] clk: stm32mp1: Add a clock entry for RNG1 device Add an entry for allowing clock enablement for the random number generator peripheral, RNG1. Signed-off-by: Sughosh Ganu Reviewed-by: Patrice Chotard Acked-by: Patrick Delaunay --- drivers/clk/clk_stm32mp1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/clk_stm32mp1.c b/drivers/clk/clk_stm32mp1.c index 3718970dc7..da66bde41b 100644 --- a/drivers/clk/clk_stm32mp1.c +++ b/drivers/clk/clk_stm32mp1.c @@ -563,6 +563,7 @@ static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = { STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 10, GPIOK, _UNKNOWN_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_AHB5ENSETR, 0, GPIOZ, _UNKNOWN_SEL), + STM32MP1_CLK_SET_CLR(RCC_MP_AHB5ENSETR, 6, RNG1_K, _UNKNOWN_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 7, ETHCK_K, _ETH_SEL), STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 8, ETHTX, _UNKNOWN_SEL), From 231ec905837010f7c50d44423f588118b3090cce Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Sat, 28 Dec 2019 23:58:29 +0530 Subject: [PATCH 29/38] stm32mp1: rng: Add a driver for random number generator(rng) device Add a driver for the rng device found on stm32mp1 platforms. The driver provides a routine for reading the random number seed from the hardware device. Signed-off-by: Sughosh Ganu Reviewed-by: Patrice Chotard Acked-by: Patrick Delaunay Remove a superfluous blank line Signed-off-by: Heinrich Schuchardt --- drivers/rng/Kconfig | 7 ++ drivers/rng/Makefile | 1 + drivers/rng/stm32mp1_rng.c | 160 +++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 drivers/rng/stm32mp1_rng.c diff --git a/drivers/rng/Kconfig b/drivers/rng/Kconfig index dd44cc0242..c9c475128e 100644 --- a/drivers/rng/Kconfig +++ b/drivers/rng/Kconfig @@ -5,3 +5,10 @@ config DM_RNG Enable driver model for random number generator(rng) devices. This interface is used to initialise the rng device and to read the random seed from the device. + +config RNG_STM32MP1 + bool "Enable random number generator for STM32MP1" + depends on ARCH_STM32MP && DM_RNG + default n + help + Enable STM32MP1 rng driver. diff --git a/drivers/rng/Makefile b/drivers/rng/Makefile index 311705b6e6..699beb383b 100644 --- a/drivers/rng/Makefile +++ b/drivers/rng/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_DM_RNG) += rng-uclass.o +obj-$(CONFIG_RNG_STM32MP1) += stm32mp1_rng.o diff --git a/drivers/rng/stm32mp1_rng.c b/drivers/rng/stm32mp1_rng.c new file mode 100644 index 0000000000..dab3b995eb --- /dev/null +++ b/drivers/rng/stm32mp1_rng.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2019, Linaro Limited + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define RNG_CR 0x00 +#define RNG_CR_RNGEN BIT(2) +#define RNG_CR_CED BIT(5) + +#define RNG_SR 0x04 +#define RNG_SR_SEIS BIT(6) +#define RNG_SR_CEIS BIT(5) +#define RNG_SR_SECS BIT(2) +#define RNG_SR_DRDY BIT(0) + +#define RNG_DR 0x08 + +struct stm32_rng_platdata { + fdt_addr_t base; + struct clk clk; + struct reset_ctl rst; +}; + +static int stm32_rng_read(struct udevice *dev, void *data, size_t len) +{ + int retval = 0, i; + u32 sr, count, reg; + size_t increment; + struct stm32_rng_platdata *pdata = dev_get_platdata(dev); + + while (len > 0) { + retval = readl_poll_timeout(pdata->base + RNG_SR, sr, + sr & RNG_SR_DRDY, 10000); + if (retval) + return retval; + + if (sr & (RNG_SR_SEIS | RNG_SR_SECS)) { + /* As per SoC TRM */ + clrbits_le32(pdata->base + RNG_SR, RNG_SR_SEIS); + for (i = 0; i < 12; i++) + readl(pdata->base + RNG_DR); + if (readl(pdata->base + RNG_SR) & RNG_SR_SEIS) { + printf("RNG Noise"); + return -EIO; + } + /* start again */ + continue; + } + + /* + * Once the DRDY bit is set, the RNG_DR register can + * be read four consecutive times. + */ + count = 4; + while (len && count) { + reg = readl(pdata->base + RNG_DR); + memcpy(data, ®, min(len, sizeof(u32))); + increment = min(len, sizeof(u32)); + data += increment; + len -= increment; + count--; + } + } + + return 0; +} + +static int stm32_rng_init(struct stm32_rng_platdata *pdata) +{ + int err; + + err = clk_enable(&pdata->clk); + if (err) + return err; + + /* Disable CED */ + writel(RNG_CR_RNGEN | RNG_CR_CED, pdata->base + RNG_CR); + + /* clear error indicators */ + writel(0, pdata->base + RNG_SR); + + return 0; +} + +static int stm32_rng_cleanup(struct stm32_rng_platdata *pdata) +{ + writel(0, pdata->base + RNG_CR); + + return clk_disable(&pdata->clk); +} + +static int stm32_rng_probe(struct udevice *dev) +{ + struct stm32_rng_platdata *pdata = dev_get_platdata(dev); + + reset_assert(&pdata->rst); + udelay(20); + reset_deassert(&pdata->rst); + + return stm32_rng_init(pdata); +} + +static int stm32_rng_remove(struct udevice *dev) +{ + struct stm32_rng_platdata *pdata = dev_get_platdata(dev); + + return stm32_rng_cleanup(pdata); +} + +static int stm32_rng_ofdata_to_platdata(struct udevice *dev) +{ + struct stm32_rng_platdata *pdata = dev_get_platdata(dev); + int err; + + pdata->base = dev_read_addr(dev); + if (!pdata->base) + return -ENOMEM; + + err = clk_get_by_index(dev, 0, &pdata->clk); + if (err) + return err; + + err = reset_get_by_index(dev, 0, &pdata->rst); + if (err) + return err; + + return 0; +} + +static const struct dm_rng_ops stm32_rng_ops = { + .read = stm32_rng_read, +}; + +static const struct udevice_id stm32_rng_match[] = { + { + .compatible = "st,stm32-rng", + }, + {}, +}; + +U_BOOT_DRIVER(stm32_rng) = { + .name = "stm32-rng", + .id = UCLASS_RNG, + .of_match = stm32_rng_match, + .ops = &stm32_rng_ops, + .probe = stm32_rng_probe, + .remove = stm32_rng_remove, + .platdata_auto_alloc_size = sizeof(struct stm32_rng_platdata), + .ofdata_to_platdata = stm32_rng_ofdata_to_platdata, +}; From 90950eec58f7b550db91588144e0e9cbaf9a49b7 Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Sat, 28 Dec 2019 23:58:30 +0530 Subject: [PATCH 30/38] configs: stm32mp15: Enable random number generator(rng) device Enable support for the rng device on the stm32mp15 configs. Signed-off-by: Sughosh Ganu Reviewed-by: Patrice Chotard Acked-by: Patrick Delaunay --- configs/stm32mp15_basic_defconfig | 2 ++ configs/stm32mp15_optee_defconfig | 2 ++ configs/stm32mp15_trusted_defconfig | 2 ++ 3 files changed, 6 insertions(+) diff --git a/configs/stm32mp15_basic_defconfig b/configs/stm32mp15_basic_defconfig index 713a7e6c57..c85369ca0f 100644 --- a/configs/stm32mp15_basic_defconfig +++ b/configs/stm32mp15_basic_defconfig @@ -143,3 +143,5 @@ CONFIG_VIDEO_STM32_DSI=y CONFIG_VIDEO_STM32_MAX_XRES=1280 CONFIG_VIDEO_STM32_MAX_YRES=800 CONFIG_FDT_FIXUP_PARTITIONS=y +CONFIG_DM_RNG=y +CONFIG_RNG_STM32MP1=y diff --git a/configs/stm32mp15_optee_defconfig b/configs/stm32mp15_optee_defconfig index f9161fd7d1..c192d8d441 100644 --- a/configs/stm32mp15_optee_defconfig +++ b/configs/stm32mp15_optee_defconfig @@ -127,3 +127,5 @@ CONFIG_VIDEO_STM32_DSI=y CONFIG_VIDEO_STM32_MAX_XRES=1280 CONFIG_VIDEO_STM32_MAX_YRES=800 CONFIG_FDT_FIXUP_PARTITIONS=y +CONFIG_DM_RNG=y +CONFIG_RNG_STM32MP1=y diff --git a/configs/stm32mp15_trusted_defconfig b/configs/stm32mp15_trusted_defconfig index a5ea528ae3..a846962af5 100644 --- a/configs/stm32mp15_trusted_defconfig +++ b/configs/stm32mp15_trusted_defconfig @@ -126,3 +126,5 @@ CONFIG_VIDEO_STM32_DSI=y CONFIG_VIDEO_STM32_MAX_XRES=1280 CONFIG_VIDEO_STM32_MAX_YRES=800 CONFIG_FDT_FIXUP_PARTITIONS=y +CONFIG_DM_RNG=y +CONFIG_RNG_STM32MP1=y From ff0dada9b863ab33a7c4b2d9955db1205f55cd01 Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Sat, 28 Dec 2019 23:58:31 +0530 Subject: [PATCH 31/38] sandbox: rng: Add a random number generator(rng) driver Add a sandbox driver for random number generation. Mostly aimed at providing a unit test for rng uclass. Signed-off-by: Sughosh Ganu Reviewed-by: Patrice Chotard Reviewed-by: Simon Glass --- arch/sandbox/dts/test.dts | 4 +++ drivers/rng/Kconfig | 8 ++++++ drivers/rng/Makefile | 1 + drivers/rng/sandbox_rng.c | 56 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 drivers/rng/sandbox_rng.c diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 57513a449f..4ea8fea674 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -630,6 +630,10 @@ reset-names = "other", "test"; }; + rng { + compatible = "sandbox,sandbox-rng"; + }; + rproc_1: rproc@1 { compatible = "sandbox,test-processor"; remoteproc-name = "remoteproc-test-dev1"; diff --git a/drivers/rng/Kconfig b/drivers/rng/Kconfig index c9c475128e..35a3bd192a 100644 --- a/drivers/rng/Kconfig +++ b/drivers/rng/Kconfig @@ -6,6 +6,14 @@ config DM_RNG This interface is used to initialise the rng device and to read the random seed from the device. +config RNG_SANDBOX + bool "Sandbox random number generator" + depends on SANDBOX && DM_RNG + select CONFIG_LIB_RAND + help + Enable random number generator for sandbox. This is an + emulation of a rng device. + config RNG_STM32MP1 bool "Enable random number generator for STM32MP1" depends on ARCH_STM32MP && DM_RNG diff --git a/drivers/rng/Makefile b/drivers/rng/Makefile index 699beb383b..3517005541 100644 --- a/drivers/rng/Makefile +++ b/drivers/rng/Makefile @@ -4,4 +4,5 @@ # obj-$(CONFIG_DM_RNG) += rng-uclass.o +obj-$(CONFIG_RNG_SANDBOX) += sandbox_rng.o obj-$(CONFIG_RNG_STM32MP1) += stm32mp1_rng.o diff --git a/drivers/rng/sandbox_rng.c b/drivers/rng/sandbox_rng.c new file mode 100644 index 0000000000..cd0b0ac77b --- /dev/null +++ b/drivers/rng/sandbox_rng.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2019, Linaro Limited + */ + +#include +#include +#include + +#include + +static int sandbox_rng_read(struct udevice *dev, void *data, size_t len) +{ + unsigned int i, seed, random; + unsigned char *buf = data; + size_t nrem, nloops; + + if (!len) + return 0; + + nloops = len / sizeof(random); + seed = get_timer(0) ^ rand(); + srand(seed); + + for (i = 0, nrem = len; i < nloops; i++) { + random = rand(); + memcpy(buf, &random, sizeof(random)); + buf += sizeof(random); + nrem -= sizeof(random); + } + + if (nrem) { + random = rand(); + memcpy(buf, &random, nrem); + } + + return 0; +} + +static const struct dm_rng_ops sandbox_rng_ops = { + .read = sandbox_rng_read, +}; + +static const struct udevice_id sandbox_rng_match[] = { + { + .compatible = "sandbox,sandbox-rng", + }, + {}, +}; + +U_BOOT_DRIVER(sandbox_rng) = { + .name = "sandbox-rng", + .id = UCLASS_RNG, + .of_match = sandbox_rng_match, + .ops = &sandbox_rng_ops, +}; From 23b3f3c0fc6f881cf9381642196ca11dcc0eb61b Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Sat, 28 Dec 2019 23:58:32 +0530 Subject: [PATCH 32/38] configs: sandbox: Enable random number generator(rng) device Enable support for random number generator on sandbox configs. This is aimed primarily at adding unit test support for rng uclass. Signed-off-by: Sughosh Ganu Reviewed-by: Patrice Chotard Reviewed-by: Patrick Delaunay Reviewed-by: Simon Glass --- configs/sandbox64_defconfig | 2 ++ configs/sandbox_defconfig | 2 ++ 2 files changed, 4 insertions(+) diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index b06ffcec3a..64d1d3102f 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -161,6 +161,8 @@ CONFIG_REGULATOR_RK8XX=y CONFIG_REGULATOR_S5M8767=y CONFIG_DM_REGULATOR_SANDBOX=y CONFIG_REGULATOR_TPS65090=y +CONFIG_DM_RNG=y +CONFIG_RNG_SANDBOX=y CONFIG_DM_PWM=y CONFIG_PWM_SANDBOX=y CONFIG_RAM=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 858ad04b10..d8d8645425 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -181,6 +181,8 @@ CONFIG_REGULATOR_RK8XX=y CONFIG_REGULATOR_S5M8767=y CONFIG_DM_REGULATOR_SANDBOX=y CONFIG_REGULATOR_TPS65090=y +CONFIG_DM_RNG=y +CONFIG_RNG_SANDBOX=y CONFIG_DM_PWM=y CONFIG_PWM_SANDBOX=y CONFIG_RAM=y From 4ee08eb115c3d22cfda415a8f6bc9e975595d204 Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Sat, 28 Dec 2019 23:58:33 +0530 Subject: [PATCH 33/38] test: rng: Add basic test for random number generator(rng) uclass Add a unit test for testing the rng uclass functionality using the sandbox rng driver. Signed-off-by: Sughosh Ganu Reviewed-by: Patrice Chotard Reviewed-by: Simon Glass --- test/dm/Makefile | 1 + test/dm/rng.c | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 test/dm/rng.c diff --git a/test/dm/Makefile b/test/dm/Makefile index a268783169..201e2b093c 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -68,4 +68,5 @@ obj-$(CONFIG_VIRTIO_SANDBOX) += virtio.o obj-$(CONFIG_DMA) += dma.o obj-$(CONFIG_DM_MDIO) += mdio.o obj-$(CONFIG_DM_MDIO_MUX) += mdio_mux.o +obj-$(CONFIG_DM_RNG) += rng.o endif diff --git a/test/dm/rng.c b/test/dm/rng.c new file mode 100644 index 0000000000..ce20e2d7c2 --- /dev/null +++ b/test/dm/rng.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2019, Linaro Limited + */ + +#include +#include +#include +#include +#include + +/* Basic test of the rng uclass */ +static int dm_test_rng_read(struct unit_test_state *uts) +{ + unsigned long rand1 = 0, rand2 = 0; + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_RNG, 0, &dev)); + ut_assertnonnull(dev); + ut_assertok(dm_rng_read(dev, &rand1, sizeof(rand1))); + ut_assertok(dm_rng_read(dev, &rand2, sizeof(rand2))); + ut_assert(rand1 != rand2); + + return 0; +} +DM_TEST(dm_test_rng_read, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); From 03018ea8fd09b3dffb63830e5c0e445de42f572a Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Sun, 29 Dec 2019 15:30:14 +0530 Subject: [PATCH 34/38] virtio: rng: Add a random number generator(rng) driver Add a driver for the virtio-rng device on the qemu platform. The device uses pci as a transport medium. The driver can be enabled with the following configs CONFIG_VIRTIO CONFIG_DM_RNG CONFIG_VIRTIO_PCI CONFIG_VIRTIO_RNG Signed-off-by: Sughosh Ganu --- drivers/virtio/Kconfig | 6 +++ drivers/virtio/Makefile | 1 + drivers/virtio/virtio-uclass.c | 1 + drivers/virtio/virtio_rng.c | 88 ++++++++++++++++++++++++++++++++++ include/virtio.h | 4 +- 5 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 drivers/virtio/virtio_rng.c diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index a9d5fd07b7..2e3dd3bad0 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -59,4 +59,10 @@ config VIRTIO_BLK This is the virtual block driver for virtio. It can be used with QEMU based targets. +config VIRTIO_RNG + bool "virtio rng driver" + depends on VIRTIO + help + This is the virtual random number generator driver. It can be used + with Qemu based targets. endmenu diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index 4579044ae3..dc8880937a 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_VIRTIO_PCI) += virtio_pci_legacy.o virtio_pci_modern.o obj-$(CONFIG_VIRTIO_SANDBOX) += virtio_sandbox.o obj-$(CONFIG_VIRTIO_NET) += virtio_net.o obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o +obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c index 34397d7dbb..436faa46ee 100644 --- a/drivers/virtio/virtio-uclass.c +++ b/drivers/virtio/virtio-uclass.c @@ -24,6 +24,7 @@ static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = { [VIRTIO_ID_NET] = VIRTIO_NET_DRV_NAME, [VIRTIO_ID_BLOCK] = VIRTIO_BLK_DRV_NAME, + [VIRTIO_ID_RNG] = VIRTIO_RNG_DRV_NAME, }; int virtio_get_config(struct udevice *vdev, unsigned int offset, diff --git a/drivers/virtio/virtio_rng.c b/drivers/virtio/virtio_rng.c new file mode 100644 index 0000000000..4edffa6f31 --- /dev/null +++ b/drivers/virtio/virtio_rng.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include + +#define BUFFER_SIZE 16UL + +struct virtio_rng_priv { + struct virtqueue *rng_vq; +}; + +static int virtio_rng_read(struct udevice *dev, void *data, size_t len) +{ + int ret; + unsigned int rsize; + unsigned char buf[BUFFER_SIZE] __aligned(4); + unsigned char *ptr = data; + struct virtio_sg sg; + struct virtio_sg *sgs[1]; + struct virtio_rng_priv *priv = dev_get_priv(dev); + + while (len) { + sg.addr = buf; + sg.length = min(len, sizeof(buf)); + sgs[0] = &sg; + + ret = virtqueue_add(priv->rng_vq, sgs, 0, 1); + if (ret) + return ret; + + virtqueue_kick(priv->rng_vq); + + while (!virtqueue_get_buf(priv->rng_vq, &rsize)) + ; + + memcpy(ptr, buf, rsize); + len -= rsize; + ptr += rsize; + } + + return 0; +} + +static int virtio_rng_bind(struct udevice *dev) +{ + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent); + + /* Indicate what driver features we support */ + virtio_driver_features_init(uc_priv, NULL, 0, NULL, 0); + + return 0; +} + +static int virtio_rng_probe(struct udevice *dev) +{ + struct virtio_rng_priv *priv = dev_get_priv(dev); + int ret; + + ret = virtio_find_vqs(dev, 1, &priv->rng_vq); + if (ret < 0) { + debug("%s: virtio_find_vqs failed\n", __func__); + return ret; + } + + return 0; +} + +static const struct dm_rng_ops virtio_rng_ops = { + .read = virtio_rng_read, +}; + +U_BOOT_DRIVER(virtio_rng) = { + .name = VIRTIO_RNG_DRV_NAME, + .id = UCLASS_RNG, + .bind = virtio_rng_bind, + .probe = virtio_rng_probe, + .remove = virtio_reset, + .ops = &virtio_rng_ops, + .priv_auto_alloc_size = sizeof(struct virtio_rng_priv), + .flags = DM_FLAG_ACTIVE_DMA, +}; diff --git a/include/virtio.h b/include/virtio.h index 654fdf154b..561dcc34ba 100644 --- a/include/virtio.h +++ b/include/virtio.h @@ -22,10 +22,12 @@ #define VIRTIO_ID_NET 1 /* virtio net */ #define VIRTIO_ID_BLOCK 2 /* virtio block */ -#define VIRTIO_ID_MAX_NUM 3 +#define VIRTIO_ID_RNG 4 /* virtio rng */ +#define VIRTIO_ID_MAX_NUM 5 #define VIRTIO_NET_DRV_NAME "virtio-net" #define VIRTIO_BLK_DRV_NAME "virtio-blk" +#define VIRTIO_RNG_DRV_NAME "virtio-rng" /* Status byte for guest to report progress, and synchronize features */ From 4f24ac08afccd5f51f0b4c7023fc8cf45efe8163 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 24 Dec 2019 22:17:37 +0100 Subject: [PATCH 35/38] cmd: add rng command For the RNG uclass we currently only have a test working on the sandbox. Provide a command to test the hardware random number generator on non-sandbox systems. Signed-off-by: Heinrich Schuchardt --- cmd/Kconfig | 7 +++++++ cmd/Makefile | 1 + cmd/rng.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 cmd/rng.c diff --git a/cmd/Kconfig b/cmd/Kconfig index a6bfa87e08..7a78343e6b 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1672,6 +1672,13 @@ config CMD_GETTIME milliseconds. See also the 'bootstage' command which provides more flexibility for boot timing. +config CMD_RNG + bool "rng command" + depends on DM_RNG + select HEXDUMP + help + Print bytes from the hardware random number generator. + # TODO: rename to CMD_SLEEP config CMD_MISC bool "sleep" diff --git a/cmd/Makefile b/cmd/Makefile index 12e898d962..8df39f3a19 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -117,6 +117,7 @@ obj-$(CONFIG_CMD_READ) += read.o obj-$(CONFIG_CMD_REGINFO) += reginfo.o obj-$(CONFIG_CMD_REISER) += reiser.o obj-$(CONFIG_CMD_REMOTEPROC) += remoteproc.o +obj-$(CONFIG_CMD_RNG) += rng.o obj-$(CONFIG_CMD_ROCKUSB) += rockusb.o obj-$(CONFIG_SANDBOX) += host.o obj-$(CONFIG_CMD_SATA) += sata.o diff --git a/cmd/rng.c b/cmd/rng.c new file mode 100644 index 0000000000..36ca7a101c --- /dev/null +++ b/cmd/rng.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * The 'rng' command prints bytes from the hardware random number generator. + * + * Copyright (c) 2019, Heinrich Schuchardt + */ +#include +#include +#include +#include +#include + +static int do_rng(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + size_t n = 0x40; + struct udevice *dev; + void *buf; + int ret = CMD_RET_SUCCESS; + + if (uclass_get_device(UCLASS_RNG, 0, &dev) || !dev) { + printf("No RNG device\n"); + return CMD_RET_FAILURE; + } + + if (argc >= 2) + n = simple_strtoul(argv[1], NULL, 16); + + buf = malloc(n); + if (!buf) { + printf("Out of memory\n"); + return CMD_RET_FAILURE; + } + + if (dm_rng_read(dev, buf, n)) { + printf("Reading RNG failed\n"); + ret = CMD_RET_FAILURE; + } else { + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, n); + } + + free(buf); + + return ret; +} + +#ifdef CONFIG_SYS_LONGHELP +static char rng_help_text[] = + "[n]\n" + " - print n random bytes\n"; +#endif + +U_BOOT_CMD( + rng, 2, 0, do_rng, + "print bytes from the hardware random number generator", + rng_help_text +); From f552fa496c9e738afa069dd33578558fe4eb41ee Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Sun, 29 Dec 2019 00:01:05 +0530 Subject: [PATCH 36/38] efi: qemu: arm64: Add efi_rng_protocol implementation for the platform Add support for the EFI_RNG_PROTOCOL routines for the qemu arm64 platform. EFI_RNG_PROTOCOL is an uefi boottime service which is invoked by the efi stub in the kernel for getting random seed for kaslr. The routines are platform specific, and use the virtio-rng device on the platform to get random data. The feature can be enabled through the following config CONFIG_EFI_RNG_PROTOCOL Signed-off-by: Sughosh Ganu Changed SPDX header to use /* instead of //. Reviewed-by: Heinrich Schuchardt --- board/emulation/qemu-arm/qemu-arm.c | 42 +++++++++++ include/efi_rng.h | 32 ++++++++ lib/efi_loader/Kconfig | 8 ++ lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_rng.c | 112 ++++++++++++++++++++++++++++ 5 files changed, 195 insertions(+) create mode 100644 include/efi_rng.h create mode 100644 lib/efi_loader/efi_rng.c diff --git a/board/emulation/qemu-arm/qemu-arm.c b/board/emulation/qemu-arm/qemu-arm.c index e1f4709c4c..4e18733001 100644 --- a/board/emulation/qemu-arm/qemu-arm.c +++ b/board/emulation/qemu-arm/qemu-arm.c @@ -91,3 +91,45 @@ void *board_fdt_blob_setup(void) /* QEMU loads a generated DTB for us at the start of RAM. */ return (void *)CONFIG_SYS_SDRAM_BASE; } + +#if defined(CONFIG_EFI_RNG_PROTOCOL) +#include +#include + +#include + +efi_status_t platform_get_rng_device(struct udevice **dev) +{ + int ret; + efi_status_t status = EFI_DEVICE_ERROR; + struct udevice *bus, *devp; + + for (uclass_first_device(UCLASS_VIRTIO, &bus); bus; + uclass_next_device(&bus)) { + for (device_find_first_child(bus, &devp); devp; + device_find_next_child(&devp)) { + if (device_get_uclass_id(devp) == UCLASS_RNG) { + *dev = devp; + status = EFI_SUCCESS; + break; + } + } + } + + if (status != EFI_SUCCESS) { + debug("No rng device found\n"); + return EFI_DEVICE_ERROR; + } + + if (*dev) { + ret = device_probe(*dev); + if (ret) + return EFI_DEVICE_ERROR; + } else { + debug("Couldn't get child device\n"); + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} +#endif /* CONFIG_EFI_RNG_PROTOCOL */ diff --git a/include/efi_rng.h b/include/efi_rng.h new file mode 100644 index 0000000000..35f59678c7 --- /dev/null +++ b/include/efi_rng.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2019, Linaro Limited + */ + +#if !defined _EFI_RNG_H_ +#define _EFI_RNG_H_ + +#include +#include + +/* EFI random number generation protocol related GUID definitions */ +#define EFI_RNG_PROTOCOL_GUID \ + EFI_GUID(0x3152bca5, 0xeade, 0x433d, 0x86, 0x2e, \ + 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44) + +#define EFI_RNG_ALGORITHM_RAW \ + EFI_GUID(0xe43176d7, 0xb6e8, 0x4827, 0xb7, 0x84, \ + 0x7f, 0xfd, 0xc4, 0xb6, 0x85, 0x61) + +struct efi_rng_protocol { + efi_status_t (EFIAPI *get_info)(struct efi_rng_protocol *protocol, + efi_uintn_t *rng_algorithm_list_size, + efi_guid_t *rng_algorithm_list); + efi_status_t (EFIAPI *get_rng)(struct efi_rng_protocol *protocol, + efi_guid_t *rng_algorithm, + efi_uintn_t rng_value_length, uint8_t *rng_value); +}; + +efi_status_t platform_get_rng_device(struct udevice **dev); + +#endif /* _EFI_RNG_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index e2cb729303..6727336169 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -121,4 +121,12 @@ config EFI_GRUB_ARM32_WORKAROUND GRUB prior to version 2.04 requires U-Boot to disable caches. This workaround currently is also needed on systems with caches that cannot be managed via CP15. + +config EFI_RNG_PROTOCOL + bool "EFI_RNG_PROTOCOL support" + depends on DM_RNG + help + "Support for EFI_RNG_PROTOCOL implementation. Uses the rng + device on the platform" + endif diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 7db4060286..04dc864851 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -42,3 +42,4 @@ obj-$(CONFIG_PARTITIONS) += efi_disk.o obj-$(CONFIG_NET) += efi_net.o obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o +obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_rng.o diff --git a/lib/efi_loader/efi_rng.c b/lib/efi_loader/efi_rng.c new file mode 100644 index 0000000000..47aaa6adca --- /dev/null +++ b/lib/efi_loader/efi_rng.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019, Linaro Limited + */ + +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +__weak efi_status_t platform_get_rng_device(struct udevice **dev) +{ + int ret; + struct udevice *devp; + + ret = uclass_get_device(UCLASS_RNG, 0, &devp); + if (ret) { + debug("Unable to get rng device\n"); + return EFI_DEVICE_ERROR; + } + + *dev = devp; + + return EFI_SUCCESS; +} + +static efi_status_t EFIAPI rng_getinfo(struct efi_rng_protocol *this, + efi_uintn_t *rng_algorithm_list_size, + efi_guid_t *rng_algorithm_list) +{ + efi_status_t ret = EFI_SUCCESS; + efi_guid_t rng_algo_guid = EFI_RNG_ALGORITHM_RAW; + + EFI_ENTRY("%p, %p, %p", this, rng_algorithm_list_size, + rng_algorithm_list); + + if (!this || !rng_algorithm_list_size) { + ret = EFI_INVALID_PARAMETER; + goto back; + } + + if (!rng_algorithm_list || + *rng_algorithm_list_size < sizeof(*rng_algorithm_list)) { + *rng_algorithm_list_size = sizeof(*rng_algorithm_list); + ret = EFI_BUFFER_TOO_SMALL; + goto back; + } + + /* + * For now, use EFI_RNG_ALGORITHM_RAW as the default + * algorithm. If a new algorithm gets added in the + * future through a Kconfig, rng_algo_guid will be set + * based on that Kconfig option + */ + *rng_algorithm_list_size = sizeof(*rng_algorithm_list); + guidcpy(rng_algorithm_list, &rng_algo_guid); + +back: + return EFI_EXIT(ret); +} + +static efi_status_t EFIAPI getrng(struct efi_rng_protocol *this, + efi_guid_t *rng_algorithm, + efi_uintn_t rng_value_length, + uint8_t *rng_value) +{ + int ret; + efi_status_t status = EFI_SUCCESS; + struct udevice *dev; + const efi_guid_t rng_raw_guid = EFI_RNG_ALGORITHM_RAW; + + EFI_ENTRY("%p, %p, %zu, %p", this, rng_algorithm, rng_value_length, + rng_value); + + if (!this || !rng_value || !rng_value_length) { + status = EFI_INVALID_PARAMETER; + goto back; + } + + if (rng_algorithm) { + EFI_PRINT("RNG algorithm %pUl\n", rng_algorithm); + if (guidcmp(rng_algorithm, &rng_raw_guid)) { + status = EFI_UNSUPPORTED; + goto back; + } + } + + ret = platform_get_rng_device(&dev); + if (ret != EFI_SUCCESS) { + EFI_PRINT("Rng device not found\n"); + status = EFI_UNSUPPORTED; + goto back; + } + + ret = dm_rng_read(dev, rng_value, rng_value_length); + if (ret < 0) { + EFI_PRINT("Rng device read failed\n"); + status = EFI_DEVICE_ERROR; + goto back; + } + +back: + return EFI_EXIT(status); +} + +const struct efi_rng_protocol efi_rng_protocol = { + .get_info = rng_getinfo, + .get_rng = getrng, +}; From 33c37d9784168ac75be91e890329712d9a849539 Mon Sep 17 00:00:00 2001 From: Sughosh Ganu Date: Sun, 29 Dec 2019 00:01:06 +0530 Subject: [PATCH 37/38] efi_rng_protocol: Install the efi_rng_protocol on the root node Install the EFI_RNG_PROTOCOL implementation for it's subsequent use by the kernel for features like kaslr. Signed-off-by: Sughosh Ganu Reviewed-by: Heinrich Schuchardt --- include/efi_loader.h | 4 ++++ lib/efi_loader/efi_rng.c | 2 ++ lib/efi_loader/efi_root_node.c | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/include/efi_loader.h b/include/efi_loader.h index e1c9b1fd6a..d4c59b54c4 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -133,6 +133,7 @@ extern const struct efi_hii_config_routing_protocol efi_hii_config_routing; extern const struct efi_hii_config_access_protocol efi_hii_config_access; extern const struct efi_hii_database_protocol efi_hii_database; extern const struct efi_hii_string_protocol efi_hii_string; +extern const struct efi_rng_protocol efi_rng_protocol; uint16_t *efi_dp_str(struct efi_device_path *dp); @@ -178,6 +179,9 @@ extern const efi_guid_t efi_guid_hii_config_access_protocol; extern const efi_guid_t efi_guid_hii_database_protocol; extern const efi_guid_t efi_guid_hii_string_protocol; +/* GUID of RNG protocol */ +extern const efi_guid_t efi_guid_rng_protocol; + extern unsigned int __efi_runtime_start, __efi_runtime_stop; extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop; diff --git a/lib/efi_loader/efi_rng.c b/lib/efi_loader/efi_rng.c index 47aaa6adca..432c986204 100644 --- a/lib/efi_loader/efi_rng.c +++ b/lib/efi_loader/efi_rng.c @@ -11,6 +11,8 @@ DECLARE_GLOBAL_DATA_PTR; +const efi_guid_t efi_guid_rng_protocol = EFI_RNG_PROTOCOL_GUID; + __weak efi_status_t platform_get_rng_device(struct udevice **dev) { int ret; diff --git a/lib/efi_loader/efi_root_node.c b/lib/efi_loader/efi_root_node.c index f68b0fdc61..76d18fb1a4 100644 --- a/lib/efi_loader/efi_root_node.c +++ b/lib/efi_loader/efi_root_node.c @@ -80,6 +80,10 @@ efi_status_t efi_root_node_register(void) /* HII configuration routing protocol */ &efi_guid_hii_config_routing_protocol, (void *)&efi_hii_config_routing, +#endif +#if CONFIG_IS_ENABLED(EFI_RNG_PROTOCOL) + &efi_guid_rng_protocol, + (void *)&efi_rng_protocol, #endif NULL)); efi_root->type = EFI_OBJECT_TYPE_U_BOOT_FIRMWARE; From 7d6f16fbde9a03adc7d85b8809cb16dbc5e311f9 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 28 Dec 2019 15:40:40 +0100 Subject: [PATCH 38/38] efi_selftest: unit test for EFI_RNG_PROTOCOL Provide a unit test for the EFI_RNG_PROTOCOL. The list of algorithms is read. Two random numbers are generated. The test checks that the two numbers differ. Signed-off-by: Heinrich Schuchardt --- lib/efi_selftest/Makefile | 1 + lib/efi_selftest/efi_selftest_rng.c | 117 ++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_rng.c diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 487cb4c674..3ad96e1cbf 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -47,6 +47,7 @@ efi_selftest_unicode_collation.o obj-$(CONFIG_CPU_V7) += efi_selftest_unaligned.o obj-$(CONFIG_EFI_LOADER_HII) += efi_selftest_hii.o +obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_selftest_rng.o obj-$(CONFIG_EFI_GET_TIME) += efi_selftest_rtc.o ifeq ($(CONFIG_GENERATE_ACPI_TABLE),) diff --git a/lib/efi_selftest/efi_selftest_rng.c b/lib/efi_selftest/efi_selftest_rng.c new file mode 100644 index 0000000000..fca9749d07 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_rng.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * efi_selftest_rng + * + * Copyright (c) 2019 Heinrich Schuchardt + * + * Test the random number generator service. + */ + +#include +#include + +#define RNG_LEN 9 + +static struct efi_boot_services *boottime; +static efi_guid_t efi_rng_guid = EFI_RNG_PROTOCOL_GUID; + +/* + * Setup unit test. + * + * @handle: handle of the loaded image + * @systable: system table + * @return: EFI_ST_SUCCESS for success + */ +static int setup(const efi_handle_t handle, + const struct efi_system_table *systable) +{ + boottime = systable->boottime; + return EFI_ST_SUCCESS; +} + +/* + * Execute unit test. + * + * Retrieve available RNG algorithms. + * Retrieve two random values and compare them. + * + * @return: EFI_ST_SUCCESS for success + */ +static int execute(void) +{ + efi_status_t ret; + efi_uintn_t size; + struct efi_rng_protocol *rng; + efi_guid_t *algo_list; + u8 rnd1[RNG_LEN] __aligned(4), rnd2[RNG_LEN] __aligned(4); + int r; + + /* Get random number generator protocol */ + ret = boottime->locate_protocol(&efi_rng_guid, NULL, (void **)&rng); + if (ret != EFI_SUCCESS) { + efi_st_error( + "Random number generator protocol not available\n"); + return EFI_ST_FAILURE; + } + + ret = rng->get_info(rng, &size, NULL); + if (ret != EFI_BUFFER_TOO_SMALL) { + efi_st_error("Could not retrieve alorithm list size\n"); + return EFI_ST_FAILURE; + } + if (size < sizeof(efi_guid_t)) { + efi_st_error("Empty alorithm list\n"); + return EFI_ST_FAILURE; + } + + ret = boottime->allocate_pool(EFI_LOADER_DATA, size, + (void **)&algo_list); + if (ret != EFI_SUCCESS) { + efi_st_error("Could not allocate pool memory\n"); + return EFI_ST_FAILURE; + } + + ret = rng->get_info(rng, &size, algo_list); + if (ret != EFI_SUCCESS) { + efi_st_error("Could not get info\n"); + return EFI_ST_FAILURE; + } + if (size < sizeof(efi_guid_t)) { + efi_st_error("Empty alorithm list\n"); + return EFI_ST_FAILURE; + } + + memset(rnd1, 0, RNG_LEN); + memset(rnd2, 0, RNG_LEN); + + ret = rng->get_rng(rng, NULL, RNG_LEN - 1, &rnd1[1]); + if (ret != EFI_SUCCESS) { + efi_st_error("Could not get random value\n"); + return EFI_ST_FAILURE; + } + ret = rng->get_rng(rng, algo_list, RNG_LEN - 1, &rnd2[1]); + if (ret != EFI_SUCCESS) { + efi_st_error("Could not get random value\n"); + return EFI_ST_FAILURE; + } + r = memcmp(rnd1, rnd2, RNG_LEN); + if (!r) { + efi_st_error("Two equal consecutive random numbers\n"); + return EFI_ST_FAILURE; + } + + ret = boottime->free_pool(algo_list); + if (ret != EFI_SUCCESS) { + efi_st_error("Could not free pool memory\n"); + return EFI_ST_FAILURE; + } + + return EFI_ST_SUCCESS; +} + +EFI_UNIT_TEST(rng) = { + .name = "random number generator", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, +};