Skip to contentSkip to conversation

Add Altera SoCFPGA Crypto Service (FCS) driver

PATCH Superseded [email protected] 2 participants 7 messages Jul 1 – Jul 9 Intel STRATIX10 firmware drivers

v1 of 9 Superseded Latest is v9 →

lore
Share & tools
lore
#0 lore raw

cover Add Altera SoCFPGA Crypto Service (FCS) driver

From: Hang Suan Wang <[email protected]>

This series adds support for the Altera SoCFPGA Crypto Service (FCS), the runtime cryptographic interface provided by the Secure Device Manager (SDM). The SDM is the hardware security controller in Altera SoCFPGA devices. It acts as the root-of-trust device and controls access to built-in cryptographic hardware such as AES, SHA, a true random number generator, and Intel PUF. The SDM is responsible for security-critical functions including secure boot, FPGA bitstream authentication and optional decryption, remote system update, and runtime crypto services. On the HPS side, software reaches the SDM through a mailbox interface exposed in Linux via the stratix10-svc layer, which uses Arm Trusted Firmware SIP SMC calls underneath.

The FPGA Crypto Service (FCS) is the runtime crypto interface provided by the SDM. It covers services such as random number generation, AES operations, HMAC/SHA, key management, attestation, and related security functions.

This series implements one FCS feature: the Secure Data Object Service (SDOS), which protects sensitive data at rest. With SDOS the SDM encrypts and decrypts data using a key derived from a device-unique root key that never leaves the secure boundary, plus an SDM-generated IV. The host never handles raw key material or IVs: it supplies plaintext and receives an authenticated ciphertext object (and vice versa for decryption). A primary use case is black key provisioning, where operational keys are installed in protected form without ever being exposed in cleartext.

The driver reaches the SDM through the existing stratix10-svc mailbox using the Arm Trusted Firmware SIP SMC transport. Data buffers are allocated from the service-layer memory pool, which provides physically-contiguous memory whose physical address is handed to the SDM.

The series is organized as follows:
- Patch 1 (prerequisite) extends the stratix10-svc service layer with the FCS command codes and matching SIP SMC function IDs, adds the Agilex 5 (intel,agilex5-svc) match, and registers a "stratix10-fcs" platform device that an FCS client driver binds to.

- Patch 2 adds the FCS firmware driver implementing SDOS encrypt/decrypt and the crypto-session lifecycle, exposed via sysfs. It relies on the command codes and the device from patch 1, so patch 1 must be applied first.

Testing:
- Built for arm64 (defconfig + CONFIG_ALTERA_SOCFPGA_FCS=m).

Hang Suan Wang (2):
firmware: stratix10-svc: add FCS crypto-service commands for Agilex 5
firmware: socfpga-fcs: add Altera SoCFPGA FCS driver with SDOS

 MAINTAINERS                                   |   8 +
 drivers/firmware/Kconfig                      |  16 +
 drivers/firmware/Makefile                     |   2 +
 drivers/firmware/socfpga-fcs-core.c           | 589 ++++++++++++++++++
 drivers/firmware/socfpga-fcs.c                | 294 +++++++++
 drivers/firmware/stratix10-svc.c              |  56 +-
 include/linux/firmware/intel/socfpga-fcs.h    | 134 ++++
 include/linux/firmware/intel/stratix10-smc.h  |  64 ++
 .../firmware/intel/stratix10-svc-client.h     |  18 +-
 9 files changed, 1176 insertions(+), 5 deletions(-)
 create mode 100644 drivers/firmware/socfpga-fcs-core.c
 create mode 100644 drivers/firmware/socfpga-fcs.c
 create mode 100644 include/linux/firmware/intel/socfpga-fcs.h
Signature · 1 line
2.43.7
#1 lore raw

PATCH 1/2 firmware: stratix10-svc: add FCS crypto-service commands for Agilex 5

From: Hang Suan Wang <[email protected]>

The Agilex 5 Secure Device Manager (SDM 1.5) exposes an FPGA Crypto Service (FCS) over the existing SIP SMC mailbox: a session-based interface for crypto primitives such as SDOS (Secure Data Object Service) encrypt/decrypt. The service layer has no command to drive it yet.

Teach stratix10-svc about this interface so an in-kernel FCS client can use it:

- add the client command codes COMMAND_FCS_CRYPTO_OPEN_SESSION, COMMAND_FCS_CRYPTO_CLOSE_SESSION and COMMAND_FCS_SDOS_DATA_EXT (all asynchronous), and grow stratix10_svc_client_msg::arg[] from three to four entries so the SDOS command can carry its session, context, mode and owner arguments;

- add the matching asynchronous SIP SMC function IDs (INTEL_SIP_SMC_ASYNC_FCS_OPEN_CS_SESSION, INTEL_SIP_SMC_ASYNC_FCS_CLOSE_CS_SESSION and INTEL_SIP_SMC_ASYNC_FCS_CRYPTION_EXT) with their register-usage documentation;

- match "intel,agilex5-svc" and register a "stratix10-fcs" child platform device, mirroring the existing RSU child, so an FCS client driver can bind without a dedicated device-tree node;

- dispatch the new commands in the asynchronous send and response paths; for the SDOS data command, translate the source and destination buffers (allocated from the service-layer gen_pool) to physical addresses and pass them, together with the session/context IDs and owner ID, to the SDM.

The transport is unchanged: Agilex 5 reuses the SIP SMC calling convention and async mailbox ABI the driver already implements, so no new transport mechanism is required.

The SDOS SMMU-remapped address slots currently carry the buffer physical addresses; SMMU remapping support is added in a follow-up series.

This is a prerequisite for the SoCFPGA FCS driver, the first in-tree consumer of these commands.

---
 drivers/firmware/stratix10-svc.c              | 56 ++++++++++++++--
 include/linux/firmware/intel/stratix10-smc.h  | 64 +++++++++++++++++++
 .../firmware/intel/stratix10-svc-client.h     | 18 +++++-
 3 files changed, 133 insertions(+), 5 deletions(-)
drivers/firmware/stratix10-svc.c +52 -4
index c24ca5823078..30035cb22b67 100644
@@ -45,6 +45,7 @@
 
 /* stratix10 service layer clients */
 #define STRATIX10_RSU				"stratix10-rsu"
+#define STRATIX10_FCS				"stratix10-fcs"
 
 /* Maximum number of SDM client IDs. */
 #define MAX_SDM_CLIENT_IDS			16
@@ -104,9 +105,11 @@ struct stratix10_svc_chan;
 /**
  * struct stratix10_svc - svc private data
  * @stratix10_svc_rsu: pointer to stratix10 RSU device
+ * @stratix10_svc_fcs: pointer to stratix10 FCS device
  */
 struct stratix10_svc {
 	struct platform_device *stratix10_svc_rsu;
+	struct platform_device *stratix10_svc_fcs;
 };
 
 /**
@@ -1319,6 +1322,30 @@ int stratix10_svc_async_send(struct stratix10_svc_chan *chan, void *msg,
 		STRATIX10_SIP_SMC_SET_TRANSACTIONID_X1(handle->transaction_id);
 
 	switch (p_msg->command) {
+	case COMMAND_FCS_CRYPTO_OPEN_SESSION:
+		args.a0 = INTEL_SIP_SMC_ASYNC_FCS_OPEN_CS_SESSION;
+		break;
+	case COMMAND_FCS_CRYPTO_CLOSE_SESSION:
+		args.a0 = INTEL_SIP_SMC_ASYNC_FCS_CLOSE_CS_SESSION;
+		args.a2 = p_msg->arg[0];
+		break;
+	case COMMAND_FCS_SDOS_DATA_EXT:
+		args.a0 = INTEL_SIP_SMC_ASYNC_FCS_CRYPTION_EXT;
+		args.a2 = p_msg->arg[0];
+		args.a3 = p_msg->arg[1];
+		args.a4 = p_msg->arg[2];
+		/* payloads are allocated from the svc gen_pool; pass phys addr */
+		args.a5 = gen_pool_virt_to_phys(ctrl->genpool,
+						(unsigned long)p_msg->payload);
+		args.a6 = p_msg->payload_length;
+		args.a7 = gen_pool_virt_to_phys(ctrl->genpool,
+						(unsigned long)p_msg->payload_output);
+		args.a8 = p_msg->payload_length_output;
+		args.a9 = p_msg->arg[3];
+		/* SMMU remapping is added later; pass phys addr for now */
+		args.a10 = args.a5;
+		args.a11 = args.a7;
+		break;
 	case COMMAND_RSU_GET_SPT_TABLE:
 		args.a0 = INTEL_SIP_SMC_ASYNC_RSU_GET_SPT;
 		break;
