* [PATCH v1 0/2] Add Altera SoCFPGA Crypto Service (FCS) driver
@ 2026-07-01 7:39 hang.suan.wang
2026-07-01 7:39 ` [PATCH v1 1/2] firmware: stratix10-svc: add FCS crypto-service commands for Agilex 5 hang.suan.wang
2026-07-01 7:39 ` [PATCH v1 2/2] firmware: socfpga-fcs: add Altera SoCFPGA FCS driver with SDOS hang.suan.wang
0 siblings, 2 replies; 3+ messages in thread
From: hang.suan.wang @ 2026-07-01 7:39 UTC (permalink / raw)
To: Dinh Nguyen, linux-kernel, Michael S . Tsirkin, Huacai Chen,
Florian Fainelli, Chen-Yu Tsai
Cc: muhammad.nazim.amirul.nazle.asmade, tze.yee.ng, chee.nouk.phoon,
genevieve.chan
From: Hang Suan Wang <hang.suan.wang@altera.com>
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
--
2.43.7
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v1 1/2] firmware: stratix10-svc: add FCS crypto-service commands for Agilex 5
2026-07-01 7:39 [PATCH v1 0/2] Add Altera SoCFPGA Crypto Service (FCS) driver hang.suan.wang
@ 2026-07-01 7:39 ` hang.suan.wang
2026-07-01 7:39 ` [PATCH v1 2/2] firmware: socfpga-fcs: add Altera SoCFPGA FCS driver with SDOS hang.suan.wang
1 sibling, 0 replies; 3+ messages in thread
From: hang.suan.wang @ 2026-07-01 7:39 UTC (permalink / raw)
To: Dinh Nguyen, linux-kernel, Michael S . Tsirkin, Huacai Chen,
Florian Fainelli, Chen-Yu Tsai
Cc: muhammad.nazim.amirul.nazle.asmade, tze.yee.ng, chee.nouk.phoon,
genevieve.chan
From: Hang Suan Wang <hang.suan.wang@altera.com>
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.
Signed-off-by: Hang Suan Wang <hang.suan.wang@altera.com>
---
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(-)
diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c
index c24ca5823078..30035cb22b67 100644
--- a/drivers/firmware/stratix10-svc.c
+++ b/drivers/firmware/stratix10-svc.c
@@ -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) {
diff --git a/include/linux/firmware/intel/stratix10-smc.h b/include/linux/firmware/intel/stratix10-smc.h
index 9116512169dc..d7ad6bd6f669 100644
--- a/include/linux/firmware/intel/stratix10-smc.h
+++ b/include/linux/firmware/intel/stratix10-smc.h
@@ -640,6 +640,70 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE)
#define INTEL_SIP_SMC_FCS_GET_PROVISION_DATA \
INTEL_SIP_SMC_STD_CALL_VAL(INTEL_SIP_SMC_FUNCID_FCS_GET_PROVISION_DATA)
+/**
+ * Request INTEL_SIP_SMC_ASYNC_FCS_CRYPTION_EXT
+ * Async call to perform encryption/decryption
+ *
+ * Call register usage:
+ * a0 INTEL_SIP_SMC_ASYNC_FCS_CRYPTION_EXT
+ * a1 transaction job id
+ * a2 session ID
+ * a3 context ID
+ * a4 cryption operating mode (1 for encryption and 0 for decryption)
+ * a5 physical address of source
+ * a6 size of source
+ * a7 physical address of destination
+ * a8 size of destination
+ * a9 sdos ownership
+ * a10 smmu remapped address of source
+ * a11 smmu remapped address of destination
+ * a12-a17 not used
+ *
+ * Return status:
+ * a0 INTEL_SIP_SMC_STATUS_OK or INTEL_SIP_SMC_STATUS_ERROR
+ * a1-a17 not used
+ */
+#define INTEL_SIP_SMC_ASYNC_FUNC_ID_FCS_CRYPTION_EXT (0x12F)
+#define INTEL_SIP_SMC_ASYNC_FCS_CRYPTION_EXT \
+ INTEL_SIP_SMC_ASYNC_VAL(INTEL_SIP_SMC_ASYNC_FUNC_ID_FCS_CRYPTION_EXT)
+
+/**
+ * Request INTEL_SIP_SMC_ASYNC_FCS_OPEN_CS_SESSION
+ * Async call to open and establish a crypto service session with firmware
+ *
+ * Call register usage:
+ * a0 INTEL_SIP_SMC_FCS_OPEN_CRYPTO_SERVICE_SESSION
+ * a1 transaction job id
+ * a2-a17 not used
+ *
+ * Return status:
+ * a0 INTEL_SIP_SMC_STATUS_OK ,INTEL_SIP_SMC_STATUS_REJECTED
+ * or INTEL_SIP_SMC_STATUS_BUSY
+ * a1-a17 not used
+ */
+#define INTEL_SIP_SMC_ASYNC_FUNC_ID_FCS_OPEN_CS_SESSION (0x13A)
+#define INTEL_SIP_SMC_ASYNC_FCS_OPEN_CS_SESSION \
+ INTEL_SIP_SMC_ASYNC_VAL(INTEL_SIP_SMC_ASYNC_FUNC_ID_FCS_OPEN_CS_SESSION)
+
+/**
+ * Request INTEL_SIP_SMC_ASYNC_FCS_CLOSE_CS_SESSION
+ * Async call to close a service session
+ *
+ * Call register usage:
+ * a0 INTEL_SIP_SMC_ASYNC_FCS_CLOSE_CS_SESSION
+ * a1 transaction job id
+ * a2 session ID
+ * a3-a17 not used
+ *
+ * Return status:
+ * a0 INTEL_SIP_SMC_STATUS_OK ,INTEL_SIP_SMC_STATUS_REJECTED
+ * or INTEL_SIP_SMC_STATUS_BUSY
+ * a1-a17 not used
+ */
+#define INTEL_SIP_SMC_ASYNC_FUNC_ID_FCS_CLOSE_CS_SESSION (0x13B)
+#define INTEL_SIP_SMC_ASYNC_FCS_CLOSE_CS_SESSION \
+ INTEL_SIP_SMC_ASYNC_VAL(INTEL_SIP_SMC_ASYNC_FUNC_ID_FCS_CLOSE_CS_SESSION)
+
/**
* Request INTEL_SIP_SMC_HWMON_READTEMP
* Sync call to request temperature
diff --git a/include/linux/firmware/intel/stratix10-svc-client.h b/include/linux/firmware/intel/stratix10-svc-client.h
index 3edd93502bf8..285fddafeacb 100644
--- a/include/linux/firmware/intel/stratix10-svc-client.h
+++ b/include/linux/firmware/intel/stratix10-svc-client.h
@@ -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];
};
/**
--
2.43.7
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH v1 2/2] firmware: socfpga-fcs: add Altera SoCFPGA FCS driver with SDOS
2026-07-01 7:39 [PATCH v1 0/2] Add Altera SoCFPGA Crypto Service (FCS) driver hang.suan.wang
2026-07-01 7:39 ` [PATCH v1 1/2] firmware: stratix10-svc: add FCS crypto-service commands for Agilex 5 hang.suan.wang
@ 2026-07-01 7:39 ` hang.suan.wang
1 sibling, 0 replies; 3+ messages in thread
From: hang.suan.wang @ 2026-07-01 7:39 UTC (permalink / raw)
To: Dinh Nguyen, linux-kernel, Michael S . Tsirkin, Huacai Chen,
Florian Fainelli, Chen-Yu Tsai
Cc: muhammad.nazim.amirul.nazle.asmade, tze.yee.ng, chee.nouk.phoon,
genevieve.chan
From: Hang Suan Wang <hang.suan.wang@altera.com>
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.
Signed-off-by: Hang Suan Wang <hang.suan.wang@altera.com>
---
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
diff --git a/MAINTAINERS b/MAINTAINERS
index 15011f5752a9..72b12b32f8fe 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -946,6 +946,14 @@ ALPS PS/2 TOUCHPAD DRIVER
R: Pali Rohár <pali@kernel.org>
F: drivers/input/mouse/alps.*
+ALTERA FCS DRIVER
+M: Hang Suan Wang <hang.suan.wang@altera.com>
+M: Genevieve Chan <genevieve.chan@altera.com>
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: drivers/firmware/socfpga-fcs*
+F: include/linux/firmware/intel/socfpga-fcs*
+
ALTERA MAILBOX DRIVER
M: Tien Sung Ang <tiensung.ang@altera.com>
S: Maintained
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 12dc70254842..9a70def6932a 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -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
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 4ddec2820c96..e9f52f0e5f7a 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -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
diff --git a/drivers/firmware/socfpga-fcs-core.c b/drivers/firmware/socfpga-fcs-core.c
new file mode 100644
index 000000000000..62460fd0e9e4
--- /dev/null
+++ b/drivers/firmware/socfpga-fcs-core.c
@@ -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;
+ 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;
+ 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;
+ }
+fun_ret:
+ kfree(msg);
+ return ret;
+ }
+
+ init_completion(&completion);
+
+ 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) {
+ 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");
+
+ ret = stratix10_svc_async_poll(priv->chan, handle, &data);
+ 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));
+
+ 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;
+ }
+
+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;
+
+ 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) {
+ 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;
+ 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;
+
+ 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) {
+ 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;
+ memcpy(&owner_id, temp + OWNER_ID_OFFSET, OWNER_ID_SIZE);
+ k_ctx->sdos.own = owner_id;
+ k_ctx->sdos.src = s_buf;
+ k_ctx->sdos.dst = d_buf;
+
+ ret = fcs_svc_send_request(FCS_DEV_SDOS_DATA_EXT,
+ SVC_FCS_REQUEST_TIMEOUT_MS);
+ if (ret) {
+ pr_err("Failed to send the cmd=%d,ret=%d\n", FCS_DEV_SDOS_DATA_EXT, ret);
+ goto free_dbuf;
+ }
+ if (priv->status &&
+ priv->status != SDOS_DECRYPTION_REPROVISION_KEY_WARN &&
+ priv->status != SDOS_DECRYPTION_NOT_LATEST_KEY_WARN) {
+ pr_err("Failed to perform SDOS operation ret: %d Mailbox Status = %d\n",
+ ret, priv->status);
+ goto copy_mbox_status;
+ }
+
+ /*
+ * priv->resp is reported by firmware; never trust it to read back
+ * more than the kernel output buffer (d_buf) actually holds,
+ * otherwise the copy below would leak adjacent kernel memory.
+ */
+ if (priv->resp > output_size) {
+ pr_err("SDOS output %u exceeds kernel buffer %u\n",
+ priv->resp, output_size);
+ ret = -EIO;
+ goto copy_mbox_status;
+ }
+
+ /* Do not write past the caller-provided output buffer */
+ if (priv->resp > dst_cap) {
+ pr_err("SDOS output %u exceeds caller buffer %u\n",
+ priv->resp, dst_cap);
+ ret = -EMSGSIZE;
+ goto copy_mbox_status;
+ }
+
+ /* Copy the encrypted/decrypted output from kernel space to user space */
+ ret = copy_to_user(ctx.sdos.dst, d_buf, priv->resp) ? -EFAULT : 0;
+ if (ret) {
+ pr_err("Failed to copy encrypted output to user ret: %d\n", ret);
+ goto copy_mbox_status;
+ }
+
+ /* Copy the encrypted output length from kernel space to user space */
+ ret = copy_to_user(ctx.sdos.dst_size, &priv->resp,
+ sizeof(priv->resp)) ? -EFAULT : 0;
+ if (ret)
+ pr_err("Failed to copy encrypted output length to user ret: %d\n", ret);
+
+copy_mbox_status:
+ if (copy_to_user(ctx.error_code_addr, &priv->status,
+ sizeof(priv->status))) {
+ pr_err("Failed to copy mailbox status code to user\n");
+ /* surface the copy failure only if nothing failed earlier */
+ if (!ret)
+ ret = -EFAULT;
+ }
+free_dbuf:
+ stratix10_svc_free_memory(priv->chan, d_buf);
+free_sbuf:
+ stratix10_svc_free_memory(priv->chan, s_buf);
+
+ return ret;
+}
+
+/**
+ * fcs_acquire_cmd_ctx() - take the FCS lock and return the command context
+ *
+ * Serialises access to the shared command context across concurrent callers.
+ * The caller must release it with fcs_release_cmd_ctx().
+ *
+ * Return: pointer to the locked FCS command context.
+ */
+struct fcs_cmd_context *fcs_acquire_cmd_ctx(void)
+{
+ mutex_lock(&priv->lock);
+ return &priv->k_ctx;
+}
+
+/**
+ * fcs_release_cmd_ctx() - release the FCS command context lock
+ * @k_ctx: pointer to the FCS command context previously acquired
+ */
+void fcs_release_cmd_ctx(struct fcs_cmd_context *const k_ctx)
+{
+ mutex_unlock(&priv->lock);
+}
+
+/**
+ * fcs_read_version_from_atf() - query the Arm Trusted Firmware build version
+ *
+ * Send the ATF version command to the SDM and cache the result in @priv.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int fcs_read_version_from_atf(void)
+{
+ int ret = 0;
+
+ ret = fcs_svc_send_request(FCS_DEV_ATF_VERSION,
+ SVC_FCS_REQUEST_TIMEOUT_MS);
+ if (ret) {
+ pr_err("Failed to send the cmd=%d,ret=%d\n", FCS_DEV_ATF_VERSION, ret);
+ return ret;
+ }
+
+ if (priv->status) {
+ ret = -EIO;
+ pr_err("Mailbox error, Failed to read ATF version ret: %d\n", ret);
+ }
+
+ stratix10_svc_done(priv->chan);
+
+ return ret;
+}
+
+/**
+ * fcs_init() - allocate and initialise the FCS private state
+ * @dev: pointer to fcs device
+ *
+ * Allocate @priv, request the service channel, register the async client,
+ * detect the platform and read the ATF version.
+ *
+ * Return: 0 on success, -EPROBE_DEFER or negative errno on failure.
+ */
+int fcs_init(struct device *dev)
+{
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(struct socfpga_fcs_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->lock);
+
+ priv->dev = dev;
+ priv->client.dev = dev;
+ priv->client.receive_cb = NULL;
+ priv->client.priv = priv;
+
+ priv->chan = stratix10_svc_request_channel_byname(&priv->client,
+ SVC_CLIENT_FCS);
+ if (IS_ERR(priv->chan)) {
+ pr_err("couldn't get service channel %s\n", SVC_CLIENT_FCS);
+ return -EPROBE_DEFER;
+ }
+
+ ret = stratix10_svc_add_async_client(priv->chan, true);
+ if (ret) {
+ pr_err("Failed to add async client\n");
+ goto free_chan;
+ }
+
+ init_completion(&priv->completion);
+
+ fcs_read_version_from_atf();
+
+ return 0;
+
+remove_async:
+ stratix10_svc_remove_async_client(priv->chan);
+free_chan:
+ stratix10_svc_free_channel(priv->chan);
+
+ return ret;
+}
+
+/**
+ * fcs_deinit() - tear down the FCS private state
+ *
+ * Close any open session, remove the async client, free the service channel
+ * and clear @priv.
+ */
+void fcs_deinit(void)
+{
+ if (priv && priv->session_id) {
+ int ret = fcs_svc_send_request(FCS_DEV_CRYPTO_CLOSE_SESSION,
+ SVC_FCS_REQUEST_TIMEOUT_MS);
+ if (ret)
+ pr_err("Failed to close FCS service session,ret=%d\n", ret);
+ }
+
+ if (priv) {
+ stratix10_svc_remove_async_client(priv->chan);
+ stratix10_svc_free_channel(priv->chan);
+ }
+
+ priv = NULL;
+}
+
+/**
+ * fcs_get_platform() - return the detected FCS platform identifier
+ *
+ * Return: the platform identifier cached in @priv.
+ */
+int fcs_get_platform(void)
+{
+ return priv->platform;
+}
+
+/**
+ * fcs_cleanup() - release the FCS service channel and clear the state
+ */
+void fcs_cleanup(void)
+{
+ if (priv)
+ stratix10_svc_free_channel(priv->chan);
+
+ priv = NULL;
+}
diff --git a/drivers/firmware/socfpga-fcs.c b/drivers/firmware/socfpga-fcs.c
new file mode 100644
index 000000000000..cfe9b25faf30
--- /dev/null
+++ b/drivers/firmware/socfpga-fcs.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026, Altera Corporation
+ */
+
+#include <linux/firmware/intel/socfpga-fcs.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+
+/**
+ * open_session_store() - open an FCS crypto service session
+ * @dev: pointer to fcs device
+ * @attr: device attribute
+ * @buf: pointer to character buffer carrying the user command context
+ * @buf_size: size of the buffer
+ *
+ * Return: @buf_size on success, negative errno on failure.
+ */
+static ssize_t open_session_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t buf_size)
+{
+ struct fcs_cmd_context *const u_ctx = *(struct fcs_cmd_context **)buf;
+ struct fcs_cmd_context *k_ctx;
+ int ret;
+
+ k_ctx = fcs_acquire_cmd_ctx();
+ if (!k_ctx) {
+ dev_err(dev, "Failed get context. Context is in use\n");
+ return -EBUSY;
+ }
+
+ if (copy_from_user(k_ctx, u_ctx, sizeof(*k_ctx))) {
+ dev_err(dev, "Failed to copy context from user space\n");
+ ret = -EFAULT;
+ goto out;
+ }
+
+ ret = fcs_session_open(k_ctx);
+ if (ret) {
+ dev_err(dev, "Failed to open session\n");
+ goto out;
+ }
+
+ ret = buf_size;
+out:
+ fcs_release_cmd_ctx(k_ctx);
+
+ return ret;
+}
+
+/**
+ * close_session_store() - close an FCS crypto service session
+ * @dev: pointer to fcs device
+ * @attr: device attribute
+ * @buf: pointer to character buffer carrying the user command context
+ * @buf_size: size of the buffer
+ *
+ * Return: @buf_size on success, negative errno on failure.
+ */
+static ssize_t close_session_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t buf_size)
+{
+ struct fcs_cmd_context *const u_ctx = *(struct fcs_cmd_context **)buf;
+ struct fcs_cmd_context *k_ctx;
+ int ret;
+
+ k_ctx = fcs_acquire_cmd_ctx();
+ if (!k_ctx) {
+ dev_err(dev, "Failed get context. Context is in use\n");
+ return -EBUSY;
+ }
+
+ if (copy_from_user(k_ctx, u_ctx, sizeof(*k_ctx))) {
+ dev_err(dev, "Failed to copy context from user space\n");
+ ret = -EFAULT;
+ goto out;
+ }
+
+ ret = fcs_session_close(k_ctx);
+ if (ret) {
+ dev_err(dev, "Failed to close session\n");
+ goto out;
+ }
+
+ ret = buf_size;
+out:
+ fcs_release_cmd_ctx(k_ctx);
+
+ return ret;
+}
+
+/**
+ * atf_version_show() - report the Arm Trusted Firmware build version
+ * @dev: pointer to fcs device
+ * @attr: device attribute
+ * @buf: pointer to character buffer to receive the version string
+ *
+ * Return: number of bytes written to @buf.
+ */
+static ssize_t atf_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int version[3];
+
+ fcs_get_atf_version(version);
+ return sysfs_emit(buf, "%u.%u.%u\n", version[0], version[1], version[2]);
+}
+
+/**
+ * platform_show() - report the detected FCS platform identifier
+ * @dev: pointer to fcs device
+ * @attr: device attribute
+ * @buf: pointer to character buffer to receive the platform identifier
+ *
+ * Return: number of bytes written to @buf.
+ */
+static ssize_t platform_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sysfs_emit(buf, "%d\n", fcs_get_platform());
+}
+
+/**
+ * sdos_store() - perform an SDOS encrypt/decrypt operation
+ * @dev: pointer to fcs device
+ * @attr: device attribute
+ * @buf: pointer to character buffer carrying the user command context
+ * @buf_size: size of the buffer
+ *
+ * Return: @buf_size on success, negative errno on failure.
+ */
+static ssize_t sdos_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t buf_size)
+{
+ struct fcs_cmd_context *const u_ctx = *(struct fcs_cmd_context **)buf;
+ struct fcs_cmd_context *k_ctx;
+ int ret;
+
+ k_ctx = fcs_acquire_cmd_ctx();
+ if (!k_ctx) {
+ dev_err(dev, "Failed get context. Context is in use\n");
+ return -EBUSY;
+ }
+
+ if (copy_from_user(k_ctx, u_ctx, sizeof(*k_ctx))) {
+ dev_err(dev, "Failed to copy context from user space\n");
+ ret = -EFAULT;
+ goto out;
+ }
+
+ ret = fcs_sdos_crypt(k_ctx);
+ if (ret) {
+ dev_err(dev, "Failed to perform SDOS operation\n");
+ goto out;
+ }
+
+ ret = buf_size;
+out:
+ fcs_release_cmd_ctx(k_ctx);
+
+ return ret;
+}
+
+static DEVICE_ATTR_WO(open_session);
+static DEVICE_ATTR_WO(close_session);
+static DEVICE_ATTR_RO(atf_version);
+static DEVICE_ATTR_WO(sdos);
+static DEVICE_ATTR_RO(platform);
+
+static struct attribute *fcs_attrs[] = {
+ &dev_attr_open_session.attr,
+ &dev_attr_close_session.attr,
+ &dev_attr_atf_version.attr,
+ &dev_attr_sdos.attr,
+ &dev_attr_platform.attr,
+ NULL
+};
+
+static struct attribute_group fcs_group = {
+ .attrs = fcs_attrs,
+};
+
+static const struct attribute_group *fcs_groups[] = {
+ &fcs_group,
+ NULL,
+};
+
+static struct kobject *sysfs_kobj;
+
+/**
+ * fcs_driver_probe() - probe the FCS platform device
+ * @pdev: pointer to the FCS platform device
+ *
+ * Initialise the FCS state and publish the sysfs attribute groups.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int fcs_driver_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ /*
+ * Initialise the private state before publishing any sysfs
+ * attribute, otherwise a concurrent access could dereference a
+ * not-yet-allocated priv.
+ */
+ ret = fcs_init(dev);
+ if (ret) {
+ dev_err(dev, "Failed to initialize FCS\n");
+ return ret;
+ }
+
+ sysfs_kobj = kobject_create_and_add("fcs_sysfs", kernel_kobj);
+ if (!sysfs_kobj) {
+ dev_err(dev, "Failed to create and add kobject\n");
+ ret = -ENOMEM;
+ goto err_deinit;
+ }
+
+ ret = sysfs_create_groups(sysfs_kobj, fcs_groups);
+ if (ret) {
+ dev_err(dev, "Failed to create sysfs groups\n");
+ kobject_put(sysfs_kobj);
+ sysfs_kobj = NULL;
+ goto err_deinit;
+ }
+
+ return 0;
+
+err_deinit:
+ fcs_deinit();
+
+ return ret;
+}
+
+/**
+ * fcs_driver_remove() - remove the FCS platform device
+ * @pdev: pointer to the FCS platform device
+ *
+ * Remove the sysfs attribute groups and tear down the FCS state.
+ */
+static void fcs_driver_remove(struct platform_device *pdev)
+{
+ sysfs_remove_groups(sysfs_kobj, fcs_groups);
+ kobject_put(sysfs_kobj);
+ sysfs_kobj = NULL;
+
+ fcs_deinit();
+}
+
+static struct platform_driver fcs_driver = {
+ .probe = fcs_driver_probe,
+ .remove = fcs_driver_remove,
+ .driver = {
+ .name = "stratix10-fcs",
+ },
+};
+
+/**
+ * socfpga_fcs_init() - register the FCS platform driver
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int __init socfpga_fcs_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&fcs_driver);
+ if (ret)
+ pr_err("Failed to register platform driver: %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * socfpga_fcs_exit() - unregister the FCS platform driver
+ */
+static void __exit socfpga_fcs_exit(void)
+{
+ platform_driver_unregister(&fcs_driver);
+}
+
+module_init(socfpga_fcs_init);
+module_exit(socfpga_fcs_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Altera SoCFPGA FCS SDOS encrypt/decrypt driver");
+MODULE_AUTHOR("Altera Corporation");
+MODULE_ALIAS("platform:stratix10-fcs");
diff --git a/include/linux/firmware/intel/socfpga-fcs.h b/include/linux/firmware/intel/socfpga-fcs.h
new file mode 100644
index 000000000000..e263d6ee8cc7
--- /dev/null
+++ b/include/linux/firmware/intel/socfpga-fcs.h
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 Altera Corporation
+ *
+ * SDOS-only subset of the SoCFPGA FCS (FPGA Crypto Service) interface,
+ * shared between the driver front-end (socfpga-fcs.c) and the command
+ * engine (socfpga-fcs-core.c).
+ */
+#ifndef SOCFPGA_FCS_H
+#define SOCFPGA_FCS_H
+
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+#include <linux/firmware/intel/stratix10-svc-client.h>
+
+#define SDOS_HEADER_SZ 40
+#define SDOS_HMAC_SZ 48
+#define SDOS_PLAINDATA_MIN_SZ 32
+#define SDOS_PLAINDATA_MAX_SZ 32672
+#define SDOS_DECRYPTED_MIN_SZ (SDOS_PLAINDATA_MIN_SZ + SDOS_HEADER_SZ)
+#define SDOS_DECRYPTED_MAX_SZ (SDOS_PLAINDATA_MAX_SZ + SDOS_HEADER_SZ)
+#define SDOS_ENCRYPTED_MIN_SZ (SDOS_PLAINDATA_MIN_SZ + SDOS_HEADER_SZ + SDOS_HMAC_SZ)
+#define SDOS_ENCRYPTED_MAX_SZ (SDOS_PLAINDATA_MAX_SZ + SDOS_HEADER_SZ + SDOS_HMAC_SZ)
+
+/* Platform definitions (also exposed via the "platform" sysfs attribute) */
+#define AGILEX5_PLAT 2
+
+#pragma pack(push, 1)
+struct fcs_cmd_context {
+ /* Error status variable address */
+ int *error_code_addr;
+ union {
+ struct {
+ uuid_t *suuid;
+ unsigned int *suuid_len;
+ } open_session;
+
+ struct {
+ uuid_t suuid;
+ } close_session;
+
+ struct {
+ uuid_t suuid;
+ u32 context_id;
+ char *rng;
+ u32 rng_len;
+ } rng;
+
+ struct {
+ uuid_t suuid;
+ u32 context_id;
+ u32 op_mode;
+ char *src;
+ u32 src_size;
+ char *dst;
+ u32 *dst_size;
+ u16 id;
+ u64 own;
+ int pad;
+ } sdos;
+ };
+};
+
+#pragma pack(pop)
+
+/**
+ * Private driver state for the SoCFPGA FCS that holds the SDM/ATF service
+ * channel, the shared command context and the lock that guards it, and the
+ * latest mailbox status/response.
+ */
+struct socfpga_fcs_priv {
+ /* Communication channel */
+ struct stratix10_svc_chan *chan;
+ u32 platform;
+ struct fcs_cmd_context k_ctx;
+ struct stratix10_svc_client client;
+ struct completion completion;
+ /*
+ * Serializes FCS command submission: guards the shared k_ctx and the
+ * single in-flight mailbox transaction (completion/status/resp) so only
+ * one SDM request is outstanding at a time. This is the lock taken by
+ * fcs_acquire_cmd_ctx() and dropped by fcs_release_cmd_ctx().
+ */
+ struct mutex lock;
+ int status;
+ u32 resp;
+ u32 session_id;
+ uuid_t uuid_id;
+ struct device *dev;
+ u32 atf_version[3];
+};
+
+enum fcs_command_code {
+ FCS_DEV_COMMAND_NONE = 0,
+ FCS_DEV_CRYPTO_OPEN_SESSION,
+ FCS_DEV_CRYPTO_CLOSE_SESSION,
+ FCS_DEV_SDOS_DATA_EXT,
+ FCS_DEV_ATF_VERSION,
+};
+
+/* Take the FCS lock and return the shared command context. */
+struct fcs_cmd_context *fcs_acquire_cmd_ctx(void);
+
+/* Release the FCS lock previously taken by fcs_acquire_cmd_ctx(). */
+void fcs_release_cmd_ctx(struct fcs_cmd_context *const k_ctx);
+
+/* Return the detected FCS platform identifier. */
+int fcs_get_platform(void);
+
+/* Allocate the FCS state and set up the service channel; read ATF version. */
+int fcs_init(struct device *dev);
+
+/* Close any open session and release the service channel. */
+void fcs_deinit(void);
+
+/* Release the service channel and clear the FCS state. */
+void fcs_cleanup(void);
+
+/* Request the SDM to open a crypto service session. */
+int fcs_session_open(struct fcs_cmd_context *const k_ctx);
+
+/* Request the SDM to close a previously opened session. */
+int fcs_session_close(struct fcs_cmd_context *const k_ctx);
+
+/* Return the cached Arm Trusted Firmware build version. */
+void fcs_get_atf_version(u32 *version);
+
+/* Perform an SDOS (Secure Data Object Service) encrypt/decrypt operation. */
+int fcs_sdos_crypt(struct fcs_cmd_context *const k_ctx);
+
+#endif /* SOCFPGA_FCS_H */
--
2.43.7
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-07-01 7:40 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-01 7:39 [PATCH v1 0/2] Add Altera SoCFPGA Crypto Service (FCS) driver hang.suan.wang
2026-07-01 7:39 ` [PATCH v1 1/2] firmware: stratix10-svc: add FCS crypto-service commands for Agilex 5 hang.suan.wang
2026-07-01 7:39 ` [PATCH v1 2/2] firmware: socfpga-fcs: add Altera SoCFPGA FCS driver with SDOS hang.suan.wang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox