mirror of https://lore.kernel.org/lkml/
 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: 19+ 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-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®