@@ -1408,6 +1435,9 @@ static int stratix10_svc_async_prepare_response(struct stratix10_svc_chan *chan,
 	data->status = STRATIX10_GET_SDM_STATUS_CODE(handle->res.a1);
 
 	switch (p_msg->command) {
+	case COMMAND_FCS_CRYPTO_OPEN_SESSION:
+	case COMMAND_FCS_CRYPTO_CLOSE_SESSION:
+	case COMMAND_FCS_SDOS_DATA_EXT:
 	case COMMAND_RSU_NOTIFY:
 		break;
 	case COMMAND_RSU_GET_SPT_TABLE:
@@ -1908,6 +1938,7 @@ EXPORT_SYMBOL_GPL(stratix10_svc_free_memory);
 static const struct of_device_id stratix10_svc_drv_match[] = {
 	{.compatible = "intel,stratix10-svc"},
 	{.compatible = "intel,agilex-svc"},
+	{.compatible = "intel,agilex5-svc"},
 	{},
 };
 
@@ -2011,20 +2042,36 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
 
 	ret = platform_device_add(svc->stratix10_svc_rsu);
 	if (ret)
-		goto err_put_device;
+		goto err_put_rsu;
+
+	svc->stratix10_svc_fcs = platform_device_alloc(STRATIX10_FCS, 0);
+	if (!svc->stratix10_svc_fcs) {
+		dev_err(dev, "failed to allocate %s device\n", STRATIX10_FCS);
+		ret = -ENOMEM;
+		goto err_unregister_rsu;
+	}
+
+	ret = platform_device_add(svc->stratix10_svc_fcs);
+	if (ret)
+		goto err_put_fcs;
 
 	ret = of_platform_default_populate(dev_of_node(dev), NULL, dev);
 	if (ret)
-		goto err_unregister_rsu_dev;
+		goto err_unregister_fcs;
 
 	pr_info("Intel Service Layer Driver Initialized\n");
 
 	return 0;
 
-err_unregister_rsu_dev:
+err_unregister_fcs:
+	platform_device_unregister(svc->stratix10_svc_fcs);
+	goto err_unregister_rsu;
+err_put_fcs:
+	platform_device_put(svc->stratix10_svc_fcs);
+err_unregister_rsu:
 	platform_device_unregister(svc->stratix10_svc_rsu);
 	goto err_free_fifos;
-err_put_device:
+err_put_rsu:
 	platform_device_put(svc->stratix10_svc_rsu);
 err_free_fifos:
 	/* only remove from list if list_add_tail() was reached */
@@ -2051,6 +2098,7 @@ static void stratix10_svc_drv_remove(struct platform_device *pdev)
 	of_platform_depopulate(ctrl->dev);
 
 	platform_device_unregister(svc->stratix10_svc_rsu);
+	platform_device_unregister(svc->stratix10_svc_fcs);
 
 	for (i = 0; i < SVC_NUM_CHANNEL; i++) {
 		if (ctrl->chans[i].task) {
include/linux/firmware/intel/stratix10-smc.h +64 -0

70 lines not on this page · raw message

include/linux/firmware/intel/stratix10-svc-client.h +17 -1
index 3edd93502bf8..285fddafeacb 100644
@@ -7,6 +7,8 @@
 #ifndef __STRATIX10_SVC_CLIENT_H
 #define __STRATIX10_SVC_CLIENT_H
 
+#include <linux/types.h>
+
 /*
  * Service layer driver supports client names
  *
@@ -122,6 +124,15 @@ struct stratix10_svc_chan;
  * @COMMAND_SMC_SVC_VERSION: Non-mailbox SMC SVC API Version,
  * return status is SVC_STATUS_OK
  *
+ * @COMMAND_FCS_CRYPTO_OPEN_SESSION: open the crypto service session(s),
+ * return status is SVC_STATUS_OK or SVC_STATUS_ERROR
+ *
+ * @COMMAND_FCS_CRYPTO_CLOSE_SESSION: close the crypto service session(s),
+ * return status is SVC_STATUS_OK or SVC_STATUS_ERROR
+ *
+ * @COMMAND_FCS_SDOS_DATA_EXT: extend SDOS data encryption & decryption,
+ * return status is SVC_STATUS_OK or SVC_STATUS_ERROR
+ *
  * @COMMAND_MBOX_SEND_CMD: send generic mailbox command, return status is
  * SVC_STATUS_OK or SVC_STATUS_ERROR
  *
@@ -185,6 +196,11 @@ enum stratix10_svc_command_code {
 	COMMAND_FCS_RANDOM_NUMBER_GEN,
 	/* for general status poll */
 	COMMAND_POLL_SERVICE_STATUS = 40,
+	/* for crypto service */
+	COMMAND_FCS_CRYPTO_OPEN_SESSION = 50,
+	COMMAND_FCS_CRYPTO_CLOSE_SESSION,
+	/* for extended SDOS encrypt/decrypt */
+	COMMAND_FCS_SDOS_DATA_EXT = 82,
 	/* for generic mailbox send command */
 	COMMAND_MBOX_SEND_CMD = 100,
 	/* Non-mailbox SMC Call */
@@ -210,7 +226,7 @@ struct stratix10_svc_client_msg {
 	void *payload_output;
 	size_t payload_length_output;
 	enum stratix10_svc_command_code command;
-	u64 arg[3];
+	u64 arg[4];
 };
 
 /**
Signature · 1 line
2.43.7
#2 lore raw

PATCH 2/2 firmware: socfpga-fcs: add Altera SoCFPGA FCS driver with SDOS

In reply to [email protected]

From: Hang Suan Wang <[email protected]>

Add the Altera SoCFPGA Crypto Service (FCS) driver, which exposes the Secure Data Object Service (SDOS) encrypt/decrypt operation and its crypto session lifecycle to non-secure host software.

The SDOS is the FCS feature that protects data at rest: the SDM encrypts and decrypts using a key derived from a device-unique SDOS root key plus an SDM-generated IV, so the host never handles raw key material or IVs. It only submits plaintext it already owns and receives authenticated ciphertext objects managed by the SDM. A primary use case is black key provisioning, where operational keys are installed without ever appearing in cleartext.

The driver is a standalone module and describes no hardware of its own. It binds by name to the "stratix10-fcs" platform device that the stratix10-svc driver registers in code, so no device-tree node is needed, and detects the SoC by matching the service-layer compatible. It exposes the following sysfs attributes. The SDOS requests are issued to the SDM through the stratix10-svc asynchronous SIP SMC. Source and destination buffers are taken from the service-layer memory pool so the SDM can reach them via physical or SMMU-remapped addresses.

For encryption the SDM returns a structured object (metadata, IV, HMAC, ciphertext); for decryption it validates the HMAC, recovers the parameters from the object header, and enforces a 64-bit owner ID so that only the creator of an object can decrypt it.

---
 MAINTAINERS                                |   8 +
 drivers/firmware/Kconfig                   |  16 +
 drivers/firmware/Makefile                  |   2 +
 drivers/firmware/socfpga-fcs-core.c        | 589 +++++++++++++++++++++
 drivers/firmware/socfpga-fcs.c             | 294 ++++++++++
 include/linux/firmware/intel/socfpga-fcs.h | 134 +++++
 6 files changed, 1043 insertions(+)
 create mode 100644 drivers/firmware/socfpga-fcs-core.c
 create mode 100644 drivers/firmware/socfpga-fcs.c
 create mode 100644 include/linux/firmware/intel/socfpga-fcs.h
MAINTAINERS +8 -0
index 15011f5752a9..72b12b32f8fe 100644
@@ -946,6 +946,14 @@ ALPS PS/2 TOUCHPAD DRIVER
 R:	Pali Rohár <[email protected]>
 F:	drivers/input/mouse/alps.*
 
+ALTERA FCS DRIVER
+M:	Hang Suan Wang <[email protected]>
+M:	Genevieve Chan <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	drivers/firmware/socfpga-fcs*
+F:	include/linux/firmware/intel/socfpga-fcs*
+
 ALTERA MAILBOX DRIVER
 M:	Tien Sung Ang <[email protected]>
 S:	Maintained
drivers/firmware/Kconfig +16 -0
index 12dc70254842..9a70def6932a 100644
@@ -172,6 +172,22 @@ config INTEL_STRATIX10_RSU
 
 	  Say Y here if you want Intel RSU support.
 
+config ALTERA_SOCFPGA_FCS
+	tristate "Altera SoCFPGA Crypto Service (FCS) configuration"
+	depends on INTEL_STRATIX10_SERVICE
+	default n
+	help
+	  Altera SoCFPGA Crypto Service (FCS) driver exposes interfaces access
+	  through the Intel Service Layer to user space via sysfs device
+	  attribute nodes. It exposes the crypto and key-management services
+	  of the Secure Device Manager (SDM) to the host software stack and
+	  requests are forwarded to Arm Trusted Firmware. The SDM then
+	  executes or authorizes them using device-rooted security resources.
+	  Protected key material remains within the secure firmware boundary
+	  and is not directly exposed to non-secure host software.
+
+	  Say Y here if you want Altera SoCFPGA FCS support.
+
 config MTK_ADSP_IPC
 	tristate "MTK ADSP IPC Protocol driver"
 	depends on MTK_ADSP_MBOX
drivers/firmware/Makefile +2 -0
index 4ddec2820c96..e9f52f0e5f7a 100644
@@ -10,6 +10,8 @@ obj-$(CONFIG_EDD)		+= edd.o
 obj-$(CONFIG_DMIID)		+= dmi-id.o
 obj-$(CONFIG_INTEL_STRATIX10_SERVICE) += stratix10-svc.o
 obj-$(CONFIG_INTEL_STRATIX10_RSU)     += stratix10-rsu.o
+obj-$(CONFIG_ALTERA_SOCFPGA_FCS) += altera-fcs.o
+altera-fcs-y := socfpga-fcs.o socfpga-fcs-core.o
 obj-$(CONFIG_ISCSI_IBFT_FIND)	+= iscsi_ibft_find.o
 obj-$(CONFIG_ISCSI_IBFT)	+= iscsi_ibft.o
 obj-$(CONFIG_FIRMWARE_MEMMAP)	+= memmap.o
drivers/firmware/socfpga-fcs-core.c new file +589 -0

589 lines not on this page · raw message

drivers/firmware/socfpga-fcs.c new file +294 -0

294 lines not on this page · raw message

include/linux/firmware/intel/socfpga-fcs.h new file +134 -0

134 lines not on this page · raw message

Signature · 1 line
2.43.7
maintainer
#3 lore raw

Re: [PATCH v1 2/2] firmware: socfpga-fcs: add Altera SoCFPGA FCS driver with SDOS

Quoted 200 lines · On 7/1/26 02:39, [email protected] wrote:

From: Hang Suan Wang <[email protected]>

Add the Altera SoCFPGA Crypto Service (FCS) driver, which exposes the Secure Data Object Service (SDOS) encrypt/decrypt operation and its crypto session lifecycle to non-secure host software.

The SDOS is the FCS feature that protects data at rest: the SDM encrypts and decrypts using a key derived from a device-unique SDOS root key plus an SDM-generated IV, so the host never handles raw key material or IVs. It only submits plaintext it already owns and receives authenticated ciphertext objects managed by the SDM. A primary use case is black key provisioning, where operational keys are installed without ever appearing in cleartext.

The driver is a standalone module and describes no hardware of its own. It binds by name to the "stratix10-fcs" platform device that the stratix10-svc driver registers in code, so no device-tree node is needed, and detects the SoC by matching the service-layer compatible. It exposes the following sysfs attributes. The SDOS requests are issued to the SDM through the stratix10-svc asynchronous SIP SMC. Source and destination buffers are taken from the service-layer memory pool so the SDM can reach them via physical or SMMU-remapped addresses.

For encryption the SDM returns a structured object (metadata, IV, HMAC, ciphertext); for decryption it validates the HMAC, recovers the parameters from the object header, and enforces a 64-bit owner ID so that only the creator of an object can decrypt it.

---
  MAINTAINERS                                |   8 +
  drivers/firmware/Kconfig                   |  16 +
  drivers/firmware/Makefile                  |   2 +
  drivers/firmware/socfpga-fcs-core.c        | 589 +++++++++++++++++++++
  drivers/firmware/socfpga-fcs.c             | 294 ++++++++++
  include/linux/firmware/intel/socfpga-fcs.h | 134 +++++
  6 files changed, 1043 insertions(+)

create mode 100644 drivers/firmware/socfpga-fcs-core.c create mode 100644 drivers/firmware/socfpga-fcs.c create mode 100644 include/linux/firmware/intel/socfpga-fcs.h

MAINTAINERS +8 -0
index 15011f5752a9..72b12b32f8fe 100644
@@ -946,6 +946,14 @@ ALPS PS/2 TOUCHPAD DRIVER
  R:	Pali Rohár <[email protected]>
  F:	drivers/input/mouse/alps.*
 
+ALTERA FCS DRIVER
+M:	Hang Suan Wang <[email protected]>
+M:	Genevieve Chan <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	drivers/firmware/socfpga-fcs*
+F:	include/linux/firmware/intel/socfpga-fcs*
+
  ALTERA MAILBOX DRIVER
  M:	Tien Sung Ang <[email protected]>
  S:	Maintained
drivers/firmware/Kconfig +16 -0
index 12dc70254842..9a70def6932a 100644
@@ -172,6 +172,22 @@ config INTEL_STRATIX10_RSU
 
  	  Say Y here if you want Intel RSU support.
 
+config ALTERA_SOCFPGA_FCS
+	tristate "Altera SoCFPGA Crypto Service (FCS) configuration"
+	depends on INTEL_STRATIX10_SERVICE
+	default n
+	help
+	  Altera SoCFPGA Crypto Service (FCS) driver exposes interfaces access
+	  through the Intel Service Layer to user space via sysfs device
+	  attribute nodes. It exposes the crypto and key-management services
+	  of the Secure Device Manager (SDM) to the host software stack and
+	  requests are forwarded to Arm Trusted Firmware. The SDM then
+	  executes or authorizes them using device-rooted security resources.
+	  Protected key material remains within the secure firmware boundary
+	  and is not directly exposed to non-secure host software.
+
+	  Say Y here if you want Altera SoCFPGA FCS support.
+
  config MTK_ADSP_IPC
  	tristate "MTK ADSP IPC Protocol driver"
  	depends on MTK_ADSP_MBOX
drivers/firmware/Makefile +2 -0
index 4ddec2820c96..e9f52f0e5f7a 100644
@@ -10,6 +10,8 @@ obj-$(CONFIG_EDD)		+= edd.o
  obj-$(CONFIG_DMIID)		+= dmi-id.o
  obj-$(CONFIG_INTEL_STRATIX10_SERVICE) += stratix10-svc.o
  obj-$(CONFIG_INTEL_STRATIX10_RSU)     += stratix10-rsu.o
+obj-$(CONFIG_ALTERA_SOCFPGA_FCS) += altera-fcs.o
+altera-fcs-y := socfpga-fcs.o socfpga-fcs-core.o
  obj-$(CONFIG_ISCSI_IBFT_FIND)	+= iscsi_ibft_find.o
  obj-$(CONFIG_ISCSI_IBFT)	+= iscsi_ibft.o
  obj-$(CONFIG_FIRMWARE_MEMMAP)	+= memmap.o
drivers/firmware/socfpga-fcs-core.c new file +94 -0
new file mode 100644
index 000000000000..62460fd0e9e4
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Altera Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/firmware/intel/socfpga-fcs.h>
+#include <linux/firmware/intel/stratix10-svc-client.h>
+
+#define OWNER_ID_OFFSET				12
+#define OWNER_ID_SIZE				8
+
+#define SDOS_DECRYPTION_REPROVISION_KEY_WARN	0x102
+#define SDOS_DECRYPTION_NOT_LATEST_KEY_WARN	0x103
+
+#define MSG_RETRY				3
+#define RETRY_SLEEP_MS				1
+#define TIMEOUT					1000
+
+static struct socfpga_fcs_priv *priv;
+
+/**
+ * fcs_atf_version_callback() - service-layer callback for the ATF version query
+ * @client: pointer to the stratix10-svc client
+ * @data: pointer to the service-layer callback data
+ *
+ * Store the returned Arm Trusted Firmware version (or mailbox error) in @priv
+ * and signal completion to the waiting caller.
+ */
+static void fcs_atf_version_callback(struct stratix10_svc_client *client,
+				     struct stratix10_svc_cb_data *data)
+{
+	struct socfpga_fcs_priv *p = client->priv;
+
+	p->status = data->status;
+	if (data->status == BIT(SVC_STATUS_OK)) {
+		p->status = 0;
+		p->atf_version[0] = *((unsigned int *)data->kaddr1);
+		p->atf_version[1] = *((unsigned int *)data->kaddr2);
+		p->atf_version[2] = *((unsigned int *)data->kaddr3);
+	} else if (data->status == BIT(SVC_STATUS_ERROR)) {
+		p->status = *((unsigned int *)data->kaddr1);
+		dev_err(client->dev, "mbox_error=0x%x\n", p->status);
+	}
+
+	complete(&p->completion);
+}
+
+/**
+ * fcs_async_callback() - completion callback for an async service request
+ * @ptr: pointer to the completion to signal
+ */
+static void fcs_async_callback(void *ptr)
+{
+	if (ptr)
+		complete(ptr);
+}
+
+/**
+ * fcs_svc_send_request() - build and send an FCS command to the service layer
+ * @command: FCS command code to dispatch
+ * @timeout: time to wait for completion, in jiffies
+ *
+ * Build the service-layer message for @command and send it through the
+ * stratix10-svc service driver, using the synchronous path for the ATF version
+ * query and the asynchronous mailbox path (with retries) for the remaining
+ * commands.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int fcs_svc_send_request(enum fcs_command_code command,
+				unsigned long timeout)
+{
+	struct fcs_cmd_context *k_ctx = &priv->k_ctx;
+	struct stratix10_svc_cb_data data;
+	struct completion completion;
+	void *handle = NULL;
+	int status, index;
+	int ret = 0;
+	struct stratix10_svc_client_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+
+	priv->status = 0;
+	priv->resp = 0;
+
+	switch (command) {
+	case FCS_DEV_CRYPTO_OPEN_SESSION:
+		pr_debug("Sending command: COMMAND_FCS_CRYPTO_OPEN_SESSION\n");
+		msg->command = COMMAND_FCS_CRYPTO_OPEN_SESSION;
+		break;
+
+	case FCS_DEV_CRYPTO_CLOSE_SESSION:

drivers/firmware/socfpga-fcs-core.c line 104

Quoted 10 lines
drivers/firmware/socfpga-fcs-core.c new file +10 -0
+		pr_debug("Sending command: COMMAND_FCS_CRYPTO_CLOSE_SESSION with session_id: 0x%x\n",
+			 priv->session_id);
+		msg->arg[0] = priv->session_id;
+		msg->command = COMMAND_FCS_CRYPTO_CLOSE_SESSION;
+		break;
+
+	case FCS_DEV_ATF_VERSION:
+		pr_debug("Sending command: COMMAND_SMC_ATF_BUILD_VER\n");
+		msg->command = COMMAND_SMC_ATF_BUILD_VER;
+		priv->client.receive_cb = fcs_atf_version_callback;

You've set the receive_cb here for this case, what if calls to the other cases come in? Wouldn't it just go to fcs_atf_version_callback()?

Quoted 37 lines
drivers/firmware/socfpga-fcs-core.c new file +37 -0
+		break;
+
+	case FCS_DEV_SDOS_DATA_EXT:
+		pr_debug("Sending command: COMMAND_FCS_SDOS_DATA_EXT with session_id: 0x%x, context_id: 0x%x, op_mode: 0x%x, own: 0x%llx\n",
+			 priv->session_id, k_ctx->sdos.context_id,
+			 k_ctx->sdos.op_mode, k_ctx->sdos.own);
+		msg->arg[0] = priv->session_id;
+		msg->arg[1] = k_ctx->sdos.context_id;
+		msg->arg[2] = k_ctx->sdos.op_mode;
+		msg->arg[3] = k_ctx->sdos.own;
+		msg->payload = k_ctx->sdos.src;
+		msg->payload_length = k_ctx->sdos.src_size;
+		msg->payload_output = k_ctx->sdos.dst;
+		msg->payload_length_output = *k_ctx->sdos.dst_size;
+		msg->command = COMMAND_FCS_SDOS_DATA_EXT;
+		break;
+
+	default:
+		pr_err("Unknown command: 0x%x\n", command);
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret) {
+		kfree(msg);
+		return ret;
+	}
+
+	if (command == FCS_DEV_ATF_VERSION) {
+		reinit_completion(&priv->completion);
+
+		ret = stratix10_svc_send(priv->chan, msg);
+		if (ret) {
+			pr_err("failed to send message to service channel\n");
+			goto fun_ret;
+		}
+

drivers/firmware/socfpga-fcs-core.c line 151

Quoted 10 lines
drivers/firmware/socfpga-fcs-core.c new file +10 -0
+		if (!wait_for_completion_timeout(&priv->completion, timeout)) {
+			pr_err("svc timeout to get completed status\n");
+			ret = -ETIMEDOUT;
+		}
+fun_ret:
+		kfree(msg);
+		return ret;
+	}
+
+	init_completion(&completion);

You're mixing a local stack completion with a priv->completion. Doesn't this local declaration of completion get destroy when there's a timeout?And if fcs_async_callback() gets fired, your local completion is no longer valid.

Quoted 11 lines
drivers/firmware/socfpga-fcs-core.c new file +11 -0
+
+	for (index = 0; index < MSG_RETRY; index++) {
+		status = stratix10_svc_async_send(priv->chan, msg, &handle,
+						  fcs_async_callback,
+						  &completion);
+		if (status == 0)
+			break;
+		msleep(RETRY_SLEEP_MS);
+	}
+
+	if (!handle || status != 0) {

drivers/firmware/socfpga-fcs-core.c line 172

Quoted 10 lines
drivers/firmware/socfpga-fcs-core.c new file +10 -0
+		pr_err("Failed to send async message\n");
+		kfree(msg);
+		return -ETIMEDOUT;
+	}
+
+	ret = wait_for_completion_io_timeout(&completion, (TIMEOUT));
+	if (ret > 0)
+		pr_debug("Received async interrupt\n");
+	else
+		pr_err("timeout occurred while waiting for async message\n");

So if a timeout happens,

drivers/firmware/socfpga-fcs-core.c line 174

Quoted 2 lines
drivers/firmware/socfpga-fcs-core.c new file +2 -0
+
+	ret = stratix10_svc_async_poll(priv->chan, handle, &data);

you fall straight into here. There's a reason why the timeout happened, this code ignoring that reason. Shouldn't you handle the timeout?

Quoted 46 lines
drivers/firmware/socfpga-fcs-core.c new file +46 -0
+	if (ret) {
+		pr_err("Failed to poll async message\n");
+		goto out;
+	}
+
+	priv->status = data.status;
+
+	if (data.kaddr1)
+		priv->resp = *((u32 *)data.kaddr1);
+	else
+		priv->resp = 0;
+
+out:
+	stratix10_svc_async_done(priv->chan, handle);
+	kfree(msg);
+
+	return ret;
+}
+
+/**
+ * fcs_session_open() - open an FCS crypto service session
+ * @k_ctx: pointer to the kernel-side FCS command context
+ *
+ * Request a new session from the SDM, generate the session UUID and copy it,
+ * together with the mailbox status, back to user space.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int fcs_session_open(struct fcs_cmd_context *const k_ctx)
+{
+	int ret = 0;
+
+	ret = fcs_svc_send_request(FCS_DEV_CRYPTO_OPEN_SESSION,
+				   SVC_FCS_REQUEST_TIMEOUT_MS);
+	if (ret) {
+		pr_err("Failed to send the cmd=%d,ret=%d\n",
+		       FCS_DEV_CRYPTO_OPEN_SESSION, ret);
+		return ret;
+	}
+
+	if (priv->status) {
+		ret = -EIO;
+		pr_err("Mailbox error, Failed to open session ret: %d\n", ret);
+		goto copy_mbox_status;
+	}
+

drivers/firmware/socfpga-fcs-core.c line 230

Quoted 10 lines
drivers/firmware/socfpga-fcs-core.c new file +10 -0
+	uuid_gen(&priv->uuid_id);
+
+	memcpy(&priv->session_id, &priv->resp, sizeof(priv->session_id));
+
+	ret = copy_to_user(k_ctx->open_session.suuid, &priv->uuid_id,
+			   sizeof(uuid_t)) ? -EFAULT : 0;
+	if (ret) {
+		pr_err("Failed to copy session ID to user suuid addr: %p ret: %d\n",
+		       k_ctx->open_session.suuid, ret);
+		goto copy_mbox_status;

Don't need this goto.

Quoted 96 lines
drivers/firmware/socfpga-fcs-core.c new file +96 -0
+	}
+
+copy_mbox_status:
+	if (copy_to_user(k_ctx->error_code_addr, &priv->status,
+			 sizeof(priv->status))) {
+		pr_err("Failed to copy mail box status code to user\n");
+		/* surface the copy failure only if nothing failed earlier */
+		if (!ret)
+			ret = -EFAULT;
+	}
+
+	return ret;
+}
+
+/**
+ * fcs_session_close() - close an FCS crypto service session
+ * @k_ctx: pointer to the kernel-side FCS command context
+ *
+ * Validate the caller-supplied session UUID, ask the SDM to close the session
+ * and copy the mailbox status back to user space.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int fcs_session_close(struct fcs_cmd_context *const k_ctx)
+{
+	int ret = 0;
+	struct fcs_cmd_context ctx;
+
+	memcpy(&ctx, k_ctx, sizeof(struct fcs_cmd_context));
+
+	if (!uuid_equal(&priv->uuid_id, &k_ctx->close_session.suuid)) {
+		ret = -EINVAL;
+		pr_err("Session UUID Mismatch ret: %d\n", ret);
+		return ret;
+	}
+
+	ret = fcs_svc_send_request(FCS_DEV_CRYPTO_CLOSE_SESSION,
+				   SVC_FCS_REQUEST_TIMEOUT_MS);
+	if (ret) {
+		pr_err("Failed to send the cmd=%d,ret=%d\n",
+		       FCS_DEV_CRYPTO_CLOSE_SESSION, ret);
+		return ret;
+	}
+
+	memset(&priv->uuid_id, 0, sizeof(uuid_t));
+	priv->session_id = 0;
+	if (priv->status) {
+		ret = -EIO;
+		pr_err("Mailbox error, Failed to close session ret: %d\n", ret);
+	}
+
+	if (copy_to_user(ctx.error_code_addr, &priv->status,
+			 sizeof(priv->status))) {
+		pr_err("Failed to copy mail box status code to user\n");
+		/* surface the copy failure only if nothing failed earlier */
+		if (!ret)
+			ret = -EFAULT;
+	}
+
+	return ret;
+}
+
+/**
+ * fcs_get_atf_version() - return the cached Arm Trusted Firmware version
+ * @version: array of three u32 entries to receive the major, minor and patch
+ *           version numbers
+ */
+void fcs_get_atf_version(u32 *version)
+{
+	memcpy(version, priv->atf_version, sizeof(priv->atf_version));
+}
+
+/**
+ * fcs_sdos_crypt() - perform an SDOS encrypt or decrypt operation
+ * @k_ctx: pointer to the kernel-side FCS command context
+ *
+ * Allocate service-layer source and destination buffers, copy the input from
+ * user space, drive the SDOS data command and copy the result and length back
+ * to user space. The operation direction is selected by @k_ctx->sdos.op_mode.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int fcs_sdos_crypt(struct fcs_cmd_context *const k_ctx)
+{
+	void *s_buf = NULL, *d_buf = NULL;
+	struct fcs_cmd_context ctx;
+	u32 output_size;
+	u32 dst_cap;
+	u64 owner_id;
+	int ret = 0;
+	char *temp;
+
+	memcpy(&ctx, k_ctx, sizeof(struct fcs_cmd_context));
+
+	if (!ctx.sdos.dst || !ctx.sdos.dst_size)
+		return -EINVAL;

drivers/firmware/socfpga-fcs-core.c line 336

Quoted 10 lines
drivers/firmware/socfpga-fcs-core.c new file +10 -0
+
+	/* Caller-provided output buffer capacity (in/out parameter) */
+	if (copy_from_user(&dst_cap, ctx.sdos.dst_size, sizeof(dst_cap)))
+		return -EFAULT;
+
+	if (ctx.sdos.op_mode) {
+		output_size = SDOS_ENCRYPTED_MAX_SZ;
+		/* encrypt: input is header + plaintext */
+		if (k_ctx->sdos.src_size < SDOS_DECRYPTED_MIN_SZ ||
+		    k_ctx->sdos.src_size > SDOS_DECRYPTED_MAX_SZ) {

You've already copied k_ctx to a local ctx, you should consistently use it ctx.

Quoted 12 lines
drivers/firmware/socfpga-fcs-core.c new file +12 -0
+			pr_err("Invalid SDOS src_size %u\n", k_ctx->sdos.src_size);
+			return -EINVAL;
+		}
+	} else {
+		output_size = SDOS_DECRYPTED_MAX_SZ;
+		/* decrypt: input is header + plaintext + HMAC */
+		if (k_ctx->sdos.src_size < SDOS_ENCRYPTED_MIN_SZ ||
+		    k_ctx->sdos.src_size > SDOS_ENCRYPTED_MAX_SZ) {
+			pr_err("Invalid SDOS src_size %u\n", k_ctx->sdos.src_size);
+			return -EINVAL;
+		}
+	}

