[PATCH v3 2/3] efi: qemu: arm64: Add efi_rng_protocol implementation for the platform

Heinrich Schuchardt xypron.glpk at gmx.de
Sat Dec 28 18:34:38 CET 2019


On 12/27/19 3:26 PM, Sughosh Ganu wrote:
> Add support for the EFI_RNG_PROTOCOL routines for the qemu arm64
> platform. EFI_RNG_PROTOCOL is an uefi boottime service which is
> invoked by the efi stub in the kernel for getting random seed for
> kaslr.
>
> The routines are platform specific, and use the virtio-rng device on
> the platform to get random data.
>
> The feature can be enabled through the following config
> CONFIG_EFI_RNG_PROTOCOL
>
> Signed-off-by: Sughosh Ganu <sughosh.ganu at linaro.org>
> ---
> Changes since V2:
> * Based on review comments from Heinrich Schuchardt, the rng drivers
>    read all the bytes requested in the individual
>    drivers. Corresponding changes made in getrng routine to remove the
>    loop to read the bytes requested, since that would be handled in the
>    drivers.
>
>   board/emulation/qemu-arm/qemu-arm.c | 41 +++++++++++++++++++
>   include/efi_rng.h                   | 32 +++++++++++++++
>   lib/efi_loader/Kconfig              |  8 ++++
>   lib/efi_loader/Makefile             |  1 +
>   lib/efi_loader/efi_rng.c            | 80 +++++++++++++++++++++++++++++++++++++
>   5 files changed, 162 insertions(+)
>   create mode 100644 include/efi_rng.h
>   create mode 100644 lib/efi_loader/efi_rng.c
>
> diff --git a/board/emulation/qemu-arm/qemu-arm.c b/board/emulation/qemu-arm/qemu-arm.c
> index e1f4709..1dcf830 100644
> --- a/board/emulation/qemu-arm/qemu-arm.c
> +++ b/board/emulation/qemu-arm/qemu-arm.c
> @@ -91,3 +91,44 @@ void *board_fdt_blob_setup(void)
>   	/* QEMU loads a generated DTB for us at the start of RAM. */
>   	return (void *)CONFIG_SYS_SDRAM_BASE;
>   }
> +
> +#if defined(CONFIG_EFI_RNG_PROTOCOL)
> +#include <efi_loader.h>
> +#include <efi_rng.h>
> +
> +#include <dm/device-internal.h>
> +
> +efi_status_t platform_get_rng_device(struct udevice **dev)
> +{
> +	int ret;
> +	efi_status_t status = EFI_DEVICE_ERROR;
> +	struct udevice *bus, *devp;
> +
> +	for (uclass_first_device(UCLASS_VIRTIO, &bus); bus;
> +	     uclass_next_device(&bus)) {
> +		for (device_find_first_child(bus, &devp); devp; device_find_next_child(&devp)) {
> +			if (device_get_uclass_id(devp) == UCLASS_RNG) {
> +				*dev = devp;
> +				status = EFI_SUCCESS;
> +				break;
> +			}
> +		}
> +	}
> +
> +	if (status != EFI_SUCCESS) {
> +		debug("No rng device found\n");
> +		return EFI_DEVICE_ERROR;
> +	}
> +
> +	if (*dev) {
> +		ret = device_probe(*dev);
> +		if (ret)
> +			return EFI_DEVICE_ERROR;
> +	} else {
> +		debug("Couldn't get child device\n");
> +		return EFI_DEVICE_ERROR;
> +	}
> +
> +	return EFI_SUCCESS;
> +}
> +#endif /* CONFIG_EFI_RNG_PROTOCOL */
> diff --git a/include/efi_rng.h b/include/efi_rng.h
> new file mode 100644
> index 0000000..a46e66d
> --- /dev/null
> +++ b/include/efi_rng.h
> @@ -0,0 +1,32 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (c) 2019, Linaro Limited
> + */
> +
> +#if !defined _EFI_RNG_H_
> +#define _EFI_RNG_H_
> +
> +#include <efi.h>
> +#include <efi_api.h>
> +
> +/* EFI random number generation protocol related GUID definitions */
> +#define EFI_RNG_PROTOCOL_GUID \
> +	EFI_GUID(0x3152bca5, 0xeade, 0x433d, 0x86, 0x2e, \
> +		 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44)
> +
> +#define EFI_RNG_ALGORITHM_RAW \
> +	EFI_GUID(0xe43176d7, 0xb6e8, 0x4827, 0xb7, 0x84, \
> +		 0x7f, 0xfd, 0xc4, 0xb6, 0x85, 0x61)
> +
> +struct efi_rng_protocol {
> +	efi_status_t (EFIAPI *get_info)(struct efi_rng_protocol *protocol,
> +					efi_uintn_t *rng_algorithm_list_size,
> +					efi_guid_t *rng_algorithm_list);
> +	efi_status_t (EFIAPI *get_rng)(struct efi_rng_protocol *protocol,
> +				       efi_guid_t *rng_algorithm,
> +				       efi_uintn_t rng_value_length, uint8_t *rng_value);
> +};
> +
> +efi_status_t platform_get_rng_device(struct udevice **dev);
> +
> +#endif /* _EFI_RNG_H_ */
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index 21ef440..24dde6f 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -120,4 +120,12 @@ config EFI_GRUB_ARM32_WORKAROUND
>   	  GRUB prior to version 2.04 requires U-Boot to disable caches. This
>   	  workaround currently is also needed on systems with caches that
>   	  cannot be managed via CP15.
> +
> +config EFI_RNG_PROTOCOL
> +	bool "EFI_RNG_PROTOCOL support"
> +	depends on DM_RNG
> +	help
> +	  "Support for EFI_RNG_PROTOCOL implementation. Uses the rng
> +	   device on the platform"
> +
>   endif
> diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
> index 7db4060..04dc864 100644
> --- a/lib/efi_loader/Makefile
> +++ b/lib/efi_loader/Makefile
> @@ -42,3 +42,4 @@ obj-$(CONFIG_PARTITIONS) += efi_disk.o
>   obj-$(CONFIG_NET) += efi_net.o
>   obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o
>   obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o
> +obj-$(CONFIG_EFI_RNG_PROTOCOL) += efi_rng.o
> diff --git a/lib/efi_loader/efi_rng.c b/lib/efi_loader/efi_rng.c
> new file mode 100644
> index 0000000..eb91aa7
> --- /dev/null
> +++ b/lib/efi_loader/efi_rng.c
> @@ -0,0 +1,80 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (c) 2019, Linaro Limited
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <efi_loader.h>
> +#include <efi_rng.h>
> +#include <rng.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +static efi_status_t EFIAPI rng_getinfo(struct efi_rng_protocol *this,
> +				       efi_uintn_t *rng_algorithm_list_size,
> +				       efi_guid_t *rng_algorithm_list)
> +{
> +	efi_guid_t rng_algo_guid = EFI_RNG_ALGORITHM_RAW;
> +
> +	EFI_ENTRY("%p, %p, %p", this, rng_algorithm_list_size,
> +		  rng_algorithm_list);
> +
> +	if (!this || !rng_algorithm_list_size)
> +		return EFI_INVALID_PARAMETER;

You have to call EFI_EXIT() when returning to restore the gd register.

> +
> +	if (!rng_algorithm_list ||
> +	    *rng_algorithm_list_size < sizeof(*rng_algorithm_list)) {
> +		*rng_algorithm_list_size = sizeof(*rng_algorithm_list);
> +		return EFI_BUFFER_TOO_SMALL;

EFI_EXIT()

> +	}
> +
> +	/*
> +	 * For now, use EFI_RNG_ALGORITHM_RAW as the default
> +	 * algorithm. If a new algorithm gets added in the
> +	 * future through a Kconfig, rng_algo_guid will be set
> +	 * based on that Kconfig option
> +	 */
> +	*rng_algorithm_list_size = sizeof(*rng_algorithm_list);
> +	guidcpy(rng_algorithm_list, &rng_algo_guid);
> +
> +	return EFI_EXIT(EFI_SUCCESS);
> +}
> +
> +static efi_status_t EFIAPI getrng(struct efi_rng_protocol *this,
> +				  efi_guid_t *rng_algorithm,
> +				  efi_uintn_t rng_value_length,
> +				  uint8_t *rng_value)
> +{
> +	int ret;
> +	struct udevice *dev;
> +	const efi_guid_t rng_raw_guid = EFI_RNG_ALGORITHM_RAW;
> +
> +	EFI_ENTRY("%p, %p, %zu, %p", this, rng_algorithm, rng_value_length,
> +		  rng_value);
> +
> +	if (!this || !rng_value || !rng_value_length)
> +		return EFI_INVALID_PARAMETER;

You have to call EFI_EXIT() when returning to get the gd register restored.

In most other protocol implementations we have here just

			ret = EFI_INVALID_PARAMETER;
			goto out;

And a label at the end of the code.

> +
> +	if (rng_algorithm) {
> +		if (guidcmp(rng_algorithm, &rng_raw_guid))

Let's have some debug output here:

EFI_PRINT("RNG algorithm %pUl\n", rng_algorthm);

> +			return EFI_UNSUPPORTED;

EFI_EXIT()

> +	}
> +
> +	ret = platform_get_rng_device(&dev);
> +	if (ret != EFI_SUCCESS)

EFI_SUCCESS is what we use for return values of type efi_status_t. It is
misleading here.

> +		return EFI_UNSUPPORTED;

EFI_EXIT()

> +
> +	ret = dm_rng_read(dev, rng_value, rng_value_length);
> +	if (ret < 0) {
> +		debug("Rng device read failed\n");

It is preferable to use EFI_PRINT() here to have correct indentation.

> +		return EFI_DEVICE_ERROR;

EFI_EXIT()

> +	}
> +
> +	return EFI_EXIT(EFI_SUCCESS);
> +}
> +
> +const struct efi_rng_protocol efi_rng_protocol = {
> +	.get_info = rng_getinfo,
> +	.get_rng = getrng,
> +};
>

