diff options
79 files changed, 4472 insertions, 352 deletions
diff --git a/.travis.yml b/.travis.yml index b3253da13c..de96b0e81d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,14 @@ addons: - clang-7 - srecord - graphviz + - coreutils + - util-linux + - dosfstools + - gdisk + - mount + - mtools + - openssl + - sbsigntool install: # Clone uboot-test-hooks @@ -57,10 +65,11 @@ install: - grub-mkimage --prefix="" -o ~/grub_x86.efi -O i386-efi normal echo lsefimmap lsefi lsefisystab efinet tftp minicmd - grub-mkimage --prefix="" -o ~/grub_x64.efi -O x86_64-efi normal echo lsefimmap lsefi lsefisystab efinet tftp minicmd - wget http://mirrors.kernel.org/ubuntu/pool/main/m/mpfr4/libmpfr4_3.1.4-1_amd64.deb && sudo dpkg -i libmpfr4_3.1.4-1_amd64.deb && rm libmpfr4_3.1.4-1_amd64.deb + - wget http://mirrors.kernel.org/ubuntu/pool/universe/e/efitools/efitools_1.8.1-0ubuntu2_amd64.deb && sudo dpkg -i efitools_1.8.1-0ubuntu2_amd64.deb && rm efitools_1.8.1-0ubuntu2_amd64.deb env: global: - - PATH=/tmp/qemu-install/bin:/tmp/uboot-test-hooks/bin:/usr/bin:/bin:/usr/local/bin + - PATH=/tmp/qemu-install/bin:/tmp/uboot-test-hooks/bin:/sbin:/usr/bin:/bin:/usr/local/bin - PYTHONPATH=/tmp/uboot-test-hooks/py/travis-ci - BUILD_DIR=build - HOSTCC="cc" diff --git a/MAINTAINERS b/MAINTAINERS index d8d420f84f..7ac7e21ba1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -654,9 +654,9 @@ LOGGING M: Simon Glass <sjg@chromium.org> S: Maintained T: git https://gitlab.denx.de/u-boot/u-boot.git -F: common/log.c +F: common/log* F: cmd/log.c -F: test/log/log_test.c +F: test/log/ F: test/py/tests/test_log.py MALI DISPLAY PROCESSORS diff --git a/arch/Kconfig b/arch/Kconfig index ae9c93ed7b..91e049b322 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -96,6 +96,7 @@ config SANDBOX select DM_SPI_FLASH select HAVE_BLOCK_DEVICE select LZO + select OF_BOARD_SETUP select PCI_ENDPOINT select SPI select SUPPORT_OF_CONTROL diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index f7c73e3a0b..e7ec892bdf 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -8,6 +8,7 @@ #include <fcntl.h> #include <getopt.h> #include <setjmp.h> +#include <signal.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> @@ -175,6 +176,13 @@ void os_fd_restore(void) } } +static void os_sigint_handler(int sig) +{ + os_fd_restore(); + signal(SIGINT, SIG_DFL); + raise(SIGINT); +} + /* Put tty into raw mode so <tab> and <ctrl+c> work */ void os_tty_raw(int fd, bool allow_sigs) { @@ -205,6 +213,7 @@ void os_tty_raw(int fd, bool allow_sigs) term_setup = true; atexit(os_fd_restore); + signal(SIGINT, os_sigint_handler); } void *os_malloc(size_t length) diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index 4dd82f6a32..20f6893829 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -10,7 +10,7 @@ aliases { i2c0 = &i2c_0; - pci0 = &pci; + pci0 = &pcic; rtc0 = &rtc_0; axi0 = &axi; spi0 = &spi; @@ -20,6 +20,25 @@ reg = <0 CONFIG_SYS_SDRAM_SIZE>; }; + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + reservation_test0 { + size = <0x4000>; + alignment = <0x2000>; + }; + + reservation_test1: restest@a000 { + reg = <0x00d0a000 0x2000>; + }; + + reservation_test2: restest@7000 { + reg = <0x00d07000 0x1000>; + }; + }; + cros_ec: cros-ec { reg = <0 0>; u-boot,dm-pre-reloc; @@ -52,9 +71,10 @@ pinctrl-0 = <&pinctrl_i2c0>; }; - pci: pci-controller { + pcic: pci@0 { compatible = "sandbox,pci"; device_type = "pci"; + bus-range = <0x00 0xff>; #address-cells = <3>; #size-cells = <2>; ranges = <0x02000000 0 0x10000000 0x10000000 0 0x2000 diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 7cd56c14f2..e1f68cd552 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -100,7 +100,7 @@ }; }; - pci-controller { + pci@0 { pci@1e,0 { compatible = "sandbox,pmc"; reg = <0xf000 0 0 0 0>; diff --git a/arch/sandbox/dts/sandbox64.dts b/arch/sandbox/dts/sandbox64.dts index 5c95cee9d7..a39f94feec 100644 --- a/arch/sandbox/dts/sandbox64.dts +++ b/arch/sandbox/dts/sandbox64.dts @@ -10,7 +10,7 @@ aliases { i2c0 = &i2c_0; - pci0 = &pci; + pci0 = &pcic; rtc0 = &rtc_0; axi0 = &axi; spi0 = &spi; @@ -20,6 +20,26 @@ reg = /bits/ 64 <0 CONFIG_SYS_SDRAM_SIZE>; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + reservation_test_size { + size = <0 0x4000>; + alignment = <0 0x2000>; + }; + + reservation_test@a000 { + reg = <0 0x00d0a000 0 0x2000>; + }; + + reservation_test@7000 { + reg = <0 0x00d07000 0 0x1000>; + }; + }; + + /* ... */ cros_ec: cros-ec { reg = <0 0 0 0>; u-boot,dm-pre-reloc; @@ -47,9 +67,10 @@ pinctrl-0 = <&pinctrl_i2c0>; }; - pci: pci-controller { + pcic: pci@0 { compatible = "sandbox,pci"; device_type = "pci"; + bus-range = <0x00 0xff>; #address-cells = <3>; #size-cells = <2>; ranges = <0x02000000 0 0x10000000 0 0x10000000 0 0x2000 diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 5fa951ad4b..6803c00f90 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -93,6 +93,8 @@ <&gpio_b 9 0xc 3 2 1>; int-value = <1234>; uint-value = <(-1234)>; + int64-value = /bits/ 64 <0x1111222233334444>; + int-array = <5678 9123 4567>; interrupts-extended = <&irq 3 0>; }; @@ -467,9 +469,10 @@ compatible = "sandbox,pch"; }; - pci0: pci-controller0 { + pci0: pci@0 { compatible = "sandbox,pci"; device_type = "pci"; + bus-range = <0x00 0xff>; #address-cells = <3>; #size-cells = <2>; ranges = <0x02000000 0 0x10000000 0x10000000 0 0x2000000 @@ -535,9 +538,10 @@ }; }; - pci1: pci-controller1 { + pci1: pci@1 { compatible = "sandbox,pci"; device_type = "pci"; + bus-range = <0x00 0xff>; #address-cells = <3>; #size-cells = <2>; ranges = <0x02000000 0 0x30000000 0x30000000 0 0x2000 @@ -550,9 +554,10 @@ }; }; - pci2: pci-controller2 { + pci2: pci@2 { compatible = "sandbox,pci"; device_type = "pci"; + bus-range = <0x00 0xff>; #address-cells = <3>; #size-cells = <2>; ranges = <0x02000000 0 0x50000000 0x50000000 0 0x2000 diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c index 0c3d245dff..1372003018 100644 --- a/board/sandbox/sandbox.c +++ b/board/sandbox/sandbox.c @@ -58,6 +58,12 @@ int board_init(void) return 0; } +int ft_board_setup(void *fdt, bd_t *bd) +{ + /* Create an arbitrary reservation to allow testing OF_BOARD_SETUP.*/ + return fdt_add_mem_rsv(fdt, 0x00d02000, 0x4000); +} + #ifdef CONFIG_BOARD_LATE_INIT int board_late_init(void) { diff --git a/cmd/efidebug.c b/cmd/efidebug.c index c1bb76477a..02ef019694 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -1089,6 +1089,78 @@ static int do_efi_boot_opt(cmd_tbl_t *cmdtp, int flag, return cp->cmd(cmdtp, flag, argc, argv); } +/** + * do_efi_test_bootmgr() - run simple bootmgr for test + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "test bootmgr" sub-command. + * Run simple bootmgr for test. + * + * efidebug test bootmgr + */ +static int do_efi_test_bootmgr(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + efi_handle_t image; + efi_uintn_t exit_data_size = 0; + u16 *exit_data = NULL; + efi_status_t ret; + + ret = efi_bootmgr_load(&image); + printf("efi_bootmgr_load() returned: %ld\n", ret & ~EFI_ERROR_MASK); + + /* We call efi_start_image() even if error for test purpose. */ + ret = EFI_CALL(efi_start_image(image, &exit_data_size, &exit_data)); + printf("efi_start_image() returned: %ld\n", ret & ~EFI_ERROR_MASK); + if (ret && exit_data) + efi_free_pool(exit_data); + + efi_restore_gd(); + + return CMD_RET_SUCCESS; +} + +static cmd_tbl_t cmd_efidebug_test_sub[] = { + U_BOOT_CMD_MKENT(bootmgr, CONFIG_SYS_MAXARGS, 1, do_efi_test_bootmgr, + "", ""), +}; + +/** + * do_efi_test() - manage UEFI load options + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "test" sub-command. + */ +static int do_efi_test(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + cmd_tbl_t *cp; + + if (argc < 2) + return CMD_RET_USAGE; + + argc--; argv++; + + cp = find_cmd_tbl(argv[0], cmd_efidebug_test_sub, + ARRAY_SIZE(cmd_efidebug_test_sub)); + if (!cp) + return CMD_RET_USAGE; + + return cp->cmd(cmdtp, flag, argc, argv); +} + static cmd_tbl_t cmd_efidebug_sub[] = { U_BOOT_CMD_MKENT(boot, CONFIG_SYS_MAXARGS, 1, do_efi_boot_opt, "", ""), U_BOOT_CMD_MKENT(devices, CONFIG_SYS_MAXARGS, 1, do_efi_show_devices, @@ -1103,6 +1175,8 @@ static cmd_tbl_t cmd_efidebug_sub[] = { "", ""), U_BOOT_CMD_MKENT(tables, CONFIG_SYS_MAXARGS, 1, do_efi_show_tables, "", ""), + U_BOOT_CMD_MKENT(test, CONFIG_SYS_MAXARGS, 1, do_efi_test, + "", ""), }; /** @@ -1172,7 +1246,9 @@ static char efidebug_help_text[] = "efidebug memmap\n" " - show UEFI memory map\n" "efidebug tables\n" - " - show UEFI configuration tables\n"; + " - show UEFI configuration tables\n" + "efidebug test bootmgr\n" + " - run simple bootmgr for test\n"; #endif U_BOOT_CMD( @@ -286,7 +286,7 @@ static int do_fdt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) /* * Set the value of a property in the working_fdt. */ - } else if (argv[1][0] == 's') { + } else if (strncmp(argv[1], "se", 2) == 0) { char *pathp; /* path */ char *prop; /* property */ int nodeoffset; /* node offset from libfdt */ diff --git a/cmd/nvedit.c b/cmd/nvedit.c index 81d94cd193..966c134059 100644 --- a/cmd/nvedit.c +++ b/cmd/nvedit.c @@ -1417,7 +1417,7 @@ static char env_help_text[] = #endif #endif #if defined(CONFIG_CMD_NVEDIT_EFI) - "env set -e [-nv][-bs][-rt][-a][-i addr,size][-v] name [arg ...]\n" + "env set -e [-nv][-bs][-rt][-at][-a][-i addr,size][-v] name [arg ...]\n" " - set UEFI variable; unset if '-i' or 'arg' not specified\n" #endif "env set [-f] name [arg ...]\n"; @@ -1479,13 +1479,14 @@ U_BOOT_CMD_COMPLETE( setenv, CONFIG_SYS_MAXARGS, 0, do_env_set, "set environment variables", #if defined(CONFIG_CMD_NVEDIT_EFI) - "-e [-guid guid][-nv][-bs][-rt][-a][-v]\n" + "-e [-guid guid][-nv][-bs][-rt][-at][-a][-v]\n" " [-i addr,size name], or [name [value ...]]\n" " - set UEFI variable 'name' to 'value' ...'\n" " \"-guid\": set vendor guid\n" " \"-nv\": set non-volatile attribute\n" " \"-bs\": set boot-service attribute\n" " \"-rt\": set runtime attribute\n" + " \"-at\": set time-based authentication attribute\n" " \"-a\": append-write\n" " \"-i addr,size\": use <addr,size> as variable's value\n" " \"-v\": verbose message\n" diff --git a/cmd/nvedit_efi.c b/cmd/nvedit_efi.c index 8ea0da0128..837e39e021 100644 --- a/cmd/nvedit_efi.c +++ b/cmd/nvedit_efi.c @@ -41,6 +41,11 @@ static const struct { } efi_guid_text[] = { /* signature database */ {EFI_GLOBAL_VARIABLE_GUID, "EFI_GLOBAL_VARIABLE_GUID"}, + {EFI_IMAGE_SECURITY_DATABASE_GUID, "EFI_IMAGE_SECURITY_DATABASE_GUID"}, + /* certificate type */ + {EFI_CERT_SHA256_GUID, "EFI_CERT_SHA256_GUID"}, + {EFI_CERT_X509_GUID, "EFI_CERT_X509_GUID"}, + {EFI_CERT_TYPE_PKCS7_GUID, "EFI_CERT_TYPE_PKCS7_GUID"}, }; /* "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" */ @@ -453,7 +458,7 @@ out: * Return: CMD_RET_SUCCESS on success, or CMD_RET_RET_FAILURE * * This function is for "env set -e" or "setenv -e" command: - * => env set -e [-guid guid][-nv][-bs][-rt][-a][-v] + * => env set -e [-guid guid][-nv][-bs][-rt][-at][-a][-v] * [-i address,size] var, or * var [value ...] * Encode values specified and set given UEFI variable. @@ -512,6 +517,9 @@ int do_env_set_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) attributes |= EFI_VARIABLE_RUNTIME_ACCESS; } else if (!strcmp(argv[0], "-nv")) { attributes |= EFI_VARIABLE_NON_VOLATILE; + } else if (!strcmp(argv[0], "-at")) { + attributes |= + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; } else if (!strcmp(argv[0], "-a")) { attributes |= EFI_VARIABLE_APPEND_WRITE; } else if (!strcmp(argv[0], "-i")) { @@ -525,9 +533,9 @@ int do_env_set_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (*ep != ',') return CMD_RET_USAGE; + /* 0 should be allowed for delete */ size = simple_strtoul(++ep, NULL, 16); - if (!size) - return CMD_RET_FAILURE; + value_on_memory = true; } else if (!strcmp(argv[0], "-v")) { verbose = true; @@ -539,8 +547,13 @@ int do_env_set_efi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return CMD_RET_USAGE; var_name = argv[0]; - if (default_guid) - guid = efi_global_variable_guid; + if (default_guid) { + if (!strcmp(var_name, "db") || !strcmp(var_name, "dbx") || + !strcmp(var_name, "dbt")) + guid = efi_guid_image_security_database; + else + guid = efi_global_variable_guid; + } if (verbose) { printf("GUID: %s\n", efi_guid_to_str((const efi_guid_t *) diff --git a/common/Kconfig b/common/Kconfig index 3072651082..ee4f748c32 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -775,9 +775,16 @@ config TPL_LOG_CONSOLE log message is shown - other details like level, category, file and line number are omitted. +config LOG_SYSLOG + bool "Log output to syslog server" + depends on LOG && NET + help + Enables a log driver which broadcasts log records via UDP port 514 + to syslog servers. + config LOG_TEST bool "Provide a test for logging" - depends on LOG + depends on LOG && UNIT_TEST default y if SANDBOX help This enables a 'log test' command to test logging. It is normally diff --git a/common/Makefile b/common/Makefile index 702f2396cf..d84e10ba99 100644 --- a/common/Makefile +++ b/common/Makefile @@ -132,6 +132,7 @@ obj-$(CONFIG_DFU_OVER_USB) += dfu.o obj-y += command.o obj-$(CONFIG_$(SPL_TPL_)LOG) += log.o obj-$(CONFIG_$(SPL_TPL_)LOG_CONSOLE) += log_console.o +obj-$(CONFIG_$(SPL_TPL_)LOG_SYSLOG) += log_syslog.o obj-y += s_record.o obj-$(CONFIG_CMD_LOADB) += xyzModem.o obj-$(CONFIG_$(SPL_TPL_)YMODEM_SUPPORT) += xyzModem.o diff --git a/common/log_syslog.c b/common/log_syslog.c new file mode 100644 index 0000000000..5e3e20e8a4 --- /dev/null +++ b/common/log_syslog.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Log to syslog. + * + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <log.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define BUFFER_SIZE 480 + +static void append(char **buf, char *buf_end, const char *fmt, ...) +{ + va_list args; + size_t size = buf_end - *buf; + + va_start(args, fmt); + vsnprintf(*buf, size, fmt, args); + va_end(args); + *buf += strlen(*buf); +} + +static int log_syslog_emit(struct log_device *ldev, struct log_rec *rec) +{ + int ret; + int fmt = gd->log_fmt; + char msg[BUFFER_SIZE]; + char *msg_end = msg + BUFFER_SIZE; + char *ptr = msg; + char *iphdr; + char *log_msg; + int eth_hdr_size; + struct in_addr bcast_ip; + static int processing_msg; + unsigned int log_level; + char *log_hostname; + + /* Fend off messages from the network stack while writing a message */ + if (processing_msg) + return 0; + + processing_msg = 1; + + /* Setup packet buffers */ + net_init(); + /* Disable hardware and put it into the reset state */ + eth_halt(); + /* Set current device according to environment variables */ + eth_set_current(); + /* Get hardware ready for send and receive operations */ + ret = eth_init(); + if (ret < 0) { + eth_halt(); + goto out; + } + + memset(msg, 0, BUFFER_SIZE); + + /* Set ethernet header */ + eth_hdr_size = net_set_ether((uchar *)ptr, net_bcast_ethaddr, PROT_IP); + ptr += eth_hdr_size; + iphdr = ptr; + ptr += IP_UDP_HDR_SIZE; + log_msg = ptr; + + /* + * The syslog log levels defined in RFC 5424 match the U-Boot ones up to + * level 7 (debug). + */ + log_level = rec->level; + if (log_level > 7) + log_level = 7; + /* Leave high bits as 0 to write a 'kernel message' */ + + /* Write log message to buffer */ + append(&ptr, msg_end, "<%u>", log_level); + log_hostname = env_get("log_hostname"); + if (log_hostname) + append(&ptr, msg_end, "%s ", log_hostname); + append(&ptr, msg_end, "uboot: "); + if (fmt & (1 << LOGF_LEVEL)) + append(&ptr, msg_end, "%s.", + log_get_level_name(rec->level)); + if (fmt & (1 << LOGF_CAT)) + append(&ptr, msg_end, "%s,", + log_get_cat_name(rec->cat)); + if (fmt & (1 << LOGF_FILE)) + append(&ptr, msg_end, "%s:", rec->file); + if (fmt & (1 << LOGF_LINE)) + append(&ptr, msg_end, "%d-", rec->line); + if (fmt & (1 << LOGF_FUNC)) + append(&ptr, msg_end, "%s()", rec->func); + if (fmt & (1 << LOGF_MSG)) + append(&ptr, msg_end, "%s%s", + fmt != (1 << LOGF_MSG) ? " " : "", rec->msg); + /* Consider trailing 0x00 */ + ptr++; + + debug("log message: '%s'\n", log_msg); + + /* Broadcast message */ + bcast_ip.s_addr = 0xFFFFFFFFL; + net_set_udp_header((uchar *)iphdr, bcast_ip, 514, 514, ptr - log_msg); + net_send_packet((uchar *)msg, ptr - msg); + +out: + processing_msg = 0; + return ret; +} + +LOG_DRIVER(syslog) = { + .name = "syslog", + .emit = log_syslog_emit, +}; diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index 8ca17d621b..9d092330ef 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -18,15 +18,18 @@ CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000 CONFIG_SILENT_CONSOLE=y CONFIG_PRE_CONSOLE_BUFFER=y CONFIG_LOG_MAX_LEVEL=6 +CONFIG_LOG_SYSLOG=y CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_CMD_CPU=y CONFIG_CMD_LICENSE=y CONFIG_CMD_BOOTZ=y +CONFIG_CMD_BOOTEFI_HELLO=y # CONFIG_CMD_ELF is not set CONFIG_CMD_ASKENV=y CONFIG_CMD_GREPENV=y CONFIG_CMD_ENV_CALLBACK=y CONFIG_CMD_ENV_FLAGS=y +CONFIG_CMD_NVEDIT_EFI=y CONFIG_LOOPW=y CONFIG_CMD_MD5SUM=y CONFIG_CMD_MEMINFO=y @@ -53,6 +56,7 @@ CONFIG_CMD_DNS=y CONFIG_CMD_LINK_LOCAL=y CONFIG_CMD_ETHSW=y CONFIG_CMD_BMP=y +CONFIG_CMD_EFIDEBUG=y CONFIG_CMD_TIME=y CONFIG_CMD_TIMER=y CONFIG_CMD_SOUND=y @@ -203,6 +207,7 @@ CONFIG_RSA_VERIFY_WITH_PKEY=y CONFIG_TPM=y CONFIG_LZ4=y CONFIG_ERRNO_STR=y +CONFIG_EFI_SECURE_BOOT=y CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index cc90f0006e..cc38315868 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -19,18 +19,21 @@ CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000 CONFIG_SILENT_CONSOLE=y CONFIG_PRE_CONSOLE_BUFFER=y CONFIG_LOG_MAX_LEVEL=6 +CONFIG_LOG_SYSLOG=y CONFIG_LOG_ERROR_RETURN=y CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_ANDROID_AB=y CONFIG_CMD_CPU=y CONFIG_CMD_LICENSE=y CONFIG_CMD_BOOTZ=y +CONFIG_CMD_BOOTEFI_HELLO=y CONFIG_CMD_ABOOTIMG=y # CONFIG_CMD_ELF is not set CONFIG_CMD_ASKENV=y CONFIG_CMD_GREPENV=y CONFIG_CMD_ENV_CALLBACK=y CONFIG_CMD_ENV_FLAGS=y +CONFIG_CMD_NVEDIT_EFI=y CONFIG_LOOPW=y CONFIG_CMD_MD5SUM=y CONFIG_CMD_MEMINFO=y @@ -62,6 +65,7 @@ CONFIG_CMD_LINK_LOCAL=y CONFIG_CMD_ETHSW=y CONFIG_CMD_BMP=y CONFIG_CMD_BOOTCOUNT=y +CONFIG_CMD_EFIDEBUG=y CONFIG_CMD_TIME=y CONFIG_CMD_TIMER=y CONFIG_CMD_SOUND=y @@ -229,6 +233,7 @@ CONFIG_RSA_VERIFY_WITH_PKEY=y CONFIG_TPM=y CONFIG_LZ4=y CONFIG_ERRNO_STR=y +CONFIG_EFI_SECURE_BOOT=y CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index 59d34cb5e0..00d9359f19 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -15,10 +15,12 @@ CONFIG_CONSOLE_RECORD=y CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000 CONFIG_SILENT_CONSOLE=y CONFIG_LOG_MAX_LEVEL=6 +CONFIG_LOG_SYSLOG=y CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_CMD_CPU=y CONFIG_CMD_LICENSE=y CONFIG_CMD_BOOTZ=y +CONFIG_CMD_BOOTEFI_HELLO=y # CONFIG_CMD_ELF is not set CONFIG_CMD_ASKENV=y CONFIG_CMD_GREPENV=y @@ -43,6 +45,7 @@ CONFIG_CMD_CDP=y CONFIG_CMD_SNTP=y CONFIG_CMD_DNS=y CONFIG_CMD_LINK_LOCAL=y +CONFIG_CMD_EFIDEBUG=y CONFIG_CMD_TIME=y CONFIG_CMD_TIMER=y CONFIG_CMD_SOUND=y diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index 53c5bd8a4e..18c6a47602 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -28,6 +28,7 @@ CONFIG_SPL_ENV_SUPPORT=y CONFIG_CMD_CPU=y CONFIG_CMD_LICENSE=y CONFIG_CMD_BOOTZ=y +CONFIG_CMD_BOOTEFI_HELLO=y # CONFIG_CMD_ELF is not set CONFIG_CMD_ASKENV=y CONFIG_CMD_GREPENV=y @@ -56,6 +57,7 @@ CONFIG_CMD_SNTP=y CONFIG_CMD_DNS=y CONFIG_CMD_LINK_LOCAL=y CONFIG_CMD_BMP=y +CONFIG_CMD_EFIDEBUG=y CONFIG_CMD_TIME=y CONFIG_CMD_TIMER=y CONFIG_CMD_SOUND=y diff --git a/doc/README.log b/doc/README.log index 19856d43da..1057981f45 100644 --- a/doc/README.log +++ b/doc/README.log @@ -147,7 +147,10 @@ several possible determinations for logging information, all of which can be enabled or disabled independently: console - goes to stdout + syslog - broadcast RFC 3164 messages to syslog servers on UDP port 514 +The syslog driver sends the value of environmental variable 'log_hostname' as +HOSTNAME if available. Log format ---------- diff --git a/doc/api/efi.rst b/doc/api/efi.rst index 631c0ceb1d..0667c3aef7 100644 --- a/doc/api/efi.rst +++ b/doc/api/efi.rst @@ -78,6 +78,12 @@ Memory services .. kernel-doc:: lib/efi_loader/efi_memory.c :internal: +SetWatchdogTimer service +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: lib/efi_loader/efi_watchdog.c + :internal: + Runtime services ---------------- @@ -151,3 +157,9 @@ Text IO protocols .. kernel-doc:: lib/efi_loader/efi_console.c :internal: + +Unicode Collation protocol +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: lib/efi_loader/efi_unicode_collation.c + :internal: diff --git a/doc/driver-model/design.rst b/doc/driver-model/design.rst index 5247ecc276..635effcef6 100644 --- a/doc/driver-model/design.rst +++ b/doc/driver-model/design.rst @@ -579,7 +579,7 @@ a USB bus with several devices attached to it, each from a different (made up) uclass:: xhci_usb (UCLASS_USB) - eth (UCLASS_ETHERNET) + eth (UCLASS_ETH) camera (UCLASS_CAMERA) flash (UCLASS_FLASH_STORAGE) @@ -683,11 +683,17 @@ probe/remove which is independent of bind/unbind. This is partly because in U-Boot it may be expensive to probe devices and we don't want to do it until they are needed, or perhaps until after relocation. -Activation/probe -^^^^^^^^^^^^^^^^ +Reading ofdata +^^^^^^^^^^^^^^ + +Most devices have data in the device tree which they can read to find out the +base address of hardware registers and parameters relating to driver +operation. This is called 'ofdata' (Open-Firmware data). -When a device needs to be used, U-Boot activates it, by following these -steps (see device_probe()): +The device's_ofdata_to_platdata() implemnents allocation and reading of +platdata. A parent's ofdata is always read before a child. + +The steps are: 1. If priv_auto_alloc_size is non-zero, then the device-private space is allocated for the device and zeroed. It will be accessible as @@ -713,32 +719,72 @@ steps (see device_probe()): space. The controller can hold information about the USB state of each of its children. - 5. All parent devices are probed. It is not possible to activate a device + 5. If the driver provides an ofdata_to_platdata() method, then this is + called to convert the device tree data into platform data. This should + do various calls like dev_read_u32(dev, ...) to access the node and store + the resulting information into dev->platdata. After this point, the device + works the same way whether it was bound using a device tree node or + U_BOOT_DEVICE() structure. In either case, the platform data is now stored + in the platdata structure. Typically you will use the + platdata_auto_alloc_size feature to specify the size of the platform data + structure, and U-Boot will automatically allocate and zero it for you before + entry to ofdata_to_platdata(). But if not, you can allocate it yourself in + ofdata_to_platdata(). Note that it is preferable to do all the device tree + decoding in ofdata_to_platdata() rather than in probe(). (Apart from the + ugliness of mixing configuration and run-time data, one day it is possible + that U-Boot will cache platform data for devices which are regularly + de/activated). + + 5. The device is marked 'platdata valid'. + +Note that ofdata reading is always done (for a child and all its parents) +before probing starts. Thus devices go through two distinct states when +probing: reading platform data and actually touching the hardware to bring +the device up. + +Having probing separate from ofdata-reading helps deal with of-platdata, where +the probe() method is common to both DT/of-platdata operation, but the +ofdata_to_platdata() method is implemented differently. + +Another case has come up where this separate is useful. Generation of ACPI +tables uses the of-platdata but does not want to probe the device. Probing +would cause U-Boot to violate one of its design principles, viz that it +should only probe devices that are used. For ACPI we want to generate a +table for each device, even if U-Boot does not use it. In fact it may not +even be possible to probe the device - e.g. an SD card which is not +present will cause an error on probe, yet we still must tell Linux about +the SD card connector in case it is used while Linux is running. + +It is important that the ofdata_to_platdata() method does not actually probe +the device itself. However there are cases where other devices must be probed +in the ofdata_to_platdata() method. An example is where a device requires a +GPIO for it to operate. To select a GPIO obviously requires that the GPIO +device is probed. This is OK when used by common, core devices such as GPIO, +clock, interrupts, reset and the like. + +If your device relies on its parent setting up a suitable address space, so +that dev_read_addr() works correctly, then make sure that the parent device +has its setup code in ofdata_to_platdata(). If it has it in the probe method, +then you cannot call dev_read_addr() from the child device's +ofdata_to_platdata() method. Move it to probe() instead. Buses like PCI can +fall afoul of this rule. + +Activation/probe +^^^^^^^^^^^^^^^^ + +When a device needs to be used, U-Boot activates it, by first reading ofdata +as above and then following these steps (see device_probe()): + + 1. All parent devices are probed. It is not possible to activate a device unless its predecessors (all the way up to the root device) are activated. This means (for example) that an I2C driver will require that its bus be activated. - 6. The device's sequence number is assigned, either the requested one + 2. The device's sequence number is assigned, either the requested one (assuming no conflicts) or the next available one if there is a conflict or nothing particular is requested. - 7. If the driver provides an ofdata_to_platdata() method, then this is - called to convert the device tree data into platform data. This should - do various calls like fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), ...) - to access the node and store the resulting information into dev->platdata. - After this point, the device works the same way whether it was bound - using a device tree node or U_BOOT_DEVICE() structure. In either case, - the platform data is now stored in the platdata structure. Typically you - will use the platdata_auto_alloc_size feature to specify the size of the - platform data structure, and U-Boot will automatically allocate and zero - it for you before entry to ofdata_to_platdata(). But if not, you can - allocate it yourself in ofdata_to_platdata(). Note that it is preferable - to do all the device tree decoding in ofdata_to_platdata() rather than - in probe(). (Apart from the ugliness of mixing configuration and run-time - data, one day it is possible that U-Boot will cache platform data for - devices which are regularly de/activated). - - 8. The device's probe() method is called. This should do anything that + 4. The device's probe() method is called. This should do anything that is required by the device to get it going. This could include checking that the hardware is actually present, setting up clocks for the hardware and setting up hardware registers to initial values. The code @@ -753,7 +799,7 @@ steps (see device_probe()): allocate the priv space here yourself. The same applies also to platdata_auto_alloc_size. Remember to free them in the remove() method. - 9. The device is marked 'activated' + 5. The device is marked 'activated' 10. The uclass's post_probe() method is called, if one exists. This may cause the uclass to do some housekeeping to record the device as diff --git a/doc/driver-model/pci-info.rst b/doc/driver-model/pci-info.rst index 3c1b1adf07..8b9faa1066 100644 --- a/doc/driver-model/pci-info.rst +++ b/doc/driver-model/pci-info.rst @@ -12,10 +12,10 @@ Bus number 0 will need to be requested first, and the alias in the device tree file will point to the correct device:: aliases { - pci0 = &pci; + pci0 = &pcic; }; - pci: pci-controller { + pcic: pci@0 { compatible = "sandbox,pci"; ... }; @@ -138,7 +138,7 @@ be scanned as a PCI device, causing confusion. When this bus is scanned we will end up with something like this:: - `- * pci-controller @ 05c660c8, 0 + `- * pci@0 @ 05c660c8, 0 `- pci@1f,0 @ 05c661c8, 63488 `- emul@1f,0 @ 05c662c8 @@ -152,7 +152,7 @@ host controller node for this functionality to work. .. code-block:: none - pci1: pci-controller1 { + pci1: pci@1 { compatible = "sandbox,pci"; ... sandbox,dev-info = <0x08 0x00 0x1234 0x5678 @@ -166,6 +166,6 @@ fourth cells are PCI vendor ID and device ID respectively. When this bus is scanned we will end up with something like this:: - pci [ + ] pci_sandbo |-- pci-controller1 + pci [ + ] pci_sandbo |-- pci1 pci_emul [ ] sandbox_sw | |-- sandbox_swap_case_emul pci_emul [ ] sandbox_sw | `-- sandbox_swap_case_emul diff --git a/doc/uefi/uefi.rst b/doc/uefi/uefi.rst index cfe2d84a4c..a35fbd331c 100644 --- a/doc/uefi/uefi.rst +++ b/doc/uefi/uefi.rst @@ -97,6 +97,83 @@ Below you find the output of an example session starting GRUB:: See doc/uImage.FIT/howto.txt for an introduction to FIT images. +Configuring UEFI secure boot +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +UEFI specification[1] defines a secure way of executing UEFI images +by verifying a signature (or message digest) of image with certificates. +This feature on U-Boot is enabled with:: + + CONFIG_UEFI_SECURE_BOOT=y + +To make the boot sequence safe, you need to establish a chain of trust; +In UEFI secure boot, you can make it with the UEFI variables, "PK" +(Platform Key), "KEK" (Key Exchange Keys), "db" (white list database) +and "dbx" (black list database). + +There are many online documents that describe what UEFI secure boot is +and how it works. Please consult some of them for details. + +Here is a simple example that you can follow for your initial attempt +(Please note that the actual steps would absolutely depend on your system +and environment.): + +1. Install utility commands on your host + * openssl + * efitools + * sbsigntool + +2. Create signing keys and key database files on your host + for PK:: + + $ openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ \ + -keyout PK.key -out PK.crt -nodes -days 365 + $ cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \ + PK.crt PK.esl; + $ sign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth + + for KEK:: + + $ openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ \ + -keyout KEK.key -out KEK.crt -nodes -days 365 + $ cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \ + KEK.crt KEK.esl + $ sign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth + + for db:: + + $ openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ \ + -keyout db.key -out db.crt -nodes -days 365 + $ cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \ + db.crt db.esl + $ sign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth + + Copy \*.auth to media, say mmc, that is accessible from U-Boot. + +3. Sign an image with one key in "db" on your host:: + + $ sbsign --key db.key --cert db.crt helloworld.efi + +4. Install keys on your board:: + + ==> fatload mmc 0:1 <tmpaddr> PK.auth + ==> setenv -e -nv -bs -rt -at -i <tmpaddr>,$filesize PK + ==> fatload mmc 0:1 <tmpaddr> KEK.auth + ==> setenv -e -nv -bs -rt -at -i <tmpaddr>,$filesize KEK + ==> fatload mmc 0:1 <tmpaddr> db.auth + ==> setenv -e -nv -bs -rt -at -i <tmpaddr>,$filesize db + +5. Set up boot parameters on your board:: + + ==> efidebug boot add 1 HELLO mmc 0:1 /helloworld.efi.signed "" + +Then your board runs that image from Boot manager (See below). +You can also try this sequence by running Pytest, test_efi_secboot, +on sandbox:: + + $ cd <U-Boot source directory> + $ pytest.py test/py/tests/test_efi_secboot/test_signed.py --bd sandbox + Executing the boot manager ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/drivers/clk/altera/clk-arria10.c b/drivers/clk/altera/clk-arria10.c index b7eed948a5..694a9427e1 100644 --- a/drivers/clk/altera/clk-arria10.c +++ b/drivers/clk/altera/clk-arria10.c @@ -258,7 +258,7 @@ static int socfpga_a10_clk_bind(struct udevice *dev) continue; if (pre_reloc_only && - !dm_ofnode_pre_reloc(offset_to_ofnode(offset))) + !ofnode_pre_reloc(offset_to_ofnode(offset))) continue; ret = device_bind_driver_to_node(dev, "clk-a10", name, diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 6b55ec59d6..f5808449a6 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -61,7 +61,7 @@ int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name) offset > 0; offset = fdt_next_subnode(fdt, offset)) { if (pre_reloc_only && - !dm_ofnode_pre_reloc(offset_to_ofnode(offset))) + !ofnode_pre_reloc(offset_to_ofnode(offset))) continue; /* * If this node has "compatible" property, this is not diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index ff5b28cb6a..efdb0f2905 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -10,6 +10,7 @@ #include <common.h> #include <errno.h> +#include <log.h> #include <malloc.h> #include <dm/device.h> #include <dm/device-internal.h> @@ -30,11 +31,14 @@ int device_chld_unbind(struct udevice *dev, struct driver *drv) continue; ret = device_unbind(pos); - if (ret && !saved_ret) + if (ret && !saved_ret) { + log_warning("device '%s' failed to unbind\n", + pos->name); saved_ret = ret; + } } - return saved_ret; + return log_ret(saved_ret); } int device_chld_remove(struct udevice *dev, struct driver *drv, @@ -63,13 +67,13 @@ int device_unbind(struct udevice *dev) int ret; if (!dev) - return -EINVAL; + return log_msg_ret("dev", -EINVAL); if (dev->flags & DM_FLAG_ACTIVATED) - return -EINVAL; + return log_msg_ret("active", -EINVAL); if (!(dev->flags & DM_FLAG_BOUND)) - return -EINVAL; + return log_msg_ret("not-bound", -EINVAL); drv = dev->driver; assert(drv); @@ -77,12 +81,12 @@ int device_unbind(struct udevice *dev) if (drv->unbind) { ret = drv->unbind(dev); if (ret) - return ret; + return log_msg_ret("unbind", ret); } ret = device_chld_unbind(dev, NULL); if (ret) - return ret; + return log_msg_ret("child unbind", ret); if (dev->flags & DM_FLAG_ALLOC_PDATA) { free(dev->platdata); @@ -98,7 +102,7 @@ int device_unbind(struct udevice *dev) } ret = uclass_unbind_device(dev); if (ret) - return ret; + return log_msg_ret("uc", ret); if (dev->parent) list_del(&dev->sibling_node); @@ -194,7 +198,8 @@ int device_remove(struct udevice *dev, uint flags) } } - if (!(drv->flags & + if (!(flags & DM_REMOVE_NO_PD) && + !(drv->flags & (DM_FLAG_DEFAULT_PD_CTRL_OFF | DM_FLAG_REMOVE_WITH_PD_ON)) && dev != gd->cur_serial_dev) dev_power_domain_off(dev); diff --git a/drivers/core/device.c b/drivers/core/device.c index 89ea820d48..0157bb1fe0 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -143,11 +143,9 @@ static int device_bind_common(struct udevice *parent, const struct driver *drv, goto fail_alloc3; } } - } - - /* put dev into parent's successor list */ - if (parent) + /* put dev into parent's successor list */ list_add_tail(&dev->sibling_node, &parent->child_head); + } ret = uclass_bind_device(dev); if (ret) @@ -323,6 +321,22 @@ int device_ofdata_to_platdata(struct udevice *dev) if (dev->flags & DM_FLAG_PLATDATA_VALID) return 0; + /* Ensure all parents have ofdata */ + if (dev->parent) { + ret = device_ofdata_to_platdata(dev->parent); + if (ret) + goto fail; + + /* + * The device might have already been probed during + * the call to device_probe() on its parent device + * (e.g. PCI bridge devices). Test the flags again + * so that we don't mess up the device. + */ + if (dev->flags & DM_FLAG_PLATDATA_VALID) + return 0; + } + drv = dev->driver; assert(drv); diff --git a/drivers/core/lists.c b/drivers/core/lists.c index 68204c303f..c7db14ed56 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -175,7 +175,7 @@ int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp, continue; if (pre_reloc_only) { - if (!dm_ofnode_pre_reloc(node) && + if (!ofnode_pre_reloc(node) && !(entry->flags & DM_FLAG_PRE_RELOC)) { log_debug("Skipping device pre-relocation\n"); return 0; diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c index acd745c121..c54baa140a 100644 --- a/drivers/core/of_access.c +++ b/drivers/core/of_access.c @@ -449,21 +449,7 @@ static void *of_find_property_value_of_size(const struct device_node *np, int of_read_u32(const struct device_node *np, const char *propname, u32 *outp) { - const __be32 *val; - - debug("%s: %s: ", __func__, propname); - if (!np) - return -EINVAL; - val = of_find_property_value_of_size(np, propname, sizeof(*outp)); - if (IS_ERR(val)) { - debug("(not found)\n"); - return PTR_ERR(val); - } - - *outp = be32_to_cpup(val); - debug("%#x (%d)\n", *outp, *outp); - - return 0; + return of_read_u32_index(np, propname, 0, outp); } int of_read_u32_array(const struct device_node *np, const char *propname, @@ -485,6 +471,28 @@ int of_read_u32_array(const struct device_node *np, const char *propname, return 0; } +int of_read_u32_index(const struct device_node *np, const char *propname, + int index, u32 *outp) +{ + const __be32 *val; + + debug("%s: %s: ", __func__, propname); + if (!np) + return -EINVAL; + + val = of_find_property_value_of_size(np, propname, + sizeof(*outp) * (index + 1)); + if (IS_ERR(val)) { + debug("(not found)\n"); + return PTR_ERR(val); + } + + *outp = be32_to_cpup(val + index); + debug("%#x (%d)\n", *outp, *outp); + + return 0; +} + int of_read_u64(const struct device_node *np, const char *propname, u64 *outp) { const __be64 *val; @@ -577,7 +585,7 @@ static int __of_parse_phandle_with_args(const struct device_node *np, { const __be32 *list, *list_end; int rc = 0, cur_index = 0; - uint32_t count = 0; + uint32_t count; struct device_node *node = NULL; phandle phandle; int size; diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 96a5dd20bd..b0be7cbe19 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -18,32 +18,53 @@ int ofnode_read_u32(ofnode node, const char *propname, u32 *outp) { + return ofnode_read_u32_index(node, propname, 0, outp); +} + +u32 ofnode_read_u32_default(ofnode node, const char *propname, u32 def) +{ + assert(ofnode_valid(node)); + ofnode_read_u32_index(node, propname, 0, &def); + + return def; +} + +int ofnode_read_u32_index(ofnode node, const char *propname, int index, + u32 *outp) +{ + const fdt32_t *cell; + int len; + assert(ofnode_valid(node)); debug("%s: %s: ", __func__, propname); - if (ofnode_is_np(node)) { - return of_read_u32(ofnode_to_np(node), propname, outp); - } else { - const fdt32_t *cell; - int len; + if (ofnode_is_np(node)) + return of_read_u32_index(ofnode_to_np(node), propname, index, + outp); - cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), - propname, &len); - if (!cell || len < sizeof(int)) { - debug("(not found)\n"); - return -EINVAL; - } - *outp = fdt32_to_cpu(cell[0]); + cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname, + &len); + if (!cell) { + debug("(not found)\n"); + return -EINVAL; + } + + if (len < (sizeof(int) * (index + 1))) { + debug("(not large enough)\n"); + return -EOVERFLOW; } + + *outp = fdt32_to_cpu(cell[index]); debug("%#x (%d)\n", *outp, *outp); return 0; } -u32 ofnode_read_u32_default(ofnode node, const char *propname, u32 def) +u32 ofnode_read_u32_index_default(ofnode node, const char *propname, int index, + u32 def) { assert(ofnode_valid(node)); - ofnode_read_u32(node, propname, &def); + ofnode_read_u32_index(node, propname, index, &def); return def; } diff --git a/drivers/core/read.c b/drivers/core/read.c index 1f999b1b31..ce78f09d28 100644 --- a/drivers/core/read.c +++ b/drivers/core/read.c @@ -22,6 +22,19 @@ int dev_read_u32_default(const struct udevice *dev, const char *propname, return ofnode_read_u32_default(dev_ofnode(dev), propname, def); } +int dev_read_u32_index(struct udevice *dev, const char *propname, int index, + u32 *outp) +{ + return ofnode_read_u32_index(dev_ofnode(dev), propname, index, outp); +} + +u32 dev_read_u32_index_default(struct udevice *dev, const char *propname, + int index, u32 def) +{ + return ofnode_read_u32_index_default(dev_ofnode(dev), propname, index, + def); +} + int dev_read_s32(const struct udevice *dev, const char *propname, s32 *outp) { return ofnode_read_u32(dev_ofnode(dev), propname, (u32 *)outp); diff --git a/drivers/core/root.c b/drivers/core/root.c index e85643819e..14df16c280 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -203,15 +203,6 @@ static int dm_scan_fdt_live(struct udevice *parent, int ret = 0, err; for (np = node_parent->child; np; np = np->sibling) { - /* "chosen" node isn't a device itself but may contain some: */ - if (!strcmp(np->name, "chosen")) { - pr_debug("parsing subnodes of \"chosen\"\n"); - - err = dm_scan_fdt_live(parent, np, pre_reloc_only); - if (err && !ret) - ret = err; - continue; - } if (!of_device_is_available(np)) { pr_debug(" - ignoring disabled device\n"); @@ -256,21 +247,6 @@ static int dm_scan_fdt_node(struct udevice *parent, const void *blob, offset = fdt_next_subnode(blob, offset)) { const char *node_name = fdt_get_name(blob, offset, NULL); - /* - * The "chosen" and "firmware" nodes aren't devices - * themselves but may contain some: - */ - if (!strcmp(node_name, "chosen") || - !strcmp(node_name, "firmware")) { - pr_debug("parsing subnodes of \"%s\"\n", node_name); - - err = dm_scan_fdt_node(parent, blob, offset, - pre_reloc_only); - if (err && !ret) - ret = err; - continue; - } - if (!fdtdec_get_is_enabled(blob, offset)) { pr_debug(" - ignoring disabled device\n"); continue; @@ -315,7 +291,8 @@ int dm_scan_fdt(const void *blob, bool pre_reloc_only) return dm_scan_fdt_node(gd->dm_root, blob, 0, pre_reloc_only); } -static int dm_scan_fdt_ofnode_path(const char *path, bool pre_reloc_only) +static int dm_scan_fdt_ofnode_path(const void *blob, const char *path, + bool pre_reloc_only) { ofnode node; @@ -327,13 +304,18 @@ static int dm_scan_fdt_ofnode_path(const char *path, bool pre_reloc_only) if (of_live_active()) return dm_scan_fdt_live(gd->dm_root, node.np, pre_reloc_only); #endif - return dm_scan_fdt_node(gd->dm_root, gd->fdt_blob, node.of_offset, + return dm_scan_fdt_node(gd->dm_root, blob, node.of_offset, pre_reloc_only); } int dm_extended_scan_fdt(const void *blob, bool pre_reloc_only) { - int ret; + int ret, i; + const char * const nodes[] = { + "/chosen", + "/clocks", + "/firmware" + }; ret = dm_scan_fdt(blob, pre_reloc_only); if (ret) { @@ -341,16 +323,16 @@ int dm_extended_scan_fdt(const void *blob, bool pre_reloc_only) return ret; } - ret = dm_scan_fdt_ofnode_path("/clocks", pre_reloc_only); - if (ret) { - debug("scan for /clocks failed: %d\n", ret); - return ret; + /* Some nodes aren't devices themselves but may contain some */ + for (i = 0; i < ARRAY_SIZE(nodes); i++) { + ret = dm_scan_fdt_ofnode_path(blob, nodes[i], pre_reloc_only); + if (ret) { + debug("dm_scan_fdt() scan for %s failed: %d\n", + nodes[i], ret); + return ret; + } } - ret = dm_scan_fdt_ofnode_path("/firmware", pre_reloc_only); - if (ret) - debug("scan for /firmware failed: %d\n", ret); - return ret; } #endif diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 58b19a4210..6849302936 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -118,12 +118,12 @@ int uclass_destroy(struct uclass *uc) while (!list_empty(&uc->dev_head)) { dev = list_first_entry(&uc->dev_head, struct udevice, uclass_node); - ret = device_remove(dev, DM_REMOVE_NORMAL); + ret = device_remove(dev, DM_REMOVE_NORMAL | DM_REMOVE_NO_PD); if (ret) - return ret; + return log_msg_ret("remove", ret); ret = device_unbind(dev); if (ret) - return ret; + return log_msg_ret("unbind", ret); } uc_drv = uc->uc_drv; diff --git a/drivers/core/util.c b/drivers/core/util.c index 69f83755f0..25b0d76f43 100644 --- a/drivers/core/util.c +++ b/drivers/core/util.c @@ -33,34 +33,6 @@ int list_count_items(struct list_head *head) return count; } -#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) -bool dm_ofnode_pre_reloc(ofnode node) -{ -#if defined(CONFIG_SPL_BUILD) || defined(CONFIG_TPL_BUILD) - /* for SPL and TPL the remaining nodes after the fdtgrep 1st pass - * had property dm-pre-reloc or u-boot,dm-spl/tpl. - * They are removed in final dtb (fdtgrep 2nd pass) - */ - return true; -#else - if (ofnode_read_bool(node, "u-boot,dm-pre-reloc")) - return true; - if (ofnode_read_bool(node, "u-boot,dm-pre-proper")) - return true; - - /* - * In regular builds individual spl and tpl handling both - * count as handled pre-relocation for later second init. - */ - if (ofnode_read_bool(node, "u-boot,dm-spl") || - ofnode_read_bool(node, "u-boot,dm-tpl")) - return true; - - return false; -#endif -} -#endif - #if !CONFIG_IS_ENABLED(OF_PLATDATA) int pci_get_devfn(struct udevice *dev) { diff --git a/drivers/crypto/fsl/fsl_rsa.c b/drivers/crypto/fsl/fsl_rsa.c index c5c76e319f..0cb3c6b5f2 100644 --- a/drivers/crypto/fsl/fsl_rsa.c +++ b/drivers/crypto/fsl/fsl_rsa.c @@ -52,7 +52,6 @@ U_BOOT_DRIVER(fsl_rsa_mod_exp) = { .name = "fsl_rsa_mod_exp", .id = UCLASS_MOD_EXP, .ops = &fsl_mod_exp_ops, - .flags = DM_FLAG_PRE_RELOC, }; U_BOOT_DEVICE(fsl_rsa) = { diff --git a/drivers/crypto/rsa_mod_exp/mod_exp_sw.c b/drivers/crypto/rsa_mod_exp/mod_exp_sw.c index 46b9f1825c..c9b571a461 100644 --- a/drivers/crypto/rsa_mod_exp/mod_exp_sw.c +++ b/drivers/crypto/rsa_mod_exp/mod_exp_sw.c @@ -31,7 +31,6 @@ U_BOOT_DRIVER(mod_exp_sw) = { .name = "mod_exp_sw", .id = UCLASS_MOD_EXP, .ops = &mod_exp_ops_sw, - .flags = DM_FLAG_PRE_RELOC, }; U_BOOT_DEVICE(mod_exp_sw) = { diff --git a/drivers/misc/p2sb_emul.c b/drivers/misc/p2sb_emul.c index a6ee9a235e..02f7a7ea67 100644 --- a/drivers/misc/p2sb_emul.c +++ b/drivers/misc/p2sb_emul.c @@ -215,7 +215,7 @@ static int sandbox_p2sb_emul_map_physmem(struct udevice *dev, void **ptrp) { struct p2sb_emul_priv *priv = dev_get_priv(dev); - struct udevice *child; + struct udevice *child = NULL; /* Silence compiler warning */ unsigned int offset; int barnum; int ret; diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index 30f9b8c939..7703c67492 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -162,6 +162,7 @@ int serial_init(void) #if CONFIG_IS_ENABLED(SERIAL_PRESENT) serial_find_console_or_panic(); gd->flags |= GD_FLG_SERIAL_READY; + serial_setbrg(); #endif return 0; diff --git a/include/dm/device.h b/include/dm/device.h index 35e19d8700..975eec5d0e 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -80,18 +80,21 @@ struct driver_info; */ enum { /* Normal remove, remove all devices */ - DM_REMOVE_NORMAL = 1 << 0, + DM_REMOVE_NORMAL = 1 << 0, /* Remove devices with active DMA */ - DM_REMOVE_ACTIVE_DMA = DM_FLAG_ACTIVE_DMA, + DM_REMOVE_ACTIVE_DMA = DM_FLAG_ACTIVE_DMA, /* Remove devices which need some final OS preparation steps */ - DM_REMOVE_OS_PREPARE = DM_FLAG_OS_PREPARE, + DM_REMOVE_OS_PREPARE = DM_FLAG_OS_PREPARE, /* Add more use cases here */ /* Remove devices with any active flag */ - DM_REMOVE_ACTIVE_ALL = DM_REMOVE_ACTIVE_DMA | DM_REMOVE_OS_PREPARE, + DM_REMOVE_ACTIVE_ALL = DM_REMOVE_ACTIVE_DMA | DM_REMOVE_OS_PREPARE, + + /* Don't power down any attached power domains */ + DM_REMOVE_NO_PD = 1 << 1, }; /** diff --git a/include/dm/of_access.h b/include/dm/of_access.h index 13fedb7cf5..92876b3ecb 100644 --- a/include/dm/of_access.h +++ b/include/dm/of_access.h @@ -235,6 +235,25 @@ struct device_node *of_find_node_by_phandle(phandle handle); int of_read_u32(const struct device_node *np, const char *propname, u32 *outp); /** + * of_read_u32_index() - Find and read a 32-bit value from a multi-value + * property + * + * Search for a property in a device node and read a 32-bit value from + * it. + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @index: index of the u32 in the list of values + * @outp: pointer to return value, modified only if return value is 0. + * + * @return 0 on success, -EINVAL if the property does not exist, + * -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + */ +int of_read_u32_index(const struct device_node *np, const char *propname, + int index, u32 *outp); + +/** * of_read_u64() - Find and read a 64-bit integer from a property * * Search for a property in a device node and read a 64-bit value from diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index b5a50e8849..ce5e366c06 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -203,6 +203,18 @@ static inline ofnode ofnode_null(void) int ofnode_read_u32(ofnode node, const char *propname, u32 *outp); /** + * ofnode_read_u32_index() - Read a 32-bit integer from a multi-value property + * + * @ref: valid node reference to read property from + * @propname: name of the property to read from + * @index: index of the integer to return + * @outp: place to put value (if found) + * @return 0 if OK, -ve on error + */ +int ofnode_read_u32_index(ofnode node, const char *propname, int index, + u32 *outp); + +/** * ofnode_read_s32() - Read a 32-bit integer from a property * * @ref: valid node reference to read property from @@ -227,6 +239,19 @@ static inline int ofnode_read_s32(ofnode node, const char *propname, u32 ofnode_read_u32_default(ofnode ref, const char *propname, u32 def); /** + * ofnode_read_u32_index_default() - Read a 32-bit integer from a multi-value + * property + * + * @ref: valid node reference to read property from + * @propname: name of the property to read from + * @index: index of the integer to return + * @def: default value to return if the property has no value + * @return property value, or @def if not found + */ +u32 ofnode_read_u32_index_default(ofnode ref, const char *propname, int index, + u32 def); + +/** * ofnode_read_s32_default() - Read a 32-bit integer from a property * * @ref: valid node reference to read property from diff --git a/include/dm/read.h b/include/dm/read.h index da8c7f25e7..77d3bc8db5 100644 --- a/include/dm/read.h +++ b/include/dm/read.h @@ -67,6 +67,32 @@ int dev_read_u32_default(const struct udevice *dev, const char *propname, int def); /** + * dev_read_u32_index() - read an indexed 32-bit integer from a device's DT + * property + * + * @dev: device to read DT property from + * @propname: name of the property to read from + * @index: index of the integer to return + * @outp: place to put value (if found) + * @return 0 if OK, -ve on error + */ +int dev_read_u32_index(struct udevice *dev, const char *propname, int index, + u32 *outp); + +/** + * dev_read_u32_index_default() - read an indexed 32-bit integer from a device's + * DT property + * + * @dev: device to read DT property from + * @propname: name of the property to read from + * @index: index of the integer to return + * @def: default value to return if the property has no value + * @return property value, or @def if not found + */ +u32 dev_read_u32_index_default(struct udevice *dev, const char *propname, + int index, u32 def); + +/** * dev_read_s32() - read a signed 32-bit integer from a device's DT property * * @dev: device to read DT property from @@ -621,6 +647,20 @@ static inline int dev_read_u32_default(const struct udevice *dev, return ofnode_read_u32_default(dev_ofnode(dev), propname, def); } +static inline int dev_read_u32_index(struct udevice *dev, + const char *propname, int index, u32 *outp) +{ + return ofnode_read_u32_index(dev_ofnode(dev), propname, index, outp); +} + +static inline u32 dev_read_u32_index_default(struct udevice *dev, + const char *propname, int index, + u32 def) +{ + return ofnode_read_u32_index_default(dev_ofnode(dev), propname, index, + def); +} + static inline int dev_read_s32(const struct udevice *dev, const char *propname, s32 *outp) { diff --git a/include/dm/util.h b/include/dm/util.h index 0ccb3fbadf..23f8deb14e 100644 --- a/include/dm/util.h +++ b/include/dm/util.h @@ -42,31 +42,4 @@ static inline void dm_dump_devres(void) /* Dump out a list of drivers */ void dm_dump_drivers(void); -/** - * Check if an of node should be or was bound before relocation. - * - * Devicetree nodes can be marked as needed to be bound - * in the loader stages via special devicetree properties. - * - * Before relocation this function can be used to check if nodes - * are required in either SPL or TPL stages. - * - * After relocation and jumping into the real U-Boot binary - * it is possible to determine if a node was bound in one of - * SPL/TPL stages. - * - * There are 4 settings currently in use - * - u-boot,dm-pre-proper: U-Boot proper pre-relocation only - * - u-boot,dm-pre-reloc: legacy and indicates any of TPL or SPL - * Existing platforms only use it to indicate nodes needed in - * SPL. Should probably be replaced by u-boot,dm-spl for - * existing platforms. - * - u-boot,dm-spl: SPL and U-Boot pre-relocation - * - u-boot,dm-tpl: TPL and U-Boot pre-relocation - * @node: of node - * - * Returns true if node is needed in SPL/TL, false otherwise. - */ -bool dm_ofnode_pre_reloc(ofnode node); - #endif diff --git a/include/efi_api.h b/include/efi_api.h index 1c40ffc4f5..77d6bf2660 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -18,6 +18,7 @@ #include <efi.h> #include <charset.h> +#include <pe.h> #ifdef CONFIG_EFI_LOADER #include <asm/setjmp.h> @@ -329,6 +330,10 @@ struct efi_runtime_services { EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, \ 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c) +#define EFI_IMAGE_SECURITY_DATABASE_GUID \ + EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, \ + 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) + #define EFI_FDT_GUID \ EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, \ 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0) @@ -1682,4 +1687,86 @@ struct efi_load_file_protocol { #define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MIN 0x00001000 #define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MAX 0x00004000 +/* Certificate types in signature database */ +#define EFI_CERT_SHA256_GUID \ + EFI_GUID(0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, \ + 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28) +#define EFI_CERT_RSA2048_GUID \ + EFI_GUID(0x3c5766e8, 0x269c, 0x4e34, 0xaa, 0x14, \ + 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6) +#define EFI_CERT_X509_GUID \ + EFI_GUID(0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, \ + 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72) +#define EFI_CERT_X509_SHA256_GUID \ + EFI_GUID(0x3bd2a492, 0x96c0, 0x4079, 0xb4, 0x20, \ + 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed) +#define EFI_CERT_TYPE_PKCS7_GUID \ + EFI_GUID(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, \ + 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7) + +/** + * win_certificate_uefi_guid - A certificate that encapsulates + * a GUID-specific signature + * + * @hdr: Windows certificate header + * @cert_type: Certificate type + * @cert_data: Certificate data + */ +struct win_certificate_uefi_guid { + WIN_CERTIFICATE hdr; + efi_guid_t cert_type; + u8 cert_data[]; +} __attribute__((__packed__)); + +/** + * efi_variable_authentication_2 - A time-based authentication method + * descriptor + * + * This structure describes an authentication information for + * a variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS + * and should be included as part of a variable's value. + * Only EFI_CERT_TYPE_PKCS7_GUID is accepted. + * + * @time_stamp: Descriptor's time stamp + * @auth_info: Authentication info + */ +struct efi_variable_authentication_2 { + struct efi_time time_stamp; + struct win_certificate_uefi_guid auth_info; +} __attribute__((__packed__)); + +/** + * efi_signature_data - A format of signature + * + * This structure describes a single signature in signature database. + * + * @signature_owner: Signature owner + * @signature_data: Signature data + */ +struct efi_signature_data { + efi_guid_t signature_owner; + u8 signature_data[]; +} __attribute__((__packed__)); + +/** + * efi_signature_list - A format of signature database + * + * This structure describes a list of signatures with the same type. + * An authenticated variable's value is a concatenation of one or more + * efi_signature_list's. + * + * @signature_type: Signature type + * @signature_list_size: Size of signature list + * @signature_header_size: Size of signature header + * @signature_size: Size of signature + */ +struct efi_signature_list { + efi_guid_t signature_type; + u32 signature_list_size; + u32 signature_header_size; + u32 signature_size; +/* u8 signature_header[signature_header_size]; */ +/* struct efi_signature_data signatures[...][signature_size]; */ +} __attribute__((__packed__)); + #endif diff --git a/include/efi_loader.h b/include/efi_loader.h index 3f2792892f..0ba9a1f702 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -11,6 +11,7 @@ #include <common.h> #include <part_efi.h> #include <efi_api.h> +#include <pe.h> static inline int guidcmp(const void *g1, const void *g2) { @@ -26,6 +27,7 @@ static inline void *guidcpy(void *dst, const void *src) #if CONFIG_IS_ENABLED(EFI_LOADER) #include <linux/list.h> +#include <linux/oid_registry.h> /* Maximum number of configuration tables */ #define EFI_MAX_CONFIGURATION_TABLES 16 @@ -178,6 +180,12 @@ extern const efi_guid_t efi_guid_hii_config_routing_protocol; 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; +/* GUIDs for authentication */ +extern const efi_guid_t efi_guid_image_security_database; +extern const efi_guid_t efi_guid_sha256; +extern const efi_guid_t efi_guid_cert_x509; +extern const efi_guid_t efi_guid_cert_x509_sha256; +extern const efi_guid_t efi_guid_cert_type_pkcs7; /* GUID of RNG protocol */ extern const efi_guid_t efi_guid_rng_protocol; @@ -256,6 +264,11 @@ struct efi_object { enum efi_object_type type; }; +enum efi_image_auth_status { + EFI_IMAGE_AUTH_FAILED = 0, + EFI_IMAGE_AUTH_PASSED, +}; + /** * struct efi_loaded_image_obj - handle of a loaded image * @@ -275,6 +288,7 @@ struct efi_loaded_image_obj { EFIAPI efi_status_t (*entry)(efi_handle_t image_handle, struct efi_system_table *st); u16 image_type; + enum efi_image_auth_status auth_status; }; /** @@ -408,7 +422,8 @@ efi_status_t efi_set_watchdog(unsigned long timeout); /* Called from places to check whether a timer expired */ void efi_timer_check(void); /* PE loader implementation */ -efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, +efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, + void *efi, size_t efi_size, struct efi_loaded_image *loaded_image_info); /* Called once to store the pristine gd pointer */ void efi_save_gd(void); @@ -680,6 +695,80 @@ void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data); unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data); efi_status_t efi_bootmgr_load(efi_handle_t *handle); +#ifdef CONFIG_EFI_SECURE_BOOT +#include <image.h> + +/** + * efi_image_regions - A list of memory regions + * + * @max: Maximum number of regions + * @num: Number of regions + * @reg: array of regions + */ +struct efi_image_regions { + int max; + int num; + struct image_region reg[]; +}; + +/** + * efi_sig_data - A decoded data of struct efi_signature_data + * + * This structure represents an internal form of signature in + * signature database. A listed list may represent a signature list. + * + * @next: Pointer to next entry + * @onwer: Signature owner + * @data: Pointer to signature data + * @size: Size of signature data + */ +struct efi_sig_data { + struct efi_sig_data *next; + efi_guid_t owner; + void *data; + size_t size; +}; + +/** + * efi_signature_store - A decoded data of signature database + * + * This structure represents an internal form of signature database. + * + * @next: Pointer to next entry + * @sig_type: Signature type + * @sig_data_list: Pointer to signature list + */ +struct efi_signature_store { + struct efi_signature_store *next; + efi_guid_t sig_type; + struct efi_sig_data *sig_data_list; +}; + +struct x509_certificate; +struct pkcs7_message; + +bool efi_signature_verify_cert(struct x509_certificate *cert, + struct efi_signature_store *dbx); +bool efi_signature_verify_signers(struct pkcs7_message *msg, + struct efi_signature_store *dbx); +bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs, + struct pkcs7_message *msg, + struct efi_signature_store *db, + struct x509_certificate **cert); + +efi_status_t efi_image_region_add(struct efi_image_regions *regs, + const void *start, const void *end, + int nocheck); + +void efi_sigstore_free(struct efi_signature_store *sigstore); +struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name); + +bool efi_secure_boot_enabled(void); + +bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp, + WIN_CERTIFICATE **auth, size_t *auth_len); +#endif /* CONFIG_EFI_SECURE_BOOT */ + #else /* CONFIG_IS_ENABLED(EFI_LOADER) */ /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */ diff --git a/include/log.h b/include/log.h index fbcbd42fb4..cf32351134 100644 --- a/include/log.h +++ b/include/log.h @@ -117,11 +117,11 @@ static inline int _log_nop(enum log_category_t cat, enum log_level_t level, #define log_io(_fmt...) log(LOG_CATEGORY, LOGL_DEBUG_IO, ##_fmt) #else #define _LOG_MAX_LEVEL LOGL_INFO -#define log_err(_fmt...) log_nop(LOG_CATEGORY, LOGL_ERR, ##_fmt) -#define log_warning(_fmt...) log_nop(LOG_CATEGORY, LOGL_WARNING, ##_fmt) -#define log_notice(_fmt...) log_nop(LOG_CATEGORY, LOGL_NOTICE, ##_fmt) -#define log_info(_fmt...) log_nop(LOG_CATEGORY, LOGL_INFO, ##_fmt) -#define log_debug(_fmt...) log_nop(LOG_CATEGORY, LOGL_DEBUG, ##_fmt) +#define log_err(_fmt, ...) printf(_fmt, ##__VA_ARGS__) +#define log_warning(_fmt, ...) printf(_fmt, ##__VA_ARGS__) +#define log_notice(_fmt, ...) printf(_fmt, ##__VA_ARGS__) +#define log_info(_fmt, ...) printf(_fmt, ##__VA_ARGS__) +#define log_debug(_fmt, ...) debug(_fmt, ##__VA_ARGS__) #define log_content(_fmt...) log_nop(LOG_CATEGORY, \ LOGL_DEBUG_CONTENT, ##_fmt) #define log_io(_fmt...) log_nop(LOG_CATEGORY, LOGL_DEBUG_IO, ##_fmt) diff --git a/include/test/log.h b/include/test/log.h new file mode 100644 index 0000000000..c661cde75a --- /dev/null +++ b/include/test/log.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Tests for logging functions + */ + +#ifndef __TEST_LOG_H__ +#define __TEST_LOG_H__ + +#include <test/test.h> + +/* Declare a new logging test */ +#define LOG_TEST(_name) UNIT_TEST(_name, 0, log_test) + +#endif /* __TEST_LOG_H__ */ diff --git a/include/test/suites.h b/include/test/suites.h index 0748185eaf..39ad81a90f 100644 --- a/include/test/suites.h +++ b/include/test/suites.h @@ -30,6 +30,7 @@ int do_ut_compression(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]); int do_ut_dm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); int do_ut_env(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); int do_ut_lib(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); +int do_ut_log(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); int do_ut_optee(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); int do_ut_overlay(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); int do_ut_time(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); diff --git a/include/test/ut.h b/include/test/ut.h index 39d15953ed..b05d719ed0 100644 --- a/include/test/ut.h +++ b/include/test/ut.h @@ -105,6 +105,22 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); } \ } +/* Assert that two 64 int expressions are equal */ +#define ut_asserteq_64(expr1, expr2) { \ + u64 _val1 = (expr1), _val2 = (expr2); \ + \ + if (_val1 != _val2) { \ + ut_failf(uts, __FILE__, __LINE__, __func__, \ + #expr1 " == " #expr2, \ + "Expected %#llx (%lld), got %#llx (%lld)", \ + (unsigned long long)_val1, \ + (unsigned long long)_val1, \ + (unsigned long long)_val2, \ + (unsigned long long)_val2); \ + return CMD_RET_FAILURE; \ + } \ +} + /* Assert that two string expressions are equal */ #define ut_asserteq_str(expr1, expr2) { \ const char *_val1 = (expr1), *_val2 = (expr2); \ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 9890144d41..1cfa24ffcf 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -126,6 +126,7 @@ config EFI_GRUB_ARM32_WORKAROUND config EFI_RNG_PROTOCOL bool "EFI_RNG_PROTOCOL support" depends on DM_RNG + default y help Provide a EFI_RNG_PROTOCOL implementation using the hardware random number generator of the platform. @@ -145,4 +146,22 @@ config EFI_INITRD_FILESPEC help Full path of the initramfs file, e.g. mmc 0:2 initramfs.cpio.gz. +config EFI_SECURE_BOOT + bool "Enable EFI secure boot support" + depends on EFI_LOADER + select SHA256 + select RSA + select RSA_VERIFY_WITH_PKEY + select IMAGE_SIGN_INFO + select ASYMMETRIC_KEY_TYPE + select ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select X509_CERTIFICATE_PARSER + select PKCS7_MESSAGE_PARSER + default n + help + Select this option to enable EFI secure boot support. + Once SecureBoot mode is enforced, any EFI binary can run only if + it is signed with a trusted key. To do that, you need to install, + at least, PK, KEK and db. + endif diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 9b3b704473..eff3c25ec3 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -44,3 +44,4 @@ obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_rng.o obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_load_initrd.o +obj-y += efi_signature.o diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 3b79a88a48..a3f11eaf62 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1882,12 +1882,12 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy, efi_dp_split_file_path(file_path, &dp, &fp); ret = efi_setup_loaded_image(dp, fp, image_obj, &info); if (ret == EFI_SUCCESS) - ret = efi_load_pe(*image_obj, dest_buffer, info); + ret = efi_load_pe(*image_obj, dest_buffer, source_size, info); if (!source_buffer) /* Release buffer to which file was loaded */ efi_free_pages((uintptr_t)dest_buffer, efi_size_in_pages(source_size)); - if (ret == EFI_SUCCESS) { + if (ret == EFI_SUCCESS || ret == EFI_SECURITY_VIOLATION) { info->system_table = &systab; info->parent_handle = parent_image; } else { @@ -2885,10 +2885,16 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data); + if (!efi_search_obj(image_handle)) + return EFI_EXIT(EFI_INVALID_PARAMETER); + /* Check parameters */ if (image_obj->header.type != EFI_OBJECT_TYPE_LOADED_IMAGE) return EFI_EXIT(EFI_INVALID_PARAMETER); + if (image_obj->auth_status != EFI_IMAGE_AUTH_PASSED) + return EFI_EXIT(EFI_SECURITY_VIOLATION); + ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image, &info, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL)); diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index fc0682bc48..fd8fe17567 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -108,6 +108,21 @@ static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this, return EFI_SUCCESS; } +/** + * efi_disk_read_blocks() - reads blocks from device + * + * This function implements the ReadBlocks service of the EFI_BLOCK_IO_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @this: pointer to the BLOCK_IO_PROTOCOL + * @media_id: id of the medium to be read from + * @lba: starting logical block for reading + * @buffer_size: size of the read buffer + * @buffer: pointer to the destination buffer + * Return: status code + */ static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this, u32 media_id, u64 lba, efi_uintn_t buffer_size, void *buffer) @@ -157,6 +172,22 @@ static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this, return EFI_EXIT(r); } +/** + * efi_disk_write_blocks() - writes blocks to device + * + * This function implements the WriteBlocks service of the + * EFI_BLOCK_IO_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @this: pointer to the BLOCK_IO_PROTOCOL + * @media_id: id of the medium to be written to + * @lba: starting logical block for writing + * @buffer_size: size of the write buffer + * @buffer: pointer to the source buffer + * Return: status code + */ static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this, u32 media_id, u64 lba, efi_uintn_t buffer_size, void *buffer) @@ -208,9 +239,22 @@ static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this, return EFI_EXIT(r); } +/** + * efi_disk_flush_blocks() - flushes modified data to the device + * + * This function implements the FlushBlocks service of the + * EFI_BLOCK_IO_PROTOCOL. + * + * As we always write synchronously nothing is done here. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @this: pointer to the BLOCK_IO_PROTOCOL + * Return: status code + */ static efi_status_t EFIAPI efi_disk_flush_blocks(struct efi_block_io *this) { - /* We always write synchronously */ EFI_ENTRY("%p", this); return EFI_EXIT(EFI_SUCCESS); } @@ -286,7 +330,7 @@ static int efi_fs_exists(struct blk_desc *desc, int part) return 1; } -/* +/** * efi_disk_add_dev() - create a handle for a partition or disk * * @parent: parent handle @@ -295,6 +339,8 @@ static int efi_fs_exists(struct blk_desc *desc, int part) * @desc: internal block device * @dev_index: device index for block device * @offset: offset into disk for simple partitions + * @part: partition + * @disk: pointer to receive the created handle * Return: disk object */ static efi_status_t efi_disk_add_dev( @@ -381,7 +427,7 @@ static efi_status_t efi_disk_add_dev( * Create handles and protocols for the partitions of a block device. * * @parent: handle of the parent disk - * @blk_desc: block device + * @desc: block device * @if_typename: interface type * @diskid: device number * @pdevname: device name diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index d5de6df16d..6c270ce94f 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -10,7 +10,10 @@ #include <common.h> #include <cpu_func.h> #include <efi_loader.h> +#include <malloc.h> #include <pe.h> +#include <sort.h> +#include "../lib/crypto/pkcs7_parser.h" const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID; const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID; @@ -206,6 +209,386 @@ static void efi_set_code_and_data_type( } } +#ifdef CONFIG_EFI_SECURE_BOOT +/** + * cmp_pe_section - compare two sections + * @arg1: Pointer to pointer to first section + * @arg2: Pointer to pointer to second section + * + * Compare two sections in PE image. + * + * Return: -1, 0, 1 respectively if arg1 < arg2, arg1 == arg2 or + * arg1 > arg2 + */ +static int cmp_pe_section(const void *arg1, const void *arg2) +{ + const IMAGE_SECTION_HEADER *section1, *section2; + + section1 = *((const IMAGE_SECTION_HEADER **)arg1); + section2 = *((const IMAGE_SECTION_HEADER **)arg2); + + if (section1->VirtualAddress < section2->VirtualAddress) + return -1; + else if (section1->VirtualAddress == section2->VirtualAddress) + return 0; + else + return 1; +} + +/** + * efi_image_parse - parse a PE image + * @efi: Pointer to image + * @len: Size of @efi + * @regp: Pointer to a list of regions + * @auth: Pointer to a pointer to authentication data in PE + * @auth_len: Size of @auth + * + * Parse image binary in PE32(+) format, assuming that sanity of PE image + * has been checked by a caller. + * On success, an address of authentication data in @efi and its size will + * be returned in @auth and @auth_len, respectively. + * + * Return: true on success, false on error + */ +bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp, + WIN_CERTIFICATE **auth, size_t *auth_len) +{ + struct efi_image_regions *regs; + IMAGE_DOS_HEADER *dos; + IMAGE_NT_HEADERS32 *nt; + IMAGE_SECTION_HEADER *sections, **sorted; + int num_regions, num_sections, i; + int ctidx = IMAGE_DIRECTORY_ENTRY_SECURITY; + u32 align, size, authsz, authoff; + size_t bytes_hashed; + + dos = (void *)efi; + nt = (void *)(efi + dos->e_lfanew); + + /* + * Count maximum number of regions to be digested. + * We don't have to have an exact number here. + * See efi_image_region_add()'s in parsing below. + */ + num_regions = 3; /* for header */ + num_regions += nt->FileHeader.NumberOfSections; + num_regions++; /* for extra */ + + regs = calloc(sizeof(*regs) + sizeof(struct image_region) * num_regions, + 1); + if (!regs) + goto err; + regs->max = num_regions; + + /* + * Collect data regions for hash calculation + * 1. File headers + */ + if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + IMAGE_NT_HEADERS64 *nt64 = (void *)nt; + IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader; + + /* Skip CheckSum */ + efi_image_region_add(regs, efi, &opt->CheckSum, 0); + if (nt64->OptionalHeader.NumberOfRvaAndSizes <= ctidx) { + efi_image_region_add(regs, + &opt->CheckSum + 1, + efi + opt->SizeOfHeaders, 0); + } else { + /* Skip Certificates Table */ + efi_image_region_add(regs, + &opt->CheckSum + 1, + &opt->DataDirectory[ctidx], 0); + efi_image_region_add(regs, + &opt->DataDirectory[ctidx] + 1, + efi + opt->SizeOfHeaders, 0); + } + + bytes_hashed = opt->SizeOfHeaders; + align = opt->FileAlignment; + authoff = opt->DataDirectory[ctidx].VirtualAddress; + authsz = opt->DataDirectory[ctidx].Size; + } else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader; + + efi_image_region_add(regs, efi, &opt->CheckSum, 0); + efi_image_region_add(regs, &opt->CheckSum + 1, + &opt->DataDirectory[ctidx], 0); + efi_image_region_add(regs, &opt->DataDirectory[ctidx] + 1, + efi + opt->SizeOfHeaders, 0); + + bytes_hashed = opt->SizeOfHeaders; + align = opt->FileAlignment; + authoff = opt->DataDirectory[ctidx].VirtualAddress; + authsz = opt->DataDirectory[ctidx].Size; + } else { + debug("%s: Invalid optional header magic %x\n", __func__, + nt->OptionalHeader.Magic); + goto err; + } + + /* 2. Sections */ + num_sections = nt->FileHeader.NumberOfSections; + sections = (void *)((uint8_t *)&nt->OptionalHeader + + nt->FileHeader.SizeOfOptionalHeader); + sorted = calloc(sizeof(IMAGE_SECTION_HEADER *), num_sections); + if (!sorted) { + debug("%s: Out of memory\n", __func__); + goto err; + } + + /* + * Make sure the section list is in ascending order. + */ + for (i = 0; i < num_sections; i++) + sorted[i] = §ions[i]; + qsort(sorted, num_sections, sizeof(sorted[0]), cmp_pe_section); + + for (i = 0; i < num_sections; i++) { + if (!sorted[i]->SizeOfRawData) + continue; + + size = (sorted[i]->SizeOfRawData + align - 1) & ~(align - 1); + efi_image_region_add(regs, efi + sorted[i]->PointerToRawData, + efi + sorted[i]->PointerToRawData + size, + 0); + debug("section[%d](%s): raw: 0x%x-0x%x, virt: %x-%x\n", + i, sorted[i]->Name, + sorted[i]->PointerToRawData, + sorted[i]->PointerToRawData + size, + sorted[i]->VirtualAddress, + sorted[i]->VirtualAddress + + sorted[i]->Misc.VirtualSize); + + bytes_hashed += size; + } + free(sorted); + + /* 3. Extra data excluding Certificates Table */ + if (bytes_hashed + authsz < len) { + debug("extra data for hash: %lu\n", + len - (bytes_hashed + authsz)); + efi_image_region_add(regs, efi + bytes_hashed, + efi + len - authsz, 0); + } + + /* Return Certificates Table */ + if (authsz) { + if (len < authoff + authsz) { + debug("%s: Size for auth too large: %u >= %zu\n", + __func__, authsz, len - authoff); + goto err; + } + if (authsz < sizeof(*auth)) { + debug("%s: Size for auth too small: %u < %zu\n", + __func__, authsz, sizeof(*auth)); + goto err; + } + *auth = efi + authoff; + *auth_len = authsz; + debug("WIN_CERTIFICATE: 0x%x, size: 0x%x\n", authoff, authsz); + } else { + *auth = NULL; + *auth_len = 0; + } + + *regp = regs; + + return true; + +err: + free(regs); + + return false; +} + +/** + * efi_image_unsigned_authenticate - authenticate unsigned image with + * SHA256 hash + * @regs: List of regions to be verified + * + * If an image is not signed, it doesn't have a signature. In this case, + * its message digest is calculated and it will be compared with one of + * hash values stored in signature databases. + * + * Return: true if authenticated, false if not + */ +static bool efi_image_unsigned_authenticate(struct efi_image_regions *regs) +{ + struct efi_signature_store *db = NULL, *dbx = NULL; + bool ret = false; + + dbx = efi_sigstore_parse_sigdb(L"dbx"); + if (!dbx) { + debug("Getting signature database(dbx) failed\n"); + goto out; + } + + db = efi_sigstore_parse_sigdb(L"db"); + if (!db) { + debug("Getting signature database(db) failed\n"); + goto out; + } + + /* try black-list first */ + if (efi_signature_verify_with_sigdb(regs, NULL, dbx, NULL)) { + debug("Image is not signed and rejected by \"dbx\"\n"); + goto out; + } + + /* try white-list */ + if (efi_signature_verify_with_sigdb(regs, NULL, db, NULL)) + ret = true; + else + debug("Image is not signed and not found in \"db\" or \"dbx\"\n"); + +out: + efi_sigstore_free(db); + efi_sigstore_free(dbx); + + return ret; +} + +/** + * efi_image_authenticate - verify a signature of signed image + * @efi: Pointer to image + * @efi_size: Size of @efi + * + * A signed image should have its signature stored in a table of its PE header. + * So if an image is signed and only if if its signature is verified using + * signature databases, an image is authenticated. + * If an image is not signed, its validity is checked by using + * efi_image_unsigned_authenticated(). + * TODO: + * When AuditMode==0, if the image's signature is not found in + * the authorized database, or is found in the forbidden database, + * the image will not be started and instead, information about it + * will be placed in this table. + * When AuditMode==1, an EFI_IMAGE_EXECUTION_INFO element is created + * in the EFI_IMAGE_EXECUTION_INFO_TABLE for every certificate found + * in the certificate table of every image that is validated. + * + * Return: true if authenticated, false if not + */ +static bool efi_image_authenticate(void *efi, size_t efi_size) +{ + struct efi_image_regions *regs = NULL; + WIN_CERTIFICATE *wincerts = NULL, *wincert; + size_t wincerts_len; + struct pkcs7_message *msg = NULL; + struct efi_signature_store *db = NULL, *dbx = NULL; + struct x509_certificate *cert = NULL; + void *new_efi = NULL; + size_t new_efi_size; + bool ret = false; + + if (!efi_secure_boot_enabled()) + return true; + + /* + * Size must be 8-byte aligned and the trailing bytes must be + * zero'ed. Otherwise hash value may be incorrect. + */ + if (efi_size & 0x7) { + new_efi_size = (efi_size + 0x7) & ~0x7ULL; + new_efi = calloc(new_efi_size, 1); + if (!new_efi) + return false; + memcpy(new_efi, efi, efi_size); + efi = new_efi; + efi_size = new_efi_size; + } + + if (!efi_image_parse(efi, efi_size, ®s, &wincerts, + &wincerts_len)) { + debug("Parsing PE executable image failed\n"); + goto err; + } + + if (!wincerts) { + /* The image is not signed */ + ret = efi_image_unsigned_authenticate(regs); + + goto err; + } + + /* + * verify signature using db and dbx + */ + db = efi_sigstore_parse_sigdb(L"db"); + if (!db) { + debug("Getting signature database(db) failed\n"); + goto err; + } + + dbx = efi_sigstore_parse_sigdb(L"dbx"); + if (!dbx) { + debug("Getting signature database(dbx) failed\n"); + goto err; + } + + /* go through WIN_CERTIFICATE list */ + for (wincert = wincerts; + (void *)wincert < (void *)wincerts + wincerts_len; + wincert = (void *)wincert + ALIGN(wincert->dwLength, 8)) { + if (wincert->dwLength < sizeof(*wincert)) { + debug("%s: dwLength too small: %u < %zu\n", + __func__, wincert->dwLength, sizeof(*wincert)); + goto err; + } + msg = pkcs7_parse_message((void *)wincert + sizeof(*wincert), + wincert->dwLength - sizeof(*wincert)); + if (!msg) { + debug("Parsing image's signature failed\n"); + goto err; + } + + /* try black-list first */ + if (efi_signature_verify_with_sigdb(regs, msg, dbx, NULL)) { + debug("Signature was rejected by \"dbx\"\n"); + goto err; + } + + if (!efi_signature_verify_signers(msg, dbx)) { + debug("Signer was rejected by \"dbx\"\n"); + goto err; + } else { + ret = true; + } + + /* try white-list */ + if (!efi_signature_verify_with_sigdb(regs, msg, db, &cert)) { + debug("Verifying signature with \"db\" failed\n"); + goto err; + } else { + ret = true; + } + + if (!efi_signature_verify_cert(cert, dbx)) { + debug("Certificate was rejected by \"dbx\"\n"); + goto err; + } else { + ret = true; + } + } + +err: + x509_free_certificate(cert); + efi_sigstore_free(db); + efi_sigstore_free(dbx); + pkcs7_free_message(msg); + free(regs); + free(new_efi); + + return ret; +} +#else +static bool efi_image_authenticate(void *efi, size_t efi_size) +{ + return true; +} +#endif /* CONFIG_EFI_SECURE_BOOT */ + /** * efi_load_pe() - relocate EFI binary * @@ -214,10 +597,12 @@ static void efi_set_code_and_data_type( * * @handle: loaded image handle * @efi: pointer to the EFI binary + * @efi_size: size of @efi binary * @loaded_image_info: loaded image protocol * Return: status code */ -efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, +efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, + void *efi, size_t efi_size, struct efi_loaded_image *loaded_image_info) { IMAGE_NT_HEADERS32 *nt; @@ -232,17 +617,41 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, uint64_t image_base; unsigned long virt_size = 0; int supported = 0; + efi_status_t ret; + + /* Sanity check for a file header */ + if (efi_size < sizeof(*dos)) { + printf("%s: Truncated DOS Header\n", __func__); + ret = EFI_LOAD_ERROR; + goto err; + } dos = efi; if (dos->e_magic != IMAGE_DOS_SIGNATURE) { printf("%s: Invalid DOS Signature\n", __func__); - return EFI_LOAD_ERROR; + ret = EFI_LOAD_ERROR; + goto err; + } + + /* assume sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64) */ + if (efi_size < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS32)) { + printf("%s: Invalid offset for Extended Header\n", __func__); + ret = EFI_LOAD_ERROR; + goto err; } nt = (void *) ((char *)efi + dos->e_lfanew); + if ((nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) && + (efi_size < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS64))) { + printf("%s: Invalid offset for Extended Header\n", __func__); + ret = EFI_LOAD_ERROR; + goto err; + } + if (nt->Signature != IMAGE_NT_SIGNATURE) { printf("%s: Invalid NT Signature\n", __func__); - return EFI_LOAD_ERROR; + ret = EFI_LOAD_ERROR; + goto err; } for (i = 0; machines[i]; i++) @@ -254,14 +663,29 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, if (!supported) { printf("%s: Machine type 0x%04x is not supported\n", __func__, nt->FileHeader.Machine); - return EFI_LOAD_ERROR; + ret = EFI_LOAD_ERROR; + goto err; } - /* Calculate upper virtual address boundary */ num_sections = nt->FileHeader.NumberOfSections; sections = (void *)&nt->OptionalHeader + nt->FileHeader.SizeOfOptionalHeader; + if (efi_size < ((void *)sections + sizeof(sections[0]) * num_sections + - efi)) { + printf("%s: Invalid number of sections: %d\n", + __func__, num_sections); + ret = EFI_LOAD_ERROR; + goto err; + } + + /* Authenticate an image */ + if (efi_image_authenticate(efi, efi_size)) + handle->auth_status = EFI_IMAGE_AUTH_PASSED; + else + handle->auth_status = EFI_IMAGE_AUTH_FAILED; + + /* Calculate upper virtual address boundary */ for (i = num_sections - 1; i >= 0; i--) { IMAGE_SECTION_HEADER *sec = §ions[i]; virt_size = max_t(unsigned long, virt_size, @@ -280,7 +704,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, if (!efi_reloc) { printf("%s: Could not allocate %lu bytes\n", __func__, virt_size); - return EFI_OUT_OF_RESOURCES; + ret = EFI_OUT_OF_RESOURCES; + goto err; } handle->entry = efi_reloc + opt->AddressOfEntryPoint; rel_size = opt->DataDirectory[rel_idx].Size; @@ -296,7 +721,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, if (!efi_reloc) { printf("%s: Could not allocate %lu bytes\n", __func__, virt_size); - return EFI_OUT_OF_RESOURCES; + ret = EFI_OUT_OF_RESOURCES; + goto err; } handle->entry = efi_reloc + opt->AddressOfEntryPoint; rel_size = opt->DataDirectory[rel_idx].Size; @@ -305,13 +731,16 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, } else { printf("%s: Invalid optional header magic %x\n", __func__, nt->OptionalHeader.Magic); - return EFI_LOAD_ERROR; + ret = EFI_LOAD_ERROR; + goto err; } /* Copy PE headers */ - memcpy(efi_reloc, efi, sizeof(*dos) + sizeof(*nt) - + nt->FileHeader.SizeOfOptionalHeader - + num_sections * sizeof(IMAGE_SECTION_HEADER)); + memcpy(efi_reloc, efi, + sizeof(*dos) + + sizeof(*nt) + + nt->FileHeader.SizeOfOptionalHeader + + num_sections * sizeof(IMAGE_SECTION_HEADER)); /* Load sections into RAM */ for (i = num_sections - 1; i >= 0; i--) { @@ -328,7 +757,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, (unsigned long)image_base) != EFI_SUCCESS) { efi_free_pages((uintptr_t) efi_reloc, (virt_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT); - return EFI_LOAD_ERROR; + ret = EFI_LOAD_ERROR; + goto err; } /* Flush cache */ @@ -340,5 +770,11 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, loaded_image_info->image_base = efi_reloc; loaded_image_info->image_size = virt_size; - return EFI_SUCCESS; + if (handle->auth_status == EFI_IMAGE_AUTH_PASSED) + return EFI_SUCCESS; + else + return EFI_SECURITY_VIOLATION; + +err: + return ret; } diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index b458093dfb..1b648c8467 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -82,6 +82,39 @@ out: return ret; } +#ifdef CONFIG_EFI_SECURE_BOOT +/** + * efi_init_secure_boot - initialize secure boot state + * + * Return: EFI_SUCCESS on success, status code (negative) on error + */ +static efi_status_t efi_init_secure_boot(void) +{ + efi_guid_t signature_types[] = { + EFI_CERT_SHA256_GUID, + EFI_CERT_X509_GUID, + }; + efi_status_t ret; + + /* TODO: read-only */ + ret = EFI_CALL(efi_set_variable(L"SignatureSupport", + &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(signature_types), + &signature_types)); + if (ret != EFI_SUCCESS) + printf("EFI: cannot initialize SignatureSupport variable\n"); + + return ret; +} +#else +static efi_status_t efi_init_secure_boot(void) +{ + return EFI_SUCCESS; +} +#endif /* CONFIG_EFI_SECURE_BOOT */ + /** * efi_init_obj_list() - Initialize and populate EFI object list * @@ -127,6 +160,11 @@ efi_status_t efi_init_obj_list(void) if (ret != EFI_SUCCESS) goto out; + /* Secure boot */ + ret = efi_init_secure_boot(); + if (ret != EFI_SUCCESS) + goto out; + /* Indicate supported runtime services */ ret = efi_init_runtime_supported(); if (ret != EFI_SUCCESS) diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c new file mode 100644 index 0000000000..658e3547da --- /dev/null +++ b/lib/efi_loader/efi_signature.c @@ -0,0 +1,804 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se> + * Copyright (c) 2019 Linaro Limited, Author: AKASHI Takahiro + */ + +#include <common.h> +#include <charset.h> +#include <efi_loader.h> +#include <image.h> +#include <hexdump.h> +#include <malloc.h> +#include <linux/compat.h> +#include <linux/oid_registry.h> +#include <u-boot/rsa.h> +#include <u-boot/sha256.h> +#include "../lib/crypto/pkcs7_parser.h" + +const efi_guid_t efi_guid_image_security_database = + EFI_IMAGE_SECURITY_DATABASE_GUID; +const efi_guid_t efi_guid_sha256 = EFI_CERT_SHA256_GUID; +const efi_guid_t efi_guid_cert_rsa2048 = EFI_CERT_RSA2048_GUID; +const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID; +const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID; + +#ifdef CONFIG_EFI_SECURE_BOOT + +/** + * efi_hash_regions - calculate a hash value + * @regs: List of regions + * @hash: Pointer to a pointer to buffer holding a hash value + * @size: Size of buffer to be returned + * + * Calculate a sha256 value of @regs and return a value in @hash. + * + * Return: true on success, false on error + */ +static bool efi_hash_regions(struct efi_image_regions *regs, void **hash, + size_t *size) +{ + *size = 0; + *hash = calloc(1, SHA256_SUM_LEN); + if (!*hash) { + debug("Out of memory\n"); + return false; + } + *size = SHA256_SUM_LEN; + + hash_calculate("sha256", regs->reg, regs->num, *hash); +#ifdef DEBUG + debug("hash calculated:\n"); + print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, + *hash, SHA256_SUM_LEN, false); +#endif + + return true; +} + +/** + * efi_hash_msg_content - calculate a hash value of contentInfo + * @msg: Signature + * @hash: Pointer to a pointer to buffer holding a hash value + * @size: Size of buffer to be returned + * + * Calculate a sha256 value of contentInfo in @msg and return a value in @hash. + * + * Return: true on success, false on error + */ +static bool efi_hash_msg_content(struct pkcs7_message *msg, void **hash, + size_t *size) +{ + struct image_region regtmp; + + *size = 0; + *hash = calloc(1, SHA256_SUM_LEN); + if (!*hash) { + debug("Out of memory\n"); + free(msg); + return false; + } + *size = SHA256_SUM_LEN; + + regtmp.data = msg->data; + regtmp.size = msg->data_len; + + hash_calculate("sha256", ®tmp, 1, *hash); +#ifdef DEBUG + debug("hash calculated based on contentInfo:\n"); + print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, + *hash, SHA256_SUM_LEN, false); +#endif + + return true; +} + +/** + * efi_signature_verify - verify a signature with a certificate + * @regs: List of regions to be authenticated + * @signed_info: Pointer to PKCS7's signed_info + * @cert: x509 certificate + * + * Signature pointed to by @signed_info against image pointed to by @regs + * is verified by a certificate pointed to by @cert. + * @signed_info holds a signature, including a message digest which is to be + * compared with a hash value calculated from @regs. + * + * Return: true if signature is verified, false if not + */ +static bool efi_signature_verify(struct efi_image_regions *regs, + struct pkcs7_message *msg, + struct pkcs7_signed_info *ps_info, + struct x509_certificate *cert) +{ + struct image_sign_info info; + struct image_region regtmp[2]; + void *hash; + size_t size; + char c; + bool verified; + + debug("%s: Enter, %p, %p, %p(issuer: %s, subject: %s)\n", __func__, + regs, ps_info, cert, cert->issuer, cert->subject); + + verified = false; + + memset(&info, '\0', sizeof(info)); + info.padding = image_get_padding_algo("pkcs-1.5"); + /* + * Note: image_get_[checksum|crypto]_algo takes an string + * argument like "<checksum>,<crypto>" + * TODO: support other hash algorithms + */ + if (!strcmp(ps_info->sig->hash_algo, "sha1")) { + info.checksum = image_get_checksum_algo("sha1,rsa2048"); + info.name = "sha1,rsa2048"; + } else if (!strcmp(ps_info->sig->hash_algo, "sha256")) { + info.checksum = image_get_checksum_algo("sha256,rsa2048"); + info.name = "sha256,rsa2048"; + } else { + debug("unknown msg digest algo: %s\n", ps_info->sig->hash_algo); + goto out; + } + info.crypto = image_get_crypto_algo(info.name); + + info.key = cert->pub->key; + info.keylen = cert->pub->keylen; + + /* verify signature */ + debug("%s: crypto: %s, signature len:%x\n", __func__, + info.name, ps_info->sig->s_size); + if (ps_info->aa_set & (1UL << sinfo_has_message_digest)) { + debug("%s: RSA verify authentication attribute\n", __func__); + /* + * NOTE: This path will be executed only for + * PE image authentication + */ + + /* check if hash matches digest first */ + debug("checking msg digest first, len:0x%x\n", + ps_info->msgdigest_len); + +#ifdef DEBUG + debug("hash in database:\n"); + print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, + ps_info->msgdigest, ps_info->msgdigest_len, + false); +#endif + /* against contentInfo first */ + if ((msg->data && efi_hash_msg_content(msg, &hash, &size)) || + /* for signed image */ + efi_hash_regions(regs, &hash, &size)) { + /* for authenticated variable */ + if (ps_info->msgdigest_len != size || + memcmp(hash, ps_info->msgdigest, size)) { + debug("Digest doesn't match\n"); + free(hash); + goto out; + } + + free(hash); + } else { + debug("Digesting image failed\n"); + goto out; + } + + /* against digest */ + c = 0x31; + regtmp[0].data = &c; + regtmp[0].size = 1; + regtmp[1].data = ps_info->authattrs; + regtmp[1].size = ps_info->authattrs_len; + + if (!rsa_verify(&info, regtmp, 2, + ps_info->sig->s, ps_info->sig->s_size)) + verified = true; + } else { + debug("%s: RSA verify content data\n", __func__); + /* against all data */ + if (!rsa_verify(&info, regs->reg, regs->num, + ps_info->sig->s, ps_info->sig->s_size)) + verified = true; + } + +out: + debug("%s: Exit, verified: %d\n", __func__, verified); + return verified; +} + +/** + * efi_signature_verify_with_list - verify a signature with signature list + * @regs: List of regions to be authenticated + * @msg: Signature + * @signed_info: Pointer to PKCS7's signed_info + * @siglist: Signature list for certificates + * @valid_cert: x509 certificate that verifies this signature + * + * Signature pointed to by @signed_info against image pointed to by @regs + * is verified by signature list pointed to by @siglist. + * Signature database is a simple concatenation of one or more + * signature list(s). + * + * Return: true if signature is verified, false if not + */ +static +bool efi_signature_verify_with_list(struct efi_image_regions *regs, + struct pkcs7_message *msg, + struct pkcs7_signed_info *signed_info, + struct efi_signature_store *siglist, + struct x509_certificate **valid_cert) +{ + struct x509_certificate *cert; + struct efi_sig_data *sig_data; + bool verified = false; + + debug("%s: Enter, %p, %p, %p, %p\n", __func__, + regs, signed_info, siglist, valid_cert); + + if (!signed_info) { + void *hash; + size_t size; + + debug("%s: unsigned image\n", __func__); + /* + * verify based on calculated hash value + * TODO: support other hash algorithms + */ + if (guidcmp(&siglist->sig_type, &efi_guid_sha256)) { + debug("Digest algorithm is not supported: %pUl\n", + &siglist->sig_type); + goto out; + } + + if (!efi_hash_regions(regs, &hash, &size)) { + debug("Digesting unsigned image failed\n"); + goto out; + } + + /* go through the list */ + for (sig_data = siglist->sig_data_list; sig_data; + sig_data = sig_data->next) { +#ifdef DEBUG + debug("Msg digest in database:\n"); + print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, + sig_data->data, sig_data->size, false); +#endif + if ((sig_data->size == size) && + !memcmp(sig_data->data, hash, size)) { + verified = true; + free(hash); + goto out; + } + } + free(hash); + goto out; + } + + debug("%s: signed image\n", __func__); + if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509)) { + debug("Signature type is not supported: %pUl\n", + &siglist->sig_type); + goto out; + } + + /* go through the list */ + for (sig_data = siglist->sig_data_list; sig_data; + sig_data = sig_data->next) { + /* TODO: support owner check based on policy */ + + cert = x509_cert_parse(sig_data->data, sig_data->size); + if (IS_ERR(cert)) { + debug("Parsing x509 certificate failed\n"); + goto out; + } + + verified = efi_signature_verify(regs, msg, signed_info, cert); + + if (verified) { + if (valid_cert) + *valid_cert = cert; + else + x509_free_certificate(cert); + break; + } + x509_free_certificate(cert); + } + +out: + debug("%s: Exit, verified: %d\n", __func__, verified); + return verified; +} + +/** + * efi_signature_verify_with_sigdb - verify a signature with db + * @regs: List of regions to be authenticated + * @msg: Signature + * @db: Signature database for trusted certificates + * @cert: x509 certificate that verifies this signature + * + * Signature pointed to by @msg against image pointed to by @regs + * is verified by signature database pointed to by @db. + * + * Return: true if signature is verified, false if not + */ +bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs, + struct pkcs7_message *msg, + struct efi_signature_store *db, + struct x509_certificate **cert) +{ + struct pkcs7_signed_info *info; + struct efi_signature_store *siglist; + bool verified = false; + + debug("%s: Enter, %p, %p, %p, %p\n", __func__, regs, msg, db, cert); + + if (!db) + goto out; + + if (!db->sig_data_list) + goto out; + + /* for unsigned image */ + if (!msg) { + debug("%s: Verify unsigned image with db\n", __func__); + for (siglist = db; siglist; siglist = siglist->next) + if (efi_signature_verify_with_list(regs, NULL, NULL, + siglist, cert)) { + verified = true; + goto out; + } + + goto out; + } + + /* for signed image or variable */ + debug("%s: Verify signed image with db\n", __func__); + for (info = msg->signed_infos; info; info = info->next) { + debug("Signed Info: digest algo: %s, pkey algo: %s\n", + info->sig->hash_algo, info->sig->pkey_algo); + + for (siglist = db; siglist; siglist = siglist->next) { + if (efi_signature_verify_with_list(regs, msg, info, + siglist, cert)) { + verified = true; + goto out; + } + } + } + +out: + debug("%s: Exit, verified: %d\n", __func__, verified); + return verified; +} + +/** + * efi_search_siglist - search signature list for a certificate + * @cert: x509 certificate + * @siglist: Signature list + * @revoc_time: Pointer to buffer for revocation time + * + * Search signature list pointed to by @siglist and find a certificate + * pointed to by @cert. + * If found, revocation time that is specified in signature database is + * returned in @revoc_time. + * + * Return: true if certificate is found, false if not + */ +static bool efi_search_siglist(struct x509_certificate *cert, + struct efi_signature_store *siglist, + time64_t *revoc_time) +{ + struct image_region reg[1]; + void *hash = NULL, *msg = NULL; + struct efi_sig_data *sig_data; + bool found = false; + + /* can be null */ + if (!siglist->sig_data_list) + return false; + + if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509_sha256)) { + /* TODO: other hash algos */ + debug("Certificate's digest type is not supported: %pUl\n", + &siglist->sig_type); + goto out; + } + + /* calculate hash of TBSCertificate */ + msg = calloc(1, SHA256_SUM_LEN); + if (!msg) { + debug("Out of memory\n"); + goto out; + } + + hash = calloc(1, SHA256_SUM_LEN); + if (!hash) { + debug("Out of memory\n"); + goto out; + } + + reg[0].data = cert->tbs; + reg[0].size = cert->tbs_size; + hash_calculate("sha256", reg, 1, msg); + + /* go through signature list */ + for (sig_data = siglist->sig_data_list; sig_data; + sig_data = sig_data->next) { + /* + * struct efi_cert_x509_sha256 { + * u8 tbs_hash[256/8]; + * time64_t revocation_time; + * }; + */ + if ((sig_data->size == SHA256_SUM_LEN) && + !memcmp(sig_data->data, hash, SHA256_SUM_LEN)) { + memcpy(revoc_time, sig_data->data + SHA256_SUM_LEN, + sizeof(*revoc_time)); + found = true; + goto out; + } + } + +out: + free(hash); + free(msg); + + return found; +} + +/** + * efi_signature_verify_cert - verify a certificate with dbx + * @cert: x509 certificate + * @dbx: Signature database + * + * Search signature database pointed to by @dbx and find a certificate + * pointed to by @cert. + * This function is expected to be used against "dbx". + * + * Return: true if a certificate is not rejected, false otherwise. + */ +bool efi_signature_verify_cert(struct x509_certificate *cert, + struct efi_signature_store *dbx) +{ + struct efi_signature_store *siglist; + time64_t revoc_time; + bool found = false; + + debug("%s: Enter, %p, %p\n", __func__, dbx, cert); + + if (!cert) + return false; + + for (siglist = dbx; siglist; siglist = siglist->next) { + if (efi_search_siglist(cert, siglist, &revoc_time)) { + /* TODO */ + /* compare signing time with revocation time */ + + found = true; + break; + } + } + + debug("%s: Exit, verified: %d\n", __func__, !found); + return !found; +} + +/** + * efi_signature_verify_signers - verify signers' certificates with dbx + * @msg: Signature + * @dbx: Signature database + * + * Determine if any of signers' certificates in @msg may be verified + * by any of certificates in signature database pointed to by @dbx. + * This function is expected to be used against "dbx". + * + * Return: true if none of certificates is rejected, false otherwise. + */ +bool efi_signature_verify_signers(struct pkcs7_message *msg, + struct efi_signature_store *dbx) +{ + struct pkcs7_signed_info *info; + bool found = false; + + debug("%s: Enter, %p, %p\n", __func__, msg, dbx); + + if (!msg) + goto out; + + for (info = msg->signed_infos; info; info = info->next) { + if (info->signer && + !efi_signature_verify_cert(info->signer, dbx)) { + found = true; + goto out; + } + } +out: + debug("%s: Exit, verified: %d\n", __func__, !found); + return !found; +} + +/** + * efi_image_region_add - add an entry of region + * @regs: Pointer to array of regions + * @start: Start address of region + * @end: End address of region + * @nocheck: flag against overlapped regions + * + * Take one entry of region [@start, @end] and append it to the list + * pointed to by @regs. If @nocheck is false, overlapping among entries + * will be checked first. + * + * Return: 0 on success, status code (negative) on error + */ +efi_status_t efi_image_region_add(struct efi_image_regions *regs, + const void *start, const void *end, + int nocheck) +{ + struct image_region *reg; + int i, j; + + if (regs->num >= regs->max) { + debug("%s: no more room for regions\n", __func__); + return EFI_OUT_OF_RESOURCES; + } + + if (end < start) + return EFI_INVALID_PARAMETER; + + for (i = 0; i < regs->num; i++) { + reg = ®s->reg[i]; + if (nocheck) + continue; + + if (start > reg->data + reg->size) + continue; + + if ((start >= reg->data && start < reg->data + reg->size) || + (end > reg->data && end < reg->data + reg->size)) { + debug("%s: new region already part of another\n", + __func__); + return EFI_INVALID_PARAMETER; + } + + if (start < reg->data && end < reg->data + reg->size) { + for (j = regs->num - 1; j >= i; j--) + memcpy(®s->reg[j], ®s->reg[j + 1], + sizeof(*reg)); + break; + } + } + + reg = ®s->reg[i]; + reg->data = start; + reg->size = end - start; + regs->num++; + + return EFI_SUCCESS; +} + +/** + * efi_sigstore_free - free signature store + * @sigstore: Pointer to signature store structure + * + * Feee all the memories held in signature store and itself, + * which were allocated by efi_sigstore_parse_sigdb(). + */ +void efi_sigstore_free(struct efi_signature_store *sigstore) +{ + struct efi_signature_store *sigstore_next; + struct efi_sig_data *sig_data, *sig_data_next; + + while (sigstore) { + sigstore_next = sigstore->next; + + sig_data = sigstore->sig_data_list; + while (sig_data) { + sig_data_next = sig_data->next; + free(sig_data->data); + free(sig_data); + sig_data = sig_data_next; + } + + free(sigstore); + sigstore = sigstore_next; + } +} + +/** + * efi_sigstore_parse_siglist - parse a signature list + * @name: Pointer to signature list + * + * Parse signature list and instantiate a signature store structure. + * Signature database is a simple concatenation of one or more + * signature list(s). + * + * Return: Pointer to signature store on success, NULL on error + */ +static struct efi_signature_store * +efi_sigstore_parse_siglist(struct efi_signature_list *esl) +{ + struct efi_signature_store *siglist = NULL; + struct efi_sig_data *sig_data, *sig_data_next; + struct efi_signature_data *esd; + size_t left; + + /* + * UEFI specification defines certificate types: + * for non-signed images, + * EFI_CERT_SHA256_GUID + * EFI_CERT_RSA2048_GUID + * EFI_CERT_RSA2048_SHA256_GUID + * EFI_CERT_SHA1_GUID + * EFI_CERT_RSA2048_SHA_GUID + * EFI_CERT_SHA224_GUID + * EFI_CERT_SHA384_GUID + * EFI_CERT_SHA512_GUID + * + * for signed images, + * EFI_CERT_X509_GUID + * NOTE: Each certificate will normally be in a separate + * EFI_SIGNATURE_LIST as the size may vary depending on + * its algo's. + * + * for timestamp revocation of certificate, + * EFI_CERT_X509_SHA512_GUID + * EFI_CERT_X509_SHA256_GUID + * EFI_CERT_X509_SHA384_GUID + */ + + if (esl->signature_list_size + <= (sizeof(*esl) + esl->signature_header_size)) { + debug("Siglist in wrong format\n"); + return NULL; + } + + /* Create a head */ + siglist = calloc(sizeof(*siglist), 1); + if (!siglist) { + debug("Out of memory\n"); + goto err; + } + memcpy(&siglist->sig_type, &esl->signature_type, sizeof(efi_guid_t)); + + /* Go through the list */ + sig_data_next = NULL; + left = esl->signature_list_size + - (sizeof(*esl) + esl->signature_header_size); + esd = (struct efi_signature_data *) + ((u8 *)esl + sizeof(*esl) + esl->signature_header_size); + + while ((left > 0) && left >= esl->signature_size) { + /* Signature must exist if there is remaining data. */ + if (left < esl->signature_size) { + debug("Certificate is too small\n"); + goto err; + } + + sig_data = calloc(esl->signature_size + - sizeof(esd->signature_owner), 1); + if (!sig_data) { + debug("Out of memory\n"); + goto err; + } + + /* Append signature data */ + memcpy(&sig_data->owner, &esd->signature_owner, + sizeof(efi_guid_t)); + sig_data->size = esl->signature_size + - sizeof(esd->signature_owner); + sig_data->data = malloc(sig_data->size); + if (!sig_data->data) { + debug("Out of memory\n"); + goto err; + } + memcpy(sig_data->data, esd->signature_data, sig_data->size); + + sig_data->next = sig_data_next; + sig_data_next = sig_data; + + /* Next */ + esd = (struct efi_signature_data *) + ((u8 *)esd + esl->signature_size); + left -= esl->signature_size; + } + siglist->sig_data_list = sig_data_next; + + return siglist; + +err: + efi_sigstore_free(siglist); + + return NULL; +} + +/** + * efi_sigstore_parse_sigdb - parse a signature database variable + * @name: Variable's name + * + * Read in a value of signature database variable pointed to by + * @name, parse it and instantiate a signature store structure. + * + * Return: Pointer to signature store on success, NULL on error + */ +struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name) +{ + struct efi_signature_store *sigstore = NULL, *siglist; + struct efi_signature_list *esl; + const efi_guid_t *vendor; + void *db; + efi_uintn_t db_size; + efi_status_t ret; + + if (!u16_strcmp(name, L"PK") || !u16_strcmp(name, L"KEK")) { + vendor = &efi_global_variable_guid; + } else if (!u16_strcmp(name, L"db") || !u16_strcmp(name, L"dbx")) { + vendor = &efi_guid_image_security_database; + } else { + debug("unknown signature database, %ls\n", name); + return NULL; + } + + /* retrieve variable data */ + db_size = 0; + ret = EFI_CALL(efi_get_variable(name, vendor, NULL, &db_size, NULL)); + if (ret == EFI_NOT_FOUND) { + debug("variable, %ls, not found\n", name); + sigstore = calloc(sizeof(*sigstore), 1); + return sigstore; + } else if (ret != EFI_BUFFER_TOO_SMALL) { + debug("Getting variable, %ls, failed\n", name); + return NULL; + } + + db = malloc(db_size); + if (!db) { + debug("Out of memory\n"); + return NULL; + } + + ret = EFI_CALL(efi_get_variable(name, vendor, NULL, &db_size, db)); + if (ret != EFI_SUCCESS) { + debug("Getting variable, %ls, failed\n", name); + goto err; + } + + /* Parse siglist list */ + esl = db; + while (db_size > 0) { + /* List must exist if there is remaining data. */ + if (db_size < sizeof(*esl)) { + debug("variable, %ls, in wrong format\n", name); + goto err; + } + + if (db_size < esl->signature_list_size) { + debug("variable, %ls, in wrong format\n", name); + goto err; + } + + /* Parse a single siglist. */ + siglist = efi_sigstore_parse_siglist(esl); + if (!siglist) { + debug("Parsing signature list of %ls failed\n", name); + goto err; + } + + /* Append siglist */ + siglist->next = sigstore; + sigstore = siglist; + + /* Next */ + db_size -= esl->signature_list_size; + esl = (void *)esl + esl->signature_list_size; + } + free(db); + + return sigstore; + +err: + efi_sigstore_free(sigstore); + free(db); + + return NULL; +} +#endif /* CONFIG_EFI_SECURE_BOOT */ diff --git a/lib/efi_loader/efi_unicode_collation.c b/lib/efi_loader/efi_unicode_collation.c index c700be8756..6655c68092 100644 --- a/lib/efi_loader/efi_unicode_collation.c +++ b/lib/efi_loader/efi_unicode_collation.c @@ -169,8 +169,8 @@ static bool metai_match(const u16 *string, const u16 *pattern) * case-insenitively * * @this: unicode collation protocol instance - * @s: string to compare - * @p: pattern string + * @string: string to compare + * @pattern: pattern string * * The pattern string may use these: * - * matches >= 0 characters @@ -199,7 +199,6 @@ static bool EFIAPI efi_metai_match(struct efi_unicode_collation_protocol *this, * * @this: unicode collation protocol instance * @string: string to convert - * @p: pattern string * * The conversion is done in place. As long as upper and lower letters use the * same number of words this does not pose a problem. @@ -221,7 +220,6 @@ static void EFIAPI efi_str_lwr(struct efi_unicode_collation_protocol *this, * * @this: unicode collation protocol instance * @string: string to convert - * @p: pattern string * * The conversion is done in place. As long as upper and lower letters use the * same number of words this does not pose a problem. diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index fe2f264591..7df881a74b 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -10,8 +10,23 @@ #include <env_internal.h> #include <hexdump.h> #include <malloc.h> +#include <rtc.h> #include <search.h> +#include <linux/compat.h> #include <u-boot/crc.h> +#include "../lib/crypto/pkcs7_parser.h" + +enum efi_secure_mode { + EFI_MODE_SETUP, + EFI_MODE_USER, + EFI_MODE_AUDIT, + EFI_MODE_DEPLOYED, +}; + +const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID; +static bool efi_secure_boot; +static int efi_secure_mode; +static u8 efi_vendor_keys; #define READ_ONLY BIT(31) @@ -106,9 +121,10 @@ static const char *prefix(const char *str, const char *prefix) * * @str: value of U-Boot variable * @attrp: pointer to UEFI attributes + * @timep: pointer to time attribute * Return: pointer to remainder of U-Boot variable value */ -static const char *parse_attr(const char *str, u32 *attrp) +static const char *parse_attr(const char *str, u32 *attrp, u64 *timep) { u32 attr = 0; char sep = '{'; @@ -131,6 +147,12 @@ static const char *parse_attr(const char *str, u32 *attrp) attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS; } else if ((s = prefix(str, "run"))) { attr |= EFI_VARIABLE_RUNTIME_ACCESS; + } else if ((s = prefix(str, "time="))) { + attr |= EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + hex2bin((u8 *)timep, s, sizeof(*timep)); + s += sizeof(*timep) * 2; + } else if (*str == '}') { + break; } else { printf("invalid attribute: %s\n", str); break; @@ -147,49 +169,528 @@ static const char *parse_attr(const char *str, u32 *attrp) return str; } +static efi_status_t efi_set_variable_internal(u16 *variable_name, + const efi_guid_t *vendor, + u32 attributes, + efi_uintn_t data_size, + const void *data, + bool ro_check); + /** - * efi_get_variable() - retrieve value of a UEFI variable + * efi_transfer_secure_state - handle a secure boot state transition + * @mode: new state * - * This function implements the GetVariable runtime service. + * Depending on @mode, secure boot related variables are updated. + * Those variables are *read-only* for users, efi_set_variable_internal() + * is called here. * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. + * Return: EFI_SUCCESS on success, status code (negative) on error + */ +static efi_status_t efi_transfer_secure_state(enum efi_secure_mode mode) +{ + u32 attributes; + u8 val; + efi_status_t ret; + + debug("Secure state from %d to %d\n", efi_secure_mode, mode); + + attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS; + if (mode == EFI_MODE_DEPLOYED) { + val = 1; + ret = efi_set_variable_internal(L"SecureBoot", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"SetupMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"AuditMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 1; + ret = efi_set_variable_internal(L"DeployedMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + + efi_secure_boot = true; + } else if (mode == EFI_MODE_AUDIT) { + ret = efi_set_variable_internal(L"PK", + &efi_global_variable_guid, + attributes, + 0, NULL, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"SecureBoot", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 1; + ret = efi_set_variable_internal(L"SetupMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 1; + ret = efi_set_variable_internal(L"AuditMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"DeployedMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + + efi_secure_boot = true; + } else if (mode == EFI_MODE_USER) { + val = 1; + ret = efi_set_variable_internal(L"SecureBoot", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"SetupMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"AuditMode", + &efi_global_variable_guid, + attributes, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"DeployedMode", + &efi_global_variable_guid, + attributes, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + + efi_secure_boot = true; + } else if (mode == EFI_MODE_SETUP) { + val = 0; + ret = efi_set_variable_internal(L"SecureBoot", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 1; + ret = efi_set_variable_internal(L"SetupMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"AuditMode", + &efi_global_variable_guid, + attributes, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + val = 0; + ret = efi_set_variable_internal(L"DeployedMode", + &efi_global_variable_guid, + attributes | READ_ONLY, + sizeof(val), &val, + false); + if (ret != EFI_SUCCESS) + goto err; + } else { + return EFI_INVALID_PARAMETER; + } + + efi_secure_mode = mode; + + return EFI_SUCCESS; + +err: + /* TODO: What action should be taken here? */ + printf("ERROR: Secure state transition failed\n"); + return ret; +} + +/** + * efi_init_secure_state - initialize secure boot state * - * @variable_name: name of the variable - * @vendor: vendor GUID - * @attributes: attributes of the variable - * @data_size: size of the buffer to which the variable value is copied - * @data: buffer to which the variable value is copied - * Return: status code + * Return: EFI_SUCCESS on success, status code (negative) on error */ -efi_status_t EFIAPI efi_get_variable(u16 *variable_name, - const efi_guid_t *vendor, u32 *attributes, - efi_uintn_t *data_size, void *data) +static efi_status_t efi_init_secure_state(void) +{ + enum efi_secure_mode mode; + efi_uintn_t size; + efi_status_t ret; + + /* + * TODO: + * Since there is currently no "platform-specific" installation + * method of Platform Key, we can't say if VendorKeys is 0 or 1 + * precisely. + */ + + size = 0; + ret = EFI_CALL(efi_get_variable(L"PK", &efi_global_variable_guid, + NULL, &size, NULL)); + if (ret == EFI_BUFFER_TOO_SMALL) { + if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT)) + mode = EFI_MODE_USER; + else + mode = EFI_MODE_SETUP; + + efi_vendor_keys = 0; + } else if (ret == EFI_NOT_FOUND) { + mode = EFI_MODE_SETUP; + efi_vendor_keys = 1; + } else { + goto err; + } + + ret = efi_transfer_secure_state(mode); + if (ret == EFI_SUCCESS) + ret = efi_set_variable_internal(L"VendorKeys", + &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_RUNTIME_ACCESS + | READ_ONLY, + sizeof(efi_vendor_keys), + &efi_vendor_keys, + false); + +err: + return ret; +} + +/** + * efi_secure_boot_enabled - return if secure boot is enabled or not + * + * Return: true if enabled, false if disabled + */ +bool efi_secure_boot_enabled(void) +{ + return efi_secure_boot; +} + +#ifdef CONFIG_EFI_SECURE_BOOT +static u8 pkcs7_hdr[] = { + /* SEQUENCE */ + 0x30, 0x82, 0x05, 0xc7, + /* OID: pkcs7-signedData */ + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, + /* Context Structured? */ + 0xa0, 0x82, 0x05, 0xb8, +}; + +/** + * efi_variable_parse_signature - parse a signature in variable + * @buf: Pointer to variable's value + * @buflen: Length of @buf + * + * Parse a signature embedded in variable's value and instantiate + * a pkcs7_message structure. Since pkcs7_parse_message() accepts only + * pkcs7's signedData, some header needed be prepended for correctly + * parsing authentication data, particularly for variable's. + * + * Return: Pointer to pkcs7_message structure on success, NULL on error + */ +static struct pkcs7_message *efi_variable_parse_signature(const void *buf, + size_t buflen) +{ + u8 *ebuf; + size_t ebuflen, len; + struct pkcs7_message *msg; + + /* + * This is the best assumption to check if the binary is + * already in a form of pkcs7's signedData. + */ + if (buflen > sizeof(pkcs7_hdr) && + !memcmp(&((u8 *)buf)[4], &pkcs7_hdr[4], 11)) { + msg = pkcs7_parse_message(buf, buflen); + goto out; + } + + /* + * Otherwise, we should add a dummy prefix sequence for pkcs7 + * message parser to be able to process. + * NOTE: EDK2 also uses similar hack in WrapPkcs7Data() + * in CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7VerifyCommon.c + * TODO: + * The header should be composed in a more refined manner. + */ + debug("Makeshift prefix added to authentication data\n"); + ebuflen = sizeof(pkcs7_hdr) + buflen; + if (ebuflen <= 0x7f) { + debug("Data is too short\n"); + return NULL; + } + + ebuf = malloc(ebuflen); + if (!ebuf) { + debug("Out of memory\n"); + return NULL; + } + + memcpy(ebuf, pkcs7_hdr, sizeof(pkcs7_hdr)); + memcpy(ebuf + sizeof(pkcs7_hdr), buf, buflen); + len = ebuflen - 4; + ebuf[2] = (len >> 8) & 0xff; + ebuf[3] = len & 0xff; + len = ebuflen - 0x13; + ebuf[0x11] = (len >> 8) & 0xff; + ebuf[0x12] = len & 0xff; + + msg = pkcs7_parse_message(ebuf, ebuflen); + + free(ebuf); + +out: + if (IS_ERR(msg)) + return NULL; + + return msg; +} + +/** + * efi_variable_authenticate - authenticate a variable + * @variable: Variable name in u16 + * @vendor: Guid of variable + * @data_size: Size of @data + * @data: Pointer to variable's value + * @given_attr: Attributes to be given at SetVariable() + * @env_attr: Attributes that an existing variable holds + * @time: signed time that an existing variable holds + * + * Called by efi_set_variable() to verify that the input is correct. + * Will replace the given data pointer with another that points to + * the actual data to store in the internal memory. + * On success, @data and @data_size will be replaced with variable's + * actual data, excluding authentication data, and its size, and variable's + * attributes and signed time will also be returned in @env_attr and @time, + * respectively. + * + * Return: EFI_SUCCESS on success, status code (negative) on error + */ +static efi_status_t efi_variable_authenticate(u16 *variable, + const efi_guid_t *vendor, + efi_uintn_t *data_size, + const void **data, u32 given_attr, + u32 *env_attr, u64 *time) +{ + const struct efi_variable_authentication_2 *auth; + struct efi_signature_store *truststore, *truststore2; + struct pkcs7_message *var_sig; + struct efi_image_regions *regs; + struct efi_time timestamp; + struct rtc_time tm; + u64 new_time; + efi_status_t ret; + + var_sig = NULL; + truststore = NULL; + truststore2 = NULL; + regs = NULL; + ret = EFI_SECURITY_VIOLATION; + + if (*data_size < sizeof(struct efi_variable_authentication_2)) + goto err; + + /* authentication data */ + auth = *data; + if (*data_size < (sizeof(auth->time_stamp) + + auth->auth_info.hdr.dwLength)) + goto err; + + if (guidcmp(&auth->auth_info.cert_type, &efi_guid_cert_type_pkcs7)) + goto err; + + *data += sizeof(auth->time_stamp) + auth->auth_info.hdr.dwLength; + *data_size -= (sizeof(auth->time_stamp) + + auth->auth_info.hdr.dwLength); + + memcpy(×tamp, &auth->time_stamp, sizeof(timestamp)); + memset(&tm, 0, sizeof(tm)); + tm.tm_year = timestamp.year; + tm.tm_mon = timestamp.month; + tm.tm_mday = timestamp.day; + tm.tm_hour = timestamp.hour; + tm.tm_min = timestamp.minute; + tm.tm_sec = timestamp.second; + new_time = rtc_mktime(&tm); + + if (!efi_secure_boot_enabled()) { + /* finished checking */ + *time = new_time; + return EFI_SUCCESS; + } + + if (new_time <= *time) + goto err; + + /* data to be digested */ + regs = calloc(sizeof(*regs) + sizeof(struct image_region) * 5, 1); + if (!regs) + goto err; + regs->max = 5; + efi_image_region_add(regs, (uint8_t *)variable, + (uint8_t *)variable + + u16_strlen(variable) * sizeof(u16), 1); + efi_image_region_add(regs, (uint8_t *)vendor, + (uint8_t *)vendor + sizeof(*vendor), 1); + efi_image_region_add(regs, (uint8_t *)&given_attr, + (uint8_t *)&given_attr + sizeof(given_attr), 1); + efi_image_region_add(regs, (uint8_t *)×tamp, + (uint8_t *)×tamp + sizeof(timestamp), 1); + efi_image_region_add(regs, (uint8_t *)*data, + (uint8_t *)*data + *data_size, 1); + + /* variable's signature list */ + if (auth->auth_info.hdr.dwLength < sizeof(auth->auth_info)) + goto err; + var_sig = efi_variable_parse_signature(auth->auth_info.cert_data, + auth->auth_info.hdr.dwLength + - sizeof(auth->auth_info)); + if (IS_ERR(var_sig)) { + debug("Parsing variable's signature failed\n"); + var_sig = NULL; + goto err; + } + + /* signature database used for authentication */ + if (u16_strcmp(variable, L"PK") == 0 || + u16_strcmp(variable, L"KEK") == 0) { + /* with PK */ + truststore = efi_sigstore_parse_sigdb(L"PK"); + if (!truststore) + goto err; + } else if (u16_strcmp(variable, L"db") == 0 || + u16_strcmp(variable, L"dbx") == 0) { + /* with PK and KEK */ + truststore = efi_sigstore_parse_sigdb(L"KEK"); + truststore2 = efi_sigstore_parse_sigdb(L"PK"); + + if (!truststore) { + if (!truststore2) + goto err; + + truststore = truststore2; + truststore2 = NULL; + } + } else { + /* TODO: support private authenticated variables */ + goto err; + } + + /* verify signature */ + if (efi_signature_verify_with_sigdb(regs, var_sig, truststore, NULL)) { + debug("Verified\n"); + } else { + if (truststore2 && + efi_signature_verify_with_sigdb(regs, var_sig, + truststore2, NULL)) { + debug("Verified\n"); + } else { + debug("Verifying variable's signature failed\n"); + goto err; + } + } + + /* finished checking */ + *time = rtc_mktime(&tm); + ret = EFI_SUCCESS; + +err: + efi_sigstore_free(truststore); + efi_sigstore_free(truststore2); + pkcs7_free_message(var_sig); + free(regs); + + return ret; +} +#else +static efi_status_t efi_variable_authenticate(u16 *variable, + const efi_guid_t *vendor, + efi_uintn_t *data_size, + const void **data, u32 given_attr, + u32 *env_attr, u64 *time) +{ + return EFI_SUCCESS; +} +#endif /* CONFIG_EFI_SECURE_BOOT */ + +static +efi_status_t EFIAPI efi_get_variable_common(u16 *variable_name, + const efi_guid_t *vendor, + u32 *attributes, + efi_uintn_t *data_size, void *data, + bool is_non_volatile) { char *native_name; efi_status_t ret; unsigned long in_size; - const char *val, *s; + const char *val = NULL, *s; + u64 time = 0; u32 attr; - EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes, - data_size, data); - if (!variable_name || !vendor || !data_size) return EFI_EXIT(EFI_INVALID_PARAMETER); ret = efi_to_native(&native_name, variable_name, vendor); if (ret) - return EFI_EXIT(ret); + return ret; EFI_PRINT("get '%s'\n", native_name); val = env_get(native_name); free(native_name); if (!val) - return EFI_EXIT(EFI_NOT_FOUND); + return EFI_NOT_FOUND; - val = parse_attr(val, &attr); + val = parse_attr(val, &attr, &time); in_size = *data_size; @@ -198,7 +699,7 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name, /* number of hexadecimal digits must be even */ if (len & 1) - return EFI_EXIT(EFI_DEVICE_ERROR); + return EFI_DEVICE_ERROR; /* two characters per byte: */ len /= 2; @@ -209,11 +710,13 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name, goto out; } - if (!data) - return EFI_EXIT(EFI_INVALID_PARAMETER); + if (!data) { + debug("Variable with no data shouldn't exist.\n"); + return EFI_INVALID_PARAMETER; + } if (hex2bin(data, s, len)) - return EFI_EXIT(EFI_DEVICE_ERROR); + return EFI_DEVICE_ERROR; EFI_PRINT("got value: \"%s\"\n", s); } else if ((s = prefix(val, "(utf8)"))) { @@ -226,8 +729,10 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name, goto out; } - if (!data) - return EFI_EXIT(EFI_INVALID_PARAMETER); + if (!data) { + debug("Variable with no data shouldn't exist.\n"); + return EFI_INVALID_PARAMETER; + } memcpy(data, s, len); ((char *)data)[len] = '\0'; @@ -235,13 +740,67 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name, EFI_PRINT("got value: \"%s\"\n", (char *)data); } else { EFI_PRINT("invalid value: '%s'\n", val); - return EFI_EXIT(EFI_DEVICE_ERROR); + return EFI_DEVICE_ERROR; } out: if (attributes) *attributes = attr & EFI_VARIABLE_MASK; + return ret; +} + +static +efi_status_t EFIAPI efi_get_volatile_variable(u16 *variable_name, + const efi_guid_t *vendor, + u32 *attributes, + efi_uintn_t *data_size, + void *data) +{ + return efi_get_variable_common(variable_name, vendor, attributes, + data_size, data, false); +} + +efi_status_t EFIAPI efi_get_nonvolatile_variable(u16 *variable_name, + const efi_guid_t *vendor, + u32 *attributes, + efi_uintn_t *data_size, + void *data) +{ + return efi_get_variable_common(variable_name, vendor, attributes, + data_size, data, true); +} + +/** + * efi_efi_get_variable() - retrieve value of a UEFI variable + * + * This function implements the GetVariable runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @variable_name: name of the variable + * @vendor: vendor GUID + * @attributes: attributes of the variable + * @data_size: size of the buffer to which the variable value is copied + * @data: buffer to which the variable value is copied + * Return: status code + */ +efi_status_t EFIAPI efi_get_variable(u16 *variable_name, + const efi_guid_t *vendor, u32 *attributes, + efi_uintn_t *data_size, void *data) +{ + efi_status_t ret; + + EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes, + data_size, data); + + ret = efi_get_volatile_variable(variable_name, vendor, attributes, + data_size, data); + if (ret == EFI_NOT_FOUND) + ret = efi_get_nonvolatile_variable(variable_name, vendor, + attributes, data_size, data); + return EFI_EXIT(ret); } @@ -275,6 +834,7 @@ static efi_status_t parse_uboot_variable(char *variable, char *guid, *name, *end, c; size_t name_len; efi_uintn_t old_variable_name_size; + u64 time; u16 *p; guid = strchr(variable, '_'); @@ -309,7 +869,7 @@ static efi_status_t parse_uboot_variable(char *variable, *(name - 1) = c; /* attributes */ - parse_attr(end, attributes); + parse_attr(end, attributes, &time); return EFI_SUCCESS; } @@ -391,7 +951,7 @@ efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size, list_len = hexport_r(&env_htab, '\n', H_MATCH_REGEX | H_MATCH_KEY, &efi_variables_list, 0, 1, regexlist); - /* 1 indicates that no match was found */ + if (list_len <= 1) return EFI_EXIT(EFI_NOT_FOUND); @@ -404,143 +964,319 @@ efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size, return EFI_EXIT(ret); } -/** - * efi_set_variable() - set value of a UEFI variable - * - * This function implements the SetVariable runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * @variable_name: name of the variable - * @vendor: vendor GUID - * @attributes: attributes of the variable - * @data_size: size of the buffer with the variable value - * @data: buffer with the variable value - * Return: status code - */ -efi_status_t EFIAPI efi_set_variable(u16 *variable_name, - const efi_guid_t *vendor, u32 attributes, - efi_uintn_t data_size, const void *data) +static +efi_status_t EFIAPI efi_set_variable_common(u16 *variable_name, + const efi_guid_t *vendor, + u32 attributes, + efi_uintn_t data_size, + const void *data, + bool ro_check, + bool is_non_volatile) { - char *native_name = NULL, *val = NULL, *s; - const char *old_val; - size_t old_size; - efi_status_t ret = EFI_SUCCESS; + char *native_name = NULL, *old_data = NULL, *val = NULL, *s; + efi_uintn_t old_size; + bool append, delete; + u64 time = 0; u32 attr; + efi_status_t ret = EFI_SUCCESS; - EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes, - data_size, data); + debug("%s: set '%s'\n", __func__, native_name); if (!variable_name || !*variable_name || !vendor || ((attributes & EFI_VARIABLE_RUNTIME_ACCESS) && !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS))) { ret = EFI_INVALID_PARAMETER; - goto out; + goto err; } ret = efi_to_native(&native_name, variable_name, vendor); if (ret) - goto out; + goto err; + + /* check if a variable exists */ + old_size = 0; + attr = 0; + ret = EFI_CALL(efi_get_variable(variable_name, vendor, &attr, + &old_size, NULL)); + if (ret == EFI_BUFFER_TOO_SMALL) { + if ((is_non_volatile && !(attr & EFI_VARIABLE_NON_VOLATILE)) || + (!is_non_volatile && (attr & EFI_VARIABLE_NON_VOLATILE))) { + ret = EFI_INVALID_PARAMETER; + goto err; + } + } - old_val = env_get(native_name); - if (old_val) { - old_val = parse_attr(old_val, &attr); + append = !!(attributes & EFI_VARIABLE_APPEND_WRITE); + attributes &= ~(u32)EFI_VARIABLE_APPEND_WRITE; + delete = !append && (!data_size || !attributes); - /* check read-only first */ - if (attr & READ_ONLY) { + /* check attributes */ + if (old_size) { + if (ro_check && (attr & READ_ONLY)) { ret = EFI_WRITE_PROTECTED; - goto out; - } - - if ((data_size == 0 && - !(attributes & EFI_VARIABLE_APPEND_WRITE)) || - !attributes) { - /* delete the variable: */ - env_set(native_name, NULL); - ret = EFI_SUCCESS; - goto out; + goto err; } /* attributes won't be changed */ - if (attr != (attributes & ~EFI_VARIABLE_APPEND_WRITE)) { + if (!delete && + ((ro_check && attr != attributes) || + (!ro_check && ((attr & ~(u32)READ_ONLY) + != (attributes & ~(u32)READ_ONLY))))) { ret = EFI_INVALID_PARAMETER; - goto out; - } - - if (attributes & EFI_VARIABLE_APPEND_WRITE) { - if (!prefix(old_val, "(blob)")) { - ret = EFI_DEVICE_ERROR; - goto out; - } - old_size = strlen(old_val); - } else { - old_size = 0; + goto err; } } else { - if (data_size == 0 || !attributes || - (attributes & EFI_VARIABLE_APPEND_WRITE)) { + if (delete || append) { /* * Trying to delete or to update a non-existent * variable. */ ret = EFI_NOT_FOUND; - goto out; + goto err; + } + } + + if (((!u16_strcmp(variable_name, L"PK") || + !u16_strcmp(variable_name, L"KEK")) && + !guidcmp(vendor, &efi_global_variable_guid)) || + ((!u16_strcmp(variable_name, L"db") || + !u16_strcmp(variable_name, L"dbx")) && + !guidcmp(vendor, &efi_guid_image_security_database))) { + /* authentication is mandatory */ + if (!(attributes & + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) { + debug("%ls: AUTHENTICATED_WRITE_ACCESS required\n", + variable_name); + ret = EFI_INVALID_PARAMETER; + goto err; + } + } + + /* authenticate a variable */ + if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT)) { + if (attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) { + ret = EFI_INVALID_PARAMETER; + goto err; + } + if (attributes & + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { + ret = efi_variable_authenticate(variable_name, vendor, + &data_size, &data, + attributes, &attr, + &time); + if (ret != EFI_SUCCESS) + goto err; + + /* last chance to check for delete */ + if (!data_size) + delete = true; } + } else { + if (attributes & + (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) { + debug("Secure boot is not configured\n"); + ret = EFI_INVALID_PARAMETER; + goto err; + } + } + + /* delete a variable */ + if (delete) { + /* !old_size case has been handled before */ + val = NULL; + ret = EFI_SUCCESS; + goto out; + } + if (append) { + old_data = malloc(old_size); + if (!old_data) { + return EFI_OUT_OF_RESOURCES; + goto err; + } + ret = EFI_CALL(efi_get_variable(variable_name, vendor, + &attr, &old_size, old_data)); + if (ret != EFI_SUCCESS) + goto err; + } else { old_size = 0; } - val = malloc(old_size + 2 * data_size - + strlen("{ro,run,boot,nv}(blob)") + 1); + val = malloc(2 * old_size + 2 * data_size + + strlen("{ro,run,boot,nv,time=0123456701234567}(blob)") + + 1); if (!val) { ret = EFI_OUT_OF_RESOURCES; - goto out; + goto err; } s = val; - /* store attributes */ - attributes &= (EFI_VARIABLE_NON_VOLATILE | + /* + * store attributes + */ + attributes &= (READ_ONLY | + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS); + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS); s += sprintf(s, "{"); while (attributes) { - u32 attr = 1 << (ffs(attributes) - 1); + attr = 1 << (ffs(attributes) - 1); - if (attr == EFI_VARIABLE_NON_VOLATILE) + if (attr == READ_ONLY) { + s += sprintf(s, "ro"); + } else if (attr == EFI_VARIABLE_NON_VOLATILE) { s += sprintf(s, "nv"); - else if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) + } else if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) { s += sprintf(s, "boot"); - else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) + } else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) { s += sprintf(s, "run"); + } else if (attr == + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { + s += sprintf(s, "time="); + s = bin2hex(s, (u8 *)&time, sizeof(time)); + } attributes &= ~attr; if (attributes) s += sprintf(s, ","); } s += sprintf(s, "}"); - - if (old_size) - /* APPEND_WRITE */ - s += sprintf(s, old_val); - else - s += sprintf(s, "(blob)"); + s += sprintf(s, "(blob)"); /* store payload: */ + if (append) + s = bin2hex(s, old_data, old_size); s = bin2hex(s, data, data_size); *s = '\0'; EFI_PRINT("setting: %s=%s\n", native_name, val); - if (env_set(native_name, val)) +out: + if (env_set(native_name, val)) { ret = EFI_DEVICE_ERROR; + } else { + bool vendor_keys_modified = false; + + if ((u16_strcmp(variable_name, L"PK") == 0 && + guidcmp(vendor, &efi_global_variable_guid) == 0)) { + ret = efi_transfer_secure_state( + (delete ? EFI_MODE_SETUP : + EFI_MODE_USER)); + if (ret != EFI_SUCCESS) + goto err; + + if (efi_secure_mode != EFI_MODE_SETUP) + vendor_keys_modified = true; + } else if ((u16_strcmp(variable_name, L"KEK") == 0 && + guidcmp(vendor, &efi_global_variable_guid) == 0)) { + if (efi_secure_mode != EFI_MODE_SETUP) + vendor_keys_modified = true; + } -out: + /* update VendorKeys */ + if (vendor_keys_modified & efi_vendor_keys) { + efi_vendor_keys = 0; + ret = efi_set_variable_internal( + L"VendorKeys", + &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_RUNTIME_ACCESS + | READ_ONLY, + sizeof(efi_vendor_keys), + &efi_vendor_keys, + false); + } else { + ret = EFI_SUCCESS; + } + } + +err: free(native_name); + free(old_data); free(val); - return EFI_EXIT(ret); + return ret; +} + +static +efi_status_t EFIAPI efi_set_volatile_variable(u16 *variable_name, + const efi_guid_t *vendor, + u32 attributes, + efi_uintn_t data_size, + const void *data, + bool ro_check) +{ + return efi_set_variable_common(variable_name, vendor, attributes, + data_size, data, ro_check, false); +} + +efi_status_t EFIAPI efi_set_nonvolatile_variable(u16 *variable_name, + const efi_guid_t *vendor, + u32 attributes, + efi_uintn_t data_size, + const void *data, + bool ro_check) +{ + efi_status_t ret; + + ret = efi_set_variable_common(variable_name, vendor, attributes, + data_size, data, ro_check, true); + + return ret; +} + +static efi_status_t efi_set_variable_internal(u16 *variable_name, + const efi_guid_t *vendor, + u32 attributes, + efi_uintn_t data_size, + const void *data, + bool ro_check) +{ + efi_status_t ret; + + if (attributes & EFI_VARIABLE_NON_VOLATILE) + ret = efi_set_nonvolatile_variable(variable_name, vendor, + attributes, + data_size, data, ro_check); + else + ret = efi_set_volatile_variable(variable_name, vendor, + attributes, data_size, data, + ro_check); + + return ret; +} + +/** + * efi_set_variable() - set value of a UEFI variable + * + * This function implements the SetVariable runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @variable_name: name of the variable + * @vendor: vendor GUID + * @attributes: attributes of the variable + * @data_size: size of the buffer with the variable value + * @data: buffer with the variable value + * Return: status code + */ +efi_status_t EFIAPI efi_set_variable(u16 *variable_name, + const efi_guid_t *vendor, u32 attributes, + efi_uintn_t data_size, const void *data) +{ + EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes, + data_size, data); + + /* READ_ONLY bit is not part of API */ + attributes &= ~(u32)READ_ONLY; + + return EFI_EXIT(efi_set_variable_internal(variable_name, vendor, + attributes, data_size, data, + true)); } /** @@ -640,5 +1376,9 @@ void efi_variables_boot_exit_notify(void) */ efi_status_t efi_init_variables(void) { - return EFI_SUCCESS; + efi_status_t ret; + + ret = efi_init_secure_state(); + + return ret; } diff --git a/lib/efi_loader/efi_watchdog.c b/lib/efi_loader/efi_watchdog.c index 6f69b76e4d..61ea0f7926 100644 --- a/lib/efi_loader/efi_watchdog.c +++ b/lib/efi_loader/efi_watchdog.c @@ -13,7 +13,9 @@ static struct efi_event *watchdog_timer_event; -/* +/** + * efi_watchdog_timer_notify() - resets system upon watchdog event + * * Reset the system when the watchdog event is notified. * * @event: the watchdog event @@ -31,13 +33,13 @@ static void EFIAPI efi_watchdog_timer_notify(struct efi_event *event, EFI_EXIT(EFI_UNSUPPORTED); } -/* - * Reset the watchdog timer. +/** + * efi_set_watchdog() - resets the watchdog timer * * This function is used by the SetWatchdogTimer service. * * @timeout: seconds before reset by watchdog - * @return: status code + * Return: status code */ efi_status_t efi_set_watchdog(unsigned long timeout) { @@ -53,10 +55,12 @@ efi_status_t efi_set_watchdog(unsigned long timeout) return r; } -/* - * Initialize the EFI watchdog. +/** + * efi_watchdog_register() - initializes the EFI watchdog + * + * This function is called by efi_init_obj_list(). * - * This function is called by efi_init_obj_list() + * Return: status code */ efi_status_t efi_watchdog_register(void) { diff --git a/lib/fdtdec.c b/lib/fdtdec.c index eb11fc898e..9ecfa2a2d7 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -1433,14 +1433,9 @@ int fdtdec_set_carveout(void *blob, const char *node, const char *prop_name, const struct fdt_memory *carveout) { uint32_t phandle; - int err, offset; + int err, offset, len; fdt32_t value; - - /* XXX implement support for multiple phandles */ - if (index > 0) { - debug("invalid index %u\n", index); - return -FDT_ERR_BADOFFSET; - } + void *prop; err = fdtdec_add_reserved_memory(blob, name, carveout, &phandle); if (err < 0) { @@ -1456,10 +1451,31 @@ int fdtdec_set_carveout(void *blob, const char *node, const char *prop_name, value = cpu_to_fdt32(phandle); - err = fdt_setprop(blob, offset, prop_name, &value, sizeof(value)); + if (!fdt_getprop(blob, offset, prop_name, &len)) { + if (len == -FDT_ERR_NOTFOUND) + len = 0; + else + return len; + } + + if ((index + 1) * sizeof(value) > len) { + err = fdt_setprop_placeholder(blob, offset, prop_name, + (index + 1) * sizeof(value), + &prop); + if (err < 0) { + debug("failed to resize reserved memory property: %s\n", + fdt_strerror(err)); + return err; + } + } + + err = fdt_setprop_inplace_namelen_partial(blob, offset, prop_name, + strlen(prop_name), + index * sizeof(value), + &value, sizeof(value)); if (err < 0) { - debug("failed to set %s property for node %s: %d\n", prop_name, - node, err); + debug("failed to update %s property for node %s: %s\n", + prop_name, node, fdt_strerror(err)); return err; } diff --git a/test/Kconfig b/test/Kconfig index 0157e0b060..28704a25b6 100644 --- a/test/Kconfig +++ b/test/Kconfig @@ -40,6 +40,15 @@ config UT_LIB_RSA endif +config UT_LOG + bool "Unit tests for logging functions" + depends on UNIT_TEST + default y + help + Enables the 'ut log' command which tests logging functions like + log_err(). + See also CONFIG_LOG_TEST which provides the 'log test' command. + config UT_TIME bool "Unit tests for time functions" depends on UNIT_TEST diff --git a/test/Makefile b/test/Makefile index 2fe41f489c..2971d0d87f 100644 --- a/test/Makefile +++ b/test/Makefile @@ -10,5 +10,5 @@ obj-$(CONFIG_SANDBOX) += compression.o obj-$(CONFIG_SANDBOX) += print_ut.o obj-$(CONFIG_UT_TIME) += time_ut.o obj-$(CONFIG_UT_UNICODE) += unicode_ut.o -obj-$(CONFIG_$(SPL_)LOG) += log/ +obj-y += log/ obj-$(CONFIG_UNIT_TEST) += lib/ diff --git a/test/cmd_ut.c b/test/cmd_ut.c index a3a9d49f7e..7fdcdbb1a6 100644 --- a/test/cmd_ut.c +++ b/test/cmd_ut.c @@ -60,6 +60,9 @@ static cmd_tbl_t cmd_ut_sub[] = { #ifdef CONFIG_UT_LIB U_BOOT_CMD_MKENT(lib, CONFIG_SYS_MAXARGS, 1, do_ut_lib, "", ""), #endif +#ifdef CONFIG_UT_LOG + U_BOOT_CMD_MKENT(log, CONFIG_SYS_MAXARGS, 1, do_ut_log, "", ""), +#endif #ifdef CONFIG_UT_TIME U_BOOT_CMD_MKENT(time, CONFIG_SYS_MAXARGS, 1, do_ut_time, "", ""), #endif @@ -125,6 +128,9 @@ static char ut_help_text[] = #ifdef CONFIG_UT_LIB "ut lib [test-name] - test library functions\n" #endif +#ifdef CONFIG_UT_LOG + "ut log [test-name] - test logging functions\n" +#endif #ifdef CONFIG_UT_OPTEE "ut optee [test-name]\n" #endif diff --git a/test/dm/Makefile b/test/dm/Makefile index 3daf8a544e..f55874c0f2 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_LED) += led.o obj-$(CONFIG_DM_MAILBOX) += mailbox.o obj-$(CONFIG_DM_MMC) += mmc.o obj-y += ofnode.o +obj-y += fdtdec.o obj-$(CONFIG_OSD) += osd.o obj-$(CONFIG_DM_VIDEO) += panel.o obj-$(CONFIG_DM_PCI) += pci.o diff --git a/test/dm/fdtdec.c b/test/dm/fdtdec.c new file mode 100644 index 0000000000..b2f75b5843 --- /dev/null +++ b/test/dm/fdtdec.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 NXP + */ + +#include <common.h> +#include <dm.h> +#include <dm/of_extra.h> +#include <dm/test.h> +#include <test/ut.h> + +static int dm_test_fdtdec_set_carveout(struct unit_test_state *uts) +{ + struct fdt_memory resv; + void *blob; + const fdt32_t *prop; + int blob_sz, len, offset; + + blob_sz = fdt_totalsize(gd->fdt_blob) + 4096; + blob = malloc(blob_sz); + ut_assertnonnull(blob); + + /* Make a writtable copy of the fdt blob */ + ut_assertok(fdt_open_into(gd->fdt_blob, blob, blob_sz)); + + resv.start = 0x1000; + resv.end = 0x2000; + ut_assertok(fdtdec_set_carveout(blob, "/a-test", + "memory-region", 2, "test_resv1", + &resv)); + + resv.start = 0x10000; + resv.end = 0x20000; + ut_assertok(fdtdec_set_carveout(blob, "/a-test", + "memory-region", 1, "test_resv2", + &resv)); + + resv.start = 0x100000; + resv.end = 0x200000; + ut_assertok(fdtdec_set_carveout(blob, "/a-test", + "memory-region", 0, "test_resv3", + &resv)); + + offset = fdt_path_offset(blob, "/a-test"); + ut_assert(offset > 0); + prop = fdt_getprop(blob, offset, "memory-region", &len); + ut_assertnonnull(prop); + + ut_asserteq(len, 12); + ut_assert(fdt_node_offset_by_phandle(blob, fdt32_to_cpu(prop[0])) > 0); + ut_assert(fdt_node_offset_by_phandle(blob, fdt32_to_cpu(prop[1])) > 0); + ut_assert(fdt_node_offset_by_phandle(blob, fdt32_to_cpu(prop[2])) > 0); + + free(blob); + + return 0; +} +DM_TEST(dm_test_fdtdec_set_carveout, + DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT | DM_TESTF_FLAT_TREE); diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index 75ae08081c..a56275aef9 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -255,7 +255,7 @@ static int dm_test_fdt(struct unit_test_state *uts) int ret; int i; - ret = dm_scan_fdt(gd->fdt_blob, false); + ret = dm_extended_scan_fdt(gd->fdt_blob, false); ut_assert(!ret); ret = uclass_get(UCLASS_TEST_FDT, &uc); @@ -867,6 +867,7 @@ static int dm_test_read_int(struct unit_test_state *uts) u32 val32; s32 sval; uint val; + u64 val64; ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); ut_asserteq_str("a-test", dev->name); @@ -891,10 +892,48 @@ static int dm_test_read_int(struct unit_test_state *uts) ut_assertok(dev_read_u32u(dev, "uint-value", &val)); ut_asserteq(-1234, val); + ut_assertok(dev_read_u64(dev, "int64-value", &val64)); + ut_asserteq_64(0x1111222233334444, val64); + + ut_asserteq_64(-EINVAL, dev_read_u64(dev, "missing", &val64)); + ut_asserteq_64(6, dev_read_u64_default(dev, "missing", 6)); + + ut_asserteq_64(0x1111222233334444, + dev_read_u64_default(dev, "int64-value", 6)); + return 0; } DM_TEST(dm_test_read_int, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); +static int dm_test_read_int_index(struct unit_test_state *uts) +{ + struct udevice *dev; + u32 val32; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); + ut_asserteq_str("a-test", dev->name); + + ut_asserteq(-EINVAL, dev_read_u32_index(dev, "missing", 0, &val32)); + ut_asserteq(19, dev_read_u32_index_default(dev, "missing", 0, 19)); + + ut_assertok(dev_read_u32_index(dev, "int-array", 0, &val32)); + ut_asserteq(5678, val32); + ut_assertok(dev_read_u32_index(dev, "int-array", 1, &val32)); + ut_asserteq(9123, val32); + ut_assertok(dev_read_u32_index(dev, "int-array", 2, &val32)); + ut_asserteq(4567, val32); + ut_asserteq(-EOVERFLOW, dev_read_u32_index(dev, "int-array", 3, + &val32)); + + ut_asserteq(5678, dev_read_u32_index_default(dev, "int-array", 0, 2)); + ut_asserteq(9123, dev_read_u32_index_default(dev, "int-array", 1, 2)); + ut_asserteq(4567, dev_read_u32_index_default(dev, "int-array", 2, 2)); + ut_asserteq(2, dev_read_u32_index_default(dev, "int-array", 3, 2)); + + return 0; +} +DM_TEST(dm_test_read_int_index, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + /* Test iteration through devices by drvdata */ static int dm_test_uclass_drvdata(struct unit_test_state *uts) { @@ -953,3 +992,28 @@ static int dm_test_first_child_probe(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_first_child_probe, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test that ofdata is read for parents before children */ +static int dm_test_ofdata_order(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + + ut_assertok(uclass_find_first_device(UCLASS_I2C, &bus)); + ut_assertnonnull(bus); + ut_assert(!(bus->flags & DM_FLAG_PLATDATA_VALID)); + + ut_assertok(device_find_first_child(bus, &dev)); + ut_assertnonnull(dev); + ut_assert(!(dev->flags & DM_FLAG_PLATDATA_VALID)); + + /* read the child's ofdata which should cause the parent's to be read */ + ut_assertok(device_ofdata_to_platdata(dev)); + ut_assert(dev->flags & DM_FLAG_PLATDATA_VALID); + ut_assert(bus->flags & DM_FLAG_PLATDATA_VALID); + + ut_assert(!(dev->flags & DM_FLAG_ACTIVATED)); + ut_assert(!(bus->flags & DM_FLAG_ACTIVATED)); + + return 0; +} +DM_TEST(dm_test_ofdata_order, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/log/Makefile b/test/log/Makefile index e0d0a4745f..4c92550f6e 100644 --- a/test/log/Makefile +++ b/test/log/Makefile @@ -3,3 +3,17 @@ # Copyright (c) 2017 Google, Inc obj-$(CONFIG_LOG_TEST) += log_test.o + +ifdef CONFIG_UT_LOG + +obj-y += test-main.o + +ifdef CONFIG_SANDBOX +obj-$(CONFIG_LOG_SYSLOG) += syslog_test.o +endif + +ifndef CONFIG_LOG +obj-$(CONFIG_CONSOLE_RECORD) += nolog_test.o +endif + +endif # CONFIG_UT_LOG diff --git a/test/log/nolog_test.c b/test/log/nolog_test.c new file mode 100644 index 0000000000..84619521c9 --- /dev/null +++ b/test/log/nolog_test.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Logging function tests for CONFIG_LOG=n. + */ + +/* Needed for testing log_debug() */ +#define DEBUG 1 + +#include <common.h> +#include <console.h> +#include <test/log.h> +#include <test/test.h> +#include <test/suites.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define BUFFSIZE 32 + +static int nolog_test_log_err(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + log_err("testing %s\n", "log_err"); + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "testing log_err")); + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(nolog_test_log_err); + +static int nolog_test_log_warning(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + log_warning("testing %s\n", "log_warning"); + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "testing log_warning")); + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(nolog_test_log_warning); + +static int nolog_test_log_notice(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + log_notice("testing %s\n", "log_notice"); + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "testing log_notice")); + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(nolog_test_log_notice); + +static int nolog_test_log_info(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + log_err("testing %s\n", "log_info"); + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "testing log_info")); + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(nolog_test_log_info); + +#undef _DEBUG +#define _DEBUG 0 +static int nolog_test_nodebug(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + debug("testing %s\n", "debug"); + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(nolog_test_nodebug); + +static int nolog_test_log_nodebug(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + log_debug("testing %s\n", "log_debug"); + gd->flags &= ~GD_FLG_RECORD; + ut_assert(!strcmp(buf, "")); + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(nolog_test_log_nodebug); + +#undef _DEBUG +#define _DEBUG 1 +static int nolog_test_debug(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + debug("testing %s\n", "debug"); + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "testing debug")); + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(nolog_test_debug); + +static int nolog_test_log_debug(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + log_debug("testing %s\n", "log_debug"); + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "testing log_debug")); + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(nolog_test_log_debug); diff --git a/test/log/syslog_test.c b/test/log/syslog_test.c new file mode 100644 index 0000000000..6ca5760eac --- /dev/null +++ b/test/log/syslog_test.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Logging function tests for CONFIG_LOG_SYSLOG=y. + * + * Invoke the test with: ./u-boot -d arch/sandbox/dts/test.dtb + */ + +/* Override CONFIG_LOG_MAX_LEVEL */ +#define LOG_DEBUG + +#include <common.h> +#include <dm/device.h> +#include <hexdump.h> +#include <test/log.h> +#include <test/test.h> +#include <test/suites.h> +#include <test/ut.h> +#include <asm/eth.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct sb_log_env - private data for sandbox ethernet driver + * + * This structure is used for the private data of the sandbox ethernet + * driver. + * + * @expected: string expected to be written by the syslog driver + * @uts: unit test state + */ +struct sb_log_env { + const char *expected; + struct unit_test_state *uts; +}; + +/** + * sb_log_tx_handler() - transmit callback function + * + * This callback function is invoked when a network package is sent using the + * sandbox Ethernet driver. The private data of the driver holds a sb_log_env + * structure with the unit test state and the expected UDP payload. + * + * The following checks are executed: + * + * * the Ethernet packet indicates a IP broadcast message + * * the IP header is for a local UDP broadcast message to port 514 + * * the UDP payload matches the expected string + * + * After testing the pointer to the expected string is set to NULL to signal + * that the callback function has been called. + * + * @dev: sandbox ethernet device + * @packet: Ethernet packet + * @len: length of Ethernet packet + * Return: 0 = success + */ +static int sb_log_tx_handler(struct udevice *dev, void *packet, + unsigned int len) +{ + struct eth_sandbox_priv *priv = dev_get_priv(dev); + struct sb_log_env *env = priv->priv; + /* uts is updated by the ut_assert* macros */ + struct unit_test_state *uts = env->uts; + char *buf = packet; + struct ethernet_hdr *eth_hdr = packet; + struct ip_udp_hdr *ip_udp_hdr; + + /* Check Ethernet header */ + ut_asserteq_mem(ð_hdr->et_dest, net_bcast_ethaddr, ARP_HLEN); + ut_asserteq(ntohs(eth_hdr->et_protlen), PROT_IP); + + /* Check IP header */ + buf += sizeof(struct ethernet_hdr); + ip_udp_hdr = (struct ip_udp_hdr *)buf; + ut_asserteq(ip_udp_hdr->ip_p, IPPROTO_UDP); + ut_asserteq(ip_udp_hdr->ip_dst.s_addr, 0xffffffff); + ut_asserteq(ntohs(ip_udp_hdr->udp_dst), 514); + ut_asserteq(UDP_HDR_SIZE + strlen(env->expected) + 1, + ntohs(ip_udp_hdr->udp_len)); + + /* Check payload */ + buf += sizeof(struct ip_udp_hdr); + ut_asserteq_mem(env->expected, buf, + ntohs(ip_udp_hdr->udp_len) - UDP_HDR_SIZE); + + /* Signal that the callback function has been executed */ + env->expected = NULL; + + return 0; +} + +/** + * syslog_test_log_err() - test log_err() function + * + * @uts: unit test state + * Return: 0 = success + */ +static int syslog_test_log_err(struct unit_test_state *uts) +{ + int old_log_level = gd->default_log_level; + struct sb_log_env env; + + gd->log_fmt = LOGF_DEFAULT; + gd->default_log_level = LOGL_INFO; + env_set("ethact", "eth@10002000"); + env_set("log_hostname", "sandbox"); + env.expected = "<3>sandbox uboot: syslog_test_log_err() " + "testing log_err\n"; + env.uts = uts; + sandbox_eth_set_tx_handler(0, sb_log_tx_handler); + /* Used by ut_assert macros in the tx_handler */ + sandbox_eth_set_priv(0, &env); + log_err("testing %s\n", "log_err"); + /* Check that the callback function was called */ + sandbox_eth_set_tx_handler(0, NULL); + gd->default_log_level = old_log_level; + + return 0; +} +LOG_TEST(syslog_test_log_err); + +/** + * syslog_test_log_warning() - test log_warning() function + * + * @uts: unit test state + * Return: 0 = success + */ +static int syslog_test_log_warning(struct unit_test_state *uts) +{ + int old_log_level = gd->default_log_level; + struct sb_log_env env; + + gd->log_fmt = LOGF_DEFAULT; + gd->default_log_level = LOGL_INFO; + env_set("ethact", "eth@10002000"); + env_set("log_hostname", "sandbox"); + env.expected = "<4>sandbox uboot: syslog_test_log_warning() " + "testing log_warning\n"; + env.uts = uts; + sandbox_eth_set_tx_handler(0, sb_log_tx_handler); + /* Used by ut_assert macros in the tx_handler */ + sandbox_eth_set_priv(0, &env); + log_warning("testing %s\n", "log_warning"); + sandbox_eth_set_tx_handler(0, NULL); + /* Check that the callback function was called */ + ut_assertnull(env.expected); + gd->default_log_level = old_log_level; + + return 0; +} +LOG_TEST(syslog_test_log_warning); + +/** + * syslog_test_log_notice() - test log_notice() function + * + * @uts: unit test state + * Return: 0 = success + */ +static int syslog_test_log_notice(struct unit_test_state *uts) +{ + int old_log_level = gd->default_log_level; + struct sb_log_env env; + + gd->log_fmt = LOGF_DEFAULT; + gd->default_log_level = LOGL_INFO; + env_set("ethact", "eth@10002000"); + env_set("log_hostname", "sandbox"); + env.expected = "<5>sandbox uboot: syslog_test_log_notice() " + "testing log_notice\n"; + env.uts = uts; + sandbox_eth_set_tx_handler(0, sb_log_tx_handler); + /* Used by ut_assert macros in the tx_handler */ + sandbox_eth_set_priv(0, &env); + log_notice("testing %s\n", "log_notice"); + sandbox_eth_set_tx_handler(0, NULL); + /* Check that the callback function was called */ + ut_assertnull(env.expected); + gd->default_log_level = old_log_level; + + return 0; +} +LOG_TEST(syslog_test_log_notice); + +/** + * syslog_test_log_info() - test log_info() function + * + * @uts: unit test state + * Return: 0 = success + */ +static int syslog_test_log_info(struct unit_test_state *uts) +{ + int old_log_level = gd->default_log_level; + struct sb_log_env env; + + gd->log_fmt = LOGF_DEFAULT; + gd->default_log_level = LOGL_INFO; + env_set("ethact", "eth@10002000"); + env_set("log_hostname", "sandbox"); + env.expected = "<6>sandbox uboot: syslog_test_log_info() " + "testing log_info\n"; + env.uts = uts; + sandbox_eth_set_tx_handler(0, sb_log_tx_handler); + /* Used by ut_assert macros in the tx_handler */ + sandbox_eth_set_priv(0, &env); + log_info("testing %s\n", "log_info"); + sandbox_eth_set_tx_handler(0, NULL); + /* Check that the callback function was called */ + ut_assertnull(env.expected); + gd->default_log_level = old_log_level; + + return 0; +} +LOG_TEST(syslog_test_log_info); + +/** + * syslog_test_log_debug() - test log_debug() function + * + * @uts: unit test state + * Return: 0 = success + */ +static int syslog_test_log_debug(struct unit_test_state *uts) +{ + int old_log_level = gd->default_log_level; + struct sb_log_env env; + + gd->log_fmt = LOGF_DEFAULT; + gd->default_log_level = LOGL_DEBUG; + env_set("ethact", "eth@10002000"); + env_set("log_hostname", "sandbox"); + env.expected = "<7>sandbox uboot: syslog_test_log_debug() " + "testing log_debug\n"; + env.uts = uts; + sandbox_eth_set_tx_handler(0, sb_log_tx_handler); + /* Used by ut_assert macros in the tx_handler */ + sandbox_eth_set_priv(0, &env); + log_debug("testing %s\n", "log_debug"); + sandbox_eth_set_tx_handler(0, NULL); + /* Check that the callback function was called */ + ut_assertnull(env.expected); + gd->default_log_level = old_log_level; + + return 0; +} +LOG_TEST(syslog_test_log_debug); + +/** + * syslog_test_log_nodebug() - test logging level filter + * + * Verify that log_debug() does not lead to a log message if the logging level + * is set to LOGL_INFO. + * + * @uts: unit test state + * Return: 0 = success + */ +static int syslog_test_log_nodebug(struct unit_test_state *uts) +{ + int old_log_level = gd->default_log_level; + struct sb_log_env env; + + gd->log_fmt = LOGF_DEFAULT; + gd->default_log_level = LOGL_INFO; + env_set("ethact", "eth@10002000"); + env_set("log_hostname", "sandbox"); + env.expected = "<7>sandbox uboot: syslog_test_log_nodebug() " + "testing log_debug\n"; + env.uts = uts; + sandbox_eth_set_tx_handler(0, sb_log_tx_handler); + /* Used by ut_assert macros in the tx_handler */ + sandbox_eth_set_priv(0, &env); + log_debug("testing %s\n", "log_debug"); + sandbox_eth_set_tx_handler(0, NULL); + /* Check that the callback function was not called */ + ut_assertnonnull(env.expected); + gd->default_log_level = old_log_level; + + return 0; +} +LOG_TEST(syslog_test_log_nodebug); diff --git a/test/log/test-main.c b/test/log/test-main.c new file mode 100644 index 0000000000..855de47f33 --- /dev/null +++ b/test/log/test-main.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Logging function tests. + */ + +#include <common.h> +#include <console.h> +#include <test/log.h> +#include <test/suites.h> + +int do_ut_log(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct unit_test *tests = ll_entry_start(struct unit_test, log_test); + const int n_ents = ll_entry_count(struct unit_test, log_test); + + return cmd_ut_category("log", "log_test_", + tests, n_ents, argc, argv); +} diff --git a/test/py/README.md b/test/py/README.md index 2e5025258d..fddc104b26 100644 --- a/test/py/README.md +++ b/test/py/README.md @@ -37,7 +37,15 @@ will be required. The following is an incomplete list: | openssl | | sudo OR guestmount | | e2fsprogs | +| util-linux | +| coreutils | | dosfstools | +| efitools | +| mount | +| mtools | +| sbsigntool | +| udisks2 | + Please use the apporirate commands for your distribution to match these tools up with the package that provides them. diff --git a/test/py/tests/test_efi_secboot/conftest.py b/test/py/tests/test_efi_secboot/conftest.py new file mode 100644 index 0000000000..e542fef6e8 --- /dev/null +++ b/test/py/tests/test_efi_secboot/conftest.py @@ -0,0 +1,151 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Linaro Limited +# Author: AKASHI Takahiro <takahiro.akashi@linaro.org> + +import os +import os.path +import pytest +import re +from subprocess import call, check_call, check_output, CalledProcessError +from defs import * + +# from test/py/conftest.py +def tool_is_in_path(tool): + for path in os.environ["PATH"].split(os.pathsep): + fn = os.path.join(path, tool) + if os.path.isfile(fn) and os.access(fn, os.X_OK): + return True + return False + +# +# Fixture for UEFI secure boot test +# +@pytest.fixture(scope='session') +def efi_boot_env(request, u_boot_config): + """Set up a file system to be used in UEFI secure boot test. + + Args: + request: Pytest request object. + u_boot_config: U-boot configuration. + + Return: + A path to disk image to be used for testing + """ + global HELLO_PATH + + image_path = u_boot_config.persistent_data_dir + image_path = image_path + '/' + EFI_SECBOOT_IMAGE_NAME + image_size = EFI_SECBOOT_IMAGE_SIZE + part_size = EFI_SECBOOT_PART_SIZE + fs_type = EFI_SECBOOT_FS_TYPE + + if HELLO_PATH == '': + HELLO_PATH = u_boot_config.build_dir + '/lib/efi_loader/helloworld.efi' + + try: + non_root = tool_is_in_path('udisksctl') + + # create a disk/partition + check_call('dd if=/dev/zero of=%s bs=1MiB count=%d' + % (image_path, image_size), shell=True) + check_call('sgdisk %s -n 1:0:+%dMiB' + % (image_path, part_size), shell=True) + # create a file system + check_call('dd if=/dev/zero of=%s.tmp bs=1MiB count=%d' + % (image_path, part_size), shell=True) + check_call('mkfs -t %s %s.tmp' % (fs_type, image_path), shell=True) + check_call('dd if=%s.tmp of=%s bs=1MiB seek=1 count=%d conv=notrunc' + % (image_path, image_path, 1), shell=True) + check_call('rm %s.tmp' % image_path, shell=True) + if non_root: + out_data = check_output('udisksctl loop-setup -f %s -o %d' + % (image_path, 1048576), shell=True).decode() + m = re.search('(?<= as )(.*)\.', out_data) + loop_dev = m.group(1) + # print 'loop device is: %s' % loop_dev + out_data = check_output('udisksctl info -b %s' + % loop_dev, shell=True).decode() + m = re.search('MountPoints:[ \t]+(.*)', out_data) + mnt_point = m.group(1) + else: + loop_dev = check_output('sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"' + % (part_size, image_path), shell=True).decode() + mnt_point = '/mnt' + check_output('sudo mount -t %s -o umask=000 %s %s' + % (fs_type, loop_dev, mnt_point), shell=True) + + # print 'mount point is: %s' % mnt_point + + # suffix + # *.key: RSA private key in PEM + # *.crt: X509 certificate (self-signed) in PEM + # *.esl: signature list + # *.hash: message digest of image as signature list + # *.auth: signed signature list in signature database format + # *.efi: UEFI image + # *.efi.signed: signed UEFI image + + # Create signature database + ## PK + check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ -keyout PK.key -out PK.crt -nodes -days 365' + % mnt_point, shell=True) + check_call('cd %s; %scert-to-efi-sig-list -g %s PK.crt PK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + ## PK_null for deletion + check_call('cd %s; sleep 2; touch PK_null.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK_null.esl PK_null.auth' + % (mnt_point, EFITOOLS_PATH), shell=True) + ## KEK + check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ -keyout KEK.key -out KEK.crt -nodes -days 365' + % mnt_point, shell=True) + check_call('cd %s; %scert-to-efi-sig-list -g %s KEK.crt KEK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + ## db + check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ -keyout db.key -out db.crt -nodes -days 365' + % mnt_point, shell=True) + check_call('cd %s; %scert-to-efi-sig-list -g %s db.crt db.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + ## db1 + check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db1/ -keyout db1.key -out db1.crt -nodes -days 365' + % mnt_point, shell=True) + check_call('cd %s; %scert-to-efi-sig-list -g %s db1.crt db1.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db1.esl db1.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + ## db1-update + check_call('cd %s; %ssign-efi-sig-list -a -c KEK.crt -k KEK.key db db1.esl db1-update.auth' + % (mnt_point, EFITOOLS_PATH), shell=True) + ## dbx + check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_dbx/ -keyout dbx.key -out dbx.crt -nodes -days 365' + % mnt_point, shell=True) + check_call('cd %s; %scert-to-efi-sig-list -g %s dbx.crt dbx.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key dbx dbx.esl dbx.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + + # Copy image + check_call('cp %s %s' % (HELLO_PATH, mnt_point), shell=True) + + ## Sign image + check_call('cd %s; sbsign --key db.key --cert db.crt helloworld.efi' + % mnt_point, shell=True) + ## Digest image + check_call('cd %s; %shash-to-efi-sig-list helloworld.efi db_hello.hash; %ssign-efi-sig-list -c KEK.crt -k KEK.key db db_hello.hash db_hello.auth' + % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH), + shell=True) + + if non_root: + check_call('udisksctl unmount -b %s' % loop_dev, shell=True) + # not needed + # check_call('udisksctl loop-delete -b %s' % loop_dev, shell=True) + else: + check_call('sudo umount %s' % loop_dev, shell=True) + check_call('sudo losetup -d %s' % loop_dev, shell=True) + + except CalledProcessError as e: + pytest.skip('Setup failed: %s' % e.cmd) + return + else: + yield image_path + finally: + call('rm -f %s' % image_path, shell=True) diff --git a/test/py/tests/test_efi_secboot/defs.py b/test/py/tests/test_efi_secboot/defs.py new file mode 100644 index 0000000000..d6222809c5 --- /dev/null +++ b/test/py/tests/test_efi_secboot/defs.py @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0+ + +# Disk image name +EFI_SECBOOT_IMAGE_NAME='test_efi_secboot.img' + +# Size in MiB +EFI_SECBOOT_IMAGE_SIZE=16 +EFI_SECBOOT_PART_SIZE=8 + +# Partition file system type +EFI_SECBOOT_FS_TYPE='vfat' + +# Owner guid +GUID='11111111-2222-3333-4444-123456789abc' + +# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and +# you need build a newer version on your own. +EFITOOLS_PATH='' + +# Hello World application for sandbox +HELLO_PATH='' diff --git a/test/py/tests/test_efi_secboot/test_authvar.py b/test/py/tests/test_efi_secboot/test_authvar.py new file mode 100644 index 0000000000..55dcaa95f1 --- /dev/null +++ b/test/py/tests/test_efi_secboot/test_authvar.py @@ -0,0 +1,282 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Linaro Limited +# Author: AKASHI Takahiro <takahiro.akashi@linaro.org> +# +# U-Boot UEFI: Variable Authentication Test + +""" +This test verifies variable authentication +""" + +import pytest +import re +from defs import * + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('efi_secure_boot') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.slow +class TestEfiAuthVar(object): + def test_efi_var_auth1(self, u_boot_console, efi_boot_env): + """ + Test Case 1 - Install signature database + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 1a'): + # Test Case 1a, Initial secure state + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'printenv -e SecureBoot']) + assert('00000000: 00' in ''.join(output)) + + output = u_boot_console.run_command( + 'printenv -e SetupMode') + assert('00000000: 01' in output) + + with u_boot_console.log.section('Test Case 1b'): + # Test Case 1b, PK without AUTHENTICATED_WRITE_ACCESS + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -i 4000000,$filesize PK']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + with u_boot_console.log.section('Test Case 1c'): + # Test Case 1c, install PK + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK', + 'printenv -e -n PK']) + assert(re.search('PK:', ''.join(output))) + + output = u_boot_console.run_command( + 'printenv -e SecureBoot') + assert('00000000: 01' in output) + output = u_boot_console.run_command( + 'printenv -e SetupMode') + assert('00000000: 00' in output) + + with u_boot_console.log.section('Test Case 1d'): + # Test Case 1d, db/dbx without KEK + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + with u_boot_console.log.section('Test Case 1e'): + # Test Case 1e, install KEK + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -i 4000000,$filesize KEK']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'printenv -e -n KEK']) + assert(re.search('KEK:', ''.join(output))) + + output = u_boot_console.run_command( + 'printenv -e SecureBoot') + assert('00000000: 01' in output) + + with u_boot_console.log.section('Test Case 1f'): + # Test Case 1f, install db + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -i 4000000,$filesize db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + + output = u_boot_console.run_command( + 'printenv -e SecureBoot') + assert('00000000: 01' in output) + + with u_boot_console.log.section('Test Case 1g'): + # Test Case 1g, install dbx + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -i 4000000,$filesize db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + + output = u_boot_console.run_command( + 'printenv -e SecureBoot') + assert('00000000: 01' in output) + + def test_efi_var_auth2(self, u_boot_console, efi_boot_env): + """ + Test Case 2 - Update database by overwriting + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 2a'): + # Test Case 2a, update without AUTHENTICATED_WRITE_ACCESS + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db1.auth', + 'setenv -e -nv -bs -rt -i 4000000,$filesize db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + with u_boot_console.log.section('Test Case 2b'): + # Test Case 2b, update without correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.esl', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + with u_boot_console.log.section('Test Case 2c'): + # Test Case 2c, update with correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db1.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + + def test_efi_var_auth3(self, u_boot_console, efi_boot_env): + """ + Test Case 3 - Append database + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 3a'): + # Test Case 3a, update without AUTHENTICATED_WRITE_ACCESS + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db1.auth', + 'setenv -e -nv -bs -rt -a -i 4000000,$filesize db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + with u_boot_console.log.section('Test Case 3b'): + # Test Case 3b, update without correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.esl', + 'setenv -e -nv -bs -rt -at -a -i 4000000,$filesize db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + + with u_boot_console.log.section('Test Case 3c'): + # Test Case 3c, update with correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db1.auth', + 'setenv -e -nv -bs -rt -at -a -i 4000000,$filesize db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + + def test_efi_var_auth4(self, u_boot_console, efi_boot_env): + """ + Test Case 4 - Delete database without authentication + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 4a'): + # Test Case 4a, update without AUTHENTICATED_WRITE_ACCESS + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'setenv -e -nv -bs -rt db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + + with u_boot_console.log.section('Test Case 4b'): + # Test Case 4b, update without correct signature/data + output = u_boot_console.run_command_list([ + 'setenv -e -nv -bs -rt -at db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('db:', ''.join(output))) + + def test_efi_var_auth5(self, u_boot_console, efi_boot_env): + """ + Test Case 5 - Uninstall(delete) PK + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 5a'): + # Test Case 5a, Uninstall PK without correct signature + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'printenv -e -n PK']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('PK:', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 PK_null.esl', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK', + 'printenv -e -n PK']) + assert(re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('PK:', ''.join(output))) + + with u_boot_console.log.section('Test Case 5b'): + # Test Case 5b, Uninstall PK with correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 PK_null.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK', + 'printenv -e -n PK']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + assert(re.search('\"PK\" not defined', ''.join(output))) + + output = u_boot_console.run_command( + 'printenv -e SecureBoot') + assert('00000000: 00' in output) + output = u_boot_console.run_command( + 'printenv -e SetupMode') + assert('00000000: 01' in output) diff --git a/test/py/tests/test_efi_secboot/test_signed.py b/test/py/tests/test_efi_secboot/test_signed.py new file mode 100644 index 0000000000..584282b338 --- /dev/null +++ b/test/py/tests/test_efi_secboot/test_signed.py @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Linaro Limited +# Author: AKASHI Takahiro <takahiro.akashi@linaro.org> +# +# U-Boot UEFI: Signed Image Authentication Test + +""" +This test verifies image authentication for signed images. +""" + +import pytest +import re +from defs import * + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('efi_secure_boot') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.slow +class TestEfiSignedImage(object): + def test_efi_signed_image_auth1(self, u_boot_console, efi_boot_env): + """ + Test Case 1 - authenticated by db + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 1a'): + # Test Case 1a, run signed image if no db/dbx + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert(re.search('Hello, world!', ''.join(output))) + + with u_boot_console.log.section('Test Case 1b'): + # Test Case 1b, run unsigned image if no db/dbx + output = u_boot_console.run_command_list([ + 'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""', + 'efidebug boot next 2', + 'bootefi bootmgr']) + assert(re.search('Hello, world!', ''.join(output))) + + with u_boot_console.log.section('Test Case 1c'): + # Test Case 1c, not authenticated by db + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + output = u_boot_console.run_command_list([ + 'efidebug boot next 2', + 'bootefi bootmgr']) + assert(re.search('\'HELLO2\' failed', ''.join(output))) + output = u_boot_console.run_command_list([ + 'efidebug boot next 2', + 'efidebug test bootmgr']) + assert(re.search('efi_start_image[(][)] returned: 26', + ''.join(output))) + assert(not re.search('Hello, world!', ''.join(output))) + + with u_boot_console.log.section('Test Case 1d'): + # Test Case 1d, authenticated by db + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert(re.search('Hello, world!', ''.join(output))) + + def test_efi_signed_image_auth2(self, u_boot_console, efi_boot_env): + """ + Test Case 2 - rejected by dbx + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 2a'): + # Test Case 2a, rejected by dbx + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + output = u_boot_console.run_command_list([ + 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert(re.search('\'HELLO\' failed', ''.join(output))) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert(re.search('efi_start_image[(][)] returned: 26', + ''.join(output))) + assert(not re.search('Hello, world!', ''.join(output))) + + with u_boot_console.log.section('Test Case 2b'): + # Test Case 2b, rejected by dbx even if db allows + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert(re.search('\'HELLO\' failed', ''.join(output))) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert(re.search('efi_start_image[(][)] returned: 26', + ''.join(output))) + assert(not re.search('Hello, world!', ''.join(output))) diff --git a/test/py/tests/test_efi_secboot/test_unsigned.py b/test/py/tests/test_efi_secboot/test_unsigned.py new file mode 100644 index 0000000000..22d849afb8 --- /dev/null +++ b/test/py/tests/test_efi_secboot/test_unsigned.py @@ -0,0 +1,121 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Linaro Limited +# Author: AKASHI Takahiro <takahiro.akashi@linaro.org> +# +# U-Boot UEFI: Signed Image Authentication Test + +""" +This test verifies image authentication for unsigned images. +""" + +import pytest +import re +from defs import * + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('efi_secure_boot') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.slow +class TestEfiUnsignedImage(object): + def test_efi_unsigned_image_auth1(self, u_boot_console, efi_boot_env): + """ + Test Case 1 - rejected when not digest in db or dbx + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 1'): + # Test Case 1 + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert(re.search('\'HELLO\' failed', ''.join(output))) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert(re.search('efi_start_image[(][)] returned: 26', + ''.join(output))) + assert(not re.search('Hello, world!', ''.join(output))) + + def test_efi_unsigned_image_auth2(self, u_boot_console, efi_boot_env): + """ + Test Case 2 - authenticated by digest in db + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 2'): + # Test Case 2 + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 db_hello.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert(re.search('Hello, world!', ''.join(output))) + + def test_efi_unsigned_image_auth3(self, u_boot_console, efi_boot_env): + """ + Test Case 3 - rejected by digest in dbx + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 3a'): + # Test Case 3a, rejected by dbx + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 db_hello.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert(re.search('\'HELLO\' failed', ''.join(output))) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert(re.search('efi_start_image[(][)] returned: 26', + ''.join(output))) + assert(not re.search('Hello, world!', ''.join(output))) + + with u_boot_console.log.section('Test Case 3b'): + # Test Case 3b, rejected by dbx even if db allows + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db_hello.auth', + 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db']) + assert(not re.search('Failed to set EFI variable', ''.join(output))) + + output = u_boot_console.run_command_list([ + 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert(re.search('\'HELLO\' failed', ''.join(output))) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert(re.search('efi_start_image[(][)] returned: 26', + ''.join(output))) + assert(not re.search('Hello, world!', ''.join(output))) diff --git a/tools/patman/series.py b/tools/patman/series.py index a15f7625ed..6d9d48b123 100644 --- a/tools/patman/series.py +++ b/tools/patman/series.py @@ -249,8 +249,10 @@ class Series(dict): if cover_fname: cover_cc = gitutil.BuildEmailList(self.get('cover_cc', '')) cover_cc = [tools.FromUnicode(m) for m in cover_cc] - cc_list = '\0'.join([tools.ToUnicode(x) - for x in sorted(set(cover_cc + all_ccs))]) + cover_cc = list(set(cover_cc + all_ccs)) + if limit is not None: + cover_cc = cover_cc[:limit] + cc_list = '\0'.join([tools.ToUnicode(x) for x in sorted(cover_cc)]) print(cover_fname, cc_list, file=fd) fd.close() |