drivers/firmware/socfpga-fcs-core.c line 358

Quoted 10 lines
drivers/firmware/socfpga-fcs-core.c new file +10 -0
+
+	s_buf = stratix10_svc_allocate_memory(priv->chan, k_ctx->sdos.src_size);
+	if (IS_ERR(s_buf)) {
+		ret = -ENOMEM;
+		pr_err("Failed to allocate memory for SDOS input data kernel buffer ret: %d\n",
+		       ret);
+		return ret;
+	}
+
+	k_ctx->sdos.dst_size = &output_size;

output_size is declared locally on a stack, but you're assigning the address to a long-living structure. That leaks the stack address and will leave you with a dangling pointer.

Quoted 8 lines
drivers/firmware/socfpga-fcs-core.c new file +8 -0
+
+	d_buf = stratix10_svc_allocate_memory(priv->chan, *k_ctx->sdos.dst_size);
+	if (IS_ERR(d_buf)) {
+		ret = -ENOMEM;
+		pr_err("Failed to allocate memory for SDOS output kernel buffer ret: %d\n", ret);
+		goto free_sbuf;
+	}
+

drivers/firmware/socfpga-fcs-core.c line 376

Quoted 10 lines
drivers/firmware/socfpga-fcs-core.c new file +10 -0
+	/* Copy the user space input data to the input data kernel buffer */
+	ret = copy_from_user(s_buf, k_ctx->sdos.src,
+			     k_ctx->sdos.src_size) ? -EFAULT : 0;
+	if (ret) {
+		pr_err("Failed to copy SDOS data from user to kernel buffer ret: %d\n", ret);
+		goto free_dbuf;
+	}
+
+	/* Get Owner ID from buf */
+	temp = (uint8_t *)s_buf;

