All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kohei Enju <enju.kohei@fujitsu.com>
To: Yeoreum Yun <yeoreum.yun@arm.com>
Cc: linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	 Lorenzo Pieralisi <lpieralisi@kernel.org>,
	Catalin Marinas <catalin.marinas@arm.com>,
	 Suzuki Poulose <suzuki.poulose@arm.com>,
	Steven Price <steven.price@arm.com>,
	 Sami Mujawar <sami.mujawar@arm.com>,
	"Aneesh Kumar K.V" <aneesh.kumar@kernel.org>,
	 Jiri Pirko <jiri@resnulli.us>,
	GONG Ruiqi <gongruiqi1@huawei.com>,
	 Jason Gunthorpe <jgg@ziepe.ca>
Subject: Re: [PATCH RFC 1/3] firmware: arm_firme: introduce arm_firme driver
Date: Thu, 22 Oct 2026 18:48:49 +0900	[thread overview]
Message-ID: <atnVCFWZLKR8GthP@FCCLS0092175.localdomain> (raw)
In-Reply-To: <20261019-firme_interface-v1-1-27895c9cebaa@arm.com>

On 10/19 15:14, Yeoreum Yun wrote:
> The Realm Management Extension (FEAT_RME) is part of Arm Confidential
> Compute Architecture (CCA) and enables support for dynamic, attestable
> trusted execution environments ("Realms") on Arm systems.
> 
> Realm management flows depend on EL3 firmware for operations such as
> Realm construction, resource allocation and isolation, and attestation
> of initial Realm state.
> 
> The FIRME defines standard interfaces provided by EL3 firmware to
> satisfy these dependencies. These interfaces include:
> 
>   - Granule Protection Table management
>   - IDE key management for PCIe and CXL Root Ports
>   - Memory Encryption Context management
>   - Attestation token management
>   - Integrated device management
> 
> Each group of interfaces forms a FIRME service. A base FIRME service
> is also defined to provide common concepts, service discovery, and
> feature discovery across services.
> 
> FIRME interfaces are implemented at the EL3 boundary between the
> Non-secure, Realm, and Secure states. They may also be used on systems
> without FEAT_RME, for example to provide firmware-based attestation or
> security services.
> 
> arm_firme driver provides the common FIRME framework and service discovery
> support for client drivers using below FIRME services:
> 
>   - Base service.
>   - Attestation token management.
> 
> Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>

Does this series assume that the kernel is running in EL2 implicitly?

The FIRME spec says:
  An implementation of one or more FIRME services at a valid Exception level boundary between EL3 and the
  highest Exception level in a different Security state is called a FIRME instance.

and I think only NS-EL2 can call FIRME interfaces as a NS FIRME
instance, right?