So I think we will have to get to something like:

static efi_status_t EFIAPI rng_getinfo(struct efi_rng_protocol *this,
				       efi_uintn_t *rng_algorithm_list_size,
				       efi_guid_t *rng_algorithm_list)
{
	efi_guid_t rng_algo_guid = EFI_RNG_ALGORITHM_RAW;
	efi_status_t ret = EFI_SUCCESS;

	EFI_ENTRY("%p, %p, %p", this, rng_algorithm_list_size,
		  rng_algorithm_list);

	if (!this || !rng_algorithm_list_size) {
		ret = EFI_INVALID_PARAMETER;
		goto out;
	}

	if (!rng_algorithm_list ||
	    *rng_algorithm_list_size < sizeof(*rng_algorithm_list)) {
		*rng_algorithm_list_size = sizeof(*rng_algorithm_list);
		ret =  EFI_BUFFER_TOO_SMALL;
		goto out;
	}

	/*
	 * For now, use EFI_RNG_ALGORITHM_RAW as the default
	 * algorithm. If a new algorithm gets added in the
	 * future through a Kconfig, rng_algo_guid will be set
	 * based on that Kconfig option
	 */
	*rng_algorithm_list_size = sizeof(*rng_algorithm_list);
	guidcpy(rng_algorithm_list, &rng_algo_guid);

out:
	return EFI_EXIT(ret);
}