temp is only used here, just declare as a (uint8_t *) to start with.

Dinh

#4 lore raw

Re: [PATCH v1 2/2] firmware: socfpga-fcs: add Altera SoCFPGA FCS driver with SDOS

thank you for review. I had address all the comment.

Quoted 204 lines · Dinh Nguyen
Quoted 201 lines · On 7/1/26 02:39, [email protected] wrote:

From: Hang Suan Wang <[email protected]>

Add the Altera SoCFPGA Crypto Service (FCS) driver, which exposes the Secure Data Object Service (SDOS) encrypt/decrypt operation and its crypto session lifecycle to non-secure host software.

The SDOS is the FCS feature that protects data at rest: the SDM encrypts and decrypts using a key derived from a device-unique SDOS root key plus an SDM-generated IV, so the host never handles raw key material or IVs. It only submits plaintext it already owns and receives authenticated ciphertext objects managed by the SDM. A primary use case is black key provisioning, where operational keys are installed without ever appearing in cleartext.

The driver is a standalone module and describes no hardware of its own. It binds by name to the "stratix10-fcs" platform device that the stratix10-svc driver registers in code, so no device-tree node is needed, and detects the SoC by matching the service-layer compatible. It exposes the following sysfs attributes. The SDOS requests are issued to the SDM through the stratix10-svc asynchronous SIP SMC. Source and destination buffers are taken from the service-layer memory pool so the SDM can reach them via physical or SMMU-remapped addresses.