> ---
>  drivers/firmware/Kconfig            |   1 +
>  drivers/firmware/Makefile           |   1 +
>  drivers/firmware/arm_firme/Kconfig  |  34 ++++++
>  drivers/firmware/arm_firme/Makefile |   5 +
>  drivers/firmware/arm_firme/attest.c | 172 ++++++++++++++++++++++++++
>  drivers/firmware/arm_firme/base.c   | 145 ++++++++++++++++++++++
>  drivers/firmware/arm_firme/driver.c | 134 +++++++++++++++++++++
>  drivers/firmware/arm_firme/firme.h  |  45 +++++++
>  include/linux/arm-smccc.h           |   1 +
>  include/linux/arm_firme.h           | 234 ++++++++++++++++++++++++++++++++++++
>  10 files changed, 772 insertions(+)
> 
> diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
> index f1a76d482429..e0c1babea1f8 100644
> --- a/drivers/firmware/Kconfig
> +++ b/drivers/firmware/Kconfig
> @@ -297,6 +297,7 @@ config TURRIS_MOX_RWTM_KEYCTL
>  endif # TURRIS_MOX_RWTM
>  
>  source "drivers/firmware/arm_ffa/Kconfig"
> +source "drivers/firmware/arm_firme/Kconfig"
>  source "drivers/firmware/arm_rmm/Kconfig"
>  source "drivers/firmware/broadcom/Kconfig"
>  source "drivers/firmware/cirrus/Kconfig"
> diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
> index 54755db0cf6e..4ae1550ed73c 100644
> --- a/drivers/firmware/Makefile
> +++ b/drivers/firmware/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o
>  obj-$(CONFIG_TURRIS_MOX_RWTM)	+= turris-mox-rwtm.o
>  
>  obj-y				+= arm_ffa/
> +obj-y				+= arm_firme/
>  obj-y				+= arm_rmm/
>  obj-y				+= arm_scmi/
>  obj-y				+= broadcom/
> diff --git a/drivers/firmware/arm_firme/Kconfig b/drivers/firmware/arm_firme/Kconfig
> new file mode 100644
> index 000000000000..7e7a826879f1
> --- /dev/null
> +++ b/drivers/firmware/arm_firme/Kconfig
> @@ -0,0 +1,34 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config ARM_FIRME
> +	tristate "Arm Firmware Interfaces for Realm Management Extension (FIRME)"
> +	depends on ARM64 && HAVE_ARM_SMCCC_DISCOVERY
> +	default n
> +	help
> +	  The Realm Management Extension (FEAT_RME) is part of Arm Confidential
> +	  Compute Architecture (CCA) and enables support for dynamic, attestable
> +	  trusted execution environments ("Realms") on Arm systems.
> +
> +	  Realm management flows depend on EL3 firmware for operations such as
> +	  Realm construction, resource allocation and isolation, and attestation
> +	  of initial Realm state.
> +
> +	  The FIRME defines standard interfaces provided by EL3 firmware to
> +	  satisfy these dependencies. These interfaces include:
> +
> +	    - Granule Protection Table management
> +	    - IDE key management for PCIe and CXL Root Ports
> +	    - Memory Encryption Context management
> +	    - Attestation token management
> +	    - Integrated device management
> +
> +	  Each group of interfaces forms a FIRME service. A base FIRME service
> +	  is also defined to provide common concepts, service discovery, and
> +	  feature discovery across services.
> +
> +	  FIRME interfaces are implemented at the EL3 boundary between the
> +	  Non-secure, Realm, and Secure states. They may also be used on
> +	  systems without FEAT_RME, for example to provide firmware-based
> +	  attestation or security services.
> +
> +	  This driver provides the common FIRME framework and service
> +	  discovery support for client drivers using FIRME services.
> diff --git a/drivers/firmware/arm_firme/Makefile b/drivers/firmware/arm_firme/Makefile
> new file mode 100644
> index 000000000000..dccac0a2b50e
> --- /dev/null
> +++ b/drivers/firmware/arm_firme/Makefile
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +obj-$(CONFIG_ARM_FIRME) += arm-firme.o
> +
> +arm-firme-$(CONFIG_ARM_FIRME) := driver.o base.o attest.o
> diff --git a/drivers/firmware/arm_firme/attest.c b/drivers/firmware/arm_firme/attest.c
> new file mode 100644
> index 000000000000..aa57b3b9e623
> --- /dev/null
> +++ b/drivers/firmware/arm_firme/attest.c
> @@ -0,0 +1,172 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2026 Arm Ltd.
> + */
> +
> +#define DRIVER_NAME "ARM FIRME"
> +#define pr_fmt(fmt) DRIVER_NAME ": " fmt
> +
> +#include <crypto/sha2.h>
> +
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +
> +#include "firme.h"
> +
> +static DEFINE_STATIC_KEY_TRUE(attest_service_disabled);

Regarding the attest_service_disabled naming, it seems a bit
counter-intuitive to me. Wouldn't it be clearer to invert the logic
with:
DEFINE_STATIC_KEY_FALSE(attest_service_available)?

Or is there any reason to choose the naming here?