static efi_status_t EFIAPI getrng(struct efi_rng_protocol *this,
				  efi_guid_t *rng_algorithm,
				  efi_uintn_t rng_value_length,
				  uint8_t *rng_value)
{
	int r;
	efi_status_t ret = EFI_SUCCESS;
	struct udevice *dev;
	const efi_guid_t rng_raw_guid = EFI_RNG_ALGORITHM_RAW;

	EFI_ENTRY("%p, %p, %zu, %p", this, rng_algorithm, rng_value_length,
		  rng_value);

	if (!this || !rng_value || !rng_value_length) {
		ret = EFI_INVALID_PARAMETER;
		goto out;
	}

	if (rng_algorithm) {
		EFI_PRINT("RNG algorithm %pUl\n", rng_algorithm);
		if (guidcmp(rng_algorithm, &rng_raw_guid)) {
			ret = EFI_UNSUPPORTED;
			goto out;
		}
	}

	r = platform_get_rng_device(&dev);
	if (r) {
		ret = EFI_UNSUPPORTED;
		goto out;
	}

	ret = dm_rng_read(dev, rng_value, rng_value_length);
	if (ret < 0) {
		EFI_PRINT("Rng device read failed\n");
		ret = EFI_DEVICE_ERROR;
	}

out:
	return EFI_EXIT(ret);
}

Best regards

Heinrich


More information about the U-Boot mailing list