For encryption the SDM returns a structured object (metadata, IV, HMAC, ciphertext); for decryption it validates the HMAC, recovers the parameters from the object header, and enforces a 64-bit owner ID so that only the creator of an object can decrypt it.

---
  MAINTAINERS                                |   8 + drivers/firmware/Kconfig                   |  16 + drivers/firmware/Makefile                  |   2 + drivers/firmware/socfpga-fcs-core.c        | 589 +++++++++++++++++++++ drivers/firmware/socfpga-fcs.c             | 294 ++++++++++ include/linux/firmware/intel/socfpga-fcs.h | 134 +++++

  6 files changed, 1043 insertions(+)

  create mode 100644 drivers/firmware/socfpga-fcs-core.c create mode 100644 drivers/firmware/socfpga-fcs.c create mode 100644 include/linux/firmware/intel/socfpga-fcs.h

MAINTAINERS +0 -0
index 15011f5752a9..72b12b32f8fe 100644
@@ -946,6 +946,14 @@ ALPS PS/2 TOUCHPAD DRIVER

  R:    Pali Rohár <[email protected]>
  F:    drivers/input/mouse/alps.*
  +ALTERA FCS DRIVER

MAINTAINERS +7 -0
+M:    Hang Suan Wang <[email protected]>
+M:    Genevieve Chan <[email protected]>
+L:    [email protected]
+S:    Maintained
+F:    drivers/firmware/socfpga-fcs*
+F:    include/linux/firmware/intel/socfpga-fcs*
+

  ALTERA MAILBOX DRIVER
  M:    Tien Sung Ang <[email protected]>
  S:    Maintained

drivers/firmware/Kconfig +0 -0
index 12dc70254842..9a70def6932a 100644
@@ -172,6 +172,22 @@ config INTEL_STRATIX10_RSU

          Say Y here if you want Intel RSU support.
  +config ALTERA_SOCFPGA_FCS
+    tristate "Altera SoCFPGA Crypto Service (FCS) configuration" +    depends on INTEL_STRATIX10_SERVICE
+    default n
+    help
+      Altera SoCFPGA Crypto Service (FCS) driver exposes interfaces access +      through the Intel Service Layer to user space via sysfs device +      attribute nodes. It exposes the crypto and key-management services +      of the Secure Device Manager (SDM) to the host software stack and +      requests are forwarded to Arm Trusted Firmware. The SDM then +      executes or authorizes them using device-rooted security resources. +      Protected key material remains within the secure firmware boundary +      and is not directly exposed to non-secure host software.

drivers/firmware/Kconfig +3 -0
+
+      Say Y here if you want Altera SoCFPGA FCS support.
+

  config MTK_ADSP_IPC
      tristate "MTK ADSP IPC Protocol driver"
      depends on MTK_ADSP_MBOX

drivers/firmware/Makefile +0 -0
index 4ddec2820c96..e9f52f0e5f7a 100644
@@ -10,6 +10,8 @@ obj-$(CONFIG_EDD)        += edd.o

  obj-$(CONFIG_DMIID)        += dmi-id.o obj-$(CONFIG_INTEL_STRATIX10_SERVICE) += stratix10-svc.o obj-$(CONFIG_INTEL_STRATIX10_RSU)     += stratix10-rsu.o

drivers/firmware/Makefile +2 -0
+obj-$(CONFIG_ALTERA_SOCFPGA_FCS) += altera-fcs.o
+altera-fcs-y := socfpga-fcs.o socfpga-fcs-core.o

  obj-$(CONFIG_ISCSI_IBFT_FIND)    += iscsi_ibft_find.o obj-$(CONFIG_ISCSI_IBFT)    += iscsi_ibft.o obj-$(CONFIG_FIRMWARE_MEMMAP)    += memmap.o

drivers/firmware/socfpga-fcs-core.c new file +98 -0
new file mode 100644
index 000000000000..62460fd0e9e4
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Altera Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/firmware/intel/socfpga-fcs.h>
+#include <linux/firmware/intel/stratix10-svc-client.h>
+
+#define OWNER_ID_OFFSET                12
+#define OWNER_ID_SIZE                8
+
+#define SDOS_DECRYPTION_REPROVISION_KEY_WARN    0x102
+#define SDOS_DECRYPTION_NOT_LATEST_KEY_WARN    0x103
+
+#define MSG_RETRY                3
+#define RETRY_SLEEP_MS                1
+#define TIMEOUT                    1000
+
+static struct socfpga_fcs_priv *priv;
+
+/**
+ * fcs_atf_version_callback() - service-layer callback for the ATF version query
+ * @client: pointer to the stratix10-svc client
+ * @data: pointer to the service-layer callback data
+ *
+ * Store the returned Arm Trusted Firmware version (or mailbox error) in @priv
+ * and signal completion to the waiting caller.
+ */
+static void fcs_atf_version_callback(struct stratix10_svc_client *client,
+                     struct stratix10_svc_cb_data *data)
+{
+    struct socfpga_fcs_priv *p = client->priv;
+
+    p->status = data->status;
+    if (data->status == BIT(SVC_STATUS_OK)) {
+        p->status = 0;
+        p->atf_version[0] = *((unsigned int *)data->kaddr1);
+        p->atf_version[1] = *((unsigned int *)data->kaddr2);
+        p->atf_version[2] = *((unsigned int *)data->kaddr3);
+    } else if (data->status == BIT(SVC_STATUS_ERROR)) {
+        p->status = *((unsigned int *)data->kaddr1);
+        dev_err(client->dev, "mbox_error=0x%x\n", p->status);
+    }
+
+    complete(&p->completion);
+}
+
+/**
+ * fcs_async_callback() - completion callback for an async service request
+ * @ptr: pointer to the completion to signal
+ */
+static void fcs_async_callback(void *ptr)
+{
+    if (ptr)
+        complete(ptr);
+}
+
+/**
+ * fcs_svc_send_request() - build and send an FCS command to the service layer
+ * @command: FCS command code to dispatch
+ * @timeout: time to wait for completion, in jiffies
+ *
+ * Build the service-layer message for @command and send it through the
+ * stratix10-svc service driver, using the synchronous path for the ATF version
+ * query and the asynchronous mailbox path (with retries) for the remaining
+ * commands.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int fcs_svc_send_request(enum fcs_command_code command,
+                unsigned long timeout)
+{
+    struct fcs_cmd_context *k_ctx = &priv->k_ctx;
+    struct stratix10_svc_cb_data data;
+    struct completion completion;
+    void *handle = NULL;
+    int status, index;
+    int ret = 0;
+    struct stratix10_svc_client_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+
+    priv->status = 0;
+    priv->resp = 0;
+
+    switch (command) {
+    case FCS_DEV_CRYPTO_OPEN_SESSION:
+        pr_debug("Sending command: COMMAND_FCS_CRYPTO_OPEN_SESSION\n");
+        msg->command = COMMAND_FCS_CRYPTO_OPEN_SESSION;
+        break;
+
+    case FCS_DEV_CRYPTO_CLOSE_SESSION:
+        pr_debug("Sending command: COMMAND_FCS_CRYPTO_CLOSE_SESSION with session_id: 0x%x\n",
+             priv->session_id);
+        msg->arg[0] = priv->session_id;
+        msg->command = COMMAND_FCS_CRYPTO_CLOSE_SESSION;
Quoted 9 lines

drivers/firmware/socfpga-fcs-core.c line 104

Quoted 6 lines
drivers/firmware/socfpga-fcs-core.c new file +6 -0
+        break;
+
+    case FCS_DEV_ATF_VERSION:
+        pr_debug("Sending command: COMMAND_SMC_ATF_BUILD_VER\n");
+        msg->command = COMMAND_SMC_ATF_BUILD_VER;
+        priv->client.receive_cb = fcs_atf_version_callback;

You've set the receive_cb here for this case, what if calls to the other cases come in? Wouldn't it just go to fcs_atf_version_callback()?

Agreed it's fragile to leave a persistent client field set from inside the switch, though.
Will fix in v2 by set receive_cb right before the sync send and clear it to NULL right after, so it is non-NULL only for that transaction and a future sync caller can't inherit a stale callback.

Quoted 42 lines
Quoted 42 lines
drivers/firmware/socfpga-fcs-core.c new file +41 -0
+        break;
+
+    case FCS_DEV_SDOS_DATA_EXT:
+        pr_debug("Sending command: COMMAND_FCS_SDOS_DATA_EXT with session_id: 0x%x, context_id: 0x%x, op_mode: 0x%x, own: 0x%llx\n",
+             priv->session_id, k_ctx->sdos.context_id,
+             k_ctx->sdos.op_mode, k_ctx->sdos.own);
+        msg->arg[0] = priv->session_id;
+        msg->arg[1] = k_ctx->sdos.context_id;
+        msg->arg[2] = k_ctx->sdos.op_mode;
+        msg->arg[3] = k_ctx->sdos.own;
+        msg->payload = k_ctx->sdos.src;
+        msg->payload_length = k_ctx->sdos.src_size;
+        msg->payload_output = k_ctx->sdos.dst;
+        msg->payload_length_output = *k_ctx->sdos.dst_size;
+        msg->command = COMMAND_FCS_SDOS_DATA_EXT;
+        break;
+
+    default:
+        pr_err("Unknown command: 0x%x\n", command);
+        ret = -EINVAL;
+        break;
+    }
+
+    if (ret) {
+        kfree(msg);
+        return ret;
+    }
+
+    if (command == FCS_DEV_ATF_VERSION) {
+        reinit_completion(&priv->completion);
+
+        ret = stratix10_svc_send(priv->chan, msg);
+        if (ret) {
+            pr_err("failed to send message to service channel\n");
+            goto fun_ret;
+        }
+
+        if (!wait_for_completion_timeout(&priv->completion, timeout)) {
+            pr_err("svc timeout to get completed status\n");
+            ret = -ETIMEDOUT;
+        }
Quoted 9 lines

drivers/firmware/socfpga-fcs-core.c line 151

Quoted 6 lines
drivers/firmware/socfpga-fcs-core.c new file +6 -0
+fun_ret:
+        kfree(msg);
+        return ret;
+    }
+
+    init_completion(&completion);

You're mixing a local stack completion with a priv->completion. Doesn't this local declaration of completion get destroy when there's a timeout?And if fcs_async_callback() gets fired, your local completion is no longer valid.

Agree, not hit today only because handle->cb is never invoked (completions are poll-delivered), but it's fragile.
Fixed in v2: dropped the stack completion and reuse the device-lifetime priv->completion (already used by the sync path; commands are serialized under priv->lock, one in-flight), so a late complete() lands on a valid object.

Quoted 24 lines
Quoted 12 lines
drivers/firmware/socfpga-fcs-core.c new file +11 -0
+
+    for (index = 0; index < MSG_RETRY; index++) {
+        status = stratix10_svc_async_send(priv->chan, msg, &handle,
+                          fcs_async_callback,
+                          &completion);
+        if (status == 0)
+            break;
+        msleep(RETRY_SLEEP_MS);
+    }
+
+    if (!handle || status != 0) {

drivers/firmware/socfpga-fcs-core.c line 172

Quoted 10 lines
drivers/firmware/socfpga-fcs-core.c new file +10 -0
+        pr_err("Failed to send async message\n");
+        kfree(msg);
+        return -ETIMEDOUT;
+    }
+
+    ret = wait_for_completion_io_timeout(&completion, (TIMEOUT));
+    if (ret > 0)
+        pr_debug("Received async interrupt\n");
+    else
+        pr_err("timeout occurred while waiting for async message\n");

So if a timeout happens,

Quoted 5 lines

drivers/firmware/socfpga-fcs-core.c line 174

Quoted 2 lines
drivers/firmware/socfpga-fcs-core.c new file +2 -0
+
+    ret = stratix10_svc_async_poll(priv->chan, handle, &data);

you fall straight into here. There's a reason why the timeout happened, this code ignoring that reason. Shouldn't you handle the timeout?

the timeout is only a hint. The wait_for_completion_io_timeout() result is intentionally a hint - the authoritative status comes from the SDM via stratix10_svc_async_poll(), so we poll even after a timeout.

will fix in v2 to poll result: STATUS_BUSY (-EAGAIN) -> return -EINPROGRESS and abort without recycling the transaction id or freeing the in-flight DMA buffers; any other poll error is propagated;

Quoted 50 lines
Quoted 50 lines
drivers/firmware/socfpga-fcs-core.c new file +50 -0
+    if (ret) {
+        pr_err("Failed to poll async message\n");
+        goto out;
+    }
+
+    priv->status = data.status;
+
+    if (data.kaddr1)
+        priv->resp = *((u32 *)data.kaddr1);
+    else
+        priv->resp = 0;
+
+out:
+    stratix10_svc_async_done(priv->chan, handle);
+    kfree(msg);
+
+    return ret;
+}
+
+/**
+ * fcs_session_open() - open an FCS crypto service session
+ * @k_ctx: pointer to the kernel-side FCS command context
+ *
+ * Request a new session from the SDM, generate the session UUID and copy it,
+ * together with the mailbox status, back to user space.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int fcs_session_open(struct fcs_cmd_context *const k_ctx)
+{
+    int ret = 0;
+
+    ret = fcs_svc_send_request(FCS_DEV_CRYPTO_OPEN_SESSION,
+                   SVC_FCS_REQUEST_TIMEOUT_MS);
+    if (ret) {
+        pr_err("Failed to send the cmd=%d,ret=%d\n",
+               FCS_DEV_CRYPTO_OPEN_SESSION, ret);
+        return ret;
+    }
+
+    if (priv->status) {
+        ret = -EIO;
+        pr_err("Mailbox error, Failed to open session ret: %d\n", ret);
+        goto copy_mbox_status;
+    }
+
+    uuid_gen(&priv->uuid_id);
+
+    memcpy(&priv->session_id, &priv->resp, sizeof(priv->session_id));
+
Quoted 9 lines

drivers/firmware/socfpga-fcs-core.c line 230

Quoted 6 lines
drivers/firmware/socfpga-fcs-core.c new file +6 -0
+    ret = copy_to_user(k_ctx->open_session.suuid, &priv->uuid_id,
+               sizeof(uuid_t)) ? -EFAULT : 0;
+    if (ret) {
+        pr_err("Failed to copy session ID to user suuid addr: %p ret: %d\n",
+               k_ctx->open_session.suuid, ret);
+        goto copy_mbox_status;

Don't need this goto.

right, will remove in v2

Quoted 100 lines
Quoted 100 lines
drivers/firmware/socfpga-fcs-core.c new file +100 -0
+    }
+
+copy_mbox_status:
+    if (copy_to_user(k_ctx->error_code_addr, &priv->status,
+             sizeof(priv->status))) {
+        pr_err("Failed to copy mail box status code to user\n");
+        /* surface the copy failure only if nothing failed earlier */
+        if (!ret)
+            ret = -EFAULT;
+    }
+
+    return ret;
+}
+
+/**
+ * fcs_session_close() - close an FCS crypto service session
+ * @k_ctx: pointer to the kernel-side FCS command context
+ *
+ * Validate the caller-supplied session UUID, ask the SDM to close the session
+ * and copy the mailbox status back to user space.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int fcs_session_close(struct fcs_cmd_context *const k_ctx)
+{
+    int ret = 0;
+    struct fcs_cmd_context ctx;
+
+    memcpy(&ctx, k_ctx, sizeof(struct fcs_cmd_context));
+
+    if (!uuid_equal(&priv->uuid_id, &k_ctx->close_session.suuid)) {
+        ret = -EINVAL;
+        pr_err("Session UUID Mismatch ret: %d\n", ret);
+        return ret;
+    }
+
+    ret = fcs_svc_send_request(FCS_DEV_CRYPTO_CLOSE_SESSION,
+                   SVC_FCS_REQUEST_TIMEOUT_MS);
+    if (ret) {
+        pr_err("Failed to send the cmd=%d,ret=%d\n",
+               FCS_DEV_CRYPTO_CLOSE_SESSION, ret);
+        return ret;
+    }
+
+    memset(&priv->uuid_id, 0, sizeof(uuid_t));
+    priv->session_id = 0;
+    if (priv->status) {
+        ret = -EIO;
+        pr_err("Mailbox error, Failed to close session ret: %d\n", ret);
+    }
+
+    if (copy_to_user(ctx.error_code_addr, &priv->status,
+             sizeof(priv->status))) {
+        pr_err("Failed to copy mail box status code to user\n");
+        /* surface the copy failure only if nothing failed earlier */
+        if (!ret)
+            ret = -EFAULT;
+    }
+
+    return ret;
+}
+
+/**
+ * fcs_get_atf_version() - return the cached Arm Trusted Firmware version
+ * @version: array of three u32 entries to receive the major, minor and patch
+ *           version numbers
+ */
+void fcs_get_atf_version(u32 *version)
+{
+    memcpy(version, priv->atf_version, sizeof(priv->atf_version));
+}
+
+/**
+ * fcs_sdos_crypt() - perform an SDOS encrypt or decrypt operation
+ * @k_ctx: pointer to the kernel-side FCS command context
+ *
+ * Allocate service-layer source and destination buffers, copy the input from
+ * user space, drive the SDOS data command and copy the result and length back
+ * to user space. The operation direction is selected by @k_ctx->sdos.op_mode.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int fcs_sdos_crypt(struct fcs_cmd_context *const k_ctx)
+{
+    void *s_buf = NULL, *d_buf = NULL;
+    struct fcs_cmd_context ctx;
+    u32 output_size;
+    u32 dst_cap;
+    u64 owner_id;
+    int ret = 0;
+    char *temp;
+
+    memcpy(&ctx, k_ctx, sizeof(struct fcs_cmd_context));
+
+    if (!ctx.sdos.dst || !ctx.sdos.dst_size)
+        return -EINVAL;
+
+    /* Caller-provided output buffer capacity (in/out parameter) */
+    if (copy_from_user(&dst_cap, ctx.sdos.dst_size, sizeof(dst_cap)))
+        return -EFAULT;
Quoted 9 lines