> +
> +int firme_attest_service_init(struct firme_service_info *info)
> +{
> +	firme_value_t ret;
> +	u8 max_pat_pgcnt;
> +
> +	if (!test_bit(FIRME_ATTEST_SERVICE_ID, &info->service_list)) {
> +		pr_info("Attestation service is not supported\n");
> +		return 0;
> +	}
> +
> +	invoke_firme_service((firme_value_t) {
> +			     .a0 = FIRME_SERVICE_VERSION,
> +			     .a1 = FIRME_ATTEST_SERVICE_ID,
> +			     }, &ret);
> +	if (ret.a0 == FIRME_RET_NOT_SUPPORTED)
> +		goto err_out;
> +
> +	info->attest_info.version = (u32)ret.a0;
> +
> +	invoke_firme_service((firme_value_t) {
> +			     .a0 = FIRME_SERVICE_FEATURES,
> +			     .a1 = FIRME_ATTEST_SERVICE_ID,
> +			     .a2 = 0x1,
> +			     }, &ret);
> +	if (ret.a0 != FIRME_RET_SUCCESS)
> +		goto err_out;
> +
> +	max_pat_pgcnt = FIELD_GET(FIRME_ATTEST_FEAT1_MAX_PAT_PG_CNT_MASK, ret.a1);
> +
> +	/*
> +	 * According to Realm Management Monitor specification, S-PNCRK,
> +	 * maximum chanllege size should be 64 bytes (SHA512_DIGEST_SIZE).
> +	 * If less than, pad with zero.
> +	 */
> +	info->attest_info.max_challenge_size = SHA512_DIGEST_SIZE;
> +	info->attest_info.max_pat_size = (max_pat_pgcnt + 1) *
> +					 info->shared_buf_info.min_size;
> +
> +	pr_info("FIRME attest service version %d.%d\n",
> +		FIRME_MAJOR_VERSION(info->attest_info.version),
> +		FIRME_MINOR_VERSION(info->attest_info.version));

The FIRME specification defines versioning semantics so we can negotiate
the required version of a specific service here. Is this intentional to
omit negotiation here?

The same applies to firme_base_service_init().

> +	pr_info("Maximum challenge size: 0x%lx\n",
> +		info->attest_info.max_challenge_size);
> +	pr_info("Maximum PAT size: 0x%lx\n", info->attest_info.max_pat_size);
> +
> +	static_branch_disable(&attest_service_disabled);
> +
> +	return 0;
> +
> +err_out:
> +	static_branch_enable(&attest_service_disabled);
> +	clear_bit(FIRME_ATTEST_SERVICE_ID, &info->service_list);
> +	return firme_status_to_errno(ret.a0);
> +}
> +
> +static int firme_get_attest_info(struct firme_attest_info *info)
> +{
> +	if (static_branch_unlikely(&attest_service_disabled))
> +		return -EOPNOTSUPP;
> +			
> +	if (!info)
> +		return -EINVAL;
> +
> +	memcpy(info, &firme_service_info->attest_info,
> +	       sizeof(struct firme_attest_info));
> +
> +	return 0;
> +}
> +
> +static int firme_get_platform_attestion_token(void *challenge,
> +					      size_t challenge_size,
> +					      void *token, size_t *token_size)
> +{
> +	firme_status_t status;
> +	firme_value_t ret;
> +	struct firme_instance *fi;
> +	struct firme_attest_info *attest_info;
> +	struct firme_shared_buf_info *shbuf_info;
> +	void *buffer;
> +	size_t alloc_size, buffer_size, buffer_pgcnt;
> +	size_t read_size, total_size;
> +
> +	if (static_branch_unlikely(&attest_service_disabled))
> +		return -EOPNOTSUPP;
> +
> +	shbuf_info = &firme_service_info->shared_buf_info;
> +	attest_info = &firme_service_info->attest_info;
> +
> +	if (!token || !token_size || (*token_size < attest_info->max_pat_size))
> +		return -EINVAL;
> +
> +	if ((challenge_size > attest_info->max_challenge_size) ||
> +	    ((challenge_size > 0) && !challenge))
> +		return -EINVAL;
> +
> +	fi = firme_get_instance();
> +	if (WARN_ON_ONCE(IS_ERR (fi)))
> +		return -EOPNOTSUPP;
> +
> +	buffer_size = min(attest_info->max_pat_size, shbuf_info->max_size);
> +	if (WARN_ON_ONCE(buffer_size & (shbuf_info->min_size - 1)))
> +		return -ENOBUFS;
> +
> +	alloc_size = buffer_size;
> +
> +	buffer = fi->ops->base_ops->alloc_shared_buffer(&alloc_size,
> +							GFP_KERNEL | __GFP_ZERO);
> +	if (!buffer)
> +		return -ENOMEM;
> +
> +	if (alloc_size < buffer_size)
> +		buffer_size = alloc_size;
> +
> +	buffer_pgcnt = buffer_size / shbuf_info->min_size;
> +
> +	if (challenge_size > 0)
> +		memcpy(buffer, challenge, challenge_size);
> +
> +	guard(migrate)();

I don't think disabling migration is required here.

> +
> +	total_size = 0;
> +
> +	do {
> +		invoke_firme_service((firme_value_t) {
> +				     .a0 = FIRME_ATTEST_PAT_GET,
> +				     .a1 = virt_to_phys(buffer),
> +				     .a2 = 0,
> +				     .a3 = buffer_pgcnt - 1,
> +				     .a4 = challenge_size,
> +				     .a5 = 0,  /**< binding size must be 0 for normal world. */
> +				     }, &ret);
> +		status = ret.a0;
> +		if ((status != FIRME_RET_SUCCESS) &&
> +		    (status != FIRME_RET_INCOMPLETE))
> +			break;
> +
> +		challenge_size = 0;
> +		read_size = ret.a1;
> +		memcpy(token + total_size, buffer, read_size);
> +		total_size += read_size;
> +	} while (status == FIRME_RET_INCOMPLETE);
> +
> +	if (status == FIRME_RET_SUCCESS)
> +		*token_size = total_size;
> +
> +	fi->ops->base_ops->free_shared_buffer(buffer, alloc_size);
> +
> +	return firme_status_to_errno(status);
> +}
> +
> +struct firme_attest_ops attest_ops = {
> +	.get_attest_info = firme_get_attest_info,
> +	.get_platform_attestion_token = firme_get_platform_attestion_token,
> +};
> diff --git a/drivers/firmware/arm_firme/base.c b/drivers/firmware/arm_firme/base.c
> new file mode 100644
> index 000000000000..b9ffb214cca4
> --- /dev/null
> +++ b/drivers/firmware/arm_firme/base.c
> @@ -0,0 +1,145 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2026 Arm Ltd.
> + */
> +
> +#define DRIVER_NAME "ARM FIRME"
> +#define pr_fmt(fmt) DRIVER_NAME ": " fmt
> +
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +
> +#include "firme.h"
> +
> +int firme_base_service_init(struct firme_service_info *info)
> +{
> +	firme_value_t ret;
> +	unsigned long service_list;
> +	u8 shbuf_min_pgs;
> +	u16 max_pgcnt;
> +	size_t min_size;
> +
> +	invoke_firme_service((firme_value_t) {
> +			     .a0 = FIRME_SERVICE_VERSION,
> +			     .a1 = FIRME_BASE_SERVICE_ID,
> +			     }, &ret);
> +	if (ret.a0 == FIRME_RET_NOT_SUPPORTED)
> +		goto err_out;
> +
> +	info->version = (u32)ret.a0;
> +
> +	invoke_firme_service((firme_value_t) {
> +			     .a0 = FIRME_SERVICE_FEATURES,
> +			     .a1 = FIRME_BASE_SERVICE_ID,
> +			     .a2 = 0x1,
> +			     }, &ret);
> +	if (ret.a0 != FIRME_RET_SUCCESS)
> +		goto err_out;
> +
> +	service_list = FIELD_GET(FIRME_BASE_FEAT1_SERVICE_LIST_MASK, ret.a1);
> +
> +	/* Base service is always supported */
> +	info->service_list = (service_list << 1) | 0x1;
> +
> +	shbuf_min_pgs = FIELD_GET(FIRME_BASE_FEAT1_SH_BUF_MIN_SZ_MASK, ret.a1);
> +	max_pgcnt = FIELD_GET(FIRME_BASE_FEAT1_SH_BUF_MAX_PG_CNT_MASK, ret.a1);
> +
> +	switch (shbuf_min_pgs) {
> +	case FIRME_PGS_4K:
> +		min_size = 1UL << 12;

nit:
I'd like to use SZ_4K, SZ_16K, SZ_64K here.

> +		break;
> +	case FIRME_PGS_16K:
> +		min_size = 1UL << 14;
> +		break;
> +	case FIRME_PGS_64K:
> +		min_size = 1UL << 16;
> +		break;
> +	default:
> +		pr_err("Invalid minimum shared buffer size: 0x%x\n", shbuf_min_pgs);
> +		return -EINVAL;
> +	}


  reply	other threads:[~2026-10-22  9:49 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-19 14:14 [PATCH RFC 0/3] firmware: arm_firme: introduce FIRME support for host attestation Yeoreum Yun
2026-10-19 14:14 ` [PATCH RFC 1/3] firmware: arm_firme: introduce arm_firme driver Yeoreum Yun
2026-10-22  9:48   ` Kohei Enju [this message]
2026-10-22 13:17     ` Yeoreum Yun
2026-10-23  5:48       ` Kohei Enju
2026-10-19 14:14 ` [PATCH RFC 2/3] firmware: arm-smccc: add FIRME device Yeoreum Yun
2026-10-19 14:14 ` [PATCH RFC 3/3] firmware: arm-firme: TSM_REPORT support using FIRME Yeoreum Yun
2026-10-19 23:35 ` [PATCH RFC 0/3] firmware: arm_firme: introduce FIRME support for host attestation Jason Gunthorpe
2026-10-20  7:13   ` Yeoreum Yun
2026-10-20 12:45     ` Jason Gunthorpe
2026-10-20 13:41       ` Yeoreum Yun
2026-10-20 13:50         ` Jason Gunthorpe
2026-10-20 14:13           ` Yeoreum Yun
2026-10-20 14:36             ` Jason Gunthorpe
2026-10-20 14:43               ` Yeoreum Yun
2026-10-20 14:58                 ` Jason Gunthorpe
2026-10-20 15:18                   ` Yeoreum Yun
2026-10-20 16:47                     ` Jason Gunthorpe
2026-10-20 17:05                       ` Yeoreum Yun
2026-10-20 18:47                         ` Jason Gunthorpe
2026-10-20 19:27                           ` Yeoreum Yun

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=atnVCFWZLKR8GthP@FCCLS0092175.localdomain \
    --to=enju.kohei@fujitsu.com \
    --cc=aneesh.kumar@kernel.org \
    --cc=catalin.marinas@arm.com \
    --cc=gongruiqi1@huawei.com \
    --cc=jgg@ziepe.ca \
    --cc=jiri@resnulli.us \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lpieralisi@kernel.org \
    --cc=sami.mujawar@arm.com \
    --cc=steven.price@arm.com \
    --cc=suzuki.poulose@arm.com \
    --cc=yeoreum.yun@arm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.