* [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
` (2 more replies)
0 siblings, 3 replies; 7+ 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] 7+ 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 2026-07-06 12:33 ` [PATCH v1 0/2] Add Altera SoCFPGA Crypto Service (FCS) driver Dinh Nguyen 2 siblings, 0 replies; 7+ 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 [flat|nested] 7+ 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 2026-07-07 18:41 ` Dinh Nguyen 2026-07-06 12:33 ` [PATCH v1 0/2] Add Altera SoCFPGA Crypto Service (FCS) driver Dinh Nguyen 2 siblings, 1 reply; 7+ 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 [flat|nested] 7+ messages in thread
* Re: [PATCH v1 2/2] firmware: socfpga-fcs: add Altera SoCFPGA FCS driver with SDOS 2026-07-01 7:39 ` [PATCH v1 2/2] firmware: socfpga-fcs: add Altera SoCFPGA FCS driver with SDOS hang.suan.wang @ 2026-07-07 18:41 ` Dinh Nguyen 2026-07-09 13:24 ` Hang Suan Wang 0 siblings, 1 reply; 7+ messages in thread From: Dinh Nguyen @ 2026-07-07 18:41 UTC (permalink / raw) To: hang.suan.wang, 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 On 7/1/26 02:39, hang.suan.wang@altera.com wrote: > 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; 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()? > + 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); 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. > + > + 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"); So if a timeout happens, > + > + 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? > + 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; Don't need this goto. > + } > + > +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) { You've already copied k_ctx to a local ctx, you should consistently use it ctx. > + 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; 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. > + > + 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; temp is only used here, just declare as a (uint8_t *) to start with. Dinh ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v1 2/2] firmware: socfpga-fcs: add Altera SoCFPGA FCS driver with SDOS 2026-07-07 18:41 ` Dinh Nguyen @ 2026-07-09 13:24 ` Hang Suan Wang 0 siblings, 0 replies; 7+ messages in thread From: Hang Suan Wang @ 2026-07-09 13:24 UTC (permalink / raw) To: Dinh Nguyen, linux-kernel, Michael S . Tsirkin, Huacai Chen, Florian Fainelli, Chen-Yu Tsai Cc: hang.suan.wang, muhammad.nazim.amirul.nazle.asmade, tze.yee.ng, chee.nouk.phoon, genevieve.chan thank you for review. I had address all the comment. On 8/7/2026 2:41 am, Dinh Nguyen wrote: > > > On 7/1/26 02:39, hang.suan.wang@altera.com wrote: >> 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; > > 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. > >> + 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); > > 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. > >> + >> + 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"); > > So if a timeout happens, >> + >> + 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; >> + 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; > > Don't need this goto. > right, will remove in v2 >> + } >> + >> +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) { > > 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() > >> + 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; > > 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. > >> + >> + 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; > > 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. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v1 0/2] Add Altera SoCFPGA Crypto Service (FCS) driver 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 @ 2026-07-06 12:33 ` Dinh Nguyen 2026-07-09 7:45 ` Hang Suan Wang 2 siblings, 1 reply; 7+ messages in thread From: Dinh Nguyen @ 2026-07-06 12:33 UTC (permalink / raw) To: hang.suan.wang, 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 On 7/1/26 02:39, hang.suan.wang@altera.com wrote: > 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). > 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 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v1 0/2] Add Altera SoCFPGA Crypto Service (FCS) driver 2026-07-06 12:33 ` [PATCH v1 0/2] Add Altera SoCFPGA Crypto Service (FCS) driver Dinh Nguyen @ 2026-07-09 7:45 ` Hang Suan Wang 0 siblings, 0 replies; 7+ messages in thread From: Hang Suan Wang @ 2026-07-09 7:45 UTC (permalink / raw) To: Dinh Nguyen, linux-kernel, Michael S . Tsirkin, Huacai Chen, Florian Fainelli, Chen-Yu Tsai Cc: hang.suan.wang, muhammad.nazim.amirul.nazle.asmade, tze.yee.ng, chee.nouk.phoon, genevieve.chan On 6/7/2026 8:33 pm, Dinh Nguyen wrote: > > > On 7/1/26 02:39, hang.suan.wang@altera.com wrote: >> 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). >> > > 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. ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-07-09 13:24 UTC | newest] Thread overview: 7+ 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 2026-07-07 18:41 ` Dinh Nguyen 2026-07-09 13:24 ` Hang Suan Wang 2026-07-06 12:33 ` [PATCH v1 0/2] Add Altera SoCFPGA Crypto Service (FCS) driver Dinh Nguyen 2026-07-09 7:45 ` 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