drivers/firmware/socfpga-fcs-core.c line 336

Quoted 6 lines
drivers/firmware/socfpga-fcs-core.c new file +6 -0
+
+    if (ctx.sdos.op_mode) {
+        output_size = SDOS_ENCRYPTED_MAX_SZ;
+        /* encrypt: input is header + plaintext */
+        if (k_ctx->sdos.src_size < SDOS_DECRYPTED_MIN_SZ ||
+            k_ctx->sdos.src_size > SDOS_DECRYPTED_MAX_SZ) {

You've already copied k_ctx to a local ctx, you should consistently use it ctx.

Agree, will fix in v2 the read/validation accesses in fcs_sdos_crypt() to use the local ctx snapshot (src_size checks, buffer sizing, copy_from_user src). I'll apply the same ctx cleanup to fcs_session_close()

Quoted 17 lines
Quoted 17 lines
drivers/firmware/socfpga-fcs-core.c new file +16 -0
+            pr_err("Invalid SDOS src_size %u\n", k_ctx->sdos.src_size);
+            return -EINVAL;
+        }
+    } else {
+        output_size = SDOS_DECRYPTED_MAX_SZ;
+        /* decrypt: input is header + plaintext + HMAC */
+        if (k_ctx->sdos.src_size < SDOS_ENCRYPTED_MIN_SZ ||
+            k_ctx->sdos.src_size > SDOS_ENCRYPTED_MAX_SZ) {
+            pr_err("Invalid SDOS src_size %u\n", k_ctx->sdos.src_size);
+            return -EINVAL;
+        }
+    }
+
+    s_buf = stratix10_svc_allocate_memory(priv->chan, k_ctx->sdos.src_size);
+    if (IS_ERR(s_buf)) {
+        ret = -ENOMEM;
Quoted 9 lines

drivers/firmware/socfpga-fcs-core.c line 358

Quoted 6 lines
drivers/firmware/socfpga-fcs-core.c new file +6 -0
+        pr_err("Failed to allocate memory for SDOS input data kernel buffer ret: %d\n",
+               ret);
+        return ret;
+    }
+
+    k_ctx->sdos.dst_size = &output_size;

output_size is declared locally on a stack, but you're assigning the address to a long-living structure. That leaks the stack address and will leave you with a dangling pointer.

Agree, k_ctx points at priv->k_ctx (device-lifetime), so storing &output_size (stack) into k_ctx->sdos.dst_size leaves a dangling pointer once fcs_sdos_crypt() returns.
Fixed in v2: added a device-lifetime backing field priv->sdos_output_size and point dst_size at that instead of a stack variable, so the staged pointer can't dangle. Access stays serialized under priv->lock.

Quoted 13 lines
Quoted 13 lines
drivers/firmware/socfpga-fcs-core.c new file +12 -0
+
+    d_buf = stratix10_svc_allocate_memory(priv->chan, *k_ctx->sdos.dst_size);
+    if (IS_ERR(d_buf)) {
+        ret = -ENOMEM;
+        pr_err("Failed to allocate memory for SDOS output kernel buffer ret: %d\n", ret);
+        goto free_sbuf;
+    }
+
+    /* Copy the user space input data to the input data kernel buffer */
+    ret = copy_from_user(s_buf, k_ctx->sdos.src,
+                 k_ctx->sdos.src_size) ? -EFAULT : 0;
+    if (ret) {
Quoted 9 lines

drivers/firmware/socfpga-fcs-core.c line 376

Quoted 6 lines
drivers/firmware/socfpga-fcs-core.c new file +6 -0
+        pr_err("Failed to copy SDOS data from user to kernel buffer ret: %d\n", ret);
+        goto free_dbuf;
+    }
+
+    /* Get Owner ID from buf */
+    temp = (uint8_t *)s_buf;

temp is only used here, just declare as a (uint8_t *) to start with.

Agreed - temp was only a byte cursor into s_buf, so in v2 I dropped it and cast inline.

maintainer
#5 lore raw
In reply to [email protected]
Quoted 39 lines · On 7/1/26 02:39, [email protected] wrote:

From: Hang Suan Wang <[email protected]>

This series adds support for the Altera SoCFPGA Crypto Service (FCS), the runtime cryptographic interface provided by the Secure Device Manager (SDM). The SDM is the hardware security controller in Altera SoCFPGA devices. It acts as the root-of-trust device and controls access to built-in cryptographic hardware such as AES, SHA, a true random number generator, and Intel PUF. The SDM is responsible for security-critical functions including secure boot, FPGA bitstream authentication and optional decryption, remote system update, and runtime crypto services. On the HPS side, software reaches the SDM through a mailbox interface exposed in Linux via the stratix10-svc layer, which uses Arm Trusted Firmware SIP SMC calls underneath.

The FPGA Crypto Service (FCS) is the runtime crypto interface provided by the SDM. It covers services such as random number generation, AES operations, HMAC/SHA, key management, attestation, and related security functions.

This series implements one FCS feature: the Secure Data Object Service (SDOS), which protects sensitive data at rest. With SDOS the SDM encrypts and decrypts data using a key derived from a device-unique root key that never leaves the secure boundary, plus an SDM-generated IV. The host never handles raw key material or IVs: it supplies plaintext and receives an authenticated ciphertext object (and vice versa for decryption). A primary use case is black key provisioning, where operational keys are installed in protected form without ever being exposed in cleartext.

The driver reaches the SDM through the existing stratix10-svc mailbox using the Arm Trusted Firmware SIP SMC transport. Data buffers are allocated from the service-layer memory pool, which provides physically-contiguous memory whose physical address is handed to the SDM.

The series is organized as follows:
- Patch 1 (prerequisite) extends the stratix10-svc service layer with the FCS command codes and matching SIP SMC function IDs, adds the Agilex 5 (intel,agilex5-svc) match, and registers a "stratix10-fcs" platform device that an FCS client driver binds to.

Quoted 8 lines

- Patch 2 adds the FCS firmware driver implementing SDOS encrypt/decrypt and the crypto-session lifecycle, exposed via sysfs. It relies on the command codes and the device from patch 1, so patch 1 must be applied first.

Testing:
- Built for arm64 (defconfig + CONFIG_ALTERA_SOCFPGA_FCS=m).

There are a few sashiko comments on this. Can you verify that they are applicable?

https://sashiko.dev/#/patchset/cover.1782888532.git.hang.suan.wang%40altera.com

Thanks,
Dinh

#6 lore raw
Quoted 51 lines · Dinh Nguyen
Quoted 39 lines · On 7/1/26 02:39, [email protected] wrote:

From: Hang Suan Wang <[email protected]>

This series adds support for the Altera SoCFPGA Crypto Service (FCS), the runtime cryptographic interface provided by the Secure Device Manager (SDM). The SDM is the hardware security controller in Altera SoCFPGA devices. It acts as the root-of-trust device and controls access to built-in cryptographic hardware such as AES, SHA, a true random number generator, and Intel PUF. The SDM is responsible for security-critical functions including secure boot, FPGA bitstream authentication and optional decryption, remote system update, and runtime crypto services. On the HPS side, software reaches the SDM through a mailbox interface exposed in Linux via the stratix10-svc layer, which uses Arm Trusted Firmware SIP SMC calls underneath.

The FPGA Crypto Service (FCS) is the runtime crypto interface provided by the SDM. It covers services such as random number generation, AES operations, HMAC/SHA, key management, attestation, and related security functions.

This series implements one FCS feature: the Secure Data Object Service (SDOS), which protects sensitive data at rest. With SDOS the SDM encrypts and decrypts data using a key derived from a device-unique root key that never leaves the secure boundary, plus an SDM-generated IV. The host never handles raw key material or IVs: it supplies plaintext and receives an authenticated ciphertext object (and vice versa for decryption). A primary use case is black key provisioning, where operational keys are installed in protected form without ever being exposed in cleartext.

The driver reaches the SDM through the existing stratix10-svc mailbox using the Arm Trusted Firmware SIP SMC transport. Data buffers are allocated from the service-layer memory pool, which provides physically-contiguous memory whose physical address is handed to the SDM.

The series is organized as follows:
  - Patch 1 (prerequisite) extends the stratix10-svc service layer with the FCS command codes and matching SIP SMC function IDs, adds the Agilex 5 (intel,agilex5-svc) match, and registers a "stratix10-fcs" platform device that an FCS client driver binds to.

Quoted 8 lines

  - Patch 2 adds the FCS firmware driver implementing SDOS encrypt/decrypt and the crypto-session lifecycle, exposed via sysfs. It relies on the command codes and the device from patch 1, so patch 1 must be applied first.

Testing:
  - Built for arm64 (defconfig + CONFIG_ALTERA_SOCFPGA_FCS=m).

Quoted 6 lines

There are a few sashiko comments on this. Can you verify that they are applicable?

https://sashiko.dev/#/patchset/cover.1782888532.git.hang.suan.wang%40altera.com

Thanks,
Dinh

Thanks for the sashiko review. Responses below, grouped by disposition.

=== Fixes coming in v2 ===
1) Async payload not returned (stratix10_svc_async_prepare_response) Correct. stratix10_svc_async_poll() zeroes data, and prepare_response() never sets kaddr1 for COMMAND_FCS_CRYPTO_OPEN_SESSION and COMMAND_FCS_SDOS_DATA_EXT, so the client reads priv->resp = 0. The session ID and SDOS output length never reach userspace. This is not about SDM trustworthiness -- the payload register is simply never forwarded.
Fixed in v2.

2) wait_for_completion_timeout() units
Correct. SVC_FCS_REQUEST_TIMEOUT_MS is in milliseconds but is passed directly as jiffies, so the real timeout scales with CONFIG_HZ (2 s at HZ=1000, 20 s at HZ=100). I'll wrap it with msecs_to_jiffies() and drop the redundant TIMEOUT define in favor of the named _MS macro. Same fix for the wait_for_completion_io_timeout() call on the async path. Fixed in v2.

3) Freeing the handle on timeout (UAF risk)
The handle is kernel-only; the SDM tracks only an integer transaction_id, so firmware never touches the handle. But on timeout stratix10_svc_async_poll() returns -EAGAIN (SDM still BUSY) and the old code called stratix10_svc_async_done(), freeing the handle and releasing the transaction_id while the SDM still owned it -- enabling id reuse/aliasing (and a real UAF once handle->cb is wired up). Fix: on -EAGAIN, return -ETIMEDOUT without async_done(), leaving the transaction tracked so its id is not recycled; only terminal OK/ERROR statuses free the handle. Fully reclaiming a never-completing transaction needs SVC-layer cancellation, which I'll track as a follow-up. Fixed in v2.

4) SDOS missing UUID check
Correct. fcs_session_close() validates sdos.suuid against priv->uuid_id, but fcs_sdos_crypt() does not, so the token is ignored. I'll add the same uuid_equal() check early in fcs_sdos_crypt(). Clarification: the attrs are DEVICE_ATTR_WO (root-only by default), so it is not an unprivileged bypass, but the missing check is still wrong and weakens per-session ownership.
Fixed in v2.

5) Premature free of DMA buffer on timeout
Correct. fcs_svc_send_request() will return a distinct code (-EINPROGRESS) for the in-flight abort so fcs_sdos_crypt() skips freeing s_buf/d_buf on that path (leaking them safely with the abandoned transaction) while still freeing them on genuine send failures. Fixed in v2.

6) NULL-deref / singleton priv
Agreed. The mutex cannot guard this since it lives inside priv, so mutex_lock(&priv->lock) is exactly what dereferences NULL. FCS is inherently single-instance (one SDM / one SVC_CLIENT_FCS channel), so fcs_init() will return -EBUSY if priv is already set (checked before allocating, so a rejected second probe cannot NULL a bound instance), plus a NULL-guard in fcs_acquire_cmd_ctx(). This also covers the devm_kzalloc() overwrite/orphan concern.
Fixed in v2.

7) Uninitialized priv->platform
Correct, it was never assigned and always returned 0. Since there is no platform-detection source wired up, I've removed the platform sysfs attribute and its supporting code (fcs_get_platform(), the platform field, the unused AGILEX5_PLAT define) rather than ship a stub. It can be reintroduced with real detection later.
Fixed in v2.

8) DEVICE_ATTR on a raw kobject (wrong first arg / panic) Right, attaching device_attributes to a standalone kobject means the callbacks get a struct kobject * where they expect struct device *.
Fixed: attributes are now attached to the platform device via .dev_groups, and the manual kobject_create_and_add() is removed. This also resolves the related comments about using pdev->dev.kobj instead of a global /sys/kernel/fcs_sysfs directory, and the sysfs_create_groups() type mismatch.

9) fw_np refcount in stratix10_svc_init() of_find_matching_node() does drop the reference on fw_np, so it is used ref-less by of_platform_populate(). This is pre-existing and unrelated to this series, and causes no observable failure because the firmware node is static (non-OF_DYNAMIC) and never freed. I'd prefer to address it as a separate standalone fix to keep this series focused.

10) Dropping arg[3] in the sync copy
Intentional. stratix10_svc_send() copies only arg[0..2] into stratix10_svc_data, the FIFO entry for the synchronous mailbox thread, whose commands use at most arg[0..1]. arg[3] is used solely by the async SMC path (stratix10_svc_async_send()), which reads p_msg->arg[3] directly and never goes through the FIFO struct. No command loses data.

11) Overwriting priv->session_id on repeated open
The previous session is not leaked: the SDM rejects opening a new session while one is active, so fcs_session_open() takes the priv->status error path and returns before the memcpy into priv->session_id; the existing session_id/uuid_id are preserved and remain closable.

12) Passing a struct fcs_cmd_context * through the sysfs buffer Acknowledged; this is not standard sysfs ASCII I/O. It is the current design choice for this interface, since the controls are mostly scalar and independent.

13) Raw pointers in the copied-in struct / 32-bit compat Acknowledged; the context embeds native pointers, so layout and pointer widths differ between 32-bit userspace and a 64-bit kernel. That is